View Javadoc

1   /*
2    * Copyright 2010 Capgemini and Contributors
3    *
4    * Licensed under the Apache License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.appstatus.core;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URL;
22  import java.util.ArrayList;
23  import java.util.Enumeration;
24  import java.util.List;
25  import java.util.Locale;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.Set;
29  import java.util.TreeMap;
30  import java.util.concurrent.Callable;
31  import java.util.concurrent.ExecutionException;
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.Future;
35  
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  import net.sf.appstatus.core.batch.IBatch;
40  import net.sf.appstatus.core.batch.IBatchManager;
41  import net.sf.appstatus.core.batch.IBatchProgressMonitor;
42  import net.sf.appstatus.core.batch.IBatchScheduleManager;
43  import net.sf.appstatus.core.check.CheckResultBuilder;
44  import net.sf.appstatus.core.check.IAppStatusAware;
45  import net.sf.appstatus.core.check.ICheck;
46  import net.sf.appstatus.core.check.ICheckResult;
47  import net.sf.appstatus.core.check.IConfigurationAware;
48  import net.sf.appstatus.core.loggers.ILoggersManager;
49  import net.sf.appstatus.core.property.IPropertyProvider;
50  import net.sf.appstatus.core.services.IService;
51  import net.sf.appstatus.core.services.IServiceManager;
52  import net.sf.appstatus.core.services.IServiceMonitor;
53  
54  /**
55   * This is the entry point for every feature of AppStatus.
56   *
57   * <p>
58   * Must be initialized once before calling other methods.
59   * <p>
60   * AppStatus status = new AppStatus(); <br/>
61   * status.init();
62   * </p>
63   *
64   * @author Nicolas Richeton
65   *
66   */
67  public class AppStatus {
68  
69      private static final String CONFIG_LOCATION = "status-check.properties";
70      private static Logger logger = LoggerFactory.getLogger(AppStatus.class);
71  
72      private IBatchManager batchManager = null;
73      private IBatchScheduleManager batchScheduleManager;
74      protected List<ICheck> checkers;
75      private Properties configuration = null;
76      private ExecutorService executorService = null;
77      private boolean initDone = false;
78      private ILoggersManager loggersManager = null;
79      private boolean maintenance = false;
80      private String maintenanceFile = null;
81      private IObjectInstantiationListener objectInstanciationListener = null;
82      private List<IPropertyProvider> propertyProviders;
83      private IServiceManager serviceManager = null;
84      private IServletContextProvider servletContextProvider = null;
85  
86      /**
87       * Status Service creator.
88       */
89      public AppStatus() {
90      }
91  
92      private void addPropertyProvider(String clazz) {
93          IPropertyProvider provider = (IPropertyProvider) getClassInstance(clazz);
94  
95          if (provider != null) {
96              propertyProviders.add(provider);
97              logger.info("Registered property provider " + clazz);
98          } else {
99              logger.error("cannot instanciate class {}, Please configure \"{}\" file properly", clazz, CONFIG_LOCATION);
100         }
101     }
102 
103     private void addStatusChecker(String clazz) {
104         ICheck check = (ICheck) getClassInstance(clazz);
105         if (check == null) {
106             logger.error("cannot instanciate class {}, Please configure \"{}\" file properly", clazz, CONFIG_LOCATION);
107             return;
108         }
109 
110         if (check instanceof IServletContextAware) {
111             ((IServletContextAware) check).setServletContext(servletContextProvider.getServletContext());
112         }
113 
114         checkers.add(check);
115         logger.info("Registered status checker {}", clazz);
116     }
117 
118     public List<ICheckResult> checkAll() {
119         return checkAll(null);
120     }
121 
122     public List<ICheckResult> checkAll(final Locale locale) {
123         checkInit();
124 
125         List<Future<ICheckResult>> statusFutureList = new ArrayList<Future<ICheckResult>>();
126         for (final ICheck check : checkers) {
127             injectServletContext(check);
128 
129             statusFutureList.add(executorService.submit(new Callable<ICheckResult>() {
130                 public ICheckResult call() throws Exception {
131                     // Inject Appstatus
132                     if (check instanceof IAppStatusAware) {
133                         ((IAppStatusAware) check).setAppStatus(AppStatus.this);
134                     }
135 
136                     // Inject configuration
137                     if (check instanceof IConfigurationAware) {
138                         ((IConfigurationAware) check).setConfiguration(getConfiguration());
139                     }
140                     return check.checkStatus(locale);
141                 }
142             }));
143         }
144 
145         ArrayList<ICheckResult> statusList = new ArrayList<ICheckResult>();
146 
147         for (int i = 0; i < statusFutureList.size(); i++) {
148             Future<ICheckResult> f = statusFutureList.get(i);
149             ICheck c = checkers.get(i);
150 
151             try {
152                 statusList.add(f.get());
153             } catch (InterruptedException e) {
154                 statusList.add(createCheckResultFromException(c, e));
155                 logger.error("", e);
156             } catch (ExecutionException e) {
157                 statusList.add(createCheckResultFromException(c, e));
158                 logger.error("", e);
159             }
160         }
161 
162         return statusList;
163     }
164 
165     private void checkInit() {
166         if (!initDone) {
167             logger.warn("Not initialized. Starting init");
168             init();
169         }
170     }
171 
172     private ICheckResult createCheckResultFromException(ICheck c, Exception e) {
173 
174         return new CheckResultBuilder().from(c).code(ICheckResult.ERROR).fatal() //
175                 .description("Check failed with exception: " + e.getClass().getCanonicalName() + " " + e.getMessage()) //
176                 .build();
177 
178     }
179 
180     private boolean existsAndReadable(String filename) {
181         return null != filename && new File(filename).canRead();
182     }
183 
184     public IBatchManager getBatchManager() {
185         return batchManager;
186     }
187 
188     public IBatchProgressMonitor getBatchProgressMonitor(String name, String group, String uuid) {
189 
190         checkInit();
191 
192         IBatch batch = null;
193         if (batchManager != null) {
194             batch = batchManager.addBatch(name, group, uuid);
195             return batchManager.getMonitor(batch);
196         }
197 
198         return null;
199     }
200 
201     /**
202      * @return the batchScheduleManager
203      */
204     public IBatchScheduleManager getBatchScheduleManager() {
205         return batchScheduleManager;
206     }
207 
208     /**
209      * Try to instantiate a class.
210      *
211      * @param className
212      * @return an object instance of "className" class or null if instantiation
213      *         is not possible
214      */
215     private Object getClassInstance(String className) {
216         Object obj = null;
217 
218         if (objectInstanciationListener != null) {
219             obj = objectInstanciationListener.getInstance(className);
220         }
221 
222         if (obj == null) {
223             try {
224                 obj = Thread.currentThread().getContextClassLoader().loadClass(className).newInstance();
225             } catch (ClassNotFoundException e) {
226                 logger.warn("Class {} not found ", className, e);
227             } catch (InstantiationException e) {
228                 logger.warn("Cannot instanciate {} ", className, e);
229             } catch (IllegalAccessException e) {
230                 logger.warn("Cannot access class {} for instantiation ", className, e);
231             }
232         }
233 
234         if (obj == null) {
235             try {
236                 obj = Class.forName(className).newInstance();
237                 logger.warn("Class {} loaded using a deprecated method. Please report to http://sourceforge.net/apps/mantisbt/appstatus/login_select_proj_page.php?ref=bug_report_page.php", className);
238             } catch (ClassNotFoundException e) {
239                 logger.warn("Class {} not found ", className, e);
240             } catch (InstantiationException e) {
241                 logger.warn("Cannot instanciate {} ", className, e);
242             } catch (IllegalAccessException e) {
243                 logger.warn("Cannot access class {} for instantiation ", className, e);
244             }
245         }
246 
247         if (obj != null) {
248             injectServletContext(obj);
249         }
250 
251         return obj;
252     }
253 
254     protected Properties getConfiguration() {
255         return this.configuration;
256     }
257 
258     public ILoggersManager getLoggersManager() {
259         return loggersManager;
260     }
261 
262     public String getMaintenanceFile() {
263         return maintenanceFile;
264     }
265 
266     public Map<String, Map<String, String>> getProperties() {
267         checkInit();
268 
269         TreeMap<String, Map<String, String>> categories = new TreeMap<String, Map<String, String>>();
270 
271         for (IPropertyProvider provider : propertyProviders) {
272             injectServletContext(provider);
273 
274             // Init Category
275             if (categories.get(provider.getCategory()) == null) {
276                 categories.put(provider.getCategory(), new TreeMap<String, String>());
277             }
278 
279             // Add all properties
280             Map<String, String> l = categories.get(provider.getCategory());
281             l.putAll(provider.getProperties());
282         }
283         return categories;
284     }
285 
286     @Deprecated
287     public List<IBatch> getRunningBatches() {
288         return batchManager.getRunningBatches();
289     }
290 
291     public IServiceManager getServiceManager() {
292         return serviceManager;
293     }
294 
295     public IServiceMonitor getServiceMonitor(String name, String group) {
296         checkInit();
297 
298         IService service = null;
299         if (serviceManager != null) {
300             service = serviceManager.getService(name, group);
301             return serviceManager.getMonitor(service);
302         }
303 
304         return null;
305     }
306 
307     public List<IService> getServices() {
308         if (serviceManager == null) {
309             return null;
310         }
311 
312         return serviceManager.getServices();
313     }
314 
315     public IServletContextProvider getServletContext() {
316         return servletContextProvider;
317     }
318 
319     public synchronized void init() {
320 
321         if (initDone) {
322             logger.warn("Already initialized");
323             return;
324         }
325         executorService = Executors.newCachedThreadPool();
326 
327         // Load plugins
328         loadPlugins();
329 
330         // Test some values where injected
331         if (checkers != null || propertyProviders != null || configuration != null) {
332             logger.info("Configuration is injected : skip loading properties from classpath");
333         } else {
334 
335             // Init
336             checkers = new ArrayList<ICheck>();
337             propertyProviders = new ArrayList<IPropertyProvider>();
338             configuration = new Properties();
339 
340             try {
341 
342                 // Load and init all probes
343                 Enumeration<URL> configFiles;
344 
345                 configFiles = Thread.currentThread().getContextClassLoader().getResources(CONFIG_LOCATION);
346 
347                 if (configFiles == null) {
348                     logger.info("config file {} not found in classpath", CONFIG_LOCATION);
349                     return;
350                 }
351 
352                 while (configFiles.hasMoreElements()) {
353                     Properties p = loadProperties(configFiles.nextElement());
354 
355                     configuration.putAll(p);
356 
357                     Set<Object> keys = p.keySet();
358                     for (Object oName : keys) {
359                         String name = (String) oName;
360                         String clazz = (String) p.get(name);
361                         if (name.startsWith("check")) {
362                             addStatusChecker(clazz);
363                         } else if (name.startsWith("property")) {
364                             addPropertyProvider(clazz);
365                         } else {
366                             logger.debug("Global property  {} : {} ", name, clazz);
367                         }
368                     }
369                 }
370 
371             } catch (Exception e) {
372                 logger.error("Initialization error", e);
373             }
374         }
375 
376         // If configuration is null (Spring with no configuration block), create
377         // empty object
378         if (configuration == null) {
379             configuration = new Properties();
380         }
381 
382         // Give all configuration properties to managers.
383         if (getBatchManager() != null) {
384             Properties newConfiguration = getBatchManager().getConfiguration();
385 
386             if (newConfiguration != null) {
387                 newConfiguration.putAll(configuration);
388             } else {
389                 newConfiguration = configuration;
390             }
391 
392             getBatchManager().setConfiguration(newConfiguration);
393         }
394         if (getServiceManager() != null) {
395             Properties newConfiguration = getServiceManager().getConfiguration();
396 
397             if (newConfiguration != null) {
398                 newConfiguration.putAll(configuration);
399             } else {
400                 newConfiguration = configuration;
401             }
402 
403             getServiceManager().setConfiguration(newConfiguration);
404         }
405 
406         if (getLoggersManager() != null) {
407             Properties newConfiguration = getLoggersManager().getConfiguration();
408 
409             if (newConfiguration != null) {
410                 newConfiguration.putAll(configuration);
411             } else {
412                 newConfiguration = configuration;
413             }
414 
415             getLoggersManager().setConfiguration(newConfiguration);
416         }
417 
418         if (null == maintenanceFile) {
419             String path = System.getProperty("java.io.tmpdir");
420             if (!path.endsWith(File.separator)) {
421                 path += File.separator;
422             }
423             path += "appstatus-maintenance";
424             maintenanceFile = path;
425         }
426 
427         initDone = true;
428 
429     }
430 
431     private void injectServletContext(Object instance) {
432         // Inject servlet context if possible
433         if (instance instanceof IServletContextAware && servletContextProvider != null) {
434             ((IServletContextAware) instance).setServletContext(servletContextProvider.getServletContext());
435         }
436     }
437 
438     public boolean isMaintenance() {
439         return maintenance || existsAndReadable(maintenanceFile);
440     }
441 
442     /**
443      * Load plugins from classpath.
444      * <p>
445      * If a manager has already been set with Spring, it is NOT overriden by
446      * plugins found in classpath.
447      */
448     private void loadPlugins() {
449         int count = 0;
450         try {
451             Enumeration<URL> plugins = Thread.currentThread().getContextClassLoader().getResources("net/sf/appstatus/plugin.properties");
452 
453             while (plugins.hasMoreElements()) {
454                 URL url = plugins.nextElement();
455                 logger.info("AppStatus: found plugin: " + url.toString());
456                 Properties p = loadProperties(url);
457 
458                 // batchManager
459                 String batchManagerClass = p.getProperty("batchManager");
460                 if (batchManagerClass != null && batchManager == null) {
461                     batchManager = (IBatchManager) getClassInstance(batchManagerClass);
462                 }
463 
464                 // serviceManager
465                 String serviceManagerClass = p.getProperty("serviceManager");
466                 if (serviceManagerClass != null && serviceManager == null) {
467                     serviceManager = (IServiceManager) getClassInstance(serviceManagerClass);
468                 }
469 
470                 // loggersManager
471                 String loggersManagerClass = p.getProperty("loggersManager");
472                 if (loggersManagerClass != null && loggersManager == null) {
473                     loggersManager = (ILoggersManager) getClassInstance(loggersManagerClass);
474                 }
475 
476                 count++;
477             }
478         } catch (IOException e) {
479             logger.warn("AppStatus: Error loading plugins", e);
480         }
481         logger.info("AppStatus: found {} plugins", count);
482     }
483 
484     /**
485      * Load a properties file from a given URL.
486      *
487      * @param url
488      *            an url
489      * @return a {@link Properties} object
490      * @throws IOException
491      *             in an error occurs
492      */
493     private Properties loadProperties(URL url) throws IOException {
494         // Load plugin configuration
495         Properties p = new Properties();
496         InputStream is = url.openStream();
497         p.load(is);
498         is.close();
499         return p;
500     }
501 
502     public void setBatchManager(IBatchManager batchManager) {
503         this.batchManager = batchManager;
504     }
505 
506     /**
507      * @param batchScheduleManager
508      *            the batchScheduleManager to set
509      */
510     public void setBatchScheduleManager(IBatchScheduleManager batchScheduleManager) {
511         this.batchScheduleManager = batchScheduleManager;
512     }
513 
514     public void setCheckers(List<ICheck> checkers) {
515         this.checkers = checkers;
516     }
517 
518     public void setConfiguration(Properties configuration) {
519         this.configuration = configuration;
520     }
521 
522     public void setMaintenance(boolean maintenanceMode) throws IOException {
523         this.maintenance = maintenanceMode;
524 
525         if (null == maintenanceFile) {
526             return;
527         }
528 
529         File modeFile = new File(maintenanceFile);
530 
531         if (maintenanceMode) {
532             modeFile.createNewFile();
533         } else if (modeFile.exists() && !modeFile.delete()) {
534             throw new IOException("Unable to delete maintenance file");
535         }
536 
537     }
538 
539     public void setMaintenanceFile(String maintenanceModeFile) {
540         this.maintenanceFile = maintenanceModeFile;
541     }
542 
543     public void setObjectInstanciationListener(IObjectInstantiationListener objectInstanciationListener) {
544         this.objectInstanciationListener = objectInstanciationListener;
545     }
546 
547     public void setPropertyProviders(List<IPropertyProvider> propertyProviders) {
548         this.propertyProviders = propertyProviders;
549     }
550 
551     public void setServiceManager(IServiceManager serviceManager) {
552         this.serviceManager = serviceManager;
553     }
554 
555     public void setServletContextProvider(IServletContextProvider servletContext) {
556         this.servletContextProvider = servletContext;
557     }
558 
559 }