1   package com.atlassian.plugin.osgi.factory.transform.stage;
2   
3   import com.atlassian.plugin.osgi.factory.transform.TransformStage;
4   import com.atlassian.plugin.osgi.factory.transform.TransformContext;
5   import com.atlassian.plugin.osgi.factory.transform.PluginTransformationException;
6   import static com.atlassian.plugin.util.validation.ValidationPattern.createPattern;
7   import static com.atlassian.plugin.util.validation.ValidationPattern.test;
8   import com.atlassian.plugin.util.validation.ValidationPattern;
9   import com.atlassian.plugin.util.PluginUtils;
10  import org.dom4j.Document;
11  import org.dom4j.Element;
12  
13  import java.util.List;
14  import java.util.ArrayList;
15  
16  /**
17   * Transforms component tags in the plugin descriptor into the appropriate spring XML configuration file
18   *
19   * @since 2.2.0
20   */
21  public class ComponentSpringStage implements TransformStage
22  {
23      /** Path of generated Spring XML file */
24      private static final String SPRING_XML = "META-INF/spring/atlassian-plugins-components.xml";
25  
26      public void execute(TransformContext context) throws PluginTransformationException
27      {
28          if (SpringHelper.shouldGenerateFile(context, SPRING_XML))
29          {
30              Document springDoc = SpringHelper.createSpringDocument();
31              Element root = springDoc.getRootElement();
32              List<Element> elements = context.getDescriptorDocument().getRootElement().elements("component");
33  
34              ValidationPattern validation = createPattern().
35                      rule(
36                          test("@key").withError("The key is required"),
37                          test("@class").withError("The class is required"),
38                          test("not(@public) or interface or @interface").withError("Interfaces must be declared for public components"),
39                          test("not(service-properties) or count(service-properties/entry[@key and @value]) > 0")
40                                  .withError("The service-properties element must contain at least one entry element with key and value attributes"));
41  
42              for (Element component : elements)
43              {
44                  if (!PluginUtils.doesModuleElementApplyToApplication(component, context.getApplicationKeys()))
45                  {
46                      continue;
47                  }
48                  validation.evaluate(component);
49  
50                  Element bean = root.addElement("beans:bean");
51                  bean.addAttribute("id", component.attributeValue("key"));
52                  bean.addAttribute("alias", component.attributeValue("alias"));
53                  bean.addAttribute("class", component.attributeValue("class"));
54                  if ("true".equalsIgnoreCase(component.attributeValue("public")))
55                  {
56                      Element osgiService = root.addElement("osgi:service");
57                      osgiService.addAttribute("id", component.attributeValue("key") + "_osgiService");
58                      osgiService.addAttribute("ref", component.attributeValue("key"));
59  
60                      List<String> interfaceNames = new ArrayList<String>();
61                      List<Element> compInterfaces = component.elements("interface");
62                      for (Element inf : compInterfaces)
63                      {
64                          interfaceNames.add(inf.getTextTrim());
65                      }
66                      if (component.attributeValue("interface") != null)
67                      {
68                          interfaceNames.add(component.attributeValue("interface"));
69                      }
70  
71                      Element interfaces = osgiService.addElement("osgi:interfaces");
72                      for (String name : interfaceNames)
73                      {
74                          ensureExported(name, context);
75                          Element e = interfaces.addElement("beans:value");
76                          e.setText(name);
77                      }
78  
79                      Element svcprops = component.element("service-properties");
80                      if (svcprops != null)
81                      {
82                          Element targetSvcprops = osgiService.addElement("osgi:service-properties");
83                          for (Element prop : new ArrayList<Element>(svcprops.elements("entry")))
84                          {
85                              Element e = targetSvcprops.addElement("beans:entry");
86                              e.addAttribute("key", prop.attributeValue("key"));
87                              e.addAttribute("value", prop.attributeValue("value"));
88                          }
89                      }
90                  }
91              }
92              if (root.elements().size() > 0)
93              {
94                  context.setShouldRequireSpring(true);
95                  context.getFileOverrides().put(SPRING_XML, SpringHelper.documentToBytes(springDoc));
96              }
97          }
98      }
99  
100     void ensureExported(String className, TransformContext context)
101     {
102         String pkg = className.substring(0, className.lastIndexOf('.'));
103         if (!context.getExtraExports().contains(pkg))
104         {
105             String fileName = className.replace('.','/') + ".class";
106             
107             if (context.getPluginArtifact().doesResourceExist(fileName))
108             {
109                 context.getExtraExports().add(pkg);
110             }
111         }
112     }
113 
114 }