001: package org.apache.ojb.broker.metadata;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import javax.xml.parsers.ParserConfigurationException;
019: import javax.xml.parsers.SAXParser;
020: import javax.xml.parsers.SAXParserFactory;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.OutputStream;
027: import java.io.PrintWriter;
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.net.URLConnection;
031: import java.util.Date;
032:
033: import org.apache.commons.lang.SerializationUtils;
034: import org.apache.commons.lang.SystemUtils;
035: import org.apache.ojb.broker.util.ClassHelper;
036: import org.apache.ojb.broker.util.configuration.Configurable;
037: import org.apache.ojb.broker.util.configuration.Configuration;
038: import org.apache.ojb.broker.util.configuration.ConfigurationException;
039: import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
040: import org.apache.ojb.broker.util.logging.Logger;
041: import org.apache.ojb.broker.util.logging.LoggerFactory;
042: import org.xml.sax.ContentHandler;
043: import org.xml.sax.ErrorHandler;
044: import org.xml.sax.InputSource;
045: import org.xml.sax.SAXException;
046: import org.xml.sax.SAXParseException;
047: import org.xml.sax.XMLReader;
048:
049: /**
050: * This class is responsible for reading and writing DescriptorRepository objects
051: * from and to persistent media.
052: * Currently only XML file based persistence is supported.
053: *
054: * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
055: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
056: * @version $Id: RepositoryPersistor.java,v 1.24.2.4 2005/12/21 22:26:11 tomdz Exp $
057: */
058: public class RepositoryPersistor implements Configurable {
059: // TODO: Refactoring of the metadata reading/handling?
060:
061: private static Logger log = LoggerFactory
062: .getLogger(RepositoryPersistor.class);
063:
064: private static final String SER_FILE_SUFFIX = "serialized";
065: private static final String SERIALIZED_REPOSITORY_PATH = "serializedRepositoryPath";
066:
067: private boolean useSerializedRepository = false;
068:
069: public RepositoryPersistor() {
070: OjbConfigurator.getInstance().configure(this );
071: }
072:
073: public void configure(Configuration pConfig)
074: throws ConfigurationException {
075: useSerializedRepository = ((MetadataConfiguration) pConfig)
076: .useSerializedRepository();
077: }
078:
079: /**
080: * Write the {@link DescriptorRepository} to the given output object.
081: */
082: public void writeToFile(DescriptorRepository repository,
083: ConnectionRepository conRepository, OutputStream out) {
084: RepositoryTags tags = RepositoryTags.getInstance();
085: try {
086: if (log.isDebugEnabled())
087: log.debug("## Write repository file ##"
088: + repository.toXML()
089: + "## End of repository file ##");
090:
091: String eol = SystemUtils.LINE_SEPARATOR;
092: StringBuffer buf = new StringBuffer();
093: // 1. write XML header
094: buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
095: + eol);
096:
097: buf
098: .append("<!DOCTYPE descriptor-repository SYSTEM \"repository.dtd\" >"
099: + eol + eol);
100: // strReturn += "<!DOCTYPE descriptor-repository SYSTEM \"repository.dtd\" [" + eol;
101: // strReturn += "<!ENTITY database-metadata SYSTEM \""+ConnectionRepository.DATABASE_METADATA_FILENAME+"\">" + eol;
102: // strReturn += "<!ENTITY user SYSTEM \"repository_user.xml\">" + eol;
103: // strReturn += "<!ENTITY junit SYSTEM \"repository_junit.xml\">" + eol;
104: // strReturn += "<!ENTITY internal SYSTEM \"repository_internal.xml\"> ]>" + eol + eol;
105:
106: buf
107: .append("<!-- OJB RepositoryPersistor generated this file on "
108: + new Date().toString() + " -->" + eol);
109:
110: buf
111: .append(tags
112: .getOpeningTagNonClosingById(RepositoryElements.MAPPING_REPOSITORY)
113: + eol);
114: buf.append(" "
115: + tags.getAttribute(
116: RepositoryElements.REPOSITORY_VERSION,
117: DescriptorRepository.getVersion()) + eol);
118: buf.append(" "
119: + tags.getAttribute(
120: RepositoryElements.ISOLATION_LEVEL,
121: repository.getIsolationLevelAsString())
122: + eol);
123: buf.append(">" + eol);
124:
125: if (conRepository != null)
126: buf.append(eol + eol + conRepository.toXML() + eol
127: + eol);
128: if (repository != null)
129: buf.append(repository.toXML());
130:
131: buf
132: .append(tags
133: .getClosingTagById(RepositoryElements.MAPPING_REPOSITORY));
134:
135: PrintWriter pw = new PrintWriter(out);
136: pw.print(buf.toString());
137: pw.flush();
138: pw.close();
139: } catch (Exception e) {
140: log.error("Could not write to output stream" + out, e);
141: }
142: }
143:
144: /**
145: * Read the repository configuration file.
146: * <br>
147: * If configuration property <code>useSerializedRepository</code> is <code>true</code>
148: * all subsequent calls read a serialized version of the repository.
149: * The directory where the serialized repository is stored can be specified
150: * with the <code>serializedRepositoryPath</code> entry in OJB.properties.
151: * Once a serialized repository is found changes to repository.xml will be
152: * ignored. To force consideration of these changes the serialized repository
153: * must be deleted manually.
154: */
155: public DescriptorRepository readDescriptorRepository(String filename)
156: throws MalformedURLException, ParserConfigurationException,
157: SAXException, IOException {
158: DescriptorRepository result;
159: if (useSerializedRepository)
160: // use serialized repository
161: {
162: // build File object pointing to serialized repository location
163: Configuration config = OjbConfigurator.getInstance()
164: .getConfigurationFor(null);
165: String pathPrefix = config.getString(
166: SERIALIZED_REPOSITORY_PATH, ".");
167: File serFile = new File(pathPrefix + File.separator
168: + filename + "." + SER_FILE_SUFFIX);
169:
170: if (serFile.exists() && serFile.length() > 0)
171: // if file exists load serialized version of repository
172: {
173: try {
174: long duration = System.currentTimeMillis();
175: result = deserialize(serFile);
176: log.info("Read serialized repository in "
177: + (System.currentTimeMillis() - duration)
178: + " ms");
179: } catch (Exception e) {
180: log
181: .error(
182: "error in loading serialized repository. Will try to use XML version.",
183: e);
184: result = (DescriptorRepository) buildRepository(
185: filename, DescriptorRepository.class);
186: }
187: } else
188: // if no serialized version exists, read it from xml and write serialized file
189: {
190: long duration = System.currentTimeMillis();
191: result = (DescriptorRepository) buildRepository(
192: filename, DescriptorRepository.class);
193: log.info("Read repository from file took "
194: + (System.currentTimeMillis() - duration)
195: + " ms");
196: serialize(result, serFile);
197: }
198: }
199: // don't use serialized repository
200: else {
201: long duration = System.currentTimeMillis();
202: result = (DescriptorRepository) buildRepository(filename,
203: DescriptorRepository.class);
204: log.info("Read class descriptors took "
205: + (System.currentTimeMillis() - duration) + " ms");
206: }
207: return result;
208: }
209:
210: public DescriptorRepository readDescriptorRepository(
211: InputStream inst) throws MalformedURLException,
212: ParserConfigurationException, SAXException, IOException {
213: long duration = System.currentTimeMillis();
214: InputSource inSource = new InputSource(inst);
215: DescriptorRepository result = (DescriptorRepository) readMetadataFromXML(
216: inSource, DescriptorRepository.class);
217: log.info("Read class descriptors took "
218: + (System.currentTimeMillis() - duration) + " ms");
219: return result;
220: }
221:
222: /**
223: * Read the repository configuration file and extract connection handling information.
224: */
225: public ConnectionRepository readConnectionRepository(String filename)
226: throws MalformedURLException, ParserConfigurationException,
227: SAXException, IOException {
228: long duration = System.currentTimeMillis();
229: ConnectionRepository result = (ConnectionRepository) buildRepository(
230: filename, ConnectionRepository.class);
231: log.info("Read connection repository took "
232: + (System.currentTimeMillis() - duration) + " ms");
233: return result;
234: }
235:
236: /**
237: * Read the repository configuration file and extract connection handling information.
238: */
239: public ConnectionRepository readConnectionRepository(
240: InputStream inst) throws MalformedURLException,
241: ParserConfigurationException, SAXException, IOException {
242: long duration = System.currentTimeMillis();
243: InputSource inSource = new InputSource(inst);
244: ConnectionRepository result = (ConnectionRepository) readMetadataFromXML(
245: inSource, ConnectionRepository.class);
246: log.info("Read connection repository took "
247: + (System.currentTimeMillis() - duration) + " ms");
248: return result;
249: }
250:
251: protected DescriptorRepository deserialize(File serFile) {
252: DescriptorRepository result = null;
253: try {
254: FileInputStream fis = new FileInputStream(serFile);
255: // deserialize repository
256: result = (DescriptorRepository) SerializationUtils
257: .deserialize(fis);
258: } catch (Exception e) {
259: log.error("Deserialisation failed, using input path: "
260: + serFile.getAbsolutePath(), e);
261: }
262: return result;
263: }
264:
265: protected void serialize(DescriptorRepository repository, File file) {
266: try {
267: FileOutputStream fos = new FileOutputStream(file);
268: // serialize repository
269: SerializationUtils.serialize(repository, fos);
270: } catch (Exception e) {
271: log.error("Serialization failed, using output path: "
272: + file.getAbsolutePath(), e);
273: }
274: }
275:
276: /**
277: *
278: * TODO: We should re-design the configuration file reading
279: */
280: private Object buildRepository(String repositoryFileName,
281: Class targetRepository) throws MalformedURLException,
282: ParserConfigurationException, SAXException, IOException {
283: URL url = buildURL(repositoryFileName);
284: /*
285: arminw:
286: strange, when using 'url.openStream()' argument repository
287: could not be parsed
288: ipriha:
289: parser needs a base url to find referenced entities.
290: */
291: // InputSource source = new InputSource(url.openStream());
292: String pathName = url.toExternalForm();
293:
294: log.info("Building repository from :" + pathName);
295: InputSource source = new InputSource(pathName);
296: URLConnection conn = url.openConnection();
297: conn.setUseCaches(false);
298: conn.connect();
299: InputStream in = conn.getInputStream();
300: source.setByteStream(in);
301: try {
302: return readMetadataFromXML(source, targetRepository);
303: } finally {
304: try {
305: in.close();
306: } catch (IOException x) {
307: log.warn("unable to close repository input stream ["
308: + x.getMessage() + "]", x);
309: }
310: }
311: }
312:
313: /**
314: * Read metadata by populating an instance of the target class
315: * using SAXParser.
316: */
317: private Object readMetadataFromXML(InputSource source, Class target)
318: throws MalformedURLException, ParserConfigurationException,
319: SAXException, IOException {
320: // TODO: make this configurable
321: boolean validate = false;
322:
323: // get a xml reader instance:
324: SAXParserFactory factory = SAXParserFactory.newInstance();
325: log.info("RepositoryPersistor using SAXParserFactory : "
326: + factory.getClass().getName());
327: if (validate) {
328: factory.setValidating(true);
329: }
330: SAXParser p = factory.newSAXParser();
331: XMLReader reader = p.getXMLReader();
332: if (validate) {
333: reader.setErrorHandler(new OJBErrorHandler());
334: }
335:
336: Object result;
337: if (DescriptorRepository.class.equals(target)) {
338: // create an empty repository:
339: DescriptorRepository repository = new DescriptorRepository();
340: // create handler for building the repository structure
341: ContentHandler handler = new RepositoryXmlHandler(
342: repository);
343: // tell parser to use our handler:
344: reader.setContentHandler(handler);
345: reader.parse(source);
346: result = repository;
347: } else if (ConnectionRepository.class.equals(target)) {
348: // create an empty repository:
349: ConnectionRepository repository = new ConnectionRepository();
350: // create handler for building the repository structure
351: ContentHandler handler = new ConnectionDescriptorXmlHandler(
352: repository);
353: // tell parser to use our handler:
354: reader.setContentHandler(handler);
355: reader.parse(source);
356: //LoggerFactory.getBootLogger().info("loading XML took " + (stop - start) + " msecs");
357: result = repository;
358: } else
359: throw new MetadataException(
360: "Could not build a repository instance for '"
361: + target + "', using source " + source);
362: return result;
363: }
364:
365: private URL buildURL(String repositoryFileName)
366: throws MalformedURLException {
367: //j2ee compliant lookup of resources
368: URL url = ClassHelper.getResource(repositoryFileName);
369:
370: // don't be too strict: if resource is not on the classpath, try ordinary file lookup
371: if (url == null) {
372: try {
373: url = new File(repositoryFileName).toURL();
374: } catch (MalformedURLException ignore) {
375: }
376: }
377:
378: if (url != null) {
379: log.info("OJB Descriptor Repository: " + url);
380: } else {
381: throw new MalformedURLException("did not find resource "
382: + repositoryFileName);
383: }
384: return url;
385: }
386:
387: // inner class
388: class OJBErrorHandler implements ErrorHandler {
389: public void warning(SAXParseException exception)
390: throws SAXException {
391: logMessage(exception, false);
392: }
393:
394: public void error(SAXParseException exception)
395: throws SAXException {
396: logMessage(exception, false);
397: }
398:
399: public void fatalError(SAXParseException exception)
400: throws SAXException {
401: logMessage(exception, true);
402: }
403:
404: void logMessage(SAXParseException e, boolean isFatal) {
405: String msg = e.getMessage();
406: if (isFatal) {
407: log.error("## " + e.getSystemId() + " - line "
408: + e.getLineNumber() + ": " + msg + " ##");
409: } else {
410: log.warn(e.getSystemId() + " - line "
411: + e.getLineNumber() + ": " + msg);
412: }
413: }
414: }
415: }
|