001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.persistence.engines.jdbcengine.configservice;
051:
052: import org.apache.log4j.Logger;
053: import java.util.*;
054: import java.io.File;
055: import java.net.MalformedURLException;
056: import java.net.URL;
057: import javax.xml.bind.JAXBContext;
058: import javax.xml.bind.Unmarshaller;
059:
060: import org.xml.sax.helpers.XMLReaderFactory;
061: import org.xml.sax.XMLReader;
062: import org.xml.sax.InputSource;
063: import org.xml.sax.helpers.DefaultHandler;
064:
065: import org.jaffa.config.Config;
066: import org.jaffa.persistence.engines.jdbcengine.configservice.exceptions.InitFileNotFoundRuntimeException;
067: import org.jaffa.persistence.engines.jdbcengine.configservice.exceptions.ConfigurationServiceRuntimeException;
068: import org.jaffa.util.URLHelper;
069: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.Init;
070: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.Database;
071: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.PreloadClass;
072: import org.jaffa.persistence.engines.jdbcengine.configservice.initdomain.DirLoc;
073: import org.jaffa.util.XmlHelper;
074:
075: /** This class implements the Singleton pattern. Use the getInstance() method to get an instance of this class.
076: * The Configuration Service reads the init.xml file. It then performs the initializations.
077: * This class caches the ClassMetaData objects.
078: * It also maintains all the Database objects.
079: */
080: public class ConfigurationService {
081: private static final Logger log = Logger
082: .getLogger(ConfigurationService.class);
083: private static final String PARSER_NAME = "org.apache.xerces.parsers.SAXParser";
084: private static final String MAPPING_FILE_PREFIX = ".xml";
085: private static ConfigurationService m_singleton = null;
086:
087: private static Map m_metaCache = new WeakHashMap();
088: private static Map m_databases = null;
089: private List m_locations = null;
090:
091: /** Creates an instance of ConfigurationService, if not already instantiated.
092: * This gets the location of the init.xml (relative to the classpath) from the framework.properties file.
093: * @return An instance of the ConfigurationService.
094: */
095: public static ConfigurationService getInstance() {
096: if (m_singleton == null)
097: createConfigurationServiceInstance();
098: return m_singleton;
099: }
100:
101: private static synchronized void createConfigurationServiceInstance() {
102: if (m_singleton == null) {
103: String initfile = (String) Config
104: .getProperty(Config.PROP_JDBC_ENGINE_INIT);
105:
106: if (log.isDebugEnabled())
107: log
108: .debug("Creating an instance of the ConfigurationService using the initialization file: "
109: + initfile);
110: m_singleton = new ConfigurationService(initfile);
111: }
112: }
113:
114: /** This reads in the init file and parses it using JAXB.
115: * It then maintains a Map of Database objects, a List of locations for finding mapping files.
116: * It then preloads the mapping files, as defined in the init file.
117: * It throws the runtime exception InitFileNotFoundRuntimeException, if the init file is not found.
118: * It throws the runtime exception ConfigurationServiceRuntimeException, if the init file or any of the mapping files give parse errors.
119: * It throws the runtime exception ClassMetaDataValidationRuntimeException, if the mapping file and the corresponding class have mismatches.
120: */
121: private ConfigurationService(String initFile) {
122: try {
123: URL initUrl = URLHelper.newExtendedURL(initFile);
124:
125: // create a JAXBContext capable of handling classes generated into the package
126: JAXBContext jc = JAXBContext
127: .newInstance("org.jaffa.persistence.engines.jdbcengine.configservice.initdomain");
128:
129: // create an Unmarshaller
130: Unmarshaller u = jc.createUnmarshaller();
131:
132: // enable validation
133: u.setValidating(true);
134:
135: // unmarshal a document into a tree of Java content objects composed of classes from the package.
136: Init init = (Init) u.unmarshal(XmlHelper
137: .stripDoctypeDeclaration(initUrl));
138: m_databases = new HashMap();
139: for (Iterator i = init.getDatabase().iterator(); i
140: .hasNext();) {
141: Database database = (Database) i.next();
142: m_databases.put(database.getName(), database);
143: }
144:
145: m_locations = init.getConfLocation().getDirLoc();
146:
147: if (init.getPreload() != null)
148: preLoad(init.getPreload().getPreloadClass());
149: } catch (MalformedURLException e) {
150: String str = "Initialisation file '" + initFile
151: + "' not found in the classpath";
152: log.error(str);
153: throw new InitFileNotFoundRuntimeException(str);
154: } catch (Exception e) {
155: String str = "Error while parsing the init file "
156: + initFile;
157: log.error(str, e);
158: throw new ConfigurationServiceRuntimeException(str, e);
159: }
160: }
161:
162: /** Returns a Map of Database objects, keyed by database name, as defined in the init file.
163: * @return a Map of Database objects, keyed by database name, as defined in the init file.
164: */
165: public Map getDatabases() {
166: return m_databases;
167: }
168:
169: /** Returns a Database object for the database name, as defined in the init file.
170: * @param name The database name.
171: * @return a Database object. A null will be returned, in case the name was not defined in the init file.
172: */
173: public Database getDatabase(String name) {
174: return (Database) m_databases.get(name);
175: }
176:
177: /** This tries to locate the mapping file for the input classname through the list of locations as defined in the init file.
178: * It will create a ClassMetaData object & cache it.
179: * It throws the runtime exception ConfigurationServiceRuntimeException, if the the mapping file could not be located.
180: * It throws the runtime exception ClassMetaDataValidationRuntimeException, if the mapping file and the corresponding class have mismatches.
181: * @param classname The class for which the mapping file is going to be parsed.
182: * @return a ClassMetaData object.
183: */
184: public ClassMetaData getMetaData(String classname) {
185: ClassMetaData classMetaData = null;
186: if (m_metaCache.containsKey(classname)) {
187: classMetaData = (ClassMetaData) m_metaCache.get(classname);
188: } else {
189: classMetaData = parseNew(classname);
190: }
191: return classMetaData;
192: }
193:
194: private void preLoad(List preLoadList) {
195: if (preLoadList != null) {
196: for (Iterator itr = preLoadList.iterator(); itr.hasNext();) {
197: PreloadClass preloadClass = (PreloadClass) itr.next();
198: parseNew(preloadClass.getName());
199: }
200: }
201: }
202:
203: private ClassMetaData parseNew(String classname) {
204: if (log.isDebugEnabled())
205: log
206: .debug("Locating the mapping file for the Persistent class "
207: + classname);
208:
209: String shortname = classname.substring(classname
210: .lastIndexOf('.') + 1);
211: for (Iterator itr = m_locations.iterator(); itr.hasNext();) {
212: DirLoc dirLoc = (DirLoc) itr.next();
213: String packageName = dirLoc.getDir();
214: StringBuffer urlNameBuf = new StringBuffer(packageName);
215: if (!packageName.endsWith("/")
216: && !packageName.endsWith("\\"))
217: urlNameBuf.append('/');
218: urlNameBuf.append(shortname).append(MAPPING_FILE_PREFIX);
219: String urlName = urlNameBuf.toString();
220: if (log.isDebugEnabled())
221: log.debug("Looking for the mapping file " + urlName);
222: URL url = null;
223: try {
224: url = URLHelper.newExtendedURL(urlName);
225: } catch (MalformedURLException e) {
226: if (log.isDebugEnabled())
227: log.debug("Could not find the mapping file "
228: + urlName + ". " + e.getMessage());
229: }
230: if (url != null) {
231: DefaultHandler handler = new MappingParser();
232: try {
233: XMLReader reader = XMLReaderFactory
234: .createXMLReader(PARSER_NAME);
235: reader.setContentHandler(handler);
236: reader.parse(new InputSource(url.openStream()));
237: } catch (Exception e) {
238: if (log.isDebugEnabled())
239: log
240: .debug("Error in parsing the mapping file "
241: + urlName
242: + ". Will try and look at another location");
243: // try another location !!!
244: continue;
245: }
246: ClassMetaData classMetaData = ((MappingParser) handler)
247: .getMetaData();
248: if (classMetaData != null
249: && classMetaData.getClassName().equals(
250: classname)) {
251: if (log.isDebugEnabled())
252: log
253: .debug("Validating the ClassMetaData object for the mapping file "
254: + urlName);
255: classMetaData.setXmlFileUrl(url);
256: classMetaData.validate();
257: synchronized (m_metaCache) {
258: // one final check before inserting
259: if (m_metaCache.containsKey(classname)) {
260: classMetaData = (ClassMetaData) m_metaCache
261: .get(classname);
262: } else {
263: m_metaCache.put(classname, classMetaData);
264: }
265: }
266: return classMetaData;
267: } else {
268: if (log.isDebugEnabled())
269: log
270: .debug("The classname in the mapping file "
271: + urlName
272: + ", does not match the required value "
273: + classname
274: + ". Will try and look at another location");
275: }
276: }
277: }
278: String str = "Could not find/parse the mapping file for the class "
279: + classname;
280: log.error(str);
281: throw new ConfigurationServiceRuntimeException(str);
282: }
283:
284: }
|