View Javadoc
1   package com.atlassian.activeobjects.plugin;
2   
3   import com.atlassian.activeobjects.ActiveObjectsPluginException;
4   import com.atlassian.activeobjects.EntitiesValidator;
5   import com.atlassian.activeobjects.admin.PluginInfo;
6   import com.atlassian.activeobjects.admin.PluginToTablesMapping;
7   import com.atlassian.activeobjects.config.ActiveObjectsConfiguration;
8   import com.atlassian.activeobjects.config.ActiveObjectsConfigurationFactory;
9   import com.atlassian.activeobjects.external.ActiveObjectsUpgradeTask;
10  import com.atlassian.activeobjects.osgi.OsgiServiceUtils;
11  import com.atlassian.plugin.Plugin;
12  import com.atlassian.plugin.PluginParseException;
13  import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
14  import com.atlassian.plugin.module.ContainerManagedPlugin;
15  import com.atlassian.plugin.module.ModuleFactory;
16  import com.atlassian.plugin.osgi.factory.OsgiPlugin;
17  import com.google.common.base.Function;
18  import com.google.common.collect.Iterables;
19  import com.google.common.collect.Lists;
20  import com.google.common.collect.Sets;
21  import net.java.ao.RawEntity;
22  import net.java.ao.schema.TableNameConverter;
23  import org.dom4j.Element;
24  import org.osgi.framework.Bundle;
25  import org.osgi.framework.ServiceRegistration;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import java.util.List;
30  import java.util.Set;
31  
32  import static com.google.common.base.Preconditions.checkNotNull;
33  import static com.google.common.collect.Lists.newLinkedList;
34  
35  /**
36   * <p>The module descriptor for active objects.</p>
37   * <p>This parses the 'ao' module definition and registers a 'bundle specific'
38   * {@link com.atlassian.activeobjects.config.ActiveObjectsConfiguration configuration}
39   * as an OSGi service.</p>
40   * <p>This configuration is then looked up when the active object service is requested by the given bundle
41   * through a &lt;component-import ... &gt; module to configure the service appropriately.</p>
42   */
43  public class ActiveObjectModuleDescriptor extends AbstractModuleDescriptor<Object> {
44  
45      private final Logger logger = LoggerFactory.getLogger(this.getClass());
46  
47      private final ActiveObjectsConfigurationFactory configurationFactory;
48      private final OsgiServiceUtils osgiUtils;
49      private final EntitiesValidator entitiesValidator;
50      private final PluginToTablesMapping pluginToTablesMapping;
51  
52      /**
53       * The service registration for the active objects configuration, defined by this plugin.
54       */
55      private ServiceRegistration activeObjectsConfigurationServiceRegistration;
56      private ServiceRegistration tableNameConverterServiceRegistration;
57  
58      private ActiveObjectsConfiguration configuration;
59  
60      ActiveObjectModuleDescriptor(ModuleFactory moduleFactory,
61                                   ActiveObjectsConfigurationFactory configurationFactory,
62                                   OsgiServiceUtils osgiUtils,
63                                   PluginToTablesMapping pluginToTablesMapping,
64                                   EntitiesValidator entitiesValidator) {
65          super(moduleFactory);
66          this.configurationFactory = checkNotNull(configurationFactory);
67          this.osgiUtils = checkNotNull(osgiUtils);
68          this.pluginToTablesMapping = checkNotNull(pluginToTablesMapping);
69          this.entitiesValidator = checkNotNull(entitiesValidator);
70      }
71  
72      @Override
73      public void init(Plugin plugin, Element element) throws PluginParseException {
74          super.init(plugin, element);
75  
76          final Set<Class<? extends RawEntity<?>>> entities = getEntities(element);
77          final List<ActiveObjectsUpgradeTask> upgradeTasks = getUpgradeTasks(element);
78          configuration = getActiveObjectsConfiguration(getNameSpace(element), entities, upgradeTasks);
79  
80          final Set<Class<? extends RawEntity<?>>> entityClasses = entitiesValidator.check(entities, configuration.getNameConverters());
81          recordTables(entityClasses, configuration.getNameConverters().getTableNameConverter());
82      }
83  
84      private List<ActiveObjectsUpgradeTask> getUpgradeTasks(Element element) {
85          final List<Element> upgradeTask = getSubElements(element, "upgradeTask");
86  
87          final List<Class<ActiveObjectsUpgradeTask>> classes = Lists.transform(upgradeTask, new Function<Element, Class<ActiveObjectsUpgradeTask>>() {
88              @Override
89              public Class<ActiveObjectsUpgradeTask> apply(Element utElement) {
90                  final String upgradeTaskClass = utElement.getText().trim();
91                  logger.debug("Found upgrade task class <{}>", upgradeTaskClass);
92                  return getUpgradeTaskClass(upgradeTaskClass);
93              }
94          });
95  
96          if (!(getPlugin() instanceof ContainerManagedPlugin)) {
97              throw new ActiveObjectsPluginException("Plugin " + getPlugin().getKey() + " " + getPlugin().getClass().getCanonicalName() + " is not a ContainerManagedPlugin, cannot wire context");
98          }
99          final ContainerManagedPlugin plugin = (ContainerManagedPlugin) getPlugin();
100         return Lists.transform(classes, new Function<Class<ActiveObjectsUpgradeTask>, ActiveObjectsUpgradeTask>() {
101             @Override
102             public ActiveObjectsUpgradeTask apply(Class<ActiveObjectsUpgradeTask> upgradeTaskClass) {
103                 return plugin.getContainerAccessor().createBean(upgradeTaskClass);
104             }
105         });
106     }
107 
108     private Class<ActiveObjectsUpgradeTask> getUpgradeTaskClass(String upgradeTask) {
109         try {
110             return getPlugin().loadClass(upgradeTask, getClass());
111         } catch (ClassNotFoundException e) {
112             throw new ActiveObjectsPluginException(e);
113         }
114     }
115 
116     private void recordTables(final Set<Class<? extends RawEntity<?>>> entityClasses, final TableNameConverter tableNameConverter) {
117         pluginToTablesMapping.add(PluginInfo.of(getPlugin()), Lists.transform(newLinkedList(entityClasses), new Function<Class<? extends RawEntity<?>>, String>() {
118             @Override
119             public String apply(Class<? extends RawEntity<?>> from) {
120                 return tableNameConverter.getName(from);
121             }
122         }));
123     }
124 
125     @Override
126     public void enabled() {
127         super.enabled();
128 
129         if (tableNameConverterServiceRegistration == null) {
130             tableNameConverterServiceRegistration = osgiUtils.registerService(getBundle(), TableNameConverter.class, configuration.getNameConverters().getTableNameConverter());
131         }
132         if (activeObjectsConfigurationServiceRegistration == null) {
133             activeObjectsConfigurationServiceRegistration = osgiUtils.registerService(getBundle(), ActiveObjectsConfiguration.class, configuration);
134         }
135     }
136 
137     @Override
138     public void disabled() {
139         unregister(activeObjectsConfigurationServiceRegistration);
140         activeObjectsConfigurationServiceRegistration = null;
141         unregister(tableNameConverterServiceRegistration);
142         tableNameConverterServiceRegistration = null;
143         super.disabled();
144     }
145 
146     @Override
147     public Object getModule() {
148         return null; // no module
149     }
150 
151     public ActiveObjectsConfiguration getConfiguration() {
152         return configuration;
153     }
154 
155     private ActiveObjectsConfiguration getActiveObjectsConfiguration(String namespace, Set<Class<? extends RawEntity<?>>> entities, List<ActiveObjectsUpgradeTask> upgradeTasks) {
156         return configurationFactory.getConfiguration(getBundle(), namespace, entities, upgradeTasks);
157     }
158 
159     private void unregister(ServiceRegistration serviceRegistration) {
160         if (serviceRegistration != null) {
161             try {
162                 serviceRegistration.unregister();
163             } catch (IllegalStateException ignored) {
164                 logger.debug("Service has already been unregistered", ignored);
165             }
166         }
167     }
168 
169     /**
170      * The table name space is either the custom namespace set by the product, or the bundle symbolic name
171      *
172      * @param element the 'ao' descriptor element
173      * @return the name space for names
174      */
175     private String getNameSpace(Element element) {
176         final String custom = element.attributeValue("namespace");
177         return custom != null ? custom : getBundle().getSymbolicName();
178     }
179 
180     private Set<Class<? extends RawEntity<?>>> getEntities(Element element) {
181         return Sets.newHashSet(Iterables.transform(getEntityClassNames(element), new Function<String, Class<? extends RawEntity<?>>>() {
182             public Class<? extends RawEntity<?>> apply(String entityClassName) {
183                 return getEntityClass(entityClassName);
184             }
185         }));
186     }
187 
188     private Class<? extends RawEntity<?>> getEntityClass(String entityClassName) {
189         try {
190             return getPlugin().loadClass(entityClassName, getClass());
191         } catch (ClassNotFoundException e) {
192             throw new ActiveObjectsPluginException(e);
193         }
194     }
195 
196     private Iterable<String> getEntityClassNames(Element element) {
197         return Iterables.transform(getSubElements(element, "entity"), new Function<Element, String>() {
198             public String apply(Element entityElement) {
199                 final String entityClassName = entityElement.getText().trim();
200                 logger.debug("Found entity class <{}>", entityClassName);
201                 return entityClassName;
202             }
203         });
204     }
205 
206     private Bundle getBundle() {
207         return ((OsgiPlugin) getPlugin()).getBundle();
208     }
209 
210     @SuppressWarnings("unchecked")
211     private static List<Element> getSubElements(Element element, String name) {
212         return element.elements(name);
213     }
214 }