/* * Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com> * Licensed under the Apache License, Version 2.0 (the "License") * $Id: Datasources.java 3918 2008-04-14 17:35:35Z gbevin $ */ package com.uwyn.rife.database; import com.uwyn.rife.database.exceptions.*; import com.uwyn.rife.rep.Participant; import com.uwyn.rife.rep.Rep; import com.uwyn.rife.resources.ResourceFinder; import com.uwyn.rife.selector.XmlSelectorResolver; import com.uwyn.rife.tools.FileUtils; import com.uwyn.rife.tools.SortListComparables; import com.uwyn.rife.tools.StringUtils; import com.uwyn.rife.tools.exceptions.FileUtilsErrorException; import com.uwyn.rife.xml.exceptions.XmlErrorException; import java.io.File; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; /** * Contains a collection of <code>Datasource</code> instances that have been * created from an XML definition. A <code>Datasources</code> instance can * either be created by calling the public constructor or by executing the * <code>ParticipantDatasources</code> which participates in the * application-wide repository. * * @author Geert Bevin (gbevin[remove] at uwyn dot com) * @version $Revision: 3918 $ * @see com.uwyn.rife.database.Datasource * @see com.uwyn.rife.database.Xml2Datasources * @see com.uwyn.rife.rep.Rep * @see com.uwyn.rife.rep.participants.ParticipantDatasources * @since 1.0 */ public class Datasources { public static final String DEFAULT_PARTICIPANT_NAME = "ParticipantDatasources"; private HashMap<String, Datasource> mDatasources = null; private String mXmlPath = null; private ResourceFinder mResourceFinder = null; /** * Creates a new empty <code>Datasources</code> instance. * * @since 1.0 */ public Datasources() throws DatasourcesException { mDatasources = new HashMap<String, Datasource>(); } /** * Creates a new <code>Datasources</code> instance from the definitions in * an XML file. * * @param xmlPath the path of the XML resource that will be used for the * population * @param resourceFinder a <code>ResourceFinder</code> instance that will be * used to find the file that corresponds to the provided * <code>xmlPath</code> * * @throws DatasourcesException when an exception occured during the * obtainance of the resource's modification time or during the processing * of the XML file * * @since 1.0 */ public Datasources(String xmlPath, ResourceFinder resourceFinder) throws DatasourcesException, DatasourceNotFoundException { if (null == xmlPath) throw new IllegalArgumentException("xmlPath can't be null."); if (0 == xmlPath.length()) throw new IllegalArgumentException("xmlPath can't be empty."); if (null == resourceFinder) throw new IllegalArgumentException("resourceFinder can't be null."); mResourceFinder = resourceFinder; String datasource_resolved = XmlSelectorResolver.resolve(xmlPath, mResourceFinder, "rep/datasources-"); if( null == datasource_resolved ) { throw new DatasourceNotFoundException(xmlPath); } URL datasource_resource = mResourceFinder.getResource(datasource_resolved); if( null == datasource_resource ) { throw new DatasourceNotFoundException(xmlPath, datasource_resolved); } mXmlPath = datasource_resolved; initialize(); assert mResourceFinder != null; assert mXmlPath != null; assert mXmlPath.length() > 0; } /** * Checks if a <code>ParticipantDatasources</code> participant has been * initialized and is available from the application-wide repository. * * @return <code>true</code> if this participant is available; or * <p> * <code>false</code> otherwise * * @see #getRepInstance() * * @since 1.0 */ public static boolean hasRepInstance() { return Rep.hasParticipant(DEFAULT_PARTICIPANT_NAME); } /** * Retrieves the <code>Datasources</code> instance that is initialized by * the <code>ParticipantDatasources</code> participant in the * application-wide repository. * * @return the requested <code>Datasources</code> instances; or * <p> * <code>null</code> if the <code>ParticipantDatasources</code> couldn't be * found * * @see #hasRepInstance() * * @since 1.0 */ public static Datasources getRepInstance() { Participant participant = Rep.getParticipant(DEFAULT_PARTICIPANT_NAME); if (null == participant) { return null; } return (Datasources)participant.getObject(); } /** * Retrieves the <code>Datasource</code> that corresponds to a provided * name. * * @param name a <code>String</code> that identifies the * <code>Datasource</code> that has to be retrieved * * @return the requested <code>Datasource</code> instance; or * <p> * <code>null</code> if name isn't known * * @since 1.0 */ public Datasource getDatasource(String name) { return mDatasources.get(name); } /** * Stores a <code>Datasource</code> with a provided name to be able to * reference it later. * * @param name a <code>String</code> that identifies the * <code>Datasource</code> * @param datasource the <code>Datasource</code> instance that has to be * stored * * @since 1.0 */ public void setDatasource(String name, Datasource datasource) { if (null == name) throw new IllegalArgumentException("name can't be null."); if (0 == name.length()) throw new IllegalArgumentException("name can't be empty."); if (null == datasource) throw new IllegalArgumentException("datasource can't be null."); mDatasources.put(name, datasource); } /** * Retrieves a collection of all the <code>Datasource</code> names that are * known by this <code>Datasources</code> instance. * * @return the requested <code>Collection</code> * * @since 1.0 */ public Collection<String> getDatasourceNames() { return mDatasources.keySet(); } /** * Retrieves the path of the XML document that populated this * <code>DataSources</code> instance * * @return the path of the XML document that populated this * <code>DataSources</code> instance * * @since 1.0 */ public String getXmlPath() { return mXmlPath; } /** * Creates an XML document with all the data in the current * <code>Datasources</code> instance * * @return the constructed XML document as a <code>String</code> * * @since 1.0 */ public String toXml() { StringBuilder xml_output = new StringBuilder(); xml_output.append("<datasources>\n"); ArrayList<String> datasource_keys_arraylist = new ArrayList<String>(); for (String datasource_key : mDatasources.keySet()) { datasource_keys_arraylist.add(datasource_key); } (new SortListComparables()).sort(datasource_keys_arraylist); Datasource datasource = null; for (String datasource_key : datasource_keys_arraylist) { datasource = mDatasources.get(datasource_key); xml_output.append("\t<datasource name=\"").append(StringUtils.encodeXml(datasource_key)).append("\">\n"); xml_output.append("\t\t<driver>").append(StringUtils.encodeXml(datasource.getDriver())).append("</driver>\n"); xml_output.append("\t\t<url>").append(StringUtils.encodeXml(datasource.getUrl())).append("</url>\n"); if (null != datasource.getUser()) { xml_output.append("\t\t<user>").append(StringUtils.encodeXml(datasource.getUser())).append("</user>\n"); } if (null != datasource.getPassword()) { xml_output.append("\t\t<password>").append(StringUtils.encodeXml(datasource.getPassword())).append("</password>\n"); } if (datasource.getPoolsize() > 0) { xml_output.append("\t\t<poolsize>").append(datasource.getPoolsize()).append("</poolsize>\n"); } xml_output.append("\t</datasource>\n"); } xml_output.append("</datasources>\n"); assert xml_output.length() > 0; return xml_output.toString(); } /** * Performs the initialization logic. * * @throws DatasourcesException when an error occurred during the * initialization * * @since 1.0 */ private void initialize() throws DatasourcesException { try { Xml2Datasources xml_datasources = new Xml2Datasources(); xml_datasources.processXml(mXmlPath, mResourceFinder); synchronized (this) { mDatasources = xml_datasources.getDatasources(); } } catch (XmlErrorException e) { throw new InitializationErrorException(mXmlPath, e); } assert mDatasources != null; } /** * Stores the XML document with all the data in the current * <code>Datasources</code> instance to the same file that populated * this instance. * * @throws DatasourcesException when an error occurred during the * storage * * @since 1.0 */ public void storeToXml() throws DatasourcesException { String xmlpath = null; URL xmlpath_resource = null; xmlpath = getXmlPath(); if (null == xmlpath) { throw new MissingXmlPathException(); } xmlpath_resource = mResourceFinder.getResource(xmlpath); if (null == xmlpath_resource) { throw new CantFindXmlPathException(xmlpath); } storeToXml(new File(URLDecoder.decode(xmlpath_resource.getPath()))); } /** * Stores the XML document with all the data in the current * <code>Datasources</code> instance to the provided file. * * @param destination the <code>File</code> in which the data will be stored * @throws DatasourcesException when an error occurred during the * storage */ public synchronized void storeToXml(File destination) throws DatasourcesException { if (null == destination) throw new IllegalArgumentException("destination can't be null"); if (destination.exists() && !destination.canWrite()) { throw new CantWriteToDestinationException(destination); } StringBuilder content = new StringBuilder("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); content.append("<!DOCTYPE datasources SYSTEM \"/dtd/datasources.dtd\">\n"); content.append(toXml()); try { FileUtils.writeString(content.toString(), destination); } catch (FileUtilsErrorException e) { throw new StoreXmlErrorException(destination, e); } } /** * Cleans up all connections that have been reserved by this datasource. * * @throws DatabaseException when an error occured during the cleanup * * @since 1.0 */ public void cleanup() throws DatabaseException { synchronized (this) { if (null == mDatasources) { return; } HashMap<String, Datasource> datasoures = mDatasources; mDatasources = null; for (Datasource datasource : datasoures.values()) { datasource.cleanup(); } } } }