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