1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/openide.util.lookup/src/org/netbeans/modules/openide/util/AbstractServiceProviderProcessor.java Fri Jan 22 14:50:19 2010 +0100
1.3 @@ -0,0 +1,298 @@
1.4 +/*
1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
1.6 + *
1.7 + * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
1.8 + *
1.9 + * The contents of this file are subject to the terms of either the GNU
1.10 + * General Public License Version 2 only ("GPL") or the Common
1.11 + * Development and Distribution License("CDDL") (collectively, the
1.12 + * "License"). You may not use this file except in compliance with the
1.13 + * License. You can obtain a copy of the License at
1.14 + * http://www.netbeans.org/cddl-gplv2.html
1.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
1.16 + * specific language governing permissions and limitations under the
1.17 + * License. When distributing the software, include this License Header
1.18 + * Notice in each file and include the License file at
1.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
1.20 + * particular file as subject to the "Classpath" exception as provided
1.21 + * by Sun in the GPL Version 2 section of the License file that
1.22 + * accompanied this code. If applicable, add the following below the
1.23 + * License Header, with the fields enclosed by brackets [] replaced by
1.24 + * your own identifying information:
1.25 + * "Portions Copyrighted [year] [name of copyright owner]"
1.26 + *
1.27 + * If you wish your version of this file to be governed by only the CDDL
1.28 + * or only the GPL Version 2, indicate your decision by adding
1.29 + * "[Contributor] elects to include this software in this distribution
1.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
1.31 + * single choice of license, a recipient has the option to distribute
1.32 + * your version of this file under either the CDDL, the GPL Version 2 or
1.33 + * to extend the choice of license to its licensees as provided above.
1.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
1.35 + * Version 2 license, then the option applies only if the new code is
1.36 + * made subject to such option by the copyright holder.
1.37 + *
1.38 + * Contributor(s):
1.39 + *
1.40 + * Portions Copyrighted 2009 Sun Microsystems, Inc.
1.41 + */
1.42 +
1.43 +package org.netbeans.modules.openide.util;
1.44 +
1.45 +import java.io.BufferedReader;
1.46 +import java.io.FileNotFoundException;
1.47 +import java.io.IOException;
1.48 +import java.io.InputStream;
1.49 +import java.io.InputStreamReader;
1.50 +import java.io.OutputStream;
1.51 +import java.io.OutputStreamWriter;
1.52 +import java.io.PrintWriter;
1.53 +import java.lang.annotation.Annotation;
1.54 +import java.util.ArrayList;
1.55 +import java.util.HashMap;
1.56 +import java.util.List;
1.57 +import java.util.Map;
1.58 +import java.util.Set;
1.59 +import java.util.WeakHashMap;
1.60 +import javax.annotation.processing.AbstractProcessor;
1.61 +import javax.annotation.processing.ProcessingEnvironment;
1.62 +import javax.annotation.processing.RoundEnvironment;
1.63 +import javax.lang.model.element.AnnotationMirror;
1.64 +import javax.lang.model.element.AnnotationValue;
1.65 +import javax.lang.model.element.Element;
1.66 +import javax.lang.model.element.ExecutableElement;
1.67 +import javax.lang.model.element.Modifier;
1.68 +import javax.lang.model.element.TypeElement;
1.69 +import javax.lang.model.type.TypeMirror;
1.70 +import javax.lang.model.util.ElementFilter;
1.71 +import javax.tools.Diagnostic.Kind;
1.72 +import javax.tools.FileObject;
1.73 +import javax.tools.StandardLocation;
1.74 +
1.75 +/**
1.76 + * Infrastructure for generating {@code META-INF/services/*} and
1.77 + * {@code META-INF/namedservices/*} registrations from annotations.
1.78 + *
1.79 + * NOTE: This class is duplicated in openide.util and openide.util.lookup to prevent implementation dependency.
1.80 + */
1.81 +public abstract class AbstractServiceProviderProcessor extends AbstractProcessor {
1.82 +
1.83 + private final Map<ProcessingEnvironment,Map<String,List<String>>> outputFilesByProcessor = new WeakHashMap<ProcessingEnvironment,Map<String,List<String>>>();
1.84 + private final Map<ProcessingEnvironment,Map<String,List<Element>>> originatingElementsByProcessor = new WeakHashMap<ProcessingEnvironment,Map<String,List<Element>>>();
1.85 + private final Map<TypeElement,Boolean> verifiedClasses = new WeakHashMap<TypeElement,Boolean>();
1.86 +
1.87 + /** For access by subclasses. */
1.88 + protected AbstractServiceProviderProcessor() {}
1.89 +
1.90 + public @Override final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1.91 + if (roundEnv.errorRaised()) {
1.92 + return false;
1.93 + }
1.94 + if (roundEnv.processingOver()) {
1.95 + writeServices();
1.96 + outputFilesByProcessor.clear();
1.97 + originatingElementsByProcessor.clear();
1.98 + return true;
1.99 + } else {
1.100 + return handleProcess(annotations, roundEnv);
1.101 + }
1.102 + }
1.103 +
1.104 + /**
1.105 + * The regular body of {@link #process}.
1.106 + * Called during regular rounds if there are no outstanding errors.
1.107 + * In the last round, one of the processors will write out generated registrations.
1.108 + * @param annotations as in {@link #process}
1.109 + * @param roundEnv as in {@link #process}
1.110 + * @return as in {@link #process}
1.111 + */
1.112 + protected abstract boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
1.113 +
1.114 + /**
1.115 + * Register a service.
1.116 + * If the class does not have an appropriate signature, an error will be printed and the registration skipped.
1.117 + * @param clazz the service implementation type
1.118 + * @param annotation the (top-level) annotation registering the service, for diagnostic purposes
1.119 + * @param type the type to which the implementation must be assignable
1.120 + * @param path a path under which to register, or "" if inapplicable
1.121 + * @param position a position at which to register, or {@link Integer#MAX_VALUE} to skip
1.122 + * @param supersedes possibly empty list of implementation to supersede
1.123 + */
1.124 + protected final void register(TypeElement clazz, Class<? extends Annotation> annotation,
1.125 + TypeMirror type, String path, int position, String[] supersedes) {
1.126 + Boolean verify = verifiedClasses.get(clazz);
1.127 + if (verify == null) {
1.128 + verify = verifyServiceProviderSignature(clazz, annotation);
1.129 + verifiedClasses.put(clazz, verify);
1.130 + }
1.131 + if (!verify) {
1.132 + return;
1.133 + }
1.134 + String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
1.135 + String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
1.136 + if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
1.137 + AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
1.138 + processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
1.139 + clazz, ann, findAnnotationValue(ann, "service"));
1.140 + return;
1.141 + }
1.142 + processingEnv.getMessager().printMessage(Kind.NOTE,
1.143 + impl + " to be registered as a " + xface + (path.length() > 0 ? " under " + path : ""));
1.144 + String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
1.145 + {
1.146 + Map<String,List<Element>> originatingElements = originatingElementsByProcessor.get(processingEnv);
1.147 + if (originatingElements == null) {
1.148 + originatingElements = new HashMap<String,List<Element>>();
1.149 + originatingElementsByProcessor.put(processingEnv, originatingElements);
1.150 + }
1.151 + List<Element> origEls = originatingElements.get(rsrc);
1.152 + if (origEls == null) {
1.153 + origEls = new ArrayList<Element>();
1.154 + originatingElements.put(rsrc, origEls);
1.155 + }
1.156 + origEls.add(clazz);
1.157 + }
1.158 + Map<String,List<String>> outputFiles = outputFilesByProcessor.get(processingEnv);
1.159 + if (outputFiles == null) {
1.160 + outputFiles = new HashMap<String,List<String>>();
1.161 + outputFilesByProcessor.put(processingEnv, outputFiles);
1.162 + }
1.163 + List<String> lines = outputFiles.get(rsrc);
1.164 + if (lines == null) {
1.165 + lines = new ArrayList<String>();
1.166 + try {
1.167 + try {
1.168 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", rsrc);
1.169 + in.openInputStream().close();
1.170 + processingEnv.getMessager().printMessage(Kind.ERROR,
1.171 + "Cannot generate " + rsrc + " because it already exists in sources: " + in.toUri());
1.172 + return;
1.173 + } catch (NullPointerException ex) {
1.174 + // trying to prevent java.lang.NullPointerException
1.175 + // at com.sun.tools.javac.util.DefaultFileManager.getFileForOutput(DefaultFileManager.java:1078)
1.176 + // at com.sun.tools.javac.util.DefaultFileManager.getFileForOutput(DefaultFileManager.java:1054)
1.177 + // at com.sun.tools.javac.processing.JavacFiler.getResource(JavacFiler.java:434)
1.178 + // at org.netbeans.modules.openide.util.AbstractServiceProviderProcessor.register(AbstractServiceProviderProcessor.java:163)
1.179 + // at org.netbeans.modules.openide.util.ServiceProviderProcessor.register(ServiceProviderProcessor.java:99)
1.180 + } catch (FileNotFoundException x) {
1.181 + // Good.
1.182 + }
1.183 + try {
1.184 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", rsrc);
1.185 + InputStream is = in.openInputStream();
1.186 + try {
1.187 + BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
1.188 + String line;
1.189 + while ((line = r.readLine()) != null) {
1.190 + lines.add(line);
1.191 + }
1.192 + } finally {
1.193 + is.close();
1.194 + }
1.195 + } catch (FileNotFoundException x) {
1.196 + // OK, created for the first time
1.197 + }
1.198 + } catch (IOException x) {
1.199 + processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
1.200 + return;
1.201 + }
1.202 + outputFiles.put(rsrc, lines);
1.203 + }
1.204 + int idx = lines.indexOf(impl);
1.205 + if (idx != -1) {
1.206 + lines.remove(idx);
1.207 + while (lines.size() > idx && lines.get(idx).matches("#position=.+|#-.+")) {
1.208 + lines.remove(idx);
1.209 + }
1.210 + }
1.211 + lines.add(impl);
1.212 + if (position != Integer.MAX_VALUE) {
1.213 + lines.add("#position=" + position);
1.214 + }
1.215 + for (String exclude : supersedes) {
1.216 + lines.add("#-" + exclude);
1.217 + }
1.218 + }
1.219 +
1.220 + /**
1.221 + * @param element a source element
1.222 + * @param annotation a type of annotation
1.223 + * @return the instance of that annotation on the element, or null if not found
1.224 + */
1.225 + private AnnotationMirror findAnnotationMirror(Element element, Class<? extends Annotation> annotation) {
1.226 + for (AnnotationMirror ann : element.getAnnotationMirrors()) {
1.227 + if (processingEnv.getElementUtils().getBinaryName((TypeElement) ann.getAnnotationType().asElement()).
1.228 + contentEquals(annotation.getName())) {
1.229 + return ann;
1.230 + }
1.231 + }
1.232 + return null;
1.233 + }
1.234 +
1.235 + /**
1.236 + * @param annotation an annotation instance (null permitted)
1.237 + * @param name the name of an attribute of that annotation
1.238 + * @return the corresponding value if found
1.239 + */
1.240 + private AnnotationValue findAnnotationValue(AnnotationMirror annotation, String name) {
1.241 + if (annotation != null) {
1.242 + for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
1.243 + if (entry.getKey().getSimpleName().contentEquals(name)) {
1.244 + return entry.getValue();
1.245 + }
1.246 + }
1.247 + }
1.248 + return null;
1.249 + }
1.250 +
1.251 + private final boolean verifyServiceProviderSignature(TypeElement clazz, Class<? extends Annotation> annotation) {
1.252 + AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
1.253 + if (!clazz.getModifiers().contains(Modifier.PUBLIC)) {
1.254 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must be public", clazz, ann);
1.255 + return false;
1.256 + }
1.257 + if (clazz.getModifiers().contains(Modifier.ABSTRACT)) {
1.258 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must not be abstract", clazz, ann);
1.259 + return false;
1.260 + }
1.261 + {
1.262 + boolean hasDefaultCtor = false;
1.263 + for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
1.264 + if (constructor.getModifiers().contains(Modifier.PUBLIC) && constructor.getParameters().isEmpty()) {
1.265 + hasDefaultCtor = true;
1.266 + break;
1.267 + }
1.268 + }
1.269 + if (!hasDefaultCtor) {
1.270 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must have a public no-argument constructor", clazz, ann);
1.271 + return false;
1.272 + }
1.273 + }
1.274 + return true;
1.275 + }
1.276 +
1.277 + private void writeServices() {
1.278 + for (Map.Entry<ProcessingEnvironment,Map<String,List<String>>> outputFiles : outputFilesByProcessor.entrySet()) {
1.279 + for (Map.Entry<String, List<String>> entry : outputFiles.getValue().entrySet()) {
1.280 + try {
1.281 + FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", entry.getKey(),
1.282 + originatingElementsByProcessor.get(outputFiles.getKey()).get(entry.getKey()).toArray(new Element[0]));
1.283 + OutputStream os = out.openOutputStream();
1.284 + try {
1.285 + PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
1.286 + for (String line : entry.getValue()) {
1.287 + w.println(line);
1.288 + }
1.289 + w.flush();
1.290 + w.close();
1.291 + } finally {
1.292 + os.close();
1.293 + }
1.294 + } catch (IOException x) {
1.295 + processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
1.296 + }
1.297 + }
1.298 + }
1.299 + }
1.300 +
1.301 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/openide.util.lookup/src/org/netbeans/modules/openide/util/ActiveQueue.java Fri Jan 22 14:50:19 2010 +0100
2.3 @@ -0,0 +1,110 @@
2.4 +package org.netbeans.modules.openide.util;
2.5 +
2.6 +import java.lang.ref.Reference;
2.7 +import java.lang.ref.ReferenceQueue;
2.8 +import java.util.logging.Level;
2.9 +import java.util.logging.Logger;
2.10 +
2.11 +/**
2.12 + * Implementation of the active reference queue.
2.13 + *
2.14 + * NOTE: This class is duplicated in openide.util and openide.util.lookup to prevent implementation dependency.
2.15 + */
2.16 +public final class ActiveQueue extends ReferenceQueue<Object> implements Runnable {
2.17 +
2.18 + private static final Logger LOGGER = Logger.getLogger(ActiveQueue.class.getName().replace('$', '.'));
2.19 + private static ActiveQueue activeReferenceQueue;
2.20 +
2.21 + /** number of known outstanding references */
2.22 + private int count;
2.23 + private boolean deprecated;
2.24 +
2.25 + ActiveQueue(boolean deprecated) {
2.26 + super();
2.27 + this.deprecated = deprecated;
2.28 + }
2.29 +
2.30 + public static synchronized ReferenceQueue<Object> queue() {
2.31 + if (activeReferenceQueue == null) {
2.32 + activeReferenceQueue = new ActiveQueue(false);
2.33 + }
2.34 +
2.35 + activeReferenceQueue.ping();
2.36 +
2.37 + return activeReferenceQueue;
2.38 + }
2.39 +
2.40 + @Override
2.41 + public Reference<Object> poll() {
2.42 + throw new UnsupportedOperationException();
2.43 + }
2.44 +
2.45 + @Override
2.46 + public Reference<Object> remove(long timeout) throws IllegalArgumentException, InterruptedException {
2.47 + throw new InterruptedException();
2.48 + }
2.49 +
2.50 + @Override
2.51 + public Reference<Object> remove() throws InterruptedException {
2.52 + throw new InterruptedException();
2.53 + }
2.54 +
2.55 + public void run() {
2.56 + while (true) {
2.57 + try {
2.58 + Reference<?> ref = super.remove(0);
2.59 + LOGGER.finer("dequeued reference");
2.60 + if (!(ref instanceof Runnable)) {
2.61 + LOGGER.warning("A reference not implementing runnable has been added to the Utilities.activeReferenceQueue(): " + ref.getClass());
2.62 + continue;
2.63 + }
2.64 + if (deprecated) {
2.65 + LOGGER.warning("Utilities.ACTIVE_REFERENCE_QUEUE has been deprecated for " + ref.getClass() + " use Utilities.activeReferenceQueue");
2.66 + }
2.67 + // do the cleanup
2.68 + try {
2.69 + ((Runnable) ref).run();
2.70 + } catch (ThreadDeath td) {
2.71 + throw td;
2.72 + } catch (Throwable t) {
2.73 + // Should not happen.
2.74 + // If it happens, it is a bug in client code, notify!
2.75 + LOGGER.log(Level.WARNING, null, t);
2.76 + } finally {
2.77 + // to allow GC
2.78 + ref = null;
2.79 + }
2.80 + } catch (InterruptedException ex) {
2.81 + // Can happen during VM shutdown, it seems. Ignore.
2.82 + continue;
2.83 + }
2.84 + synchronized (this) {
2.85 + assert count > 0;
2.86 + count--;
2.87 + if (count == 0) {
2.88 + // We have processed all we have to process (for now at least).
2.89 + // Could be restarted later if ping() called again.
2.90 + // This could also happen in case someone called queue() once and tried
2.91 + // to use it for several references; in that case run() might never be called on
2.92 + // the later ones to be collected. Can't really protect against that situation.
2.93 + // See issue #86625 for details.
2.94 + LOGGER.fine("stopping thread");
2.95 + break;
2.96 + }
2.97 + }
2.98 + }
2.99 + }
2.100 +
2.101 + synchronized void ping() {
2.102 + if (count == 0) {
2.103 + Thread t = new Thread(this, "Active Reference Queue Daemon");
2.104 + t.setPriority(Thread.MIN_PRIORITY);
2.105 + t.setDaemon(true);
2.106 + t.start();
2.107 + LOGGER.fine("starting thread");
2.108 + } else {
2.109 + LOGGER.finer("enqueuing reference");
2.110 + }
2.111 + count++;
2.112 + }
2.113 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/openide.util/nbproject/project.xml Fri Jan 22 14:50:19 2010 +0100
3.3 @@ -0,0 +1,88 @@
3.4 +<?xml version="1.0" encoding="UTF-8"?>
3.5 +<!--
3.6 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3.7 +
3.8 +Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
3.9 +
3.10 +
3.11 +The contents of this file are subject to the terms of either the GNU
3.12 +General Public License Version 2 only ("GPL") or the Common
3.13 +Development and Distribution License("CDDL") (collectively, the
3.14 +"License"). You may not use this file except in compliance with the
3.15 +License. You can obtain a copy of the License at
3.16 +http://www.netbeans.org/cddl-gplv2.html
3.17 +or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
3.18 +specific language governing permissions and limitations under the
3.19 +License. When distributing the software, include this License Header
3.20 +Notice in each file and include the License file at
3.21 +nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
3.22 +particular file as subject to the "Classpath" exception as provided
3.23 +by Sun in the GPL Version 2 section of the License file that
3.24 +accompanied this code. If applicable, add the following below the
3.25 +License Header, with the fields enclosed by brackets [] replaced by
3.26 +your own identifying information:
3.27 +"Portions Copyrighted [year] [name of copyright owner]"
3.28 +
3.29 +Contributor(s):
3.30 +
3.31 +The Original Software is NetBeans. The Initial Developer of the Original
3.32 +Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
3.33 +Microsystems, Inc. All Rights Reserved.
3.34 +
3.35 +If you wish your version of this file to be governed by only the CDDL
3.36 +or only the GPL Version 2, indicate your decision by adding
3.37 +"[Contributor] elects to include this software in this distribution
3.38 +under the [CDDL or GPL Version 2] license." If you do not indicate a
3.39 +single choice of license, a recipient has the option to distribute
3.40 +your version of this file under either the CDDL, the GPL Version 2 or
3.41 +to extend the choice of license to its licensees as provided above.
3.42 +However, if you add GPL Version 2 code and therefore, elected the GPL
3.43 +Version 2 license, then the option applies only if the new code is
3.44 +made subject to such option by the copyright holder.
3.45 +-->
3.46 +<project xmlns="http://www.netbeans.org/ns/project/1">
3.47 + <type>org.netbeans.modules.apisupport.project</type>
3.48 + <configuration>
3.49 + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
3.50 + <code-name-base>org.openide.util</code-name-base>
3.51 + <module-dependencies>
3.52 + <dependency>
3.53 + <code-name-base>org.openide.util.lookup</code-name-base>
3.54 + <build-prerequisite/>
3.55 + <compile-dependency/>
3.56 + <run-dependency>
3.57 + <specification-version>8.0</specification-version>
3.58 + </run-dependency>
3.59 + </dependency>
3.60 + </module-dependencies>
3.61 + <test-dependencies>
3.62 + <test-type>
3.63 + <name>unit</name>
3.64 + <test-dependency>
3.65 + <code-name-base>org.netbeans.libs.junit4</code-name-base>
3.66 + <compile-dependency/>
3.67 + </test-dependency>
3.68 + <test-dependency>
3.69 + <code-name-base>org.netbeans.modules.nbjunit</code-name-base>
3.70 + <recursive/>
3.71 + <compile-dependency/>
3.72 + </test-dependency>
3.73 + <test-dependency>
3.74 + <code-name-base>org.openide.util.lookup</code-name-base>
3.75 + <compile-dependency/>
3.76 + <test/>
3.77 + </test-dependency>
3.78 + </test-type>
3.79 + </test-dependencies>
3.80 + <public-packages>
3.81 + <package>org.openide</package>
3.82 + <package>org.openide.util</package>
3.83 + <package>org.openide.util.datatransfer</package>
3.84 + <package>org.openide.util.actions</package>
3.85 + <package>org.openide.util.lookup</package>
3.86 + <package>org.openide.util.io</package>
3.87 + <package>org.openide.xml</package>
3.88 + </public-packages>
3.89 + </data>
3.90 + </configuration>
3.91 +</project>
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/openide.util/src/org/netbeans/modules/openide/util/AbstractServiceProviderProcessor.java Fri Jan 22 14:50:19 2010 +0100
4.3 @@ -0,0 +1,298 @@
4.4 +/*
4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4.6 + *
4.7 + * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
4.8 + *
4.9 + * The contents of this file are subject to the terms of either the GNU
4.10 + * General Public License Version 2 only ("GPL") or the Common
4.11 + * Development and Distribution License("CDDL") (collectively, the
4.12 + * "License"). You may not use this file except in compliance with the
4.13 + * License. You can obtain a copy of the License at
4.14 + * http://www.netbeans.org/cddl-gplv2.html
4.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
4.16 + * specific language governing permissions and limitations under the
4.17 + * License. When distributing the software, include this License Header
4.18 + * Notice in each file and include the License file at
4.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
4.20 + * particular file as subject to the "Classpath" exception as provided
4.21 + * by Sun in the GPL Version 2 section of the License file that
4.22 + * accompanied this code. If applicable, add the following below the
4.23 + * License Header, with the fields enclosed by brackets [] replaced by
4.24 + * your own identifying information:
4.25 + * "Portions Copyrighted [year] [name of copyright owner]"
4.26 + *
4.27 + * If you wish your version of this file to be governed by only the CDDL
4.28 + * or only the GPL Version 2, indicate your decision by adding
4.29 + * "[Contributor] elects to include this software in this distribution
4.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
4.31 + * single choice of license, a recipient has the option to distribute
4.32 + * your version of this file under either the CDDL, the GPL Version 2 or
4.33 + * to extend the choice of license to its licensees as provided above.
4.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
4.35 + * Version 2 license, then the option applies only if the new code is
4.36 + * made subject to such option by the copyright holder.
4.37 + *
4.38 + * Contributor(s):
4.39 + *
4.40 + * Portions Copyrighted 2009 Sun Microsystems, Inc.
4.41 + */
4.42 +
4.43 +package org.netbeans.modules.openide.util;
4.44 +
4.45 +import java.io.BufferedReader;
4.46 +import java.io.FileNotFoundException;
4.47 +import java.io.IOException;
4.48 +import java.io.InputStream;
4.49 +import java.io.InputStreamReader;
4.50 +import java.io.OutputStream;
4.51 +import java.io.OutputStreamWriter;
4.52 +import java.io.PrintWriter;
4.53 +import java.lang.annotation.Annotation;
4.54 +import java.util.ArrayList;
4.55 +import java.util.HashMap;
4.56 +import java.util.List;
4.57 +import java.util.Map;
4.58 +import java.util.Set;
4.59 +import java.util.WeakHashMap;
4.60 +import javax.annotation.processing.AbstractProcessor;
4.61 +import javax.annotation.processing.ProcessingEnvironment;
4.62 +import javax.annotation.processing.RoundEnvironment;
4.63 +import javax.lang.model.element.AnnotationMirror;
4.64 +import javax.lang.model.element.AnnotationValue;
4.65 +import javax.lang.model.element.Element;
4.66 +import javax.lang.model.element.ExecutableElement;
4.67 +import javax.lang.model.element.Modifier;
4.68 +import javax.lang.model.element.TypeElement;
4.69 +import javax.lang.model.type.TypeMirror;
4.70 +import javax.lang.model.util.ElementFilter;
4.71 +import javax.tools.Diagnostic.Kind;
4.72 +import javax.tools.FileObject;
4.73 +import javax.tools.StandardLocation;
4.74 +
4.75 +/**
4.76 + * Infrastructure for generating {@code META-INF/services/*} and
4.77 + * {@code META-INF/namedservices/*} registrations from annotations.
4.78 + *
4.79 + * NOTE: This class is duplicated in openide.util and openide.util.lookup to prevent implementation dependency.
4.80 + */
4.81 +public abstract class AbstractServiceProviderProcessor extends AbstractProcessor {
4.82 +
4.83 + private final Map<ProcessingEnvironment,Map<String,List<String>>> outputFilesByProcessor = new WeakHashMap<ProcessingEnvironment,Map<String,List<String>>>();
4.84 + private final Map<ProcessingEnvironment,Map<String,List<Element>>> originatingElementsByProcessor = new WeakHashMap<ProcessingEnvironment,Map<String,List<Element>>>();
4.85 + private final Map<TypeElement,Boolean> verifiedClasses = new WeakHashMap<TypeElement,Boolean>();
4.86 +
4.87 + /** For access by subclasses. */
4.88 + protected AbstractServiceProviderProcessor() {}
4.89 +
4.90 + public @Override final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
4.91 + if (roundEnv.errorRaised()) {
4.92 + return false;
4.93 + }
4.94 + if (roundEnv.processingOver()) {
4.95 + writeServices();
4.96 + outputFilesByProcessor.clear();
4.97 + originatingElementsByProcessor.clear();
4.98 + return true;
4.99 + } else {
4.100 + return handleProcess(annotations, roundEnv);
4.101 + }
4.102 + }
4.103 +
4.104 + /**
4.105 + * The regular body of {@link #process}.
4.106 + * Called during regular rounds if there are no outstanding errors.
4.107 + * In the last round, one of the processors will write out generated registrations.
4.108 + * @param annotations as in {@link #process}
4.109 + * @param roundEnv as in {@link #process}
4.110 + * @return as in {@link #process}
4.111 + */
4.112 + protected abstract boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
4.113 +
4.114 + /**
4.115 + * Register a service.
4.116 + * If the class does not have an appropriate signature, an error will be printed and the registration skipped.
4.117 + * @param clazz the service implementation type
4.118 + * @param annotation the (top-level) annotation registering the service, for diagnostic purposes
4.119 + * @param type the type to which the implementation must be assignable
4.120 + * @param path a path under which to register, or "" if inapplicable
4.121 + * @param position a position at which to register, or {@link Integer#MAX_VALUE} to skip
4.122 + * @param supersedes possibly empty list of implementation to supersede
4.123 + */
4.124 + protected final void register(TypeElement clazz, Class<? extends Annotation> annotation,
4.125 + TypeMirror type, String path, int position, String[] supersedes) {
4.126 + Boolean verify = verifiedClasses.get(clazz);
4.127 + if (verify == null) {
4.128 + verify = verifyServiceProviderSignature(clazz, annotation);
4.129 + verifiedClasses.put(clazz, verify);
4.130 + }
4.131 + if (!verify) {
4.132 + return;
4.133 + }
4.134 + String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
4.135 + String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
4.136 + if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
4.137 + AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
4.138 + processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
4.139 + clazz, ann, findAnnotationValue(ann, "service"));
4.140 + return;
4.141 + }
4.142 + processingEnv.getMessager().printMessage(Kind.NOTE,
4.143 + impl + " to be registered as a " + xface + (path.length() > 0 ? " under " + path : ""));
4.144 + String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
4.145 + {
4.146 + Map<String,List<Element>> originatingElements = originatingElementsByProcessor.get(processingEnv);
4.147 + if (originatingElements == null) {
4.148 + originatingElements = new HashMap<String,List<Element>>();
4.149 + originatingElementsByProcessor.put(processingEnv, originatingElements);
4.150 + }
4.151 + List<Element> origEls = originatingElements.get(rsrc);
4.152 + if (origEls == null) {
4.153 + origEls = new ArrayList<Element>();
4.154 + originatingElements.put(rsrc, origEls);
4.155 + }
4.156 + origEls.add(clazz);
4.157 + }
4.158 + Map<String,List<String>> outputFiles = outputFilesByProcessor.get(processingEnv);
4.159 + if (outputFiles == null) {
4.160 + outputFiles = new HashMap<String,List<String>>();
4.161 + outputFilesByProcessor.put(processingEnv, outputFiles);
4.162 + }
4.163 + List<String> lines = outputFiles.get(rsrc);
4.164 + if (lines == null) {
4.165 + lines = new ArrayList<String>();
4.166 + try {
4.167 + try {
4.168 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", rsrc);
4.169 + in.openInputStream().close();
4.170 + processingEnv.getMessager().printMessage(Kind.ERROR,
4.171 + "Cannot generate " + rsrc + " because it already exists in sources: " + in.toUri());
4.172 + return;
4.173 + } catch (NullPointerException ex) {
4.174 + // trying to prevent java.lang.NullPointerException
4.175 + // at com.sun.tools.javac.util.DefaultFileManager.getFileForOutput(DefaultFileManager.java:1078)
4.176 + // at com.sun.tools.javac.util.DefaultFileManager.getFileForOutput(DefaultFileManager.java:1054)
4.177 + // at com.sun.tools.javac.processing.JavacFiler.getResource(JavacFiler.java:434)
4.178 + // at org.netbeans.modules.openide.util.AbstractServiceProviderProcessor.register(AbstractServiceProviderProcessor.java:163)
4.179 + // at org.netbeans.modules.openide.util.ServiceProviderProcessor.register(ServiceProviderProcessor.java:99)
4.180 + } catch (FileNotFoundException x) {
4.181 + // Good.
4.182 + }
4.183 + try {
4.184 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", rsrc);
4.185 + InputStream is = in.openInputStream();
4.186 + try {
4.187 + BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
4.188 + String line;
4.189 + while ((line = r.readLine()) != null) {
4.190 + lines.add(line);
4.191 + }
4.192 + } finally {
4.193 + is.close();
4.194 + }
4.195 + } catch (FileNotFoundException x) {
4.196 + // OK, created for the first time
4.197 + }
4.198 + } catch (IOException x) {
4.199 + processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
4.200 + return;
4.201 + }
4.202 + outputFiles.put(rsrc, lines);
4.203 + }
4.204 + int idx = lines.indexOf(impl);
4.205 + if (idx != -1) {
4.206 + lines.remove(idx);
4.207 + while (lines.size() > idx && lines.get(idx).matches("#position=.+|#-.+")) {
4.208 + lines.remove(idx);
4.209 + }
4.210 + }
4.211 + lines.add(impl);
4.212 + if (position != Integer.MAX_VALUE) {
4.213 + lines.add("#position=" + position);
4.214 + }
4.215 + for (String exclude : supersedes) {
4.216 + lines.add("#-" + exclude);
4.217 + }
4.218 + }
4.219 +
4.220 + /**
4.221 + * @param element a source element
4.222 + * @param annotation a type of annotation
4.223 + * @return the instance of that annotation on the element, or null if not found
4.224 + */
4.225 + private AnnotationMirror findAnnotationMirror(Element element, Class<? extends Annotation> annotation) {
4.226 + for (AnnotationMirror ann : element.getAnnotationMirrors()) {
4.227 + if (processingEnv.getElementUtils().getBinaryName((TypeElement) ann.getAnnotationType().asElement()).
4.228 + contentEquals(annotation.getName())) {
4.229 + return ann;
4.230 + }
4.231 + }
4.232 + return null;
4.233 + }
4.234 +
4.235 + /**
4.236 + * @param annotation an annotation instance (null permitted)
4.237 + * @param name the name of an attribute of that annotation
4.238 + * @return the corresponding value if found
4.239 + */
4.240 + private AnnotationValue findAnnotationValue(AnnotationMirror annotation, String name) {
4.241 + if (annotation != null) {
4.242 + for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
4.243 + if (entry.getKey().getSimpleName().contentEquals(name)) {
4.244 + return entry.getValue();
4.245 + }
4.246 + }
4.247 + }
4.248 + return null;
4.249 + }
4.250 +
4.251 + private final boolean verifyServiceProviderSignature(TypeElement clazz, Class<? extends Annotation> annotation) {
4.252 + AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
4.253 + if (!clazz.getModifiers().contains(Modifier.PUBLIC)) {
4.254 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must be public", clazz, ann);
4.255 + return false;
4.256 + }
4.257 + if (clazz.getModifiers().contains(Modifier.ABSTRACT)) {
4.258 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must not be abstract", clazz, ann);
4.259 + return false;
4.260 + }
4.261 + {
4.262 + boolean hasDefaultCtor = false;
4.263 + for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
4.264 + if (constructor.getModifiers().contains(Modifier.PUBLIC) && constructor.getParameters().isEmpty()) {
4.265 + hasDefaultCtor = true;
4.266 + break;
4.267 + }
4.268 + }
4.269 + if (!hasDefaultCtor) {
4.270 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must have a public no-argument constructor", clazz, ann);
4.271 + return false;
4.272 + }
4.273 + }
4.274 + return true;
4.275 + }
4.276 +
4.277 + private void writeServices() {
4.278 + for (Map.Entry<ProcessingEnvironment,Map<String,List<String>>> outputFiles : outputFilesByProcessor.entrySet()) {
4.279 + for (Map.Entry<String, List<String>> entry : outputFiles.getValue().entrySet()) {
4.280 + try {
4.281 + FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", entry.getKey(),
4.282 + originatingElementsByProcessor.get(outputFiles.getKey()).get(entry.getKey()).toArray(new Element[0]));
4.283 + OutputStream os = out.openOutputStream();
4.284 + try {
4.285 + PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
4.286 + for (String line : entry.getValue()) {
4.287 + w.println(line);
4.288 + }
4.289 + w.flush();
4.290 + w.close();
4.291 + } finally {
4.292 + os.close();
4.293 + }
4.294 + } catch (IOException x) {
4.295 + processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
4.296 + }
4.297 + }
4.298 + }
4.299 + }
4.300 +
4.301 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/openide.util/src/org/netbeans/modules/openide/util/ActiveQueue.java Fri Jan 22 14:50:19 2010 +0100
5.3 @@ -0,0 +1,110 @@
5.4 +package org.netbeans.modules.openide.util;
5.5 +
5.6 +import java.lang.ref.Reference;
5.7 +import java.lang.ref.ReferenceQueue;
5.8 +import java.util.logging.Level;
5.9 +import java.util.logging.Logger;
5.10 +
5.11 +/**
5.12 + * Implementation of the active reference queue.
5.13 + *
5.14 + * NOTE: This class is duplicated in openide.util and openide.util.lookup to prevent implementation dependency.
5.15 + */
5.16 +public final class ActiveQueue extends ReferenceQueue<Object> implements Runnable {
5.17 +
5.18 + private static final Logger LOGGER = Logger.getLogger(ActiveQueue.class.getName().replace('$', '.'));
5.19 + private static ActiveQueue activeReferenceQueue;
5.20 +
5.21 + /** number of known outstanding references */
5.22 + private int count;
5.23 + private boolean deprecated;
5.24 +
5.25 + ActiveQueue(boolean deprecated) {
5.26 + super();
5.27 + this.deprecated = deprecated;
5.28 + }
5.29 +
5.30 + public static synchronized ReferenceQueue<Object> queue() {
5.31 + if (activeReferenceQueue == null) {
5.32 + activeReferenceQueue = new ActiveQueue(false);
5.33 + }
5.34 +
5.35 + activeReferenceQueue.ping();
5.36 +
5.37 + return activeReferenceQueue;
5.38 + }
5.39 +
5.40 + @Override
5.41 + public Reference<Object> poll() {
5.42 + throw new UnsupportedOperationException();
5.43 + }
5.44 +
5.45 + @Override
5.46 + public Reference<Object> remove(long timeout) throws IllegalArgumentException, InterruptedException {
5.47 + throw new InterruptedException();
5.48 + }
5.49 +
5.50 + @Override
5.51 + public Reference<Object> remove() throws InterruptedException {
5.52 + throw new InterruptedException();
5.53 + }
5.54 +
5.55 + public void run() {
5.56 + while (true) {
5.57 + try {
5.58 + Reference<?> ref = super.remove(0);
5.59 + LOGGER.finer("dequeued reference");
5.60 + if (!(ref instanceof Runnable)) {
5.61 + LOGGER.warning("A reference not implementing runnable has been added to the Utilities.activeReferenceQueue(): " + ref.getClass());
5.62 + continue;
5.63 + }
5.64 + if (deprecated) {
5.65 + LOGGER.warning("Utilities.ACTIVE_REFERENCE_QUEUE has been deprecated for " + ref.getClass() + " use Utilities.activeReferenceQueue");
5.66 + }
5.67 + // do the cleanup
5.68 + try {
5.69 + ((Runnable) ref).run();
5.70 + } catch (ThreadDeath td) {
5.71 + throw td;
5.72 + } catch (Throwable t) {
5.73 + // Should not happen.
5.74 + // If it happens, it is a bug in client code, notify!
5.75 + LOGGER.log(Level.WARNING, null, t);
5.76 + } finally {
5.77 + // to allow GC
5.78 + ref = null;
5.79 + }
5.80 + } catch (InterruptedException ex) {
5.81 + // Can happen during VM shutdown, it seems. Ignore.
5.82 + continue;
5.83 + }
5.84 + synchronized (this) {
5.85 + assert count > 0;
5.86 + count--;
5.87 + if (count == 0) {
5.88 + // We have processed all we have to process (for now at least).
5.89 + // Could be restarted later if ping() called again.
5.90 + // This could also happen in case someone called queue() once and tried
5.91 + // to use it for several references; in that case run() might never be called on
5.92 + // the later ones to be collected. Can't really protect against that situation.
5.93 + // See issue #86625 for details.
5.94 + LOGGER.fine("stopping thread");
5.95 + break;
5.96 + }
5.97 + }
5.98 + }
5.99 + }
5.100 +
5.101 + synchronized void ping() {
5.102 + if (count == 0) {
5.103 + Thread t = new Thread(this, "Active Reference Queue Daemon");
5.104 + t.setPriority(Thread.MIN_PRIORITY);
5.105 + t.setDaemon(true);
5.106 + t.start();
5.107 + LOGGER.fine("starting thread");
5.108 + } else {
5.109 + LOGGER.finer("enqueuing reference");
5.110 + }
5.111 + count++;
5.112 + }
5.113 +}