View Javadoc

1   /***
2    * Copyright (c) 2003, 2004, Chess iT
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    * 
8    * - Redistributions of source code must retain the above copyright notice, this
9    *   list of conditions and the following disclaimer.
10   *
11   * - Redistributions in binary form must reproduce the above copyright notice,
12   *   this list of conditions and the following disclaimer in the documentation
13   *   and/or other materials provided with the distribution.
14   *
15   * - Neither the name of Chess iT, nor the names of its contributors may be used 
16   *   to endorse or promote products derived from this software without specific
17   *   prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
20   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
22   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
23   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
24   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
25   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
26   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
27   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
29   * POSSIBILITY OF SUCH DAMAGE.
30   * 
31   */
32  package nl.chess.it.util.config.resolv;
33  
34  import java.io.BufferedInputStream;
35  import java.io.ByteArrayInputStream;
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.IOException;
39  import java.io.InputStream;
40  import java.net.URL;
41  import java.util.ArrayList;
42  import java.util.List;
43  import java.util.Properties;
44  
45  import nl.chess.it.util.config.ConfigurationException;
46  
47  /***
48   * Opens a file or URL which name is given in an environment variable. Two situations can occur:
49   * <ul>
50   * <li>file is okay and readable: {@link #getSourceDescription()}and {@link #getProperties()}are
51   * garantueed to be not null</li>
52   * <li>file is not usable or cannot be read: {@link #getSourceDescription()}and
53   * {@link #getProperties()}might be null</li>
54   * </ul>
55   * <b>Note: </b> this class is still in development; the external interface might change (slightly)
56   * later.
57   * 
58   * @author Guus Bosman (Chess-iT)
59   */
60  public class ConfigurationResolverEnvironmentVariable {
61  
62      private Exception exception;
63      private String sourceDescription;
64      private Properties properties;
65  
66      public ConfigurationResolverEnvironmentVariable(final String key) {
67          String fileName = System.getProperty(key);
68          if (fileName == null) {
69              exception = new ConfigurationException(
70                      "no property for configuration file or URL was given as JVM argument. Please add the following to the java.exe startup command: -D"
71                              + key + "=<location of file>");
72              return;
73          }
74          boolean isURL = false;
75          try {
76              new URL(fileName);
77              isURL = true;
78          } catch (Exception e) {
79              // it's not an URL. Assume it's a file then.
80              isURL = false;
81          }
82          if (isURL) {
83              String interpretedURLName = null;
84              Properties tmpProperties = new Properties();
85              try {
86                  URL url = new URL(fileName);
87                  interpretedURLName = url.toExternalForm();
88  
89                  byte[] bytes = readEntireFileContents(url.openStream());
90  
91                  InputStream is = new ByteArrayInputStream(bytes);
92  
93                  PropertyInputStreamValidator.validate(is);
94                  // re-open
95                  is.reset();
96                  tmpProperties.load(is);
97              } catch (Exception e) {
98                  exception = new ConfigurationException("environment property '" + key + "' had value '" + fileName
99                          + "', which I interpreted as url '" + interpretedURLName
100                         + "' but this can't be used to read the configuration from: " + e.getMessage(), e);
101                 return;
102             }
103 
104             sourceDescription = "url " + interpretedURLName + " (from environment property '" + key + "')";
105             properties = tmpProperties;
106         } else {
107             File file = new File(fileName);
108             String interpretedFileName = null;
109             Properties tmpProperties = new Properties();
110             try {
111                 interpretedFileName = file.getCanonicalPath();
112 
113                 FileInputStream is = new FileInputStream(file);
114                 PropertyInputStreamValidator.validate(is);
115                 // re-open
116                 is = new FileInputStream(file);
117                 tmpProperties.load(is);
118             } catch (Exception e) {
119                 exception = new ConfigurationException("environment property '" + key + "' had value '" + fileName
120                         + "', which I interpreted as '" + interpretedFileName
121                         + "' but this can't be used to read the configuration from: " + e.getMessage(), e);
122                 return;
123             }
124 
125             sourceDescription = "file " + interpretedFileName + " (from environment property '" + key + "')";
126             properties = tmpProperties;
127         }
128 
129     }
130 
131     /***
132      * Returns the Exception that occurred while reading from the environment variable (if any).
133      * 
134      * @return Might be <code>null</code>.
135      */
136     public Exception getException() {
137         return exception;
138     }
139 
140     /***
141      * Description of the file or URL read.
142      * 
143      * @return If {@link #getException()}was not null then this might be <code>null</code>.
144      */
145     public String getSourceDescription() {
146         return sourceDescription;
147     }
148 
149     /***
150      * Returns Properties object for configuration
151      * 
152      * @return If {@link #getException()}was not null then this might be <code>null</code>.
153      */
154     public Properties getProperties() {
155         return properties;
156     }
157 
158     /***
159      * Reads a file into a byte-array. When an Exception is thrown the inputstream will always be
160      * closed. Memory expensive method, use only with limited size of InputStreams.
161      * 
162      * @param toRead the file to be read
163      * @return a file data byte-array
164      * @throws NullPointerException If toRead is <code>null</code>.
165      * @throws IOException if reading the file fails somehow
166      */
167     private static byte[] readEntireFileContents(InputStream toRead) throws IOException {
168         if (toRead == null) {
169             throw new NullPointerException("toRead cannot be null here.");
170         }
171 
172         InputStream in = null;
173         byte[] buf = null; // output buffer
174 
175         /***
176          * A large buffer for reading from the InputStream. The larger the better as it increases
177          * the speed in which the file can be read.
178          */
179         int bufLen = 10000 * 1024;
180 
181         try {
182             in = new BufferedInputStream(toRead);
183             buf = new byte[bufLen];
184 
185             byte[] tmp = null;
186             int len = 0;
187             List data = new ArrayList();
188 
189             while ((len = in.read(buf, 0, bufLen)) != -1) {
190                 tmp = new byte[len];
191                 System.arraycopy(buf, 0, tmp, 0, len);
192                 data.add(tmp);
193             }
194 
195             len = 0;
196 
197             /***
198              * Trivial case.
199              */
200             if (data.size() == 1) {
201                 return (byte[]) data.get(0);
202             }
203 
204             for (int i = 0; i < data.size(); i++)
205                 len += ((byte[]) data.get(i)).length;
206 
207             /***
208              * The result.
209              */
210             buf = new byte[len];
211             len = 0;
212 
213             for (int i = 0; i < data.size(); i++) {
214                 tmp = (byte[]) data.get(i);
215                 System.arraycopy(tmp, 0, buf, len, tmp.length);
216                 len += tmp.length;
217             }
218         } finally {
219             if (in != null) {
220                 try {
221                     in.close();
222                 } catch (Exception e) {
223                     throw new RuntimeException(e);
224                 }
225             }
226         }
227 
228         return buf;
229     }
230 }