View Javadoc
1   package com.atlassian.plugin.osgi.factory.transform.stage;
2   
3   import com.atlassian.plugin.osgi.factory.transform.PluginTransformationException;
4   import com.atlassian.plugin.osgi.factory.transform.TransformContext;
5   import com.atlassian.plugin.osgi.factory.transform.TransformStage;
6   import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
7   import com.atlassian.plugin.util.ClassLoaderUtils;
8   import org.dom4j.Attribute;
9   import org.dom4j.DocumentHelper;
10  import org.dom4j.XPath;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  import java.lang.reflect.Constructor;
15  import java.lang.reflect.Method;
16  import java.util.LinkedHashMap;
17  import java.util.List;
18  import java.util.Map;
19  
20  import static com.atlassian.plugin.util.PluginUtils.isAtlassianDevMode;
21  
22  /**
23   * Scans the plugin descriptor for any "class" attribute, and ensures that it will be imported, if appropriate.
24   *
25   * @since 2.2.0
26   */
27  public class ScanDescriptorForHostClassesStage implements TransformStage {
28      private static final Logger log = LoggerFactory.getLogger(ScanDescriptorForHostClassesStage.class);
29  
30      @SuppressWarnings("unchecked")
31      public void execute(TransformContext context) throws PluginTransformationException {
32          XPath xpath = DocumentHelper.createXPath("//@class");
33          List<Attribute> attributes = xpath.selectNodes(context.getDescriptorDocument());
34          for (Attribute attr : attributes) {
35              String className = attr.getValue();
36  
37              scanForHostComponents(context, className);
38  
39              int dotpos = className.lastIndexOf(".");
40              if (dotpos > -1) {
41                  String pkg = className.substring(0, dotpos);
42                  String pkgPath = pkg.replace('.', '/') + '/';
43  
44                  // Only add an import if the system exports it and the plugin isn't using the package
45                  if (context.getSystemExports().isExported(pkg)) {
46                      if (context.getPluginArtifact().doesResourceExist(pkgPath)) {
47                          if (isAtlassianDevMode())
48                              log.warn("The plugin '{}' uses a package '{}' that is also exported by the application. "
49                                      + "It is highly recommended that the plugin use its own packages.",
50                                      context.getPluginArtifact(), pkg);
51                      } else {
52                          context.getExtraImports().add(pkg);
53                      }
54                  }
55              }
56          }
57      }
58  
59      private void scanForHostComponents(TransformContext context, String className) {
60          // Class name can be prefixed with 'bean:' to reference a spring bean, in this case don't attempt to load it. 
61          if (className != null && className.contains(":")) {
62              return;
63          }
64  
65          Map<Class<?>, HostComponentRegistration> hostComponentInterfaces = new LinkedHashMap<>();
66          for (HostComponentRegistration registration : context.getHostComponentRegistrations()) {
67              for (Class<?> cls : registration.getMainInterfaceClasses()) {
68                  hostComponentInterfaces.put(cls, registration);
69              }
70          }
71  
72          Class cls;
73          try {
74              cls = ClassLoaderUtils.loadClass(className, getClass());
75          } catch (ClassNotFoundException e) {
76              // not a host class, ignore
77              return;
78          }
79  
80          // Check constructor arguments for host component interfaces
81          for (Constructor ctor : cls.getConstructors()) {
82              for (Class<?> ctorParam : ctor.getParameterTypes()) {
83                  if (hostComponentInterfaces.containsKey(ctorParam)) {
84                      context.addRequiredHostComponent(hostComponentInterfaces.get(ctorParam));
85                  }
86              }
87          }
88  
89          // Check setters for host component interface arguments
90          for (Method method : cls.getMethods()) {
91              if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
92                  if (hostComponentInterfaces.containsKey(method.getParameterTypes()[0])) {
93                      context.addRequiredHostComponent(hostComponentInterfaces.get(method.getParameterTypes()[0]));
94                  }
95              }
96          }
97      }
98  }