View Javadoc

1   package org.lcsim.conditions;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.FileInputStream;
6   import java.io.FileNotFoundException;
7   import java.io.FileReader;
8   import java.io.IOException;
9   import java.io.InputStream;
10  import java.net.MalformedURLException;
11  import java.net.URL;
12  import java.util.ArrayList;
13  import java.util.Collections;
14  import java.util.HashSet;
15  import java.util.List;
16  import java.util.Properties;
17  import java.util.Set;
18  import java.util.zip.ZipEntry;
19  import java.util.zip.ZipFile;
20  
21  import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
22  import org.lcsim.conditions.readers.BaseClasspathConditionsReader;
23  import org.lcsim.conditions.readers.DirectoryConditionsReader;
24  import org.lcsim.conditions.readers.DummyConditionsReader;
25  import org.lcsim.conditions.readers.ZipConditionsReader;
26  import org.lcsim.util.cache.FileCache;
27  import org.lcsim.util.cache.FileCache.Validator;
28  
29  /**
30   * 
31   * @author tonyj
32   * @author Jeremy McCormick <jeremym@slac.stanford.edu>
33   */
34  public abstract class ConditionsReader {
35  
36      private static Properties aliases;
37      private static final File home = new File(FileCache.getCacheRoot(), ".lcsim");
38      private static FileCache cache;
39  
40      /**
41       * Called by {@link ConditionsManager#setDetector} to tell this reader that
42       * it needs to prepare itself for reading conditions for the specified
43       * detector and run.
44       * <p>
45       * The implementation provided by this class returns <tt>false</tt> if the
46       * specified detector name is equal to the name of the current detector
47       * known to the specified {@link ConditionsManager}, and throws
48       * <tt>IllegalArgumentException</tt> if it is not. Subclasses need to
49       * override this method if conditions might be different for different runs
50       * with the same detector.
51       * 
52       * @return <tt>true</tt> if conditions for the specified detector/run may be
53       *         different from conditions for the previous detector/run
54       *         combination; <tt>false</tt> otherwise.
55       * @throws IllegalArgumentException if this <tt>ConditionsReader</tt> cannot
56       *             handle the specified detector/run.
57       * @throws IOException if the reader fails to update for any other reason.
58       */
59      protected boolean update(ConditionsManager manager, String detectorName, int run) throws IOException {
60          if (detectorName.equals(manager.getDetector())) {
61              return false;
62          } else {
63          	// ?????
64              throw new IllegalArgumentException();
65          }
66      }
67  
68      /**
69       * Get a list of available detectors
70       */
71      // FIXME: This should be removed.
72      public static List<String> getDetectorNames() {
73          Set<String> set = new HashSet<String>();
74          if (aliases == null) {
75              aliases = loadAliases();
76          }
77          for (Object key : aliases.keySet()) {
78              set.add(key.toString());
79          }
80  
81          try {
82              if (cache == null) {
83                  cache = new FileCache(new File(home, "cache"));
84              }
85              // FIXME: Not even sure this taglist should be supported at all any longer.
86              File file = cache.getCachedFile(new URL("http://www.lcsim.org/detectors/taglist.txt"));
87              if (file != null) {
88                  BufferedReader reader = new BufferedReader(new FileReader(file));
89                  for (;;) {
90                      String line = reader.readLine();
91                      if (line == null) {
92                          break;
93                      }
94                      set.add(line);
95                  }
96                  reader.close();
97              }
98          } catch (Exception ex) {
99              System.err.println("Error reading file taglist.txt: " + ex);
100         }
101 
102         List result = new ArrayList(set);
103         Collections.sort(result);
104         return result;
105     }
106 
107     public static void addAlias(String alias, String target) {
108         if (aliases == null) {
109             aliases = loadAliases();
110         }
111         aliases.setProperty(alias, target);
112     }
113 
114     public static ConditionsReader createDummy() {
115         return new DummyConditionsReader();
116     }
117 
118     // FIXME: Alias should be a feature that is easily turned off.
119     private static String resolveAlias(final String detectorName) throws IOException {
120         String name = detectorName;
121         for (int i = 0;; i++) {
122             String alias = aliases.getProperty(name);
123             if (alias == null) {
124                 break;
125             }
126             if (i > 100) {
127                 throw new IOException("Recursive name translation: " + name);
128             }
129             name = alias;
130         }
131         return name;
132     }
133 
134     /**
135      * Try to find the conditions associated with this detector. For more
136      * details see @link
137      * http://confluence.slac.stanford.edu/display/ilc/Conditions+database
138      */
139     // FIXME: This method should not be present in this class.  It uses the ConditionsReader interface,
140     // as well as all its sub-classes.  Probably better put in the ConditionsManagerImplementation.
141     static ConditionsReader create(String detectorName, int run) throws ConditionsNotFoundException {
142         String name = detectorName;
143         try {
144             if (cache == null) {
145                 cache = new FileCache(new File(home, "cache"));
146             }
147             if (aliases == null) {
148                 aliases = loadAliases();
149             }
150 
151             name = resolveAlias(detectorName);
152               
153             if (name.contains(":")) {
154                 // Name is a URL.
155                 URL url = new URL(name);
156                 if (url.getProtocol().equals("file") && (url.getHost() == null || url.getHost().length() == 0)) {
157                     File file = new File(url.getPath());
158                     // Check if exists.
159                     if (!file.exists()) {
160                         throw new RuntimeException("The URL " + url.toString() + " used by detector " + name + " does not exist.");
161                     }
162                     if (file.isDirectory()) {
163                         return new DirectoryConditionsReader(file);
164                     } else {
165                         return new ZipConditionsReader(file);
166                     }
167                 } else {
168                     File file = downloadDetectorDescription(url);
169                     return new ZipConditionsReader(file);
170                 }
171             } else {
172 
173                 // Search the classpath for conditions.
174                 try {
175                     return new BaseClasspathConditionsReader(name);
176                 } catch (IOException x) {
177                     // System.out.println(x.getLocalizedMessage());
178                 }
179 
180                 // Search for a local, cached copy.
181                 File detectorDir = new File(home, "detectors"); // FIXME: Hard-coded directory location.
182                 File zipFile = new File(detectorDir, name + ".zip");
183                 if (zipFile.exists()) {
184                     return new ZipConditionsReader(zipFile);
185                 }
186                 File dirFile = new File(detectorDir, name);
187                 if (dirFile.exists() && dirFile.isDirectory()) {
188                     return new DirectoryConditionsReader(dirFile);
189                 }
190 
191                 // Finally, try to pull the detector conditions from the lcsim.org website.
192                 try {
193                     URL url = new URL("http://www.lcsim.org/detectors/" + name + ".zip"); // FIXME: Hard-coded URL.
194                     File file = downloadDetectorDescription(url);
195                     return new ZipConditionsReader(file);
196                 } catch (FileNotFoundException x) {
197                     throw new ConditionsNotFoundException(name, run);
198                 }
199             }
200         } catch (MalformedURLException x) {
201             throw new ConditionsNotFoundException(name, run, x);
202         } catch (IOException x) {
203             throw new ConditionsNotFoundException(name, run, x);
204         }
205     }
206 
207     /**
208      * Creates <tt>ConditionsReader</tt> to handle the specified detector and
209      * run.
210      * @throws ConditionsNotFoundException if creation of the reader fails for
211      *             any reason.
212      */
213     static ConditionsReader create(ConditionsManager manager, String detectorName, int run) throws ConditionsNotFoundException {
214         ConditionsReader reader = create(detectorName, run);
215         Properties prop = new Properties();
216         try {
217             InputStream in = reader.open("detector", "properties");
218             try {
219                 prop.load(in);
220             } finally {
221                 in.close();
222             }
223         } catch (IOException x) {
224             // For now: having failed to find or load detector.properties, use
225             // unmodified reader.  Uncomment the line below if we decide this should 
226             // be treated as an error.
227             //throw new ConditionsNotFoundException(detectorName, run, x);
228         }
229         // FIXME: The detector conditions themselves should not determine what reader is used.  
230         // This should be handled by configuring the conditions system itself directly.
231         String readerClassName = prop.getProperty("ConditionsReader");
232         if (readerClassName != null) {
233             try {
234                 Class readerClass = Class.forName(readerClassName);
235                 reader = (ConditionsReader) readerClass.getDeclaredConstructor(ConditionsReader.class).newInstance(reader);
236                 reader.update(manager, detectorName, run);
237             } catch (Exception x) {
238                 throw new ConditionsNotFoundException(detectorName, run, x);
239             }
240         }
241         return reader;
242     }
243 
244     abstract public InputStream open(String name, String type) throws IOException;
245 
246     abstract public void close() throws IOException;
247 
248     // FIXME: Aliasing is very dangerous when matching detectors with event data files.  
249     // There should be an option to turn this off.
250     private static Properties loadAliases() {
251         Properties result = new Properties();
252         try {
253             File f = new File(home, "alias.properties");
254             InputStream in = new FileInputStream(f);
255             if (in != null) {
256                 try {
257                     result.load(in);
258                 } finally {
259                     in.close();
260                 }
261             }
262         } catch (IOException x) {
263         }
264         return result;
265     }
266 
267     private static File downloadDetectorDescription(URL url) throws IOException {
268         return cache.getCachedFile(url, new DetectorFileValidator());
269     }
270 
271     private static class DetectorFileValidator implements Validator {
272 
273         public void checkValidity(URL url, File file) throws IOException {
274             // Check if the file looks good. It should contain a file called
275             // detector.properties in the root directory
276             ZipFile zip = new ZipFile(file, ZipFile.OPEN_READ);
277             try {
278                 ZipEntry header = zip.getEntry("detector.properties");
279                 if (header == null) {
280                     throw new IOException("No detector.properties entry in file downloaded from " + url);
281                 }
282                 Properties props = new Properties();
283                 props.load(zip.getInputStream(header));
284             } finally {
285                 zip.close();
286             }
287         }
288     }
289 }