View Javadoc

1   package com.atlassian.plugin.schema.impl;
2   
3   import com.atlassian.plugin.ModuleDescriptor;
4   import com.atlassian.plugin.ModuleDescriptorFactory;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.PluginParseException;
7   import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
8   import com.atlassian.plugin.descriptors.CannotDisable;
9   import com.atlassian.plugin.module.ContainerManagedPlugin;
10  import com.atlassian.plugin.module.ModuleFactory;
11  import com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory;
12  import com.atlassian.plugin.osgi.factory.OsgiPlugin;
13  import com.atlassian.plugin.schema.descriptor.DescribedModuleDescriptorFactory;
14  import com.atlassian.plugin.schema.spi.DocumentBasedSchema;
15  import com.atlassian.plugin.schema.spi.Schema;
16  import com.atlassian.plugin.schema.spi.SchemaFactory;
17  import com.atlassian.plugin.schema.spi.SchemaTransformer;
18  import com.atlassian.plugin.util.resource.AlternativeResourceLoader;
19  import com.atlassian.util.concurrent.NotNull;
20  import com.google.common.base.Function;
21  import com.google.common.base.Suppliers;
22  import com.google.common.collect.ImmutableList;
23  import org.dom4j.Element;
24  import org.osgi.framework.BundleContext;
25  
26  import java.io.InputStream;
27  import java.net.URL;
28  import java.util.List;
29  
30  import static com.atlassian.fugue.Option.option;
31  import static com.google.common.base.Preconditions.checkNotNull;
32  import static com.google.common.base.Preconditions.checkState;
33  import static com.google.common.collect.Iterables.transform;
34  
35  /**
36   * Descriptor that allows described module descriptor factories to be configured in XML.  Main value
37   * is the ability to reuse the name and description of the module descriptor configuration.
38   */
39  @CannotDisable
40  public final class DescribedModuleTypeModuleDescriptor extends AbstractModuleDescriptor<DescribedModuleDescriptorFactory>
41  {
42      private static final String[] PUBLIC_INTERFACES = new String[] {
43              ModuleDescriptorFactory.class.getName(),
44              ListableModuleDescriptorFactory.class.getName(),
45              DescribedModuleDescriptorFactory.class.getName()
46      };
47  
48      private String schemaFactoryClassName;
49      private String type;
50      private String schemaTransformerClassName;
51      private String maxOccurs;
52      private Iterable<String> requiredPermissions;
53      private Iterable<String> optionalPermissions;
54  
55      public DescribedModuleTypeModuleDescriptor(ModuleFactory moduleFactory)
56      {
57          super(moduleFactory);
58      }
59  
60      @Override
61      public void init(@NotNull Plugin plugin, @NotNull Element element) throws PluginParseException
62      {
63          checkState(plugin instanceof OsgiPlugin, "Described module types can only be declared in OSGi Plugins, %s is not such a plugin", plugin.getKey());
64  
65          super.init(plugin, element);
66  
67          this.type = getOptionalAttribute(element, "type", getKey());
68          this.schemaFactoryClassName = getOptionalAttribute(element, "schema-factory-class", null);
69          this.schemaTransformerClassName = getOptionalAttribute(element, "schema-transformer-class", null);
70          this.maxOccurs = getOptionalAttribute(element, "max-occurs", "unbounded");
71          this.requiredPermissions = getPermissions(element.element("required-permissions"));
72          this.optionalPermissions = getPermissions(element.element("optional-permissions"));
73      }
74  
75      private static Iterable<String> getPermissions(Element element)
76      {
77          return option(element).fold(
78                  Suppliers.ofInstance(ImmutableList.<String>of()),
79                  new Function<Element, Iterable<String>>()
80                  {
81                      @Override
82                      public Iterable<String> apply(Element e)
83                      {
84                          return transform(getElements(e, "permission"), new Function<Element, String>()
85                          {
86                              @Override
87                              public String apply(Element input)
88                              {
89                                  return input.getTextTrim();
90                              }
91                          });
92                      }
93                  });
94      }
95  
96      @SuppressWarnings("unchecked")
97      private static List<Element> getElements(Element element, String name)
98      {
99          return element.elements(name);
100     }
101 
102     @Override
103     public void enabled()
104     {
105         checkState(plugin instanceof OsgiPlugin, "Described module types can only be declared in OSGi Plugins, %s is not such a plugin", plugin.getKey());
106 
107         super.enabled();
108 
109         final SchemaTransformer schemaTransformer = schemaTransformerClassName != null
110                 ? create(findClass(schemaTransformerClassName, SchemaTransformer.class))
111                 : SchemaTransformer.IDENTITY;
112 
113         final SchemaFactory schemaFactory = schemaFactoryClassName != null
114                 ? create(findClass(schemaFactoryClassName, SchemaFactory.class))
115                 : buildSingleton(DocumentBasedSchema.builder(type)
116                 .setResourceLoader(new AlternativePluginResourceLoader(plugin))
117                 .setName(getName() != null ? getName() : getKey())
118                 .setDescription(getDescription() != null ? getDescription() : "")
119                 .setTransformer(schemaTransformer)
120                 .setMaxOccurs(maxOccurs)
121                 .setRequiredPermissions(requiredPermissions)
122                 .setOptionalPermissions(optionalPermissions)
123                 .build()
124         );
125 
126         @SuppressWarnings("unchecked")
127         final DescribedModuleDescriptorFactory factory = new DescribedModuleTypeDescribedModuleDescriptorFactory(
128                 (ContainerManagedPlugin) plugin,
129                 type,
130                 findClass(moduleClassName, ModuleDescriptor.class),
131                 schemaFactory);
132 
133         getBundleContext().registerService(PUBLIC_INTERFACES, factory, null);
134     }
135 
136     private BundleContext getBundleContext()
137     {
138         return ((OsgiPlugin) plugin).getBundle().getBundleContext();
139     }
140 
141     private <T> T create(Class<? extends T> type)
142     {
143         return ((ContainerManagedPlugin) plugin).getContainerAccessor().createBean(type);
144     }
145 
146     private <T> Class<? extends T> findClass(String className, Class<T> castTo)
147     {
148         checkNotNull(className);
149         Class<T> clazz;
150         try
151         {
152             clazz = plugin.loadClass(className, getClass());
153         }
154         catch (ClassNotFoundException e)
155         {
156             throw new PluginParseException("Unable to find class " + className);
157         }
158         return clazz.asSubclass(castTo);
159     }
160 
161     @Override
162     public DescribedModuleDescriptorFactory getModule()
163     {
164         return moduleFactory.createModule(moduleClassName, this);
165     }
166 
167     private SchemaFactory buildSingleton(final Schema schema)
168     {
169         return new SchemaFactory()
170         {
171             @Override
172             public Schema getSchema()
173             {
174                 return schema;
175             }
176         };
177     }
178 
179     public static String getOptionalAttribute(Element e, String name, Object defaultValue)
180     {
181         String value = e.attributeValue(name);
182         return value != null ? value :
183                 defaultValue != null ? defaultValue.toString() : null;
184     }
185 
186     private static final class AlternativePluginResourceLoader implements AlternativeResourceLoader
187     {
188         private final Plugin plugin;
189 
190         public AlternativePluginResourceLoader(final Plugin plugin)
191         {
192             this.plugin = checkNotNull(plugin);
193         }
194 
195         @Override
196         public URL getResource(final String path)
197         {
198             return plugin.getResource(path);
199         }
200 
201         @Override
202         public InputStream getResourceAsStream(final String name)
203         {
204             return plugin.getResourceAsStream(name);
205         }
206     }
207 }