001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: Datasources.java 3634 2007-01-08 21:42:24Z gbevin $
007: */
008: package com.uwyn.rife.database;
009:
010: import com.uwyn.rife.database.exceptions.*;
011:
012: import com.uwyn.rife.rep.Participant;
013: import com.uwyn.rife.rep.Rep;
014: import com.uwyn.rife.resources.ResourceFinder;
015: import com.uwyn.rife.selector.XmlSelectorResolver;
016: import com.uwyn.rife.tools.FileUtils;
017: import com.uwyn.rife.tools.SortListComparables;
018: import com.uwyn.rife.tools.StringUtils;
019: import com.uwyn.rife.tools.exceptions.FileUtilsErrorException;
020: import com.uwyn.rife.xml.exceptions.XmlErrorException;
021: import java.io.File;
022: import java.net.URL;
023: import java.net.URLDecoder;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.HashMap;
027:
028: /**
029: * Contains a collection of <code>Datasource</code> instances that have been
030: * created from an XML definition. A <code>Datasources</code> instance can
031: * either be created by calling the public constructor or by executing the
032: * <code>ParticipantDatasources</code> which participates in the
033: * application-wide repository.
034: *
035: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
036: * @version $Revision: 3634 $
037: * @see com.uwyn.rife.database.Datasource
038: * @see com.uwyn.rife.database.Xml2Datasources
039: * @see com.uwyn.rife.rep.Rep
040: * @see com.uwyn.rife.rep.participants.ParticipantDatasources
041: * @since 1.0
042: */
043: public class Datasources {
044: public static final String DEFAULT_PARTICIPANT_NAME = "ParticipantDatasources";
045:
046: private HashMap<String, Datasource> mDatasources = null;
047: private String mXmlPath = null;
048: private ResourceFinder mResourceFinder = null;
049:
050: /**
051: * Creates a new empty <code>Datasources</code> instance.
052: *
053: * @since 1.0
054: */
055: public Datasources() throws DatasourcesException {
056: mDatasources = new HashMap<String, Datasource>();
057: }
058:
059: /**
060: * Creates a new <code>Datasources</code> instance from the definitions in
061: * an XML file.
062: *
063: * @param xmlPath the path of the XML resource that will be used for the
064: * population
065: * @param resourceFinder a <code>ResourceFinder</code> instance that will be
066: * used to find the file that corresponds to the provided
067: * <code>xmlPath</code>
068: *
069: * @throws DatasourcesException when an exception occured during the
070: * obtainance of the resource's modification time or during the processing
071: * of the XML file
072: *
073: * @since 1.0
074: */
075: public Datasources(String xmlPath, ResourceFinder resourceFinder)
076: throws DatasourcesException, DatasourceNotFoundException {
077: if (null == xmlPath)
078: throw new IllegalArgumentException("xmlPath can't be null.");
079: if (0 == xmlPath.length())
080: throw new IllegalArgumentException(
081: "xmlPath can't be empty.");
082: if (null == resourceFinder)
083: throw new IllegalArgumentException(
084: "resourceFinder can't be null.");
085:
086: mResourceFinder = resourceFinder;
087:
088: String datasource_resolved = XmlSelectorResolver.resolve(
089: xmlPath, mResourceFinder, "rep/datasources-");
090: if (null == datasource_resolved) {
091: throw new DatasourceNotFoundException(xmlPath);
092: }
093:
094: URL datasource_resource = mResourceFinder
095: .getResource(datasource_resolved);
096: if (null == datasource_resource) {
097: throw new DatasourceNotFoundException(xmlPath,
098: datasource_resolved);
099: }
100:
101: mXmlPath = datasource_resolved;
102:
103: initialize();
104:
105: assert mResourceFinder != null;
106: assert mXmlPath != null;
107: assert mXmlPath.length() > 0;
108: }
109:
110: /**
111: * Checks if a <code>ParticipantDatasources</code> participant has been
112: * initialized and is available from the application-wide repository.
113: *
114: * @return <code>true</code> if this participant is available; or
115: * <p>
116: * <code>false</code> otherwise
117: *
118: * @see #getRepInstance()
119: *
120: * @since 1.0
121: */
122: public static boolean hasRepInstance() {
123: return Rep.hasParticipant(DEFAULT_PARTICIPANT_NAME);
124: }
125:
126: /**
127: * Retrieves the <code>Datasources</code> instance that is initialized by
128: * the <code>ParticipantDatasources</code> participant in the
129: * application-wide repository.
130: *
131: * @return the requested <code>Datasources</code> instances; or
132: * <p>
133: * <code>null</code> if the <code>ParticipantDatasources</code> couldn't be
134: * found
135: *
136: * @see #hasRepInstance()
137: *
138: * @since 1.0
139: */
140: public static Datasources getRepInstance() {
141: Participant participant = Rep
142: .getParticipant(DEFAULT_PARTICIPANT_NAME);
143: if (null == participant) {
144: return null;
145: }
146:
147: return (Datasources) participant.getObject();
148: }
149:
150: /**
151: * Retrieves the <code>Datasource</code> that corresponds to a provided
152: * name.
153: *
154: * @param name a <code>String</code> that identifies the
155: * <code>Datasource</code> that has to be retrieved
156: *
157: * @return the requested <code>Datasource</code> instance; or
158: * <p>
159: * <code>null</code> if name isn't known
160: *
161: * @since 1.0
162: */
163: public Datasource getDatasource(String name) {
164: return mDatasources.get(name);
165: }
166:
167: /**
168: * Stores a <code>Datasource</code> with a provided name to be able to
169: * reference it later.
170: *
171: * @param name a <code>String</code> that identifies the
172: * <code>Datasource</code>
173: * @param datasource the <code>Datasource</code> instance that has to be
174: * stored
175: *
176: * @since 1.0
177: */
178: public void setDatasource(String name, Datasource datasource) {
179: if (null == name)
180: throw new IllegalArgumentException("name can't be null.");
181: if (0 == name.length())
182: throw new IllegalArgumentException("name can't be empty.");
183: if (null == datasource)
184: throw new IllegalArgumentException(
185: "datasource can't be null.");
186:
187: mDatasources.put(name, datasource);
188: }
189:
190: /**
191: * Retrieves a collection of all the <code>Datasource</code> names that are
192: * known by this <code>Datasources</code> instance.
193: *
194: * @return the requested <code>Collection</code>
195: *
196: * @since 1.0
197: */
198: public Collection<String> getDatasourceNames() {
199: return mDatasources.keySet();
200: }
201:
202: /**
203: * Retrieves the path of the XML document that populated this
204: * <code>DataSources</code> instance
205: *
206: * @return the path of the XML document that populated this
207: * <code>DataSources</code> instance
208: *
209: * @since 1.0
210: */
211: public String getXmlPath() {
212: return mXmlPath;
213: }
214:
215: /**
216: * Creates an XML document with all the data in the current
217: * <code>Datasources</code> instance
218: *
219: * @return the constructed XML document as a <code>String</code>
220: *
221: * @since 1.0
222: */
223: public String toXml() {
224: StringBuilder xml_output = new StringBuilder();
225:
226: xml_output.append("<datasources>\n");
227:
228: ArrayList<String> datasource_keys_arraylist = new ArrayList<String>();
229: for (String datasource_key : mDatasources.keySet()) {
230: datasource_keys_arraylist.add(datasource_key);
231: }
232:
233: (new SortListComparables()).sort(datasource_keys_arraylist);
234:
235: Datasource datasource = null;
236: for (String datasource_key : datasource_keys_arraylist) {
237: datasource = mDatasources.get(datasource_key);
238:
239: xml_output.append("\t<datasource name=\"").append(
240: StringUtils.encodeXml(datasource_key)).append(
241: "\">\n");
242: xml_output.append("\t\t<driver>").append(
243: StringUtils.encodeXml(datasource.getDriver()))
244: .append("</driver>\n");
245: xml_output.append("\t\t<url>").append(
246: StringUtils.encodeXml(datasource.getUrl())).append(
247: "</url>\n");
248: if (null != datasource.getUser()) {
249: xml_output.append("\t\t<user>").append(
250: StringUtils.encodeXml(datasource.getUser()))
251: .append("</user>\n");
252: }
253: if (null != datasource.getPassword()) {
254: xml_output.append("\t\t<password>")
255: .append(
256: StringUtils.encodeXml(datasource
257: .getPassword())).append(
258: "</password>\n");
259: }
260: if (datasource.getPoolsize() > 0) {
261: xml_output.append("\t\t<poolsize>").append(
262: datasource.getPoolsize()).append(
263: "</poolsize>\n");
264: }
265: xml_output.append("\t</datasource>\n");
266: }
267:
268: xml_output.append("</datasources>\n");
269:
270: assert xml_output.length() > 0;
271:
272: return xml_output.toString();
273: }
274:
275: /**
276: * Performs the initialization logic.
277: *
278: * @throws DatasourcesException when an error occurred during the
279: * initialization
280: *
281: * @since 1.0
282: */
283: private void initialize() throws DatasourcesException {
284: try {
285: Xml2Datasources xml_datasources = new Xml2Datasources();
286: xml_datasources.processXml(mXmlPath, mResourceFinder);
287: synchronized (this ) {
288: mDatasources = xml_datasources.getDatasources();
289: }
290: } catch (XmlErrorException e) {
291: throw new InitializationErrorException(mXmlPath, e);
292: }
293:
294: assert mDatasources != null;
295: }
296:
297: /**
298: * Stores the XML document with all the data in the current
299: * <code>Datasources</code> instance to the same file that populated
300: * this instance.
301: *
302: * @throws DatasourcesException when an error occurred during the
303: * storage
304: *
305: * @since 1.0
306: */
307: public void storeToXml() throws DatasourcesException {
308: String xmlpath = null;
309: URL xmlpath_resource = null;
310:
311: xmlpath = getXmlPath();
312: if (null == xmlpath) {
313: throw new MissingXmlPathException();
314: }
315:
316: xmlpath_resource = mResourceFinder.getResource(xmlpath);
317: if (null == xmlpath_resource) {
318: throw new CantFindXmlPathException(xmlpath);
319: }
320:
321: storeToXml(new File(URLDecoder.decode(xmlpath_resource
322: .getPath())));
323: }
324:
325: /**
326: * Stores the XML document with all the data in the current
327: * <code>Datasources</code> instance to the provided file.
328: *
329: * @param destination the <code>File</code> in which the data will be stored
330:
331: * @throws DatasourcesException when an error occurred during the
332: * storage
333: */
334: public synchronized void storeToXml(File destination)
335: throws DatasourcesException {
336: if (null == destination)
337: throw new IllegalArgumentException(
338: "destination can't be null");
339:
340: if (destination.exists() && !destination.canWrite()) {
341: throw new CantWriteToDestinationException(destination);
342: }
343:
344: StringBuilder content = new StringBuilder(
345: "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
346: content
347: .append("<!DOCTYPE datasources SYSTEM \"/dtd/datasources.dtd\">\n");
348: content.append(toXml());
349: try {
350: FileUtils.writeString(content.toString(), destination);
351: } catch (FileUtilsErrorException e) {
352: throw new StoreXmlErrorException(destination, e);
353: }
354: }
355:
356: /**
357: * Cleans up all connections that have been reserved by this datasource.
358: *
359: * @throws DatabaseException when an error occured during the cleanup
360: *
361: * @since 1.0
362: */
363: public void cleanup() throws DatabaseException {
364: synchronized (this ) {
365: if (null == mDatasources) {
366: return;
367: }
368:
369: HashMap<String, Datasource> datasoures = mDatasources;
370: mDatasources = null;
371:
372: for (Datasource datasource : datasoures.values()) {
373: datasource.cleanup();
374: }
375: }
376: }
377: }
|