1   package org.daisy.pipeline.modules.impl;
2   
3   import java.net.URI;
4   import java.nio.file.NoSuchFileException;
5   import java.util.LinkedList;
6   
7   import org.daisy.pipeline.modules.Component;
8   import org.daisy.pipeline.modules.Module;
9   import org.daisy.pipeline.modules.ResolutionException;
10  import org.daisy.pipeline.xmlcatalog.XmlCatalogParser;
11  import org.osgi.service.component.annotations.Activate;
12  import org.osgi.service.component.annotations.Reference;
13  import org.osgi.service.component.annotations.ReferenceCardinality;
14  import org.osgi.service.component.annotations.ReferencePolicy;
15  import org.slf4j.Logger;
16  import org.slf4j.LoggerFactory;
17  import org.slf4j.helpers.NOPLogger;
18  
19  @org.osgi.service.component.annotations.Component(
20      name = "org.daisy.pipeline.modules.impl.Module_common_utils",
21      service = { Module.class }
22  )
23  public class Module_common_utils extends Module {
24  
25      private static final Logger logger = LoggerFactory.getLogger(Module_common_utils.class);
26  
27      private boolean initialized = false;
28      private XmlCatalogParser catalogParser;
29      private final LinkedList<Component> componentsToAdd;
30      private final LinkedList<Component> componentsBeingAdded;
31  
32      public Module_common_utils() {
33          super("common-utils",
34                "3.1.3",
35                "DAISY Pipeline 2 module :: Common Utilities");
36          componentsToAdd = new LinkedList<>();
37          componentsBeingAdded = new LinkedList<>();
38      }
39  
40      @Reference(
41          name = "XmlCatalogParser",
42          unbind = "-",
43          service = XmlCatalogParser.class,
44          cardinality = ReferenceCardinality.MANDATORY,
45          policy = ReferencePolicy.STATIC
46      )
47      public void setParser(XmlCatalogParser parser) {
48          catalogParser = parser;
49      }
50  
51      @Activate
52      protected void init() {
53          logger.debug("Initializing module " + getName());
54          Module tmpModule = new Module(getName(), getVersion(), getTitle()) {
55              @Override public void resolveDependencies() {}
56              @Override public Logger getLogger() { return NOPLogger.NOP_LOGGER; }
57          };
58          Module.parseCatalog(tmpModule, catalogParser);
59          for (String e : tmpModule.getEntities()) {
60              addEntity(tmpModule.getEntity(e));
61              logger.debug("Loaded entity: " + e);
62          }
63          for (URI c : tmpModule.getComponents()) {
64              components.put(c, () -> null); // will be replaced during resolveDependencies()
65              componentsToAdd.add(tmpModule.getComponent(c));
66          }
67          logger.debug("Module " + getName() + " initialized, but dependencies have not been resolved yet");
68      }
69  
70      // Not calling resolveDependencies() during object construction yet because it depends
71      // on other modules, and other modules may depend on this module. resolveDependencies()
72      // will be called when the object gets bound in ModuleRegistry. The method may recursively
73      // call itself. It copes with that by activating components in a different order
74      // when called recursively. ModuleRegistry has protection against endless recursion.
75      @Override
76      public void resolveDependencies() {
77          if (!initialized) {
78              boolean recursive = !componentsBeingAdded.isEmpty();
79              if (recursive)
80                  logger.debug("Resolving dependencies of module " + getName() + " (continuing in recursive call)");
81              else
82                  logger.debug("Resolving dependencies of module " + getName());
83              while (true) {
84                  if (componentsToAdd.isEmpty()) {
85                      if (componentsBeingAdded.isEmpty())
86                          break;
87                      componentsToAdd.addAll(componentsBeingAdded);
88                      componentsBeingAdded.clear();
89                  }
90                  Component c = componentsToAdd.poll();
91                  componentsBeingAdded.add(c);
92                  if (addComponent(c)) // may call resolveDependencies() recursively
93                      logger.debug("Loaded component: " + c.getURI());
94                  componentsBeingAdded.remove(c);
95                  componentsToAdd.remove(c);
96              }
97              if (!initialized)
98                  logger.debug("Done resolving dependencies of module " + getName());
99              initialized = true;
100         }
101     }
102 
103     @Override
104     protected boolean addComponent(Component component) {
105         String name = component.getURI().toString();
106         try {
107             if ("http://www.daisy.org/pipeline/modules/common-utils/i18n.xsl".equals(name)) {
108                 tryResolveXPathExtensionFunction("org.daisy.pipeline.common.NormalizeLang");
109             }
110         } catch (ResolutionException re) {
111             logger.warn("Component " + component.getURI() + " can not be loaded: " + re.getMessage());
112             return false;
113         }
114         return super.addComponent(component);
115     }
116 
117     private void tryResolveXPathExtensionFunction(String className) throws ResolutionException {
118         try {
119             getResource("../" + className.replace('.', '/') + ".class");
120             return;
121         } catch (NoSuchFileException e) {
122         }
123         // if the function is not implemented in the same module, the class must be on the class path
124         try {
125             Class.forName(className);
126             return;
127         } catch (Throwable e) {
128         }
129         throw new ResolutionException(
130             "Unresolved Java dependency: no class " + className + " found on the class path");
131     }
132 }