This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 209780
Collapse All | Expand All

(-)a/openide.util.lookup/apichanges.xml (+24 lines)
Lines 49-54 Link Here
49
    <apidef name="lookup">Lookup API</apidef>
49
    <apidef name="lookup">Lookup API</apidef>
50
</apidefs>
50
</apidefs>
51
<changes>
51
<changes>
52
    <change id="named.service.definition">
53
        <api name="lookup"/>
54
        <summary>Easy and robust way to register named services</summary>
55
        <version major="8" minor="14"/>
56
        <date day="27" month="3" year="2012"/>
57
        <author login="jtulach"/>
58
        <compatibility
59
            addition="yes"
60
            binary="compatible" deletion="no" deprecation="no"
61
            modification="no" semantic="compatible" source="compatible"
62
        />
63
        <description>
64
            <p>
65
                Meta annotation 
66
                <a href="@TOP@org/openide/util/lookup/NamedServiceDefinition.html">NamedServiceDefinition</a> 
67
                for those who define their own annotations that register
68
                something into 
69
                <a href="@TOP@org/openide/util/lookup/Lookups.html#forPath(java.lang.String)">
70
                Lookups.forPath</a> registration area.
71
            </p>
72
        </description>
73
        <class package="org.openide.util.lookup" name="NamedServiceDefinition"/>
74
        <issue number="209780"/>
75
    </change>
52
    <change id="lazy.proxy.lookup">
76
    <change id="lazy.proxy.lookup">
53
        <api name="lookup"/>
77
        <api name="lookup"/>
54
        <summary><code>ProxyLookup</code> computes results lazily</summary>
78
        <summary><code>ProxyLookup</code> computes results lazily</summary>
(-)a/openide.util.lookup/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util.lookup
2
OpenIDE-Module: org.openide.util.lookup
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
4
OpenIDE-Module-Specification-Version: 8.13
4
OpenIDE-Module-Specification-Version: 8.14
5
5
(-)a/openide.util.lookup/src/META-INF/services/javax.annotation.processing.Processor (+1 lines)
Line 1 Link Here
1
org.netbeans.modules.openide.util.ServiceProviderProcessor
1
org.netbeans.modules.openide.util.ServiceProviderProcessor
2
org.netbeans.modules.openide.util.NamedServiceProcessor
(-)e5f32a52bff2 (+288 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.openide.util;
43
44
import java.io.BufferedReader;
45
import java.io.IOException;
46
import java.io.InputStream;
47
import java.io.InputStreamReader;
48
import java.lang.annotation.Annotation;
49
import java.net.URL;
50
import java.util.ArrayList;
51
import java.util.Enumeration;
52
import java.util.HashSet;
53
import java.util.List;
54
import java.util.Set;
55
import java.util.regex.Matcher;
56
import java.util.regex.Pattern;
57
import javax.annotation.processing.RoundEnvironment;
58
import javax.annotation.processing.SupportedSourceVersion;
59
import javax.lang.model.SourceVersion;
60
import javax.lang.model.element.Element;
61
import javax.lang.model.element.ElementKind;
62
import javax.lang.model.element.ExecutableElement;
63
import javax.lang.model.element.TypeElement;
64
import javax.lang.model.type.ArrayType;
65
import javax.lang.model.type.TypeKind;
66
import javax.lang.model.type.TypeMirror;
67
import javax.tools.Diagnostic;
68
import org.openide.util.lookup.NamedServiceDefinition;
69
import org.openide.util.lookup.implspi.AbstractServiceProviderProcessor;
70
71
@SupportedSourceVersion(SourceVersion.RELEASE_6)
72
public final class NamedServiceProcessor extends AbstractServiceProviderProcessor {
73
    private static final String PATH = "META-INF/namedservices.index"; // NOI18N
74
    private static Pattern reference = Pattern.compile("@([^/]+)\\(\\)"); // NOI18N
75
76
    @Override
77
    public Set<String> getSupportedAnnotationTypes() {
78
        Set<String> all = new HashSet<String>();
79
        all.add(NamedServiceDefinition.class.getName());
80
        searchAnnotations(all, true);
81
        return all;
82
    }
83
    
84
85
    @Override
86
    protected boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
87
        for (Element e : roundEnv.getElementsAnnotatedWith(NamedServiceDefinition.class)) {
88
            NamedServiceDefinition nsd = e.getAnnotation(NamedServiceDefinition.class);
89
            Matcher m = reference.matcher(nsd.path());
90
            while (m.find()) {
91
                final ExecutableElement attr = findAttribute(e, m.group(1));
92
                if (attr == null) {
93
                    processingEnv.getMessager().printMessage(
94
                        Diagnostic.Kind.ERROR, 
95
                        "The path attribute contains '" + 
96
                        m.group(0) + 
97
                        "' reference, but there is no attribute named '" + 
98
                        m.group(1) + "'", 
99
                        e
100
                    );
101
                    continue;
102
                }
103
                final TypeMirror toCheck = attr.getReturnType();
104
                TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType(); // NOI18N
105
                if (processingEnv.getTypeUtils().isAssignable(toCheck, stringType)) {
106
                    continue;
107
                }
108
                ArrayType arrStringType = processingEnv.getTypeUtils().getArrayType(stringType);
109
                if (processingEnv.getTypeUtils().isAssignable(toCheck, arrStringType)) {
110
                    continue;
111
                }
112
                processingEnv.getMessager().printMessage(
113
                    Diagnostic.Kind.ERROR,
114
                        "The path attribute contains '" + m.group(0) + 
115
                        "' reference, but attribute '" + m.group(1) + 
116
                        "' does not return String or String[]", 
117
                        e
118
                );
119
            }
120
            if (!nsd.position().equals("-")) {
121
                ExecutableElement attr = findAttribute(e, nsd.position());
122
                if (attr == null) {
123
                    processingEnv.getMessager().printMessage(
124
                        Diagnostic.Kind.ERROR,
125
                        "The position attribute contains '"
126
                        + nsd.position() + "' but no such attribute found.",
127
                        e
128
                    );
129
                } else {
130
                    if (!processingEnv.getTypeUtils().isSameType(
131
                        processingEnv.getTypeUtils().getPrimitiveType(TypeKind.INT),
132
                        attr.getReturnType()
133
                    )) {
134
                        processingEnv.getMessager().printMessage(
135
                            Diagnostic.Kind.ERROR,
136
                            "The position attribute contains '"
137
                            + nsd.position() + "' but the attribute does not return int.",
138
                            e
139
                        );
140
                    }
141
                }
142
            }
143
            register(e, PATH);
144
        }
145
        
146
        Set<String> index = new HashSet<String>();
147
        searchAnnotations(index, false);
148
        for (String className : index) {
149
            Class<? extends Annotation> c;
150
            try {
151
                c = Class.forName(className).asSubclass(Annotation.class);
152
            } catch (ClassNotFoundException ex) {
153
                throw new IllegalStateException(ex);
154
            }
155
            for (Element e : roundEnv.getElementsAnnotatedWith(c)) {
156
                Annotation a = e.getAnnotation(c);
157
                NamedServiceDefinition nsd = c.getAnnotation(NamedServiceDefinition.class);
158
                int cnt = 0;
159
                for (Class<?> type : nsd.serviceType()) {
160
                    TypeMirror typeMirror = asType(type);
161
                    if (processingEnv.getTypeUtils().isSubtype(e.asType(), typeMirror)) {
162
                        cnt++;
163
                        for (String p : findPath(nsd.path(), a)) {
164
                            register(
165
                                e, c, typeMirror, p,
166
                                findPosition(nsd.position(), a)
167
                            );
168
                        }
169
                    }
170
                }
171
                if (cnt == 0) {
172
                    StringBuilder sb = new StringBuilder();
173
                    String prefix = "The type does not ";
174
                    for (Class<?> type : nsd.serviceType()) {
175
                        sb.append(prefix);
176
                        if (type.isInterface()) {
177
                            sb.append("implement ").append(type.getCanonicalName());
178
                        } else {
179
                            sb.append("subclass ").append(type.getCanonicalName());
180
                        }
181
                        prefix = ", neither it does ";
182
                    }
183
                    sb.append('.');
184
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, sb.toString(), e);
185
                }
186
            }
187
        }
188
        return true;
189
    }
190
191
    private TypeMirror asType(Class<?> type) {
192
        return processingEnv.getElementUtils().getTypeElement(type.getName()).asType();
193
    }
194
195
    private List<String> findPath(String path, Annotation a) {
196
        List<String> arr = new ArrayList<String>();
197
        arr.add(path);
198
        RESTART: for (;;) {
199
            for (int i = 0; i < arr.size(); i++) {
200
                Matcher m = reference.matcher(arr.get(i));
201
                if (m.find()) {
202
                    String methodName = m.group(1);
203
                    Object obj;
204
                    try {
205
                        obj = a.getClass().getMethod(methodName).invoke(a);
206
                    } catch (Exception ex) {
207
                        throw new IllegalStateException(methodName, ex);
208
                    }
209
                    if (obj instanceof String) {
210
                        arr.set(i, substitute(path, m, (String)obj));
211
                    } else if (obj instanceof String[]) {
212
                        String[] subs = (String[])obj;
213
                        arr.set(i, substitute(path, m, subs[0]));
214
                        for (int j = 1; j < subs.length; j++) {
215
                            arr.add(substitute(path, m, subs[j]));
216
                        }
217
                    } else {
218
                        throw new IllegalStateException("Wrong return value " + obj); // NOI18N
219
                    }
220
                    continue RESTART;
221
                }
222
            }
223
            break RESTART;
224
        }
225
        return arr;
226
    }
227
    
228
    private Integer findPosition(String posDefinition, Annotation a) {
229
        if (posDefinition.length() == 1 && posDefinition.charAt(0) == '-') {
230
            try {
231
                return (Integer)a.getClass().getMethod("position").invoke(a);
232
            } catch (NoSuchMethodException ex) {
233
                return Integer.MAX_VALUE;
234
            } catch (Exception ex) {
235
                throw new IllegalStateException(ex);
236
            }
237
        }
238
        try {
239
            return (Integer)a.getClass().getMethod(posDefinition).invoke(a);
240
        } catch (Exception ex) {
241
            throw new IllegalStateException(ex);
242
        }
243
    }
244
    
245
    private static void searchAnnotations(Set<String> found, boolean canonicalName) {
246
247
        try {
248
            Enumeration<URL> en = NamedServiceProcessor.class.getClassLoader().getResources(PATH);
249
            while (en.hasMoreElements()) {
250
                URL url = en.nextElement();
251
                InputStream is = url.openStream();
252
                BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); // NOI18N
253
254
                // XXX consider using ServiceLoaderLine instead
255
                while (true) {
256
                    String line = reader.readLine();
257
258
                    if (line == null) {
259
                        break;
260
                    }
261
                    line = line.trim();
262
                    if (line.startsWith("#")) { // NOI18N
263
                        continue;
264
                    }
265
                    if (canonicalName) {
266
                        line = line.replace('$', '.');
267
                    }
268
                    found.add(line);
269
                }
270
            }
271
        } catch (IOException ex) {
272
            throw new IllegalStateException(ex);
273
        }
274
    }
275
276
    private static String substitute(String path, Matcher m, String obj) {
277
        return path.substring(0, m.start(0)) + obj + path.substring(m.end(0));
278
    }
279
280
    private static ExecutableElement findAttribute(Element e, String attrName) {
281
        for (Element attr : e.getEnclosedElements()) {
282
            if (attr.getKind() == ElementKind.METHOD && attr.getSimpleName().contentEquals(attrName)) {
283
                return (ExecutableElement)attr;
284
            }
285
        }
286
        return null;
287
    }
288
}
(-)a/openide.util.lookup/src/org/openide/util/lookup/Lookups.java (+1 lines)
Lines 221-226 Link Here
221
     * @param path the path identifying the lookup, e.g. <code>Servers/J2EEWrapper</code>
221
     * @param path the path identifying the lookup, e.g. <code>Servers/J2EEWrapper</code>
222
     * @return lookup associated with this path
222
     * @return lookup associated with this path
223
     * @since 7.9
223
     * @since 7.9
224
     * @see NamedServiceDefinition
224
     */
225
     */
225
    public static Lookup forPath(String path) {
226
    public static Lookup forPath(String path) {
226
        if (!path.endsWith("/")) {
227
        if (!path.endsWith("/")) {
(-)e5f32a52bff2 (+98 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup;
43
44
import java.lang.annotation.ElementType;
45
import java.lang.annotation.Retention;
46
import java.lang.annotation.RetentionPolicy;
47
import java.lang.annotation.Target;
48
49
/** Annotation to simplify creation and add robustness to usage of
50
 * various named service registration annotations. For example
51
 * the {@code @}<a href="@org-openide-util@/org/openide/util/URLStreamHandlerRegistration.html">
52
 * URLStreamHandlerRegistration</a> annotation uses the {@link NamedServiceDefinition}
53
 * as: <pre>
54
 * {@code @NamedServiceDefinition(path="URLStreamHandler/@protocol()", serviceType=URLStreamHandler.class)}
55
 * </pre>
56
 * The above instructs the annotation processor that handles {@link NamedServiceDefinition}s
57
 * to verify the annotated type is subclass of <code>URLStreamHandler</code> and
58
 * if so, register it into <code>URLStreamHandler/@protocol</code> where the
59
 * value of <code>@protocol()</code> is replaced by the value of annotation's
60
 * <a href="@org-openide-util@/org/openide/util/URLStreamHandlerRegistration.html#protocol()">
61
 * protocol attribute</a>. The registration can later be found by using
62
 * {@link Lookups#forPath(java.lang.String) Lookups.forPath("URLStreamHandler/ftp")}
63
 * (in case the protocol was ftp).
64
 *
65
 * @author Jaroslav Tulach <jtulach@netbeans.org>
66
 * @since 8.14
67
 * @see ServiceProvider#path() 
68
 */
69
@Retention(RetentionPolicy.RUNTIME)
70
@Target(ElementType.ANNOTATION_TYPE)
71
public @interface NamedServiceDefinition {
72
    /** Type, or array of types that the registered type
73
     * has to implement. The annotated type needs to register at least
74
     * one of the enumerated classes.
75
     */
76
    public Class<?>[] serviceType();
77
    /** Path to register the annotation to, so it can later be found by
78
     * using {@link Lookups#forPath(java.lang.String) Lookups.forPath(theSamePath)}.
79
     * The path may reference attributes of the annotated annotation by prefixing
80
     * them with {@code @}. To reuse attribute named <code>location</code> one
81
     * can for example use <code>"how/to/get/to/@location()s/please"</code>
82
     * These attributes must be of type <code>String</code>
83
     * or array of <code>String</code>s (then one registration is performed
84
     * per each string in the array).
85
     */
86
    public String path();
87
    /** Name of attribute that specifies position. By default the system tries
88
     * to find <code>int position()</code> attribute in the defined annotation
89
     * and use it to specify the order of registrations. In case a different
90
     * attribute should be used to specify the position, one can be provide its
91
     * name by specifying non-default here. Should the position be ignored,
92
     * specify empty string.
93
     * 
94
     * @param name of attribute in the annotated annotation to use for defining
95
     *   position of the registration. The attribute should return int value.
96
     */
97
    public String position() default "-";
98
}
(-)a/openide.util.lookup/src/org/openide/util/lookup/ServiceProvider.java (+7 lines)
Lines 102-107 Link Here
102
     * For example, <code>Projects/sometype/Nodes</code> could be used.
102
     * For example, <code>Projects/sometype/Nodes</code> could be used.
103
     * This style of registration would be recognized by {@link Lookups#forPath}
103
     * This style of registration would be recognized by {@link Lookups#forPath}
104
     * rather than {@link Lookup#getDefault}.
104
     * rather than {@link Lookup#getDefault}.
105
     * <p>
106
     * The providers of a registration slot are advised to have
107
     * a look at {@link NamedServiceDefinition} and consider using it since 
108
     * version 8.14.
109
     * The {@link NamedServiceDefinition} offers various benefits over 
110
     * plain {@link #path()} usage including type checking and lower 
111
     * possibility of typos.
105
     */
112
     */
106
    String path() default "";
113
    String path() default "";
107
114
(-)a/openide.util.lookup/src/org/openide/util/lookup/implspi/AbstractServiceProviderProcessor.java (-11 / +29 lines)
Lines 97-105 Link Here
97
            // OK subclass
97
            // OK subclass
98
            return;
98
            return;
99
        }
99
        }
100
        if (getClass().getName().equals("org.netbeans.modules.openide.util.NamedServiceProcessor")) { // NOI18N
101
            // OK subclass
102
            return;
103
        }
100
        throw new IllegalStateException();
104
        throw new IllegalStateException();
101
    }
105
    }
102
106
    
103
    public @Override final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
107
    public @Override final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
104
        if (roundEnv.errorRaised()) {
108
        if (roundEnv.errorRaised()) {
105
            return false;
109
            return false;
Lines 135-147 Link Here
135
     * @param supersedes possibly empty list of implementation to supersede
139
     * @param supersedes possibly empty list of implementation to supersede
136
     * @since 8.8
140
     * @since 8.8
137
     */
141
     */
138
    protected final void register(Element el, Class<? extends Annotation> annotation,
142
    protected final void register(
139
            TypeMirror type, String path, int position, String[] supersedes) {
143
        Element el, Class<? extends Annotation> annotation,
144
        TypeMirror type, String path, int position, String... supersedes
145
    ) {
140
        if (el.getKind() != ElementKind.CLASS) {
146
        if (el.getKind() != ElementKind.CLASS) {
141
            processingEnv.getMessager().printMessage(Kind.ERROR, annotation.getName() + " is not applicable to a " + el.getKind(), el);
147
            processingEnv.getMessager().printMessage(Kind.ERROR, annotation.getName() + " is not applicable to a " + el.getKind(), el);
142
            return;
148
            return;
143
        }
149
        }
144
        TypeElement clazz = (TypeElement) el;
150
        TypeElement clazz = (TypeElement) el;
151
        String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
152
        String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
153
        if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
154
            AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
155
            processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
156
                    clazz, ann, findAnnotationValue(ann, "service"));
157
            return;
158
        }
159
        String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
145
        Boolean verify = verifiedClasses.get(clazz);
160
        Boolean verify = verifiedClasses.get(clazz);
146
        if (verify == null) {
161
        if (verify == null) {
147
            verify = verifyServiceProviderSignature(clazz, annotation);
162
            verify = verifyServiceProviderSignature(clazz, annotation);
Lines 150-168 Link Here
150
        if (!verify) {
165
        if (!verify) {
151
            return;
166
            return;
152
        }
167
        }
168
        registerImpl(clazz, impl, rsrc, position, supersedes);
169
    }
170
    
171
    protected final void register(Element el, String path) {
172
        TypeElement clazz = (TypeElement)el;
153
        String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
173
        String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
154
        String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
174
        registerImpl(clazz, impl, path, Integer.MAX_VALUE);
155
        if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
175
    }
156
            AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
176
    
157
            processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
177
    private void registerImpl(
158
                    clazz, ann, findAnnotationValue(ann, "service"));
178
        TypeElement clazz, String impl, String rsrc, int position, String... supersedes
159
            return;
179
    ) {
160
        }
161
        /*
180
        /*
162
        processingEnv.getMessager().printMessage(Kind.NOTE,
181
        processingEnv.getMessager().printMessage(Kind.NOTE,
163
                impl + " to be registered as a " + xface + (path.length() > 0 ? " under " + path : ""));
182
                impl + " to be registered as a " + xface + (path.length() > 0 ? " under " + path : ""));
164
        */
183
        */
165
        String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
166
        Filer filer = processingEnv.getFiler();
184
        Filer filer = processingEnv.getFiler();
167
        {
185
        {
168
            Map<String,List<Element>> originatingElements = originatingElementsByProcessor.get(filer);
186
            Map<String,List<Element>> originatingElements = originatingElementsByProcessor.get(filer);
(-)e5f32a52bff2 (+221 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup;
43
44
import java.io.ByteArrayOutputStream;
45
import java.lang.annotation.Retention;
46
import java.lang.annotation.RetentionPolicy;
47
import java.net.URL;
48
import java.net.URLClassLoader;
49
import java.util.concurrent.Callable;
50
import org.netbeans.junit.NbTestCase;
51
import org.openide.util.Lookup;
52
import org.openide.util.test.AnnotationProcessorTestUtils;
53
54
/**
55
 *
56
 * @author Jaroslav Tulach <jtulach@netbeans.org>
57
 */
58
public class NamedServiceDefinitionTest extends NbTestCase {
59
    
60
    public NamedServiceDefinitionTest(String n) {
61
        super(n);
62
    }
63
64
    @Override
65
    protected void setUp() throws Exception {
66
        clearWorkDir();
67
    }
68
    
69
    public void testNamedDefinition() throws Exception {
70
        System.setProperty("executed", "false");
71
        String content = "import org.openide.util.lookup.NamedServiceDefinitionTest.RunTestReg;\n"
72
            + "@RunTestReg(position=10,when=\"now\")\n"
73
            + "public class Test implements Runnable {\n"
74
            + "  public void run() { System.setProperty(\"executed\", \"true\"); }\n"
75
            + "}\n";
76
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
77
        assertTrue("Compiles OK",
78
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, System.err)
79
            );
80
        
81
        URLClassLoader l = new URLClassLoader(new URL[] { getWorkDir().toURI().toURL() }, NamedServiceDefinitionTest.class.getClassLoader());
82
        Lookup lkp = Lookups.metaInfServices(l, "META-INF/namedservices/runtest/now/below/");
83
        for (Runnable r : lkp.lookupAll(Runnable.class)) {
84
            r.run();
85
        }
86
        assertEquals("Our runnable was executed", "true", System.getProperty("executed"));
87
    }
88
    
89
    public void testNamedDefinitionWithArray() throws Exception {
90
        System.setProperty("executed", "false");
91
        String content = "import org.openide.util.lookup.NamedServiceDefinitionTest.RunTestArray;\n"
92
            + "@RunTestArray(position=10,array={\"now\", \"then\" })\n"
93
            + "public class Test implements Runnable {\n"
94
            + "  public void run() { System.setProperty(\"executed\", \"true\"); }\n"
95
            + "}\n";
96
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
97
        assertTrue("Compiles OK",
98
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, System.err)
99
            );
100
        
101
        URLClassLoader l = new URLClassLoader(new URL[] { getWorkDir().toURI().toURL() }, NamedServiceDefinitionTest.class.getClassLoader());
102
        Lookup lkp = Lookups.metaInfServices(l, "META-INF/namedservices/runtest/now/");
103
        for (Runnable r : lkp.lookupAll(Runnable.class)) {
104
            r.run();
105
        }
106
        assertEquals("Our runnable was executed", "true", System.getProperty("executed"));
107
        System.setProperty("executed", "false");
108
        Lookup lkp2 = Lookups.metaInfServices(l, "META-INF/namedservices/runtest/then/");
109
        for (Runnable r : lkp2.lookupAll(Runnable.class)) {
110
            r.run();
111
        }
112
        assertEquals("Our runnable was executed again", "true", System.getProperty("executed"));
113
    }
114
    
115
    public void testDoesNotImplementInterfaces() throws Exception {
116
        System.setProperty("executed", "false");
117
        String content = "import org.openide.util.lookup.NamedServiceDefinitionTest.RunTestReg;\n"
118
            + "@RunTestReg(position=10,when=\"now\")\n"
119
            + "public class Test {\n"
120
            + "  public void run() { System.setProperty(\"executed\", \"true\"); }\n"
121
            + "}\n";
122
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
123
        ByteArrayOutputStream os = new ByteArrayOutputStream();
124
        assertFalse("Compilation fails",
125
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
126
        );
127
        String err = new String(os.toByteArray(), "UTF-8");
128
        if (err.indexOf("java.lang.Runnable") == -1) {
129
            fail("The error messages should say something about interface Runnable\n" + err);
130
        }
131
        if (err.indexOf("Callable") == -1) {
132
            fail("The error messages should say something about interface Callable\n" + err);
133
        }
134
    }
135
    
136
    public void testMissingPathAttribute() throws Exception {
137
        String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
138
            + "@NamedServiceDefinition(path=\"runtest/@when()/below\",serviceType=Runnable.class)\n"
139
            + "public @interface Test {\n"
140
            + "  String noWhenAttributeHere();"
141
            + "}\n";
142
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
143
        ByteArrayOutputStream os = new ByteArrayOutputStream();
144
        assertFalse("Compilation fails",
145
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
146
        );
147
        String err = new String(os.toByteArray(), "UTF-8");
148
        if (err.indexOf("@when()") == -1) {
149
            fail("The error messages should say something about missing @when\n" + err);
150
        }
151
    }
152
153
    public void testNonStringPathAttribute() throws Exception {
154
        String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
155
            + "@NamedServiceDefinition(path=\"runtest/@when()/below\",serviceType=Runnable.class)\n"
156
            + "public @interface Test {\n"
157
            + "  int when();"
158
            + "}\n";
159
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
160
        ByteArrayOutputStream os = new ByteArrayOutputStream();
161
        assertFalse("Compilation fails",
162
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
163
        );
164
        String err = new String(os.toByteArray(), "UTF-8");
165
        if (err.indexOf("@when()") == -1) {
166
            fail("The error messages should say something about missing @when\n" + err);
167
        }
168
    }
169
170
    public void testNonExistentPositionAttribute() throws Exception {
171
        String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
172
            + "@NamedServiceDefinition(path=\"fixed\",serviceType=Runnable.class,position=\"where\")\n"
173
            + "public @interface Test {\n"
174
            + "  int when();"
175
            + "}\n";
176
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
177
        ByteArrayOutputStream os = new ByteArrayOutputStream();
178
        assertFalse("Compilation fails",
179
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
180
        );
181
        String err = new String(os.toByteArray(), "UTF-8");
182
        if (err.indexOf("where") == -1) {
183
            fail("The error messages should say something about missing where\n" + err);
184
        }
185
    }
186
    public void testNonIntegerPositionAttribute() throws Exception {
187
        String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
188
            + "@NamedServiceDefinition(path=\"fixed\",serviceType=Runnable.class,position=\"where\")\n"
189
            + "public @interface Test {\n"
190
            + "  Class<?> where();"
191
            + "}\n";
192
        AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
193
        ByteArrayOutputStream os = new ByteArrayOutputStream();
194
        assertFalse("Compilation fails",
195
            AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
196
        );
197
        String err = new String(os.toByteArray(), "UTF-8");
198
        if (err.indexOf("where") == -1) {
199
            fail("The error messages should say something about missing where\n" + err);
200
        }
201
    }
202
    
203
    @NamedServiceDefinition(
204
        path="runtest/@when()/below",
205
        serviceType={ Runnable.class, Callable.class }
206
    )
207
    @Retention(RetentionPolicy.SOURCE)
208
    public static @interface RunTestReg {
209
        public int position();
210
        public String when();
211
    }
212
    @NamedServiceDefinition(
213
        path="runtest/@array()",
214
        serviceType={ Runnable.class, Callable.class }
215
    )
216
    @Retention(RetentionPolicy.SOURCE)
217
    public static @interface RunTestArray {
218
        public int position();
219
        public String[] array();
220
    }
221
}
(-)a/openide.util/nbproject/project.xml (-1 / +1 lines)
Lines 54-60 Link Here
54
                    <build-prerequisite/>
54
                    <build-prerequisite/>
55
                    <compile-dependency/>
55
                    <compile-dependency/>
56
                    <run-dependency>
56
                    <run-dependency>
57
                        <specification-version>8.8</specification-version>
57
                        <specification-version>8.14</specification-version>
58
                    </run-dependency>
58
                    </run-dependency>
59
                </dependency>
59
                </dependency>
60
            </module-dependencies>
60
            </module-dependencies>
(-)a/openide.util/src/org/netbeans/modules/openide/util/ProxyURLStreamHandlerFactory.java (-2 / +1 lines)
Lines 62-68 Link Here
62
 */
62
 */
63
@ServiceProvider(service=URLStreamHandlerFactory.class)
63
@ServiceProvider(service=URLStreamHandlerFactory.class)
64
public final class ProxyURLStreamHandlerFactory implements URLStreamHandlerFactory {
64
public final class ProxyURLStreamHandlerFactory implements URLStreamHandlerFactory {
65
66
    /** prevents GC only */
65
    /** prevents GC only */
67
    private final Map<String, Lookup.Result<URLStreamHandler>> results = new HashMap<String, Lookup.Result<URLStreamHandler>>();
66
    private final Map<String, Lookup.Result<URLStreamHandler>> results = new HashMap<String, Lookup.Result<URLStreamHandler>>();
68
    private final Map<String, URLStreamHandler> handlers = new HashMap<String, URLStreamHandler>();
67
    private final Map<String, URLStreamHandler> handlers = new HashMap<String, URLStreamHandler>();
Lines 74-80 Link Here
74
            return null;
73
            return null;
75
        }
74
        }
76
        if (!results.containsKey(protocol)) {
75
        if (!results.containsKey(protocol)) {
77
            final Lookup.Result<URLStreamHandler> result = Lookups.forPath(URLStreamHandlerRegistrationProcessor.REGISTRATION_PREFIX + protocol).lookupResult(URLStreamHandler.class);
76
            final Lookup.Result<URLStreamHandler> result = Lookups.forPath("URLStreamHandler/" + protocol).lookupResult(URLStreamHandler.class);
78
            LookupListener listener = new LookupListener() {
77
            LookupListener listener = new LookupListener() {
79
                public @Override void resultChanged(LookupEvent ev) {
78
                public @Override void resultChanged(LookupEvent ev) {
80
                    synchronized (ProxyURLStreamHandlerFactory.this) {
79
                    synchronized (ProxyURLStreamHandlerFactory.this) {
(-)a/openide.util/src/org/netbeans/modules/openide/util/URLStreamHandlerRegistrationProcessor.java (-82 lines)
Removed Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2009 Sun Microsystems, Inc.
41
 */
42
43
package org.netbeans.modules.openide.util;
44
45
import java.net.URLStreamHandler;
46
import java.util.Collections;
47
import java.util.Set;
48
import javax.annotation.processing.Processor;
49
import javax.annotation.processing.RoundEnvironment;
50
import javax.annotation.processing.SupportedSourceVersion;
51
import javax.lang.model.SourceVersion;
52
import javax.lang.model.element.Element;
53
import javax.lang.model.element.TypeElement;
54
import javax.lang.model.type.TypeMirror;
55
import org.openide.util.URLStreamHandlerRegistration;
56
import org.openide.util.lookup.ServiceProvider;
57
import org.openide.util.lookup.implspi.AbstractServiceProviderProcessor;
58
59
@ServiceProvider(service=Processor.class)
60
@SupportedSourceVersion(SourceVersion.RELEASE_6)
61
public class URLStreamHandlerRegistrationProcessor extends AbstractServiceProviderProcessor {
62
63
    public @Override Set<String> getSupportedAnnotationTypes() {
64
        return Collections.singleton(URLStreamHandlerRegistration.class.getCanonicalName());
65
    }
66
67
    public static final String REGISTRATION_PREFIX = "URLStreamHandler/"; // NOI18N
68
69
    protected @Override boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
70
        for (Element el : roundEnv.getElementsAnnotatedWith(URLStreamHandlerRegistration.class)) {
71
            URLStreamHandlerRegistration r = el.getAnnotation(URLStreamHandlerRegistration.class);
72
            TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(
73
                    processingEnv.getElementUtils().getTypeElement(URLStreamHandler.class.getName()));
74
            for (String protocol : r.protocol()) {
75
                register(el, URLStreamHandlerRegistration.class, type,
76
                        REGISTRATION_PREFIX + protocol, r.position(), new String[0]);
77
            }
78
        }
79
        return true;
80
    }
81
82
}
(-)a/openide.util/src/org/openide/util/URLStreamHandlerRegistration.java (+2 lines)
Lines 49-54 Link Here
49
import java.net.URL;
49
import java.net.URL;
50
import java.net.URLStreamHandler;
50
import java.net.URLStreamHandler;
51
import java.net.URLStreamHandlerFactory;
51
import java.net.URLStreamHandlerFactory;
52
import org.openide.util.lookup.NamedServiceDefinition;
52
53
53
/**
54
/**
54
 * Replacement for {@link URLStreamHandlerFactory} within the NetBeans platform.
55
 * Replacement for {@link URLStreamHandlerFactory} within the NetBeans platform.
Lines 62-67 Link Here
62
 * from a unit test or otherwise without the module system active.
63
 * from a unit test or otherwise without the module system active.
63
 * @since org.openide.util 7.31
64
 * @since org.openide.util 7.31
64
 */
65
 */
66
@NamedServiceDefinition(path="URLStreamHandler/@protocol()", serviceType=URLStreamHandler.class)
65
@Retention(RetentionPolicy.SOURCE)
67
@Retention(RetentionPolicy.SOURCE)
66
@Target(ElementType.TYPE)
68
@Target(ElementType.TYPE)
67
public @interface URLStreamHandlerRegistration {
69
public @interface URLStreamHandlerRegistration {

Return to bug 209780