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
37
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 }