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
40
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
143
144
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 }