• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /openjdk10/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider.processor/src/org/graalvm/compiler/serviceprovider/processor/
1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.serviceprovider.processor;
24
25import java.io.IOException;
26import java.io.OutputStreamWriter;
27import java.io.PrintWriter;
28import java.util.HashMap;
29import java.util.HashSet;
30import java.util.Map;
31import java.util.Map.Entry;
32import java.util.Set;
33
34import javax.annotation.processing.AbstractProcessor;
35import javax.annotation.processing.FilerException;
36import javax.annotation.processing.RoundEnvironment;
37import javax.annotation.processing.SupportedAnnotationTypes;
38import javax.lang.model.SourceVersion;
39import javax.lang.model.element.Element;
40import javax.lang.model.element.TypeElement;
41import javax.lang.model.type.MirroredTypeException;
42import javax.lang.model.type.TypeMirror;
43import javax.tools.Diagnostic.Kind;
44import javax.tools.FileObject;
45import javax.tools.StandardLocation;
46
47import org.graalvm.compiler.serviceprovider.ServiceProvider;
48
49/**
50 * Processes classes annotated with {@link ServiceProvider}. For a service defined by {@code S} and
51 * a class {@code P} implementing the service, this processor generates the file
52 * {@code META-INF/providers/P} whose contents are a single line containing the fully qualified name
53 * of {@code S}.
54 */
55@SupportedAnnotationTypes("org.graalvm.compiler.serviceprovider.ServiceProvider")
56public class ServiceProviderProcessor extends AbstractProcessor {
57
58    private final Set<TypeElement> processed = new HashSet<>();
59    private final Map<TypeElement, String> serviceProviders = new HashMap<>();
60
61    @Override
62    public SourceVersion getSupportedSourceVersion() {
63        return SourceVersion.latest();
64    }
65
66    private boolean verifyAnnotation(TypeMirror serviceInterface, TypeElement serviceProvider) {
67        if (!processingEnv.getTypeUtils().isSubtype(serviceProvider.asType(), serviceInterface)) {
68            String msg = String.format("Service provider class %s must implement service interface %s", serviceProvider.getSimpleName(), serviceInterface);
69            processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
70            return false;
71        }
72
73        return true;
74    }
75
76    private void processElement(TypeElement serviceProvider) {
77        if (processed.contains(serviceProvider)) {
78            return;
79        }
80
81        processed.add(serviceProvider);
82        ServiceProvider annotation = serviceProvider.getAnnotation(ServiceProvider.class);
83        if (annotation != null) {
84            try {
85                annotation.value();
86            } catch (MirroredTypeException ex) {
87                TypeMirror serviceInterface = ex.getTypeMirror();
88                if (verifyAnnotation(serviceInterface, serviceProvider)) {
89                    if (serviceProvider.getNestingKind().isNested()) {
90                        /*
91                         * This is a simplifying constraint that means we don't have to process the
92                         * qualified name to insert '$' characters at the relevant positions.
93                         */
94                        String msg = String.format("Service provider class %s must be a top level class", serviceProvider.getSimpleName());
95                        processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
96                    } else {
97                        serviceProviders.put(serviceProvider, ex.getTypeMirror().toString());
98                    }
99                }
100            }
101        }
102    }
103
104    private void writeProviderFile(TypeElement serviceProvider, String interfaceName) {
105        String filename = "META-INF/providers/" + serviceProvider.getQualifiedName();
106        try {
107            FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, serviceProvider);
108            PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
109            writer.println(interfaceName);
110            writer.close();
111        } catch (IOException e) {
112            processingEnv.getMessager().printMessage(isBug367599(e) ? Kind.NOTE : Kind.ERROR, e.getMessage(), serviceProvider);
113        }
114    }
115
116    /**
117     * Determines if a given exception is (most likely) caused by
118     * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599">Bug 367599</a>.
119     */
120    private static boolean isBug367599(Throwable t) {
121        if (t instanceof FilerException) {
122            for (StackTraceElement ste : t.getStackTrace()) {
123                if (ste.toString().contains("org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.create")) {
124                    // See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599
125                    return true;
126                }
127            }
128        }
129        return t.getCause() != null && isBug367599(t.getCause());
130    }
131
132    @Override
133    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
134        if (roundEnv.processingOver()) {
135            for (Entry<TypeElement, String> e : serviceProviders.entrySet()) {
136                writeProviderFile(e.getKey(), e.getValue());
137            }
138            serviceProviders.clear();
139            return true;
140        }
141
142        for (Element element : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) {
143            assert element.getKind().isClass();
144            processElement((TypeElement) element);
145        }
146
147        return true;
148    }
149}
150