001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. 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: package org.apache.cocoon.components.source.impl;
018:
019: import java.io.IOException;
020: import java.net.MalformedURLException;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.apache.avalon.framework.configuration.Configurable;
025: import org.apache.avalon.framework.configuration.Configuration;
026: import org.apache.avalon.framework.configuration.ConfigurationException;
027: import org.apache.avalon.framework.logger.AbstractLogEnabled;
028: import org.apache.avalon.framework.thread.ThreadSafe;
029: import org.apache.cocoon.components.source.helpers.SourceCredential;
030: import org.apache.excalibur.source.Source;
031: import org.apache.excalibur.source.SourceFactory;
032: import org.xmldb.api.DatabaseManager;
033: import org.xmldb.api.base.Database;
034: import org.xmldb.api.base.XMLDBException;
035:
036: /**
037: * This class implements the xmldb:// pseudo-protocol and allows to get XML
038: * content from an XML:DB enabled XML database.
039: * <p>
040: * The configuration of this protocol is as follows:
041: * <pre>
042: * <source-factory name="xmldb" src="org.apache.cocoon.components.source.impl.XMLDBSourceFactory>
043: * <driver type="foo" class="org.foomaker.FooXMLDBDriver"
044: * user="scott" password="tiger"
045: * collection="//localhost:8080/foo/base-path/"/>
046: * <driver...
047: * <source-factory>
048: * </pre>
049: * <p>
050: * The <code>type</code> attribute indicates the database type that will be used for URLs (e.g.
051: * <code>xmldb:foo:/path/</code>). The <code>collection</code> attribute specifies a base collection
052: * for paths that do not start with "<code>//</code>".
053: * <p>
054: * The returned sources are traversable, modifiable and xml-izable.
055: *
056: * @version CVS $Id: XMLDBSourceFactory.java 433543 2006-08-22 06:22:54Z crossley $
057: */
058: public final class XMLDBSourceFactory extends AbstractLogEnabled
059: implements SourceFactory, Configurable, ThreadSafe {
060:
061: /** A Map containing the authentication credentials */
062: protected HashMap credentialMap;
063:
064: /** An optional base collection for each of the drivers */
065: protected HashMap baseMap;
066:
067: /**
068: * Configure the instance and initialize XML:DB connections (load and register the drivers).
069: */
070: public void configure(final Configuration conf)
071: throws ConfigurationException {
072:
073: credentialMap = new HashMap();
074: baseMap = new HashMap();
075:
076: Configuration[] drivers = conf.getChildren("driver");
077: for (int i = 0; i < drivers.length; i++) {
078: String type = drivers[i].getAttribute("type");
079: String driver = drivers[i].getAttribute("class");
080:
081: SourceCredential credential = new SourceCredential(null,
082: null);
083: credential.setPrincipal(drivers[i].getAttribute("user",
084: null));
085: credential.setPassword(drivers[i].getAttribute("password",
086: null));
087: credentialMap.put(type, credential);
088:
089: String base = drivers[i].getAttribute("collection", null);
090: if (base != null && base.length() > 0) {
091: // Ensure the base collection ends with a '/'
092: if (base.charAt(base.length() - 1) != '/') {
093: base = base + '/';
094: }
095: baseMap.put(type, base);
096: }
097:
098: if (getLogger().isDebugEnabled()) {
099: getLogger().debug(
100: "Initializing XML:DB connection, using driver "
101: + driver);
102: }
103:
104: try {
105: Database db = (Database) Class.forName(driver)
106: .newInstance();
107:
108: Configuration[] params = drivers[i].getChildren();
109: for (int j = 0; j < params.length; j++) {
110: db.setProperty(params[j].getName(), params[j]
111: .getValue());
112: }
113:
114: DatabaseManager.registerDatabase(db);
115:
116: } catch (XMLDBException e) {
117: String msg = "Unable to connect to the XMLDB database '"
118: + type
119: + "'."
120: + " Error "
121: + e.errorCode
122: + ": "
123: + e.getMessage();
124: getLogger().debug(msg, e);
125: throw new ConfigurationException(msg, e);
126:
127: } catch (Exception e) {
128: String msg = "Unable to load XMLDB database driver '"
129: + driver
130: + "'."
131: + " Make sure that the driver is available. Error: "
132: + e.getMessage();
133: getLogger().debug(msg, e);
134: throw new ConfigurationException(msg, e);
135: }
136: }
137: }
138:
139: /**
140: * Resolve the source
141: */
142: public Source getSource(String location, Map parameters)
143: throws MalformedURLException, IOException {
144:
145: int start = location.indexOf(':') + 1;
146: int end = location.indexOf(':', start);
147:
148: if (start == 0 || end == -1) {
149: throw new MalformedURLException(
150: "Mispelled XML:DB URL. "
151: + "The syntax is \"xmldb:databasetype://host/collection/resource\"");
152: }
153:
154: String type = location.substring(start, end);
155: SourceCredential credential = (SourceCredential) credentialMap
156: .get(type);
157:
158: if (credential == null) {
159: throw new MalformedURLException("xmldb type '" + type
160: + "' is unknown for URL " + location);
161: }
162:
163: String base = (String) baseMap.get(type);
164:
165: if (base != null && base.length() > 0) {
166: String path = location.substring(end + 1);
167: if (!path.startsWith("//")) {
168: // URL is not absolute, add base, avoiding to double the '/'
169: if (path.charAt(0) == '/') {
170: path = path.substring(1);
171: }
172: location = location.substring(0, end + 1) + base + path;
173: }
174: }
175:
176: return new XMLDBSource(this .getLogger(), credential
177: .getPrincipal(), credential.getPassword(), location);
178: }
179:
180: public void release(org.apache.excalibur.source.Source source) {
181: // nothing to do here
182: }
183: }
|