Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: er/extensions/ERXConfigurationManager.java


1   /*
2    * Copyright (C) NetStruxr, Inc. All rights reserved.
3    *
4    * This software is published under the terms of the NetStruxr
5    * Public Software License version 0.5, a copy of which has been
6    * included with this distribution in the LICENSE.NPL file.  */
7   package er.extensions;
8   
9   import com.webobjects.foundation.*;
10  import com.webobjects.eocontrol.*;
11  import com.webobjects.eoaccess.*;
12  import com.webobjects.appserver.*;
13  import java.util.*;
14  import java.io.*;
15  import java.lang.reflect.*;
16  
17  /** 
18   * <code>Configuration Manager</code> handles rapid turnaround for 
19   * system configuration as well as swizzling of the EOModel connection 
20   * dictionaries. 
21   * <p>
22   * <strong>Placing configuration parameters</strong>
23   * <p> 
24   * You can provide the system configuration by the following ways:<br/>
25   * Note: This is the standard way for WebObjects 5.x applications.
26   * <ul>
27   *   <li><code>Properties</code> file under the Resources group of the  
28   *       application and framework project. 
29   *       It's a {@link java.util.Properties} file and Project Wonder's 
30   *       standard project templates include it. (The templates won't 
31   *       be available on some platforms at this moment.)</li>
32   * 
33   *   <li><code>WebObjects.properties</code> under the user home directory; 
34   *       same format to Properties. <br/> 
35   *       Note that the user home directory depends on the user who 
36   *       launch the application. They may change between the 
37   *       developent and deployment time.</li>
38   * 
39   *   <li>Command line arguments<br/>
40   *       For example: <code>-WOCachingEnabled false -com.webobjects.pid $$</code></br>
41   *       Don't forget to put a dash "-" before the key.</li> 
42   * </ul>
43   * <p>
44   * <strong>Loading order of the configuration parameters</strong>
45   * <p>
46   * When the application launches, configuration parameters will 
47   * be loaded by the following order. ERXConfigurationManager trys 
48   * to reload them by the exactly same order when one of those 
49   * configuration files changes. 
50   * <p>
51   * 1. Properties in frameworks that the application links to<br/>
52   * 2. Properties in the application<br/>
53   * 3. WebObjects.properties under the home directory<br/>
54   * 4. Command line arguments<br/>
55   * <p>
56   * If there is a conflicting parameter between the files and 
57   * arguments, the latter one overrides the earlier one. 
58   * <p>
59   * Note that the order between frameworks does not seems 
60   * to be specified. You should not put conflicting parameters 
61   * between framework Properties files. On the other hand, 
62   * the application Properties should be always loaded after 
63   * all framework Properties are loaded. You can safely 
64   * override parameters on the frameworks from the applications
65   * Properties. 
66   * 
67   * 
68   * 
69   * <p>
70   * <strong>Changing the connection dictionary</strong>
71   * <p>
72   * To do this for Oracle you can either specify on a per model basis 
73   * or on a global basis.
74   * <pre>
75   * <strong>Global:</strong>
76   *     dbConnectServerGLOBAL = myDatabaseServer
77   *     dbConnectUserGLOBAL = me
78   *     dbConnectPasswordGLOBAL = secret
79   * <strong>Per Model for say model ER:</strong>
80   *     ER.DBServer = myDatabaseServer
81   *     ER.DBUser = me
82   *     ER.DBPassword = secret
83   * 
84   * <strong>Openbase:</strong> same, with DBDatabase and DBHostname
85   * 
86   * <strong>JDBC:</strong> same with urlGlobal, or db.url
87   * 
88   * </pre>
89   * <p>
90   * Prototypes can be swapped globally or per model either by 
91   * hydrating an archived prototype entity for a file or from 
92   * another entity.
93   */
94  public class ERXConfigurationManager {
95  
96      /** logging support */
97      public static final ERXLogger log = ERXLogger.getERXLogger(ERXConfigurationManager.class);
98  
99      /** 
100      * Notification posted when the configuration is updated.  
101      * The Java system properties is the part of the configuration.
102      */ 
103     public static final String ConfigurationDidChangeNotification = "ConfigurationDidChangeNotification";        
104 
105     /** Configuration manager singleton */ 
106     static ERXConfigurationManager defaultManager = null;
107     
108     private String[] _commandLineArguments; 
109     private NSMutableArray _monitoredProperties;
110     private boolean _isInitialized = false;
111     private boolean _isRapidTurnAroundInitialized = false;
112 
113     /** Private constructor to prevent instantiation from outside the class */
114     private ERXConfigurationManager() {
115         /* empty */
116     }
117 
118     /**
119      * Returns the single instance of this class
120      * 
121      * @return the configuration manager
122      */
123     public static ERXConfigurationManager defaultManager() {
124         if (defaultManager == null)
125             defaultManager = new ERXConfigurationManager();
126         return defaultManager;
127     }
128     
129     /** 
130      * Returns the command line arguments. 
131      * {@link ERXApplication#main} sets this value. 
132      * 
133      * @return the command line arguments as a String[]
134      * @see #setCommandLineArguments
135      */
136     public String[] commandLineArguments() {
137         return _commandLineArguments;
138     }
139     
140     /** 
141      * Sets the command line arguments. 
142      * {@link ERXApplication#main} will call this method 
143      * when the application starts up. 
144      * 
145      * @see #commandLineArguments
146      */
147     public void setCommandLineArguments(String [] newCommandLineArguments) {
148         _commandLineArguments = newCommandLineArguments;
149     }
150 
151     /**
152      * Initializes the configuration manager. 
153      * The framework principal {@link ERXExtensions} calles 
154      * this method when the ERExtensions framework is loaded. 
155      */
156     public void initialize() {
157         if (! _isInitialized) {
158             _isInitialized = true;
159             NSNotificationCenter.defaultCenter().addObserver(this,
160                     new NSSelector("modelAddedHandler", ERXConstant.NotificationClassArray),
161                     EOModelGroup.ModelAddedNotification,
162                     null);
163         }
164     }
165 
166     /**
167      * Sets up the system for rapid turnaround mode. It will watch the 
168      * changes on Properties files in application and framework bundles 
169      * and WebObjects.properties under the home directory. 
170      * Rapid turnaround mode will only be enabled if there are such 
171      * files available and system has WOCaching disabled.
172      */
173     public void configureRapidTurnAround() {
174         if (_isRapidTurnAroundInitialized)      return;
175 
176         _isRapidTurnAroundInitialized = true;
177         
178         if (WOApplication.application()!=null && WOApplication.application().isCachingEnabled()) {
179             log.info("WOCachingEnabled is true. Disabling the rapid turnaround for Properties files");
180             return;
181         }
182         
183         NSArray propertyPaths = ERXProperties.pathsForUserAndBundleProperties(/* logging */ true);
184         _monitoredProperties = new NSMutableArray();
185                  
186         Enumeration e = propertyPaths.objectEnumerator();
187         while (e.hasMoreElements()) {
188             String path = (String) e.nextElement();
189             try {
190                 ERXFileNotificationCenter.defaultCenter().addObserver(this,
191                         new NSSelector("updateSystemProperties", ERXConstant.NotificationClassArray),
192                         path);
193                 _monitoredProperties.addObject(path);
194                 log.debug("Registered: " + path);
195             } catch (Exception ex) {
196                 log.error("An exception occured while registering the observer for the "
197                             + "logging configuration file: " 
198                             + ex.getClass().getName() + " " + ex.getMessage());
199             }
200         }
201     }
202 
203     /** 
204      * Updates the configuration from the current configuration and 
205      * posts {@link #ConfigurationDidChangeNotification}. It also  
206      * calls {@link ERXLogger#configureLogging} to reconfigure 
207      * the logging system. 
208      * <p>
209      * The configuration files: Properties and WebObjects.properties 
210      * files are reloaded to the Java system properties by the same 
211      * order to the when the system starts up. Then the command line 
212      * arguments will be applied to the properties again so that 
213      * the configuration will be consistent during the application 
214      * lifespan. 
215      * <p>
216      * This method is called when rapid turnaround is enabled and one 
217      * of the configuration files changes.
218      * 
219      * @param  n NSNotification object for the event 
220      */
221     public synchronized void updateSystemProperties(NSNotification n) {
222         _updateSystemPropertiesFromMonitoredProperties((File)n.object(), _monitoredProperties);
223         if (_commandLineArguments != null  &&  _commandLineArguments.length > 0) 
224             _reinsertCommandLineArgumentsToSystemProperties(_commandLineArguments);
225         ERXLogger.configureLogging(System.getProperties());
226         
227         NSNotificationCenter.defaultCenter().postNotification(ConfigurationDidChangeNotification, null);
228     }
229 
230     private void _updateSystemPropertiesFromMonitoredProperties(File updatedFile, NSArray monitoredProperties) {
231         if (monitoredProperties == null  ||  monitoredProperties.count() == 0)  return;
232         
233         String updatedFilePath = null;
234         try {
235             updatedFilePath = updatedFile.getCanonicalPath();
236         } catch (IOException ex) {
237             log.error(ex.toString());
238             return; 
239         }
240 
241         Properties systemProperties = System.getProperties();
242         // Find the position of the updatedFile in the monitoredProperties list, 
243         // then reload it and everything after it on the list. 
244         for (int i = monitoredProperties.indexOfObject(updatedFilePath); 
245                   0 <= i  &&  i < _monitoredProperties.count(); i++) {
246             String monitoredPropertiesPath = (String) _monitoredProperties.objectAtIndex(i);
247             Properties loadedProperty = ERXProperties.propertiesFromPath(monitoredPropertiesPath);
248             ERXProperties.transferPropertiesFromSourceToDest(loadedProperty, systemProperties);
249         }
250     }
251 
252     private void _reinsertCommandLineArgumentsToSystemProperties(String[] commandLineArguments) {
253         Properties commandLineProperties = ERXProperties.propertiesFromArgv(commandLineArguments);
254         Properties systemProperties = System.getProperties(); 
255         ERXProperties.transferPropertiesFromSourceToDest(commandLineProperties, systemProperties);
256         log.info("Reinserted the command line arguments to the system properties.");
257     }
258 
259     //CHECKME: shouldn't this use ERXProperties.stringForKey?
260     private String stringForKey(String key) { return System.getProperty(key); }
261 
262     public void modelAddedHandler(NSNotification n) {
263         resetConnectionDictionaryInModel((EOModel)n.object());
264     }
265     
266     /* reset the connection dictionary to the specified values that are in the defaults.
267   This method will look for defaults in the form 
268     <MODELNAME>.DBServer
269     <MODELNAME>.DBUser
270     <MODELNAME>.DBPassword
271     <MODELNAME>.URL (for JDBC)        
272         if the serverName and username both exists, we overwrite the connection dict
273            (password is optional). Otherwise we fall back to what's in the model.
274     */
275     public void resetConnectionDictionaryInModel(EOModel aModel)  {
276         if(aModel!=null) {
277             String aModelName=aModel.name();
278             log.debug("Adjusting "+aModelName);
279             NSMutableDictionary newConnectionDictionary=null;
280             if (aModel.adaptorName().indexOf("Oracle")!=-1) {
281                 String serverName= stringForKey(aModelName + ".DBServer");
282                 serverName=serverName==null ? stringForKey("dbConnectServerGLOBAL") : serverName;
283                 String userName= stringForKey(aModelName + ".DBUser");
284                 userName= userName ==null ? stringForKey("dbConnectUserGLOBAL") : userName;
285                 String passwd= stringForKey(aModelName + ".DBPassword");
286                 passwd= passwd ==null ? stringForKey("dbConnectPasswordGLOBAL") : passwd;
287 
288                 if((serverName!=null) || (userName!=null) || (passwd!=null)) {
289                     newConnectionDictionary=new NSMutableDictionary(aModel.connectionDictionary());
290                     if (serverName!=null) newConnectionDictionary.setObjectForKey(serverName,"serverId");
291                     if (userName!=null) newConnectionDictionary.setObjectForKey(userName,"userName");
292                     if (passwd!=null) newConnectionDictionary.setObjectForKey(passwd,"password");
293                     aModel.setConnectionDictionary(newConnectionDictionary);
294                 }
295                 
296             } else if (aModel.adaptorName().indexOf("Flat")!=-1) {
297                 String path= stringForKey(aModelName + ".DBPath");
298                 path = path ==null ? stringForKey("dbConnectPathGLOBAL") : path;
299                 if (path!=null) {                    
300                     if (path.indexOf(" ")!=-1) {
301                         NSArray a=NSArray.componentsSeparatedByString(path," ");
302                         //System.out.println("found "+a);
303                         if (a.count()==2) {
304                             path =WOApplication.application().resourceManager().pathForResourceNamed((String)a.objectAtIndex(0),
305                                                                                                     (String)a.objectAtIndex(1),
306                                                                                                      null);
307                             //System.out.println("path= "+path);
308                         }
309                     }
310                 } else {
311                     // by default we take <modelName>.db in the directory we found the model
312                     path=aModel.path();
313                     path=NSPathUtilities.stringByDeletingLastPathComponent(path);
314                     path=NSPathUtilities.stringByAppendingPathComponent(path,aModel.name()+".db");                    
315                 }
316                 newConnectionDictionary=new NSMutableDictionary(aModel.connectionDictionary());
317                 if (path!=null) newConnectionDictionary.setObjectForKey(path,"path");
318                 if (operatingSystem()==WindowsOperatingSystem)
319                     newConnectionDictionary.setObjectForKey("\r\n","rowSeparator");
320                 aModel.setConnectionDictionary(newConnectionDictionary);
321             } else if (aModel.adaptorName().indexOf("OpenBase")!=-1) {
322                 String db= stringForKey(aModelName + ".DBDatabase");
323                 db = db ==null ? stringForKey("dbConnectDatabaseGLOBAL") : db;
324                 String h= stringForKey(aModelName + ".DBHostName");
325                 h = h ==null ? stringForKey("dbConnectHostNameGLOBAL") : h;
326                 if (h!=null || db!=null) {
327                     newConnectionDictionary=new NSMutableDictionary(aModel.connectionDictionary());
328                     if (db!=null) newConnectionDictionary.setObjectForKey(db, "databaseName");
329                     if (h!=null) newConnectionDictionary.setObjectForKey(h, "hostName");
330                     aModel.setConnectionDictionary(newConnectionDictionary);
331                 }
332             } else if (aModel.adaptorName().indexOf("JDBC")!=-1) {
333                 String url= stringForKey(aModelName + ".URL");
334                 url = url ==null ? stringForKey("dbConnectURLGLOBAL") : url;
335                 String userName= stringForKey(aModelName + ".DBUser");
336                 userName= userName ==null ? stringForKey("dbConnectUserGLOBAL") : userName;
337                 String passwd= stringForKey(aModelName + ".DBPassword");
338                 passwd= passwd ==null ? stringForKey("dbConnectPasswordGLOBAL") : passwd;
339                 String driver= stringForKey(aModelName + ".DBDriver");
340                 driver= driver ==null ? stringForKey("dbConnectDriverGLOBAL") : driver;
341                 String jdbcInfo= stringForKey(aModelName + ".DBJDBCInfo");
342                 jdbcInfo= jdbcInfo ==null ? stringForKey("dbConnectJDBCInfoGLOBAL") : jdbcInfo;
343                 String plugin= stringForKey(aModelName + ".DBPlugin");
344                 plugin= plugin ==null ? stringForKey("dbConnectPluginGLOBAL") : plugin;
345                 if (url!=null || userName!=null || passwd!=null || driver!=null || jdbcInfo!=null || plugin!=null) {
346                     newConnectionDictionary=new NSMutableDictionary(aModel.connectionDictionary());
347                     if (url!=null) newConnectionDictionary.setObjectForKey(url, "URL");
348                     if (userName!=null) newConnectionDictionary.setObjectForKey(userName,"username");
349                     if (passwd!=null) newConnectionDictionary.setObjectForKey(passwd,"password");
350                     if (driver!=null) newConnectionDictionary.setObjectForKey(driver,"driver");
351                     if (jdbcInfo!=null) {
352                         NSDictionary d=(NSDictionary)NSPropertyListSerialization.propertyListFromString(jdbcInfo);
353                         if (d!=null)
354                             newConnectionDictionary.setObjectForKey(d,"jdbc2Info");
355                         else
356                             newConnectionDictionary.removeObjectForKey("jdbc2Info");
357                     }
358                     if (plugin!=null) newConnectionDictionary.setObjectForKey(plugin,"plugin");                    
359                     aModel.setConnectionDictionary(newConnectionDictionary);
360                 }
361             }
362 
363             if (newConnectionDictionary!=null && log.isDebugEnabled()) {
364                 if (newConnectionDictionary.objectForKey("password")!=null)
365                     newConnectionDictionary.setObjectForKey("<deleted for log>", "password");
366                 log.debug("New Connection Dictionary for "+aModelName+": "+newConnectionDictionary);                
367             }
368             
369             
370             // based on an idea from Stefan Apelt <stefan@tetlabors.de>
371             String f = stringForKey(aModelName + ".EOPrototypesFile");
372             f = f ==null ? stringForKey("EOPrototypesFileGLOBAL") : f;
373             if(f != null) {
374                 NSDictionary dict = (NSDictionary)NSPropertyListSerialization.propertyListFromString(ERXStringUtilities.stringFromResource(f, "", null));
375                 if(dict != null) {
376                     if (log.isDebugEnabled()) log.debug("Adjusting prototypes from " + f);
377                     EOEntity proto = aModel.entityNamed("EOPrototypes");
378                     if (proto == null) {
379                         log.warn("No prototypes found in model named \"" + aModelName + "\", although the EOPrototypesFile default was set!");
380                     } else {
381                         aModel.removeEntity(proto);
382                         proto = new EOEntity(dict, aModel);
383                         proto.awakeWithPropertyList(dict);
384                         aModel.addEntity(proto);
385                     }
386                 }
387             }
388             String e = stringForKey(aModelName + ".EOPrototypesEntity");
389             // global prototype setting not supported yet
390             //e = e ==null ? stringForKey("EOPrototypesEntityGLOBAL") : e;
391             if(e != null) {
392                 // we look for the entity globally so we can have one prototype entity
393                 EOEntity newPrototypeEntity = aModel.modelGroup().entityNamed(e);
394                 if (newPrototypeEntity == null) {
395                     log.warn("Prototype Entity named "+e+" not found in model "+aModel.name());
396                 } else {
397                     if (log.isDebugEnabled()) log.debug("Adjusting prototypes to those from entity " + e);
398 
399                     EOEntity proto = aModel.entityNamed("EOPrototypes");
400                     if(proto != null) aModel.removeEntity(proto);
401                     
402                     aModel.removeEntity(newPrototypeEntity);
403                     newPrototypeEntity.setName("EOPrototypes");
404                     aModel.addEntity(newPrototypeEntity);
405                 }
406             }
407         }
408         
409     }
410 
411 
412     public final static int WindowsOperatingSystem=1;
413     public final static int MacOSXOperatingSystem=2;
414     public final static int SolarisOperatingSystem=3;
415     public final static int UnknownOperatingSystem=3;
416 
417     private int _operatingSystem=0;
418     public int operatingSystem() {
419         if (_operatingSystem==0) {
420             String osName=System.getProperty("os.name").toLowerCase();
421             if (osName.indexOf("windows")!=-1) _operatingSystem=WindowsOperatingSystem;
422             else if (osName.indexOf("solaris")!=-1) _operatingSystem=SolarisOperatingSystem;
423             else if (osName.indexOf("macos")!=-1) _operatingSystem=MacOSXOperatingSystem;
424             else _operatingSystem=UnknownOperatingSystem;
425         }
426         return _operatingSystem;
427     }
428 
429     /**
430      * Path to the web server's document root.
431      * This implementation tries first to resolve the
432      * <code>application.name()+ "DocumentRoot"</code> property value,
433      * then the <code>ERXDocumentRoot</> property before
434      * getting the <code>DocumentRoot</code> key in your WebServerConfig.plist in the
435      * JavaWebObjects bundle.
436      * @returns to the web server's document root.
437      */
438     protected String documentRoot; 
439     public String documentRoot() {
440         if (documentRoot == null) {
441             // for WebObjects.properties
442             documentRoot = ERXProperties.stringForKey(WOApplication.application().name() + "DocumentRoot");
443             if(documentRoot == null) {
444                 // for command line and Properties
445                 documentRoot = ERXProperties.stringForKey("ERXDocumentRoot");
446                 if(documentRoot == null) {
447                     // default value
448                     NSDictionary dict = ERXDictionaryUtilities.dictionaryFromPropertyList("WebServerConfig", NSBundle.bundleForName("JavaWebObjects"));
449                     if(dict != null)
450                         documentRoot = (String)dict.objectForKey("DocumentRoot");
451                 }
452             }
453         }
454         return documentRoot;
455     }
456 
457     /** holds the host name */
458     protected String _hostName;
459 
460     /**
461      * Gets the default host name for the current local host.
462      * @return host name or UnknownHost if the host is unknown.
463      */
464     public String hostName() {
465         if (_hostName == null) {
466             try {
467                 _hostName = java.net.InetAddress.getLocalHost().getHostName();
468             } catch (java.net.UnknownHostException ehe) {
469                 log.warn("Caught unknown host exception: " + ehe.getMessage());
470                 _hostName = "UnknownHost";
471             }
472         }
473         return _hostName;
474     }    
475     
476     /**
477      * Checks if the application is deployed as a servlet.
478      * <p>
479      * The current implementation only checks if the application  
480      * is linked against <code>JavaWOJSPServlet.framework</code>. 
481      * 
482      * @return true if the application is deployed as a servlet
483      */
484     public boolean isDeployedAsServlet() {
485         NSArray frameworkNames = (NSArray)NSBundle.frameworkBundles().valueForKey("name");
486         return frameworkNames.containsObject("JavaWOJSPServlet");
487     }
488         
489 }