001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.engine.repository;
046:
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049: import org.obe.client.api.repository.ObjectNotFoundException;
050: import org.obe.client.api.repository.RepositoryException;
051: import org.obe.client.api.repository.ResourceMetaData;
052: import org.obe.spi.service.ResourceRepository;
053: import org.obe.spi.service.ServiceManager;
054: import org.obe.util.CommonConfig;
055: import org.obe.util.SchemaUtils;
056: import org.xml.sax.InputSource;
057: import org.xml.sax.SAXException;
058:
059: import javax.xml.transform.Source;
060: import javax.xml.transform.TransformerException;
061: import javax.xml.transform.stream.StreamSource;
062: import java.io.*;
063: import java.net.URI;
064: import java.net.URISyntaxException;
065: import java.net.URL;
066:
067: /**
068: * A repository for XML documents, DTDs, schemas, XSL transforms, etc.
069: *
070: * @author Adrian Price
071: */
072: public class BasicResourceRepository extends AbstractRepository
073: implements ResourceRepository {
074:
075: private static final Log _logger = LogFactory
076: .getLog(BasicResourceRepository.class);
077: protected static final String RESOURCE_DIR = "resources";
078: protected static final String RESOURCE_PATH = RESOURCE_DIR
079: + System.getProperty("file.separator");
080: private static final File _resourceDir = new File(CommonConfig
081: .getConfigDir(), RESOURCE_DIR);
082: private static final int BUF_SIZE = 1024;
083:
084: private static void copy(InputStream in, OutputStream out)
085: throws IOException {
086:
087: try {
088: byte[] ba = new byte[BUF_SIZE];
089: int n;
090: while ((n = in.read(ba)) > 0)
091: out.write(ba, 0, n);
092: } finally {
093: try {
094: in.close();
095: } catch (IOException e) {
096: // We don't care about exceptions on close.
097: }
098: try {
099: out.close();
100: } catch (IOException e) {
101: // We don't care about exceptions on close.
102: }
103: }
104: }
105:
106: public BasicResourceRepository(ServiceManager svcMgr) {
107: super (svcMgr, ResourceMetaData.class);
108: }
109:
110: public synchronized void init() throws IOException,
111: RepositoryException {
112: super .init();
113:
114: // TODO: Need a dependency registration mechanism to initialize this.
115: SchemaUtils.setEntityResolver(this );
116: SchemaUtils.setURIResolver(this );
117: }
118:
119: public synchronized void exit() {
120: SchemaUtils.setEntityResolver(null);
121: SchemaUtils.setURIResolver(null);
122: super .exit();
123: }
124:
125: public ResourceMetaData[] findXMLTypes(String locale)
126: throws RepositoryException {
127:
128: return (ResourceMetaData[]) findObjectTypes();
129: }
130:
131: public ResourceMetaData findXMLType(String type, String locale)
132: throws RepositoryException {
133:
134: return (ResourceMetaData) findObjectType(type);
135: }
136:
137: public void createEntity(ResourceMetaData entity)
138: throws RepositoryException {
139: // Ensure that this is a known entity type.
140: findXMLType(entity.getResourceType(), null);
141:
142: // Persist the entity
143: try {
144: createEntity(entity, null);
145: } catch (IOException e) {
146: throw new RepositoryException(e);
147: }
148: }
149:
150: public void deleteEntity(String id) throws RepositoryException {
151: String resource = (String) findInstance(id, true);
152: File file = new File(_resourceDir, resource);
153: file.delete();
154: super .deleteEntry(id);
155: }
156:
157: public void updateEntity(ResourceMetaData entity)
158: throws RepositoryException {
159: Entry entry = updateEntry(entity.getId(), entity);
160:
161: // If new content has been supplied, write it to a file.
162: try {
163: if (entity.getContent() != null)
164: writeFile(entry);
165: } catch (IOException e) {
166: throw new RepositoryException(e);
167: }
168: }
169:
170: public ResourceMetaData[] findXMLMetaData(boolean includeContent)
171: throws RepositoryException {
172:
173: ResourceMetaData[] metaData;
174: if (includeContent) {
175: Entry[] entries = findEntries();
176: metaData = new ResourceMetaData[entries.length];
177: try {
178: for (int i = 0; i < metaData.length; i++) {
179: if (metaData[i].getContent() == null)
180: readFile(entries[i]);
181: }
182: } catch (IOException e) {
183: throw new RepositoryException(e);
184: }
185: } else {
186: metaData = (ResourceMetaData[]) findMetaData();
187: }
188: return metaData;
189: }
190:
191: public ResourceMetaData findXMLMetaData(String id,
192: boolean includeContent) throws RepositoryException {
193:
194: Entry entry = findEntry(id, true);
195: ResourceMetaData metaData = (ResourceMetaData) entry
196: .getMetaData();
197: if (includeContent && metaData.getContent() == null) {
198: try {
199: readFile(entry);
200: } catch (IOException e) {
201: throw new RepositoryException(e);
202: }
203: }
204: return metaData;
205: }
206:
207: public InputStream findEntity(String id) throws RepositoryException {
208: InputStream in = null;
209:
210: // Check whether we already have the entity in memory.
211: Entry entry = findEntry(id, true);
212: ResourceMetaData resourceMetaData = (ResourceMetaData) entry
213: .getMetaData();
214: if (resourceMetaData != null) {
215: byte[] content = resourceMetaData.getContent();
216: if (content != null)
217: in = new ByteArrayInputStream(content);
218: }
219: if (in == null) {
220: // If not in memory, read it from the backing store (file or JAR).
221: String resource = RESOURCE_PATH + entry.getInstance(this );
222: in = CommonConfig.openInputStream(resource);
223: if (in == null)
224: throw new ObjectNotFoundException(resource);
225: }
226: return in;
227: }
228:
229: public InputSource resolveEntity(String publicId, String systemId)
230: throws SAXException, IOException {
231:
232: if (_logger.isDebugEnabled())
233: _logger.debug("resolveEntity(" + publicId + ", " + systemId
234: + ')');
235:
236: InputStream in = null;
237: if (publicId != null) {
238: try {
239: in = findEntity(publicId);
240: } catch (ObjectNotFoundException e) {
241: // Never mind, perhaps we don't know its public ID.
242: } catch (RepositoryException e) {
243: _logger.error(e);
244: throw new IOException(e.getMessage());
245: }
246: }
247: if (in == null) {
248: try {
249: in = findEntity(systemId);
250: } catch (ObjectNotFoundException e) {
251: // We don't have a local copy, so try and download one.
252: _logger.info("Downloading " + systemId);
253: in = getServiceManager().getDataConverter()
254: .toInputStream(new URL(systemId));
255:
256: // Store the resolved entity into the repository, keyed on
257: // publicId, or systemId if the former is null.
258: try {
259: int index = systemId.lastIndexOf('.');
260: if (index == -1)
261: index = systemId.lastIndexOf('?');
262: String xmlType = systemId.substring(index + 1)
263: .toLowerCase();
264: ResourceMetaData type = findXMLType(xmlType, null);
265: ResourceMetaData metaData = new ResourceMetaData(
266: publicId, systemId);
267: metaData.setType(type);
268: createEntity(metaData, in);
269: in = findEntity(metaData.getId());
270: } catch (RepositoryException re) {
271: throw new SAXException(re);
272: }
273: } catch (RepositoryException e) {
274: throw new SAXException(e);
275: }
276: }
277: InputSource inputSource = new InputSource(in);
278: inputSource.setPublicId(publicId);
279: inputSource.setSystemId(systemId);
280: return inputSource;
281: }
282:
283: public Source resolve(String href, String base)
284: throws TransformerException {
285:
286: InputStream in;
287: try {
288: URI hrefUri = new URI(href);
289: URI uri = base == null ? hrefUri : new URI(base)
290: .relativize(hrefUri);
291: String id = uri.isAbsolute() ? uri.toURL().toString()
292: : href;
293: try {
294: in = findEntity(id);
295: } catch (ObjectNotFoundException e) {
296: // Can't download from a URI with no scheme component.
297: if (!uri.isAbsolute())
298: throw new TransformerException(e);
299:
300: // We don't have a local copy, so try and download one.
301: _logger.info("Downloading " + uri);
302: in = getServiceManager().getDataConverter()
303: .toInputStream(uri.toURL());
304:
305: // Store the resolved entity into the repository, keyed on
306: // publicId, or systemId if the former is null.
307: // TODO: Link to type metadata.
308: // String type = id.substring(id.lastIndexOf('.') + 1);
309: ResourceMetaData metaData = new ResourceMetaData(null,
310: id);
311: createEntity(metaData, in);
312: in = findEntity(metaData.getId());
313: }
314: } catch (IOException ioe) {
315: throw new TransformerException(ioe);
316: } catch (RepositoryException e) {
317: throw new TransformerException(e);
318: } catch (URISyntaxException e) {
319: throw new TransformerException(e);
320: }
321: return in == null ? null : new StreamSource(in);
322: }
323:
324: private void createEntity(ResourceMetaData entity, InputStream in)
325: throws IOException, RepositoryException {
326:
327: File destFile = null;
328: FileOutputStream out = null;
329: try {
330: // Make sure this is a known XML type.
331: String xmltype = entity.getResourceType();
332: findXMLType(xmltype, null);
333:
334: // Make sure we have content.
335: if (in == null) {
336: byte[] content = entity.getContent();
337: if (content == null) {
338: throw new RepositoryException(
339: "No content supplied for entity: " + entity);
340: }
341: in = new ByteArrayInputStream(content);
342: }
343:
344: // Generate the content file name.
345: destFile = File.createTempFile("obe", '.' + xmltype,
346: _resourceDir);
347:
348: // Store the meta-data.
349: createEntry(entity.getId(), entity, destFile.getName());
350:
351: // Write the content to disk.
352: out = new FileOutputStream(destFile);
353: copy(in, out);
354: } catch (IOException e) {
355: if (destFile != null)
356: destFile.delete();
357: throw e;
358: } finally {
359: if (in != null) {
360: try {
361: in.close();
362: } catch (IOException e) {
363: // We don't care about exceptions on close.
364: }
365: }
366: if (out != null) {
367: try {
368: out.close();
369: } catch (IOException e) {
370: // We don't care about exceptions on close.
371: }
372: }
373: }
374: }
375:
376: private void readFile(Entry entry) throws IOException,
377: RepositoryException {
378: String resource = RESOURCE_DIR + entry.getInstance(this );
379: InputStream in = CommonConfig.openInputStream(resource);
380: if (in != null) {
381: ByteArrayOutputStream out = new ByteArrayOutputStream(
382: BUF_SIZE);
383: copy(in, out);
384: ((ResourceMetaData) entry.getMetaData()).setContent(out
385: .toByteArray());
386: }
387: }
388:
389: private void writeFile(Entry entry) throws IOException,
390: RepositoryException {
391:
392: byte[] content = ((ResourceMetaData) entry.getMetaData())
393: .getContent();
394: if (content != null) {
395: String resource = (String) entry.getInstance(this );
396: OutputStream out = new FileOutputStream(new File(
397: _resourceDir, resource));
398: try {
399: out.write(content);
400: } finally {
401: out.close();
402: }
403: }
404: }
405:
406: protected Log getLogger() {
407: return _logger;
408: }
409:
410: protected String getConfigurationFileName() {
411: return "BasicResourceRepository.xml";
412: }
413:
414: public String getServiceName() {
415: return SERVICE_NAME;
416: }
417: }
|