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