package ca.sqlpower.sql;
import java.io.InputStream;
import java.rmi.Naming;
import java.util.Collection;
import java.util.LinkedList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* A collection of support methods for implementing a
* <code>DBCSSource</code>. Code in here was harvested from various
* places in the web-development classes where it shouldn't have been.
*
* <p>As the class name implies, you shouldn't use this class directly
* unless you are implementing a <code>DBCSSource</code>. If you're
* tempted to use this class directly, you probably need to do one of
* two things:
*
* <ol>
* <li>Use an existing <code>DBCSSource</code> implementation
* <li>Write a new <code>DBCSSource</code> implementation and use that.
* </ol>
*
* @author Jonathan Fuerth, Dan Fraser, Gillian Mereweather
* @version $Id$
*/
public class DBCSSourceSupport {
/**
* Returns a Collection of DBConnectionSpec objects which
* represent all database entries known to the named RMI host.
*
* @return a Collection of databases the user can try to connect
* to. All elements will be of type
* ca.sqlpower.sql.DBConnectionSpec. The Collection will be empty
* if no databases could be found.
* @throws DatabaseListReadException if anything goes wrong on the
* remote end or with the RMI communication itself.
*/
public static Collection getListUsingRMI(String host)
throws DatabaseListReadException {
Collection databases = null;
DBConnectionSpecServer obj = null;
try {
obj = (DBConnectionSpecServer) Naming.lookup
("//"+host+"/DBConnectionSpecServer");
databases = obj.getAvailableDatabases();
} catch (Exception e) {
// something bad happened getting the list of databases.
throw new DatabaseListReadException(e);
}
return databases;
}
/**
* Returns a Collection of DBConnectionSpec objects which
* represent all database entries in the XML document available
* through the given <code>InputStream</code>.
*
* @return a Collection of databases the user can try to connect
* to. All elements will be of type
* ca.sqlpower.sql.DBConnectionSpec. The Collection will be empty
* if no databases could be found.
* @throws DatabaseListReadException if the XML document is
* invalid or contains unexpected elements.
*/
public static Collection getListUsingXMLStream(InputStream xmlStream)
throws DatabaseListReadException {
return getDBSpecsFromInputStream(xmlStream);
}
/**
* Uses a list of available databases (set up by a sysadmin) to
* generate a Collection of DBConnectionSpec objects.
*
* @param xmlStream An input stream which contains a valid XML
* document describing the list of available databases. Typically
* a FileInputStream from /WEB-INF/databases.xml.
* @return a Collection of DBConnectionSpec objects describing all
* available databases.
* @throws DatabaseListReadException when the underlying list
* could not be loaded and parsed.
*/
public static Collection getDBSpecsFromInputStream(InputStream xmlStream)
throws DatabaseListReadException {
try {
DocumentBuilder db=DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document d=db.parse(xmlStream);
NodeList databaseList=d.getElementsByTagName("database");
LinkedList dbcsList=new LinkedList();
for(int i=0; i<databaseList.getLength(); i++) {
Element databaseNode=(Element)databaseList.item(i);
String databaseName=databaseNode.getAttribute("name");
dbcsList.add(makeSpecFromDBNode(databaseNode));
}
return(dbcsList);
} catch(Exception e){
throw new DatabaseListReadException(e);
}
}
/**
* Reads the values from the children of the given DOM element,
* populating a DBConnectionSpec bean with the corresponding
* values. The child elements it looks for are:
* <ul>
* <li>database element attribute <code>name</code>
* <li><code>display-name</code>
* <li><code>driver-class</code>
* <li><code>url</code>
* </ul>
*
* @return The populated DBConnectionSpec bean.
* @throws IllegalArgumentException if the given element isn't a
* <code>database</code> element.
*/
protected static DBConnectionSpec makeSpecFromDBNode(Element dbElem) {
if(!dbElem.getNodeName().equals("database")) {
throw new IllegalArgumentException("This method only supports nodes of type 'database'.");
}
DBConnectionSpec spec=new DBConnectionSpec();
spec.setName(dbElem.getAttributes().getNamedItem("name").getNodeValue());
spec.setSeqNo(Integer.parseInt(dbElem.getAttributes().getNamedItem("seqNo").getNodeValue()));
Node singleLoginAttr = dbElem.getAttributes().getNamedItem("singleLogin");
if (singleLoginAttr != null) {
spec.setSingleLogin(Boolean.valueOf(singleLoginAttr.getNodeValue()).booleanValue());
} else {
spec.setSingleLogin(false);
}
NodeList databaseProperties=dbElem.getChildNodes();
for(int j=0; j<databaseProperties.getLength(); j++) {
Node databaseProperty=databaseProperties.item(j);
if(databaseProperty.getNodeType() != Node.ELEMENT_NODE) continue;
databaseProperty.normalize();
if(databaseProperty.getNodeName().equals("display-name")) {
spec.setDisplayName(databaseProperty.getFirstChild().getNodeValue());
} else if(databaseProperty.getNodeName().equals("driver-class")) {
spec.setDriverClass(databaseProperty.getFirstChild().getNodeValue());
} else if(databaseProperty.getNodeName().equals("url")) {
spec.setUrl(databaseProperty.getFirstChild().getNodeValue());
} else if(databaseProperty.getNodeName().equals("user")) {
if (databaseProperty.getFirstChild() != null) {
spec.setUser(databaseProperty.getFirstChild().getNodeValue());
}
} else if(databaseProperty.getNodeName().equals("pass")) {
if (databaseProperty.getFirstChild() != null) {
spec.setPass(databaseProperty.getFirstChild().getNodeValue());
}
}
}
return spec;
}
}