View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.common.config;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URI;
25  import java.net.URISyntaxException;
26  import java.net.URL;
27  import java.util.Collections;
28  import java.util.Enumeration;
29  import java.util.Properties;
30  import java.util.SortedSet;
31  import java.util.TreeSet;
32  import java.util.jar.Attributes;
33  import java.util.jar.Attributes.Name;
34  import java.util.jar.Manifest;
35  import java.util.stream.Collectors;
36  
37  import org.apache.logging.log4j.LogManager;
38  import org.apache.logging.log4j.Logger;
39  import org.mycore.common.MCRClassTools;
40  
41  import com.google.common.collect.Sets;
42  
43  /**
44   * On first access this class detects all components, that is either MyCoRe components or application modules, that are
45   * available via the current ClassLoader. Every {@link Manifest} of the jar file requires to have a main attribute "POM"
46   * and application modules also need to have a "MCR-Application-Module" main attribute present.
47   * 
48   * @author Thomas Scheffler (yagee)
49   * @since 2013.12
50   */
51  public class MCRRuntimeComponentDetector {
52  
53      private static Logger LOGGER = LogManager.getLogger(MCRRuntimeComponentDetector.class);
54  
55      private static final Name ATT_POM = new Name("POM");
56  
57      private static final Name ATT_MCR_APPLICATION_MODULE = new Name("MCR-Application-Module");
58  
59      private static final Name ATT_MCR_ARTIFACT_ID = new Name("MCR-Artifact-Id");
60  
61      private static SortedSet<MCRComponent> ALL_COMPONENTS = Collections
62          .unmodifiableSortedSet(getConfiguredComponents());
63  
64      private static SortedSet<MCRComponent> MYCORE_COMPONENTS = Collections.unmodifiableSortedSet(
65          ALL_COMPONENTS.stream()
66              .filter(MCRComponent::isMyCoReComponent)
67              .collect(Collectors.toCollection(TreeSet::new)));
68  
69      private static SortedSet<MCRComponent> APP_MODULES = Collections.unmodifiableSortedSet(
70          ALL_COMPONENTS.stream()
71              .filter(MCRComponent::isAppModule)
72              .collect(Collectors.toCollection(TreeSet::new)));
73  
74      private static SortedSet<MCRComponent> getConfiguredComponents() {
75          try {
76              String underTesting = System.getProperty("MCRRuntimeComponentDetector.underTesting");
77              Enumeration<URL> resources = MCRClassTools.getClassLoader().getResources("META-INF/MANIFEST.MF");
78              if (!resources.hasMoreElements() && underTesting == null) {
79                  LOGGER.warn("Did not find any Manifests.");
80                  return Collections.emptySortedSet();
81              }
82              SortedSet<MCRComponent> components = Sets.newTreeSet();
83              while (resources.hasMoreElements()) {
84                  URL manifestURL = resources.nextElement();
85                  try (InputStream manifestStream = manifestURL.openStream()) {
86                      Manifest manifest = new Manifest(manifestStream);
87                      MCRComponent component = buildComponent(manifest, manifestURL);
88  
89                      if (component != null) {
90                          components.add(component);
91                      }
92                  }
93              }
94              if (underTesting != null) {
95                  //support JUnit-Tests
96                  MCRComponent component = new MCRComponent(underTesting, new Manifest());
97                  components.add(component);
98              }
99              return components;
100         } catch (IOException e) {
101             LOGGER.warn("Error while detecting MyCoRe components", e);
102             return Sets.newTreeSet();
103         }
104     }
105 
106     private static MCRComponent buildComponent(Manifest manifest, URL manifestURL) throws IOException {
107         Attributes mainAttributes = manifest.getMainAttributes();
108         String artifactId = mainAttributes.getValue(ATT_MCR_ARTIFACT_ID);
109         String pomPropertiesPath = mainAttributes.getValue(ATT_POM);
110         boolean usePomProperties = false;
111 
112         if (artifactId == null) {
113             if (!mainAttributes.containsKey(ATT_POM)) {
114                 return null;
115             }
116 
117             if (pomPropertiesPath == null) {
118                 return null;
119             }
120 
121             try (InputStream pi = MCRClassTools.getClassLoader().getResourceAsStream(
122                 pomPropertiesPath)) {
123                 if (pi == null) {
124                     LOGGER.warn("Manifest entry {} set to \"{}\", but resource could not be loaded.", ATT_POM,
125                         pomPropertiesPath);
126                     return null;
127                 }
128                 Properties pomProperties = new Properties();
129                 pomProperties.load(pi);
130                 artifactId = (String) pomProperties.get("artifactId");
131                 usePomProperties = true;
132             }
133         }
134 
135         if (artifactId != null && artifactId.startsWith("mycore-")
136             || mainAttributes.containsKey(ATT_MCR_APPLICATION_MODULE)) {
137             if (usePomProperties) {
138                 LOGGER.warn("No Attribute \"{}\" in Manifest of {}.", ATT_MCR_ARTIFACT_ID,
139                     mainAttributes.getValue(ATT_MCR_APPLICATION_MODULE));
140                 LOGGER.warn("Change this in the future, pom.properties path definition is deprecated.");
141                 LOGGER.info("Using artifactId in {}.", pomPropertiesPath);
142             }
143 
144             return new MCRComponent(artifactId, manifest, extractJarFile(manifestURL));
145         }
146         return null;
147     }
148 
149     private static File extractJarFile(URL manifestURL) {
150         try {
151             if (manifestURL.toExternalForm().startsWith("jar:")) {
152                 return new File(new URI(manifestURL.getPath().replaceAll("!.*$", "")));
153             }
154         } catch (URISyntaxException e) {
155             LOGGER.error("Couldn't extract jar file path from MANIFEST.MF url.", e);
156         }
157 
158         return null;
159     }
160 
161     /**
162      * Returns all components sorted via their natural ordering.
163      * 
164      * @see MCRComponent#compareTo(MCRComponent)
165      */
166     public static SortedSet<MCRComponent> getAllComponents() {
167         return ALL_COMPONENTS;
168     }
169 
170     /**
171      * Returns only mycore components sorted via their natural ordering.
172      * 
173      * @see MCRComponent#compareTo(MCRComponent)
174      */
175     public static SortedSet<MCRComponent> getMyCoReComponents() {
176         return MYCORE_COMPONENTS;
177     }
178 
179     /**
180      * Returns only application modules sorted via their natural ordering.
181      * 
182      * @see MCRComponent#compareTo(MCRComponent)
183      */
184     public static SortedSet<MCRComponent> getApplicationModules() {
185         return APP_MODULES;
186     }
187 
188 }