View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import java.io.BufferedReader;
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.text.CollationKey;
28  import java.text.Collator;
29  import java.util.ArrayList;
30  import java.util.Comparator;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.regex.Matcher;
34  import java.util.regex.Pattern;
35  
36  import org.eclipse.jetty.start.graph.Node;
37  
38  /**
39   * Represents a Module metadata, as defined in Jetty.
40   */
41  public class Module extends Node<Module>
42  {
43      private static final String VERSION_UNSPECIFIED = "9.2";
44  
45      public static class NameComparator implements Comparator<Module>
46      {
47          private Collator collator = Collator.getInstance();
48  
49          @Override
50          public int compare(Module o1, Module o2)
51          {
52              // by name (not really needed, but makes for predictable test cases)
53              CollationKey k1 = collator.getCollationKey(o1.fileRef);
54              CollationKey k2 = collator.getCollationKey(o2.fileRef);
55              return k1.compareTo(k2);
56          }
57      }
58  
59      /** The file of the module */
60      private Path file;
61      
62      /** The name of this Module (as a filesystem reference) */
63      private String fileRef;
64      
65      /** The version of Jetty the module supports */
66      private Version version;
67  
68      /** List of xml configurations for this Module */
69      private List<String> xmls;
70      
71      /** List of ini template lines */
72      private List<String> iniTemplate;
73      private boolean hasIniTemplate = false;
74      
75      /** List of default config */
76      private List<String> defaultConfig;
77      private boolean hasDefaultConfig = false;
78      
79      /** List of library options for this Module */
80      private List<String> libs;
81      
82      /** List of files for this Module */
83      private List<String> files;
84      /** Skip File Validation (default: false) */
85      private boolean skipFilesValidation = false;
86      
87      /** List of jvm Args */
88      private List<String> jvmArgs;
89      
90      /** License lines */
91      private List<String> license;
92  
93      public Module(BaseHome basehome, Path file) throws FileNotFoundException, IOException
94      {
95          super();
96          this.file = file;
97  
98          // Strip .mod
99          this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getFileName().toString()).replaceFirst("");
100         this.setName(fileRef);
101 
102         init(basehome);
103         process(basehome);
104     }
105 
106     @Override
107     public boolean equals(Object obj)
108     {
109         if (this == obj)
110         {
111             return true;
112         }
113         if (obj == null)
114         {
115             return false;
116         }
117         if (getClass() != obj.getClass())
118         {
119             return false;
120         }
121         Module other = (Module)obj;
122         if (fileRef == null)
123         {
124             if (other.fileRef != null)
125             {
126                 return false;
127             }
128         }
129         else if (!fileRef.equals(other.fileRef))
130         {
131             return false;
132         }
133         return true;
134     }
135 
136     public void expandProperties(Props props)
137     {
138         // Expand Parents
139         List<String> parents = new ArrayList<>();
140         for (String parent : getParentNames())
141         {
142             parents.add(props.expand(parent));
143         }
144         setParentNames(parents);
145     }
146 
147     public List<String> getDefaultConfig()
148     {
149         return defaultConfig;
150     }
151     
152     public List<String> getIniTemplate()
153     {
154         return iniTemplate;
155     }
156 
157     public List<String> getFiles()
158     {
159         return files;
160     }
161 
162     public boolean isSkipFilesValidation()
163     {
164         return skipFilesValidation;
165     }
166 
167     public String getFilesystemRef()
168     {
169         return fileRef;
170     }
171 
172     public List<String> getJvmArgs()
173     {
174         return jvmArgs;
175     }
176 
177     public List<String> getLibs()
178     {
179         return libs;
180     }
181 
182     public List<String> getLicense()
183     {
184         return license;
185     }
186     
187     public List<String> getXmls()
188     {
189         return xmls;
190     }
191     
192     public Version getVersion()
193     {
194         return version;
195     }
196 
197     public boolean hasDefaultConfig()
198     {
199         return hasDefaultConfig;
200     }
201     
202     public boolean hasIniTemplate()
203     {
204         return hasIniTemplate;
205     }
206 
207     @Override
208     public int hashCode()
209     {
210         final int prime = 31;
211         int result = 1;
212         result = (prime * result) + ((fileRef == null)?0:fileRef.hashCode());
213         return result;
214     }
215 
216     public boolean hasLicense()
217     {
218         return (license != null) && (license.size() > 0);
219     }
220 
221     private void init(BaseHome basehome)
222     {
223         xmls = new ArrayList<>();
224         defaultConfig = new ArrayList<>();
225         iniTemplate = new ArrayList<>();
226         libs = new ArrayList<>();
227         files = new ArrayList<>();
228         jvmArgs = new ArrayList<>();
229         license = new ArrayList<>();
230 
231         String name = basehome.toShortForm(file);
232 
233         // Find module system name (usually in the form of a filesystem reference)
234         Pattern pat = Pattern.compile("^.*[/\\\\]{1}modules[/\\\\]{1}(.*).mod$",Pattern.CASE_INSENSITIVE);
235         Matcher mat = pat.matcher(name);
236         if (!mat.find())
237         {
238             throw new RuntimeException("Invalid Module location (must be located under /modules/ directory): " + name);
239         }
240         this.fileRef = mat.group(1).replace('\\','/');
241         setName(this.fileRef);
242     }
243 
244     /**
245      * Indicates a module that is dynamic in nature
246      * 
247      * @return a module where the declared metadata name does not match the filename reference (aka a dynamic module)
248      */
249     public boolean isDynamic()
250     {
251         return !getName().equals(fileRef);
252     }
253 
254     public boolean hasFiles(BaseHome baseHome, Props props)
255     {
256         for (String ref : getFiles())
257         {
258             FileArg farg = new FileArg(this,props.expand(ref));
259             Path refPath = baseHome.getBasePath(farg.location);
260             if (!Files.exists(refPath))
261             {
262                 return false;
263             }
264         }
265         return true;
266     }
267 
268     public void process(BaseHome basehome) throws FileNotFoundException, IOException
269     {
270         Pattern section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
271 
272         if (!FS.canReadFile(file))
273         {
274             StartLog.debug("Skipping read of missing file: %s",basehome.toShortForm(file));
275             return;
276         }
277 
278         try (BufferedReader buf = Files.newBufferedReader(file,StandardCharsets.UTF_8))
279         {
280             String sectionType = "";
281             String line;
282             while ((line = buf.readLine()) != null)
283             {
284                 line = line.trim();
285 
286                 Matcher sectionMatcher = section.matcher(line);
287 
288                 if (sectionMatcher.matches())
289                 {
290                     sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
291                 }
292                 else
293                 {
294                     // blank lines and comments are valid for ini-template section
295                     if ((line.length() == 0) || line.startsWith("#"))
296                     {
297                         // Remember ini comments and whitespace (empty lines)
298                         // for the [ini-template] section
299                         if ("INI-TEMPLATE".equals(sectionType))
300                         {
301                             iniTemplate.add(line);
302                             hasIniTemplate = true;
303                         }
304                     }
305                     else
306                     {
307                         switch (sectionType)
308                         {
309                             case "":
310                                 // ignore (this would be entries before first section)
311                                 break;
312                             case "DEPEND":
313                                 addParentName(line);
314                                 break;
315                             case "FILES":
316                                 files.add(line);
317                                 break;
318                             case "DEFAULTS": // old name introduced in 9.2.x
319                             case "INI": // new name for 9.3+
320                                 defaultConfig.add(line);
321                                 hasDefaultConfig = true;
322                                 break;
323                             case "INI-TEMPLATE":
324                                 iniTemplate.add(line);
325                                 hasIniTemplate = true;
326                                 break;
327                             case "LIB":
328                                 libs.add(line);
329                                 break;
330                             case "LICENSE":
331                             case "LICENCE":
332                                 license.add(line);
333                                 break;
334                             case "NAME":
335                                 setName(line);
336                                 break;
337                             case "OPTIONAL":
338                                 addOptionalParentName(line);
339                                 break;
340                             case "EXEC":
341                                 jvmArgs.add(line);
342                                 break;
343                             case "VERSION":
344                                 if (version != null)
345                                 {
346                                     throw new IOException("[version] already specified");
347                                 }
348                                 version = new Version(line);
349                                 break;
350                             case "XML":
351                                 xmls.add(line);
352                                 break;
353                             default:
354                                 throw new IOException("Unrecognized Module section: [" + sectionType + "]");
355                         }
356                     }
357                 }
358             }
359         }
360         
361         if (version == null)
362         {
363             version = new Version(VERSION_UNSPECIFIED);
364         }
365     }
366 
367     public void setEnabled(boolean enabled)
368     {
369         throw new RuntimeException("Don't enable directly");
370     }
371     
372     public void setSkipFilesValidation(boolean skipFilesValidation)
373     {
374         this.skipFilesValidation = skipFilesValidation;
375     }
376     
377     @Override
378     public String toString()
379     {
380         StringBuilder str = new StringBuilder();
381         str.append("Module[").append(getName());
382         if (isDynamic())
383         {
384             str.append(",file=").append(fileRef);
385         }
386         if (isSelected())
387         {
388             str.append(",selected");
389         }
390         str.append(']');
391         return str.toString();
392     }
393 }