+ * @since 8.14
+ * @see ServiceProvider#path()
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface NamedServiceDefinition {
+ /** Type, or array of types that the registered type
+ * has to implement. The annotated type needs to register at least
+ * one of the enumerated classes.
+ */
+ public Class>[] serviceType();
+ /** Path to register the annotation to, so it can later be found by
+ * using {@link Lookups#forPath(java.lang.String) Lookups.forPath(theSamePath)}.
+ * The path may reference attributes of the annotated annotation by prefixing
+ * them with {@code @}. To reuse attribute named location
one
+ * can for example use "how/to/get/to/@location()s/please"
+ * These attributes must be of type String
+ * or array of String
s (then one registration is performed
+ * per each string in the array).
+ */
+ public String path();
+ /** Name of attribute that specifies position. By default the system tries
+ * to find int position()
attribute in the defined annotation
+ * and use it to specify the order of registrations. In case a different
+ * attribute should be used to specify the position, one can be provide its
+ * name by specifying non-default here. Should the position be ignored,
+ * specify empty string.
+ *
+ * @param name of attribute in the annotated annotation to use for defining
+ * position of the registration. The attribute should return int value.
+ */
+ public String position() default "-";
+}
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util.lookup/src/org/openide/util/lookup/ServiceProvider.java
--- a/openide.util.lookup/src/org/openide/util/lookup/ServiceProvider.java Thu Mar 22 17:15:03 2012 +0100
+++ b/openide.util.lookup/src/org/openide/util/lookup/ServiceProvider.java Fri Mar 23 15:13:30 2012 -0400
@@ -102,6 +102,13 @@
* For example, Projects/sometype/Nodes
could be used.
* This style of registration would be recognized by {@link Lookups#forPath}
* rather than {@link Lookup#getDefault}.
+ *
+ * The providers of a registration slot are advised to have
+ * a look at {@link NamedServiceDefinition} and consider using it since
+ * version 8.14.
+ * The {@link NamedServiceDefinition} offers various benefits over
+ * plain {@link #path()} usage including type checking and lower
+ * possibility of typos.
*/
String path() default "";
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util.lookup/src/org/openide/util/lookup/implspi/AbstractServiceProviderProcessor.java
--- a/openide.util.lookup/src/org/openide/util/lookup/implspi/AbstractServiceProviderProcessor.java Thu Mar 22 17:15:03 2012 +0100
+++ b/openide.util.lookup/src/org/openide/util/lookup/implspi/AbstractServiceProviderProcessor.java Fri Mar 23 15:13:30 2012 -0400
@@ -97,9 +97,13 @@
// OK subclass
return;
}
+ if (getClass().getName().equals("org.netbeans.modules.openide.util.NamedServiceProcessor")) { // NOI18N
+ // OK subclass
+ return;
+ }
throw new IllegalStateException();
}
-
+
public @Override final boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.errorRaised()) {
return false;
@@ -135,13 +139,24 @@
* @param supersedes possibly empty list of implementation to supersede
* @since 8.8
*/
- protected final void register(Element el, Class extends Annotation> annotation,
- TypeMirror type, String path, int position, String[] supersedes) {
+ protected final void register(
+ Element el, Class extends Annotation> annotation,
+ TypeMirror type, String path, int position, String... supersedes
+ ) {
if (el.getKind() != ElementKind.CLASS) {
processingEnv.getMessager().printMessage(Kind.ERROR, annotation.getName() + " is not applicable to a " + el.getKind(), el);
return;
}
TypeElement clazz = (TypeElement) el;
+ String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
+ String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
+ if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
+ AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
+ processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
+ clazz, ann, findAnnotationValue(ann, "service"));
+ return;
+ }
+ String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
Boolean verify = verifiedClasses.get(clazz);
if (verify == null) {
verify = verifyServiceProviderSignature(clazz, annotation);
@@ -150,19 +165,22 @@
if (!verify) {
return;
}
+ registerImpl(clazz, impl, rsrc, position, supersedes);
+ }
+
+ protected final void register(Element el, String path) {
+ TypeElement clazz = (TypeElement)el;
String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
- String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
- if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
- AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
- processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
- clazz, ann, findAnnotationValue(ann, "service"));
- return;
- }
+ registerImpl(clazz, impl, path, Integer.MAX_VALUE);
+ }
+
+ private void registerImpl(
+ TypeElement clazz, String impl, String rsrc, int position, String... supersedes
+ ) {
/*
processingEnv.getMessager().printMessage(Kind.NOTE,
impl + " to be registered as a " + xface + (path.length() > 0 ? " under " + path : ""));
*/
- String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
Filer filer = processingEnv.getFiler();
{
Map> originatingElements = originatingElementsByProcessor.get(filer);
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util.lookup/test/unit/src/org/openide/util/lookup/NamedServiceDefinitionTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openide.util.lookup/test/unit/src/org/openide/util/lookup/NamedServiceDefinitionTest.java Fri Mar 23 15:13:30 2012 -0400
@@ -0,0 +1,221 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.openide.util.lookup;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.concurrent.Callable;
+import org.netbeans.junit.NbTestCase;
+import org.openide.util.Lookup;
+import org.openide.util.test.AnnotationProcessorTestUtils;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class NamedServiceDefinitionTest extends NbTestCase {
+
+ public NamedServiceDefinitionTest(String n) {
+ super(n);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ clearWorkDir();
+ }
+
+ public void testNamedDefinition() throws Exception {
+ System.setProperty("executed", "false");
+ String content = "import org.openide.util.lookup.NamedServiceDefinitionTest.RunTestReg;\n"
+ + "@RunTestReg(position=10,when=\"now\")\n"
+ + "public class Test implements Runnable {\n"
+ + " public void run() { System.setProperty(\"executed\", \"true\"); }\n"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ assertTrue("Compiles OK",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, System.err)
+ );
+
+ URLClassLoader l = new URLClassLoader(new URL[] { getWorkDir().toURI().toURL() }, NamedServiceDefinitionTest.class.getClassLoader());
+ Lookup lkp = Lookups.metaInfServices(l, "META-INF/namedservices/runtest/now/below/");
+ for (Runnable r : lkp.lookupAll(Runnable.class)) {
+ r.run();
+ }
+ assertEquals("Our runnable was executed", "true", System.getProperty("executed"));
+ }
+
+ public void testNamedDefinitionWithArray() throws Exception {
+ System.setProperty("executed", "false");
+ String content = "import org.openide.util.lookup.NamedServiceDefinitionTest.RunTestArray;\n"
+ + "@RunTestArray(position=10,array={\"now\", \"then\" })\n"
+ + "public class Test implements Runnable {\n"
+ + " public void run() { System.setProperty(\"executed\", \"true\"); }\n"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ assertTrue("Compiles OK",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, System.err)
+ );
+
+ URLClassLoader l = new URLClassLoader(new URL[] { getWorkDir().toURI().toURL() }, NamedServiceDefinitionTest.class.getClassLoader());
+ Lookup lkp = Lookups.metaInfServices(l, "META-INF/namedservices/runtest/now/");
+ for (Runnable r : lkp.lookupAll(Runnable.class)) {
+ r.run();
+ }
+ assertEquals("Our runnable was executed", "true", System.getProperty("executed"));
+ System.setProperty("executed", "false");
+ Lookup lkp2 = Lookups.metaInfServices(l, "META-INF/namedservices/runtest/then/");
+ for (Runnable r : lkp2.lookupAll(Runnable.class)) {
+ r.run();
+ }
+ assertEquals("Our runnable was executed again", "true", System.getProperty("executed"));
+ }
+
+ public void testDoesNotImplementInterfaces() throws Exception {
+ System.setProperty("executed", "false");
+ String content = "import org.openide.util.lookup.NamedServiceDefinitionTest.RunTestReg;\n"
+ + "@RunTestReg(position=10,when=\"now\")\n"
+ + "public class Test {\n"
+ + " public void run() { System.setProperty(\"executed\", \"true\"); }\n"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ assertFalse("Compilation fails",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
+ );
+ String err = new String(os.toByteArray(), "UTF-8");
+ if (err.indexOf("java.lang.Runnable") == -1) {
+ fail("The error messages should say something about interface Runnable\n" + err);
+ }
+ if (err.indexOf("Callable") == -1) {
+ fail("The error messages should say something about interface Callable\n" + err);
+ }
+ }
+
+ public void testMissingPathAttribute() throws Exception {
+ String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
+ + "@NamedServiceDefinition(path=\"runtest/@when()/below\",serviceType=Runnable.class)\n"
+ + "public @interface Test {\n"
+ + " String noWhenAttributeHere();"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ assertFalse("Compilation fails",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
+ );
+ String err = new String(os.toByteArray(), "UTF-8");
+ if (err.indexOf("@when()") == -1) {
+ fail("The error messages should say something about missing @when\n" + err);
+ }
+ }
+
+ public void testNonStringPathAttribute() throws Exception {
+ String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
+ + "@NamedServiceDefinition(path=\"runtest/@when()/below\",serviceType=Runnable.class)\n"
+ + "public @interface Test {\n"
+ + " int when();"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ assertFalse("Compilation fails",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
+ );
+ String err = new String(os.toByteArray(), "UTF-8");
+ if (err.indexOf("@when()") == -1) {
+ fail("The error messages should say something about missing @when\n" + err);
+ }
+ }
+
+ public void testNonExistentPositionAttribute() throws Exception {
+ String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
+ + "@NamedServiceDefinition(path=\"fixed\",serviceType=Runnable.class,position=\"where\")\n"
+ + "public @interface Test {\n"
+ + " int when();"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ assertFalse("Compilation fails",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
+ );
+ String err = new String(os.toByteArray(), "UTF-8");
+ if (err.indexOf("where") == -1) {
+ fail("The error messages should say something about missing where\n" + err);
+ }
+ }
+ public void testNonIntegerPositionAttribute() throws Exception {
+ String content = "import org.openide.util.lookup.NamedServiceDefinition;\n"
+ + "@NamedServiceDefinition(path=\"fixed\",serviceType=Runnable.class,position=\"where\")\n"
+ + "public @interface Test {\n"
+ + " Class> where();"
+ + "}\n";
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.Test", content);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ assertFalse("Compilation fails",
+ AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, os)
+ );
+ String err = new String(os.toByteArray(), "UTF-8");
+ if (err.indexOf("where") == -1) {
+ fail("The error messages should say something about missing where\n" + err);
+ }
+ }
+
+ @NamedServiceDefinition(
+ path="runtest/@when()/below",
+ serviceType={ Runnable.class, Callable.class }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface RunTestReg {
+ public int position();
+ public String when();
+ }
+ @NamedServiceDefinition(
+ path="runtest/@array()",
+ serviceType={ Runnable.class, Callable.class }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface RunTestArray {
+ public int position();
+ public String[] array();
+ }
+}
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util/nbproject/project.xml
--- a/openide.util/nbproject/project.xml Thu Mar 22 17:15:03 2012 +0100
+++ b/openide.util/nbproject/project.xml Fri Mar 23 15:13:30 2012 -0400
@@ -54,7 +54,7 @@
- 8.8
+ 8.14
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util/src/org/netbeans/modules/openide/util/ProxyURLStreamHandlerFactory.java
--- a/openide.util/src/org/netbeans/modules/openide/util/ProxyURLStreamHandlerFactory.java Thu Mar 22 17:15:03 2012 +0100
+++ b/openide.util/src/org/netbeans/modules/openide/util/ProxyURLStreamHandlerFactory.java Fri Mar 23 15:13:30 2012 -0400
@@ -62,7 +62,6 @@
*/
@ServiceProvider(service=URLStreamHandlerFactory.class)
public final class ProxyURLStreamHandlerFactory implements URLStreamHandlerFactory {
-
/** prevents GC only */
private final Map> results = new HashMap>();
private final Map handlers = new HashMap();
@@ -74,7 +73,7 @@
return null;
}
if (!results.containsKey(protocol)) {
- final Lookup.Result result = Lookups.forPath(URLStreamHandlerRegistrationProcessor.REGISTRATION_PREFIX + protocol).lookupResult(URLStreamHandler.class);
+ final Lookup.Result result = Lookups.forPath("URLStreamHandler/" + protocol).lookupResult(URLStreamHandler.class);
LookupListener listener = new LookupListener() {
public @Override void resultChanged(LookupEvent ev) {
synchronized (ProxyURLStreamHandlerFactory.this) {
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util/src/org/netbeans/modules/openide/util/URLStreamHandlerRegistrationProcessor.java
--- a/openide.util/src/org/netbeans/modules/openide/util/URLStreamHandlerRegistrationProcessor.java Thu Mar 22 17:15:03 2012 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
- *
- * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
- * Other names may be trademarks of their respective owners.
- *
- * The contents of this file are subject to the terms of either the GNU
- * General Public License Version 2 only ("GPL") or the Common
- * Development and Distribution License("CDDL") (collectively, the
- * "License"). You may not use this file except in compliance with the
- * License. You can obtain a copy of the License at
- * http://www.netbeans.org/cddl-gplv2.html
- * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
- * specific language governing permissions and limitations under the
- * License. When distributing the software, include this License Header
- * Notice in each file and include the License file at
- * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the GPL Version 2 section of the License file that
- * accompanied this code. If applicable, add the following below the
- * License Header, with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * If you wish your version of this file to be governed by only the CDDL
- * or only the GPL Version 2, indicate your decision by adding
- * "[Contributor] elects to include this software in this distribution
- * under the [CDDL or GPL Version 2] license." If you do not indicate a
- * single choice of license, a recipient has the option to distribute
- * your version of this file under either the CDDL, the GPL Version 2 or
- * to extend the choice of license to its licensees as provided above.
- * However, if you add GPL Version 2 code and therefore, elected the GPL
- * Version 2 license, then the option applies only if the new code is
- * made subject to such option by the copyright holder.
- *
- * Contributor(s):
- *
- * Portions Copyrighted 2009 Sun Microsystems, Inc.
- */
-
-package org.netbeans.modules.openide.util;
-
-import java.net.URLStreamHandler;
-import java.util.Collections;
-import java.util.Set;
-import javax.annotation.processing.Processor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-import org.openide.util.URLStreamHandlerRegistration;
-import org.openide.util.lookup.ServiceProvider;
-import org.openide.util.lookup.implspi.AbstractServiceProviderProcessor;
-
-@ServiceProvider(service=Processor.class)
-@SupportedSourceVersion(SourceVersion.RELEASE_6)
-public class URLStreamHandlerRegistrationProcessor extends AbstractServiceProviderProcessor {
-
- public @Override Set getSupportedAnnotationTypes() {
- return Collections.singleton(URLStreamHandlerRegistration.class.getCanonicalName());
- }
-
- public static final String REGISTRATION_PREFIX = "URLStreamHandler/"; // NOI18N
-
- protected @Override boolean handleProcess(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
- for (Element el : roundEnv.getElementsAnnotatedWith(URLStreamHandlerRegistration.class)) {
- URLStreamHandlerRegistration r = el.getAnnotation(URLStreamHandlerRegistration.class);
- TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(
- processingEnv.getElementUtils().getTypeElement(URLStreamHandler.class.getName()));
- for (String protocol : r.protocol()) {
- register(el, URLStreamHandlerRegistration.class, type,
- REGISTRATION_PREFIX + protocol, r.position(), new String[0]);
- }
- }
- return true;
- }
-
-}
diff -r e5f32a52bff2 -r f9ebf6d55b55 openide.util/src/org/openide/util/URLStreamHandlerRegistration.java
--- a/openide.util/src/org/openide/util/URLStreamHandlerRegistration.java Thu Mar 22 17:15:03 2012 +0100
+++ b/openide.util/src/org/openide/util/URLStreamHandlerRegistration.java Fri Mar 23 15:13:30 2012 -0400
@@ -49,6 +49,7 @@
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
+import org.openide.util.lookup.NamedServiceDefinition;
/**
* Replacement for {@link URLStreamHandlerFactory} within the NetBeans platform.
@@ -62,6 +63,7 @@
* from a unit test or otherwise without the module system active.
* @since org.openide.util 7.31
*/
+@NamedServiceDefinition(path="URLStreamHandler/@protocol()", serviceType=URLStreamHandler.class)
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface URLStreamHandlerRegistration {