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_css_utils",
22      service = { Module.class }
23  )
24  public class Module_css_utils extends Module {
25  
26      private static final Logger logger = LoggerFactory.getLogger(Module_css_utils.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_css_utils() {
35          super("css-utils",
36                "6.0.1",
37                "DAISY Pipeline 2 module :: CSS Utils");
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/css-utils/library.xpl".equals(name)) {
126                 tryResolveComponent("http://www.daisy.org/pipeline/modules/mediatype-utils/library.xpl",
127                                     "[2.0.2,3.0.0)");
128                 tryResolveComponent("http://www.daisy.org/pipeline/modules/file-utils/library.xsl",
129                                     "[4.3.2,5.0.0)");
130                 tryResolveXPathExtensionFunction("org.daisy.pipeline.css.impl.StylesheetParametersParser");
131                 tryResolveComponent("http://www.daisy.org/pipeline/modules/fileset-utils/library.xpl",
132                                     "[6.1.4,7.0.0)");
133                 tryResolveComponent("http://www.daisy.org/pipeline/modules/common-utils/library.xpl",
134                                     "[3.1.3,4.0.0)");
135             } else if ("http://www.daisy.org/pipeline/modules/css-utils/library.xsl".equals(name)) {
136                 tryResolveComponent("http://www.daisy.org/pipeline/modules/file-utils/library.xsl",
137                                     "[4.3.2,5.0.0)");
138                 tryResolveXPathExtensionFunction("org.daisy.pipeline.css.impl.StylesheetParametersParser");
139             }
140         } catch (ResolutionException re) {
141             logger.warn("Component " + component.getURI() + " can not be loaded: " + re.getMessage());
142             return false;
143         }
144         return super.addComponent(component);
145     }
146 
147     private void tryResolveXPathExtensionFunction(String className) throws ResolutionException {
148         try {
149             getResource("../" + className.replace('.', '/') + ".class");
150             return;
151         } catch (NoSuchFileException e) {
152         }
153         // if the function is not implemented in the same module, the class must be on the class path
154         try {
155             Class.forName(className);
156             return;
157         } catch (Throwable e) {
158         }
159         throw new ResolutionException(
160             "Unresolved Java dependency: no class " + className + " found on the class path");
161     }
162 }