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