View Javadoc
1   package org.codehaus.plexus.classworlds.launcher;
2   
3   /*
4    * Copyright 2001-2006 Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.BufferedReader;
20  import java.io.File;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.nio.charset.StandardCharsets;
28  import java.nio.file.Files;
29  import java.nio.file.Paths;
30  import java.util.Properties;
31  
32  import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
33  import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
34  
35  /**
36   * Event based launcher configuration parser, delegating effective configuration handling to ConfigurationHandler.
37   *
38   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
39   * @author Jason van Zyl
40   * @author Igor Fedorenko
41   * @see ConfigurationHandler
42   */
43  public class ConfigurationParser {
44      public static final String MAIN_PREFIX = "main is";
45  
46      public static final String SET_PREFIX = "set";
47  
48      public static final String IMPORT_PREFIX = "import";
49  
50      public static final String LOAD_PREFIX = "load";
51  
52      /**
53       * Optionally spec prefix.
54       */
55      public static final String OPTIONALLY_PREFIX = "optionally";
56  
57      protected static final String FROM_SEPARATOR = " from ";
58  
59      protected static final String USING_SEPARATOR = " using ";
60  
61      protected static final String DEFAULT_SEPARATOR = " default ";
62  
63      private final ConfigurationHandler handler;
64  
65      private final Properties systemProperties;
66  
67      public ConfigurationParser(ConfigurationHandler handler, Properties systemProperties) {
68          this.handler = handler;
69          this.systemProperties = systemProperties;
70      }
71  
72      /**
73       * Parse launcher configuration file and send events to the handler.
74       *
75       * @param is the inputstream
76       * @throws IOException when IOException occurs
77       * @throws ConfigurationException when ConfigurationException occurs
78       * @throws DuplicateRealmException when realm already exists
79       * @throws NoSuchRealmException when realm doesn't exist
80       */
81      public void parse(InputStream is)
82              throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException {
83          try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
84  
85              String line;
86  
87              int lineNo = 0;
88  
89              boolean mainSet = false;
90  
91              String curRealm = null;
92  
93              while (true) {
94                  line = reader.readLine();
95  
96                  if (line == null) {
97                      break;
98                  }
99  
100                 ++lineNo;
101                 line = line.trim();
102 
103                 if (canIgnore(line)) {
104                     continue;
105                 }
106 
107                 char lineFirstChar = line.charAt(0);
108                 switch (lineFirstChar) {
109                     case 'm': {
110                         if (line.startsWith(MAIN_PREFIX)) {
111                             if (mainSet) {
112                                 throw new ConfigurationException("Duplicate main configuration", lineNo, line);
113                             }
114 
115                             int fromLoc = line.indexOf(FROM_SEPARATOR, MAIN_PREFIX.length());
116 
117                             if (fromLoc < 0) {
118                                 throw new ConfigurationException("Missing from clause", lineNo, line);
119                             }
120 
121                             String mainClassName = filter(line.substring(MAIN_PREFIX.length(), fromLoc)
122                                     .trim());
123 
124                             String mainRealmName = filter(line.substring(fromLoc + FROM_SEPARATOR.length())
125                                     .trim());
126 
127                             this.handler.setAppMain(mainClassName, mainRealmName);
128 
129                             mainSet = true;
130 
131                             break;
132                         }
133                         throw new ConfigurationException("Unhandled configuration", lineNo, line);
134                     }
135                     case 's': {
136                         if (line.startsWith(SET_PREFIX)) {
137                             String conf = line.substring(SET_PREFIX.length()).trim();
138 
139                             int usingLoc = conf.indexOf(USING_SEPARATOR);
140 
141                             String property = null;
142 
143                             String propertiesFileName = null;
144 
145                             if (usingLoc >= 0) {
146                                 property = conf.substring(0, usingLoc).trim();
147 
148                                 propertiesFileName = filter(conf.substring(usingLoc + USING_SEPARATOR.length())
149                                         .trim());
150 
151                                 conf = propertiesFileName;
152                             }
153 
154                             String defaultValue = null;
155 
156                             int defaultLoc = conf.indexOf(DEFAULT_SEPARATOR);
157 
158                             if (defaultLoc >= 0) {
159                                 defaultValue = filter(conf.substring(defaultLoc + DEFAULT_SEPARATOR.length())
160                                         .trim());
161 
162                                 if (property == null) {
163                                     property = conf.substring(0, defaultLoc).trim();
164                                 } else {
165                                     propertiesFileName =
166                                             conf.substring(0, defaultLoc).trim();
167                                 }
168                             }
169 
170                             String value = systemProperties.getProperty(property);
171 
172                             if (value != null) {
173                                 continue;
174                             }
175 
176                             if (propertiesFileName != null) {
177                                 File propertiesFile = new File(propertiesFileName);
178 
179                                 if (propertiesFile.exists()) {
180                                     Properties properties = new Properties();
181 
182                                     try (InputStream inputStream =
183                                             Files.newInputStream(Paths.get(propertiesFileName))) {
184                                         properties.load(inputStream);
185                                         value = properties.getProperty(property);
186                                     } catch (Exception e) {
187                                         // do nothing
188                                     }
189                                 }
190                             }
191 
192                             if (value == null && defaultValue != null) {
193                                 value = defaultValue;
194                             }
195 
196                             if (value != null) {
197                                 value = filter(value);
198                                 systemProperties.setProperty(property, value);
199                             }
200 
201                             break;
202                         }
203                         throw new ConfigurationException("Unhandled configuration", lineNo, line);
204                     }
205                     case '[': {
206                         int rbrack = line.indexOf("]");
207 
208                         if (rbrack < 0) {
209                             throw new ConfigurationException("Invalid realm specifier", lineNo, line);
210                         }
211 
212                         String realmName = line.substring(1, rbrack);
213 
214                         handler.addRealm(realmName);
215 
216                         curRealm = realmName;
217 
218                         break;
219                     }
220                     case 'i': {
221                         if (line.startsWith(IMPORT_PREFIX)) {
222                             if (curRealm == null) {
223                                 throw new ConfigurationException("Unhandled import", lineNo, line);
224                             }
225                             int fromLoc = line.indexOf(FROM_SEPARATOR, IMPORT_PREFIX.length());
226 
227                             if (fromLoc < 0) {
228                                 throw new ConfigurationException("Missing from clause", lineNo, line);
229                             }
230 
231                             String importSpec = line.substring(IMPORT_PREFIX.length(), fromLoc)
232                                     .trim();
233 
234                             String relamName = line.substring(fromLoc + FROM_SEPARATOR.length())
235                                     .trim();
236 
237                             handler.addImportFrom(relamName, importSpec);
238 
239                             break;
240                         }
241                         throw new ConfigurationException("Unhandled configuration", lineNo, line);
242                     }
243                     case 'l': {
244                         if (line.startsWith(LOAD_PREFIX)) {
245                             String constituent =
246                                     line.substring(LOAD_PREFIX.length()).trim();
247 
248                             constituent = filter(constituent);
249 
250                             if (constituent.contains("*")) {
251                                 loadGlob(constituent, false /*not optionally*/);
252                             } else {
253                                 File file = new File(constituent);
254 
255                                 if (file.exists()) {
256                                     handler.addLoadFile(file);
257                                 } else {
258                                     try {
259                                         handler.addLoadURL(new URL(constituent));
260                                     } catch (MalformedURLException e) {
261                                         throw new FileNotFoundException(constituent);
262                                     }
263                                 }
264                             }
265 
266                             break;
267                         }
268                         throw new ConfigurationException("Unhandled configuration", lineNo, line);
269                     }
270                     case 'o': {
271                         if (line.startsWith(OPTIONALLY_PREFIX)) {
272                             String constituent =
273                                     line.substring(OPTIONALLY_PREFIX.length()).trim();
274 
275                             constituent = filter(constituent);
276 
277                             if (constituent.contains("*")) {
278                                 loadGlob(constituent, true /*optionally*/);
279                             } else {
280                                 File file = new File(constituent);
281 
282                                 if (file.exists()) {
283                                     handler.addLoadFile(file);
284                                 } else {
285                                     try {
286                                         handler.addLoadURL(new URL(constituent));
287                                     } catch (MalformedURLException e) {
288                                         // swallow
289                                     }
290                                 }
291                             }
292 
293                             break;
294                         }
295                         throw new ConfigurationException("Unhandled configuration", lineNo, line);
296                     }
297                     default:
298                         throw new ConfigurationException("Unhandled configuration", lineNo, line);
299                 }
300             }
301         }
302     }
303 
304     /**
305      * Load a glob into the specified classloader.
306      *
307      * @param line       The path configuration line.
308      * @param optionally Whether the path is optional or required
309      * @throws MalformedURLException If the line does not represent
310      *                               a valid path element.
311      * @throws FileNotFoundException If the line does not represent
312      *                               a valid path element in the filesystem.
313      * @throws ConfigurationException will never occur (thrown for backwards compatibility)
314      */
315     protected void loadGlob(String line, boolean optionally)
316             throws MalformedURLException, FileNotFoundException, ConfigurationException {
317         File globFile = new File(line);
318 
319         File dir = globFile.getParentFile();
320         if (!dir.exists()) {
321             if (optionally) {
322                 return;
323             } else {
324                 throw new FileNotFoundException(dir.toString());
325             }
326         }
327 
328         String localName = globFile.getName();
329 
330         int starLoc = localName.indexOf("*");
331 
332         final String prefix = localName.substring(0, starLoc);
333 
334         final String suffix = localName.substring(starLoc + 1);
335 
336         File[] matches = dir.listFiles((dir1, name) -> {
337             if (!name.startsWith(prefix)) {
338                 return false;
339             }
340 
341             if (!name.endsWith(suffix)) {
342                 return false;
343             }
344 
345             return true;
346         });
347 
348         for (File match : matches) {
349             handler.addLoadFile(match);
350         }
351     }
352 
353     /**
354      * Filter a string for system properties.
355      *
356      * @param text The text to filter.
357      * @return The filtered text.
358      * @throws ConfigurationException If the property does not
359      *                                exist or if there is a syntax error.
360      */
361     protected String filter(String text) throws ConfigurationException {
362         StringBuilder result = new StringBuilder();
363 
364         int cur = 0;
365         int textLen = text.length();
366 
367         int propStart;
368         int propStop;
369 
370         String propName;
371         String propValue;
372 
373         while (cur < textLen) {
374             propStart = text.indexOf("${", cur);
375 
376             if (propStart < 0) {
377                 break;
378             }
379 
380             result.append(text, cur, propStart);
381 
382             propStop = text.indexOf("}", propStart);
383 
384             if (propStop < 0) {
385                 throw new ConfigurationException("Unterminated property: " + text.substring(propStart));
386             }
387 
388             propName = text.substring(propStart + 2, propStop);
389 
390             propValue = systemProperties.getProperty(propName);
391 
392             /* do our best if we are not running from surefire */
393             if (propName.equals("basedir") && (propValue == null || propValue.equals(""))) {
394                 propValue = (new File("")).getAbsolutePath();
395             }
396 
397             if (propValue == null) {
398                 throw new ConfigurationException("No such property: " + propName);
399             }
400             result.append(propValue);
401 
402             cur = propStop + 1;
403         }
404 
405         result.append(text.substring(cur));
406 
407         return result.toString();
408     }
409 
410     /**
411      * Determine if a line can be ignored because it is
412      * a comment or simply blank.
413      *
414      * @param line The line to test.
415      * @return <code>true</code> if the line is ignorable,
416      *         otherwise <code>false</code>.
417      */
418     private boolean canIgnore(String line) {
419         return (line.isEmpty() || line.startsWith("#"));
420     }
421 }