package net.xoetrope.optional.data;
import java.io.Reader;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import net.xoetrope.data.XDataSource;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.optional.data.sql.DatabaseTableModel;
import net.xoetrope.optional.data.sql.NamedConnectionManager;
import net.xoetrope.optional.service.XRouteManager;
import net.xoetrope.os.LibraryLoader;
import net.xoetrope.optional.service.XServiceModelNode;
import net.xoetrope.optional.service.XServiceProxyNotFoundException;
//import net.xoetrope.xlib.plugin.dialog.XLibDataChooser;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.xui.data.XModel;
import net.xoetrope.optional.data.sql.DatabaseTable;
import net.xoetrope.xui.helper.ReflectionHelper;
/**
* <p>An extended datasource for handling databses, services and routing</p>
* <p>Copyright: Copyright Xoetrope Ltd. (c) 2003</p>
* $Revision: 2.15 $
* License: see license.txt
*/
public class XOptionalDataSource extends XDataSource
{
protected XRouteManager routeMgr;
protected XDataModelLoader modelLoader;
/**
* @param project the owner project
*/
public XOptionalDataSource( XProject project )
{
super( project );
XOptionalBindingFactory.register( project );
String caseSensisitive = currentProject.getStartupParam( "CaseSensitiveDatabase" );
if (( caseSensisitive != null ) && caseSensisitive.toLowerCase().equals( "false" ))
DatabaseTable.setCaseSensitive( false );
// default way to build data sources via reflection
modelLoader = ( new XDataModelLoader() {
public void loadModel( String className, XmlElement src, XModel model ) {
Object[] values = new Object[ 1 ];
values[ 0 ] = currentProject;
XDataSource dataSource = (XDataSource)ReflectionHelper.constructViaReflection( className, values );
dataSource.loadTable( src, model );
}
});
}
/**
* Gets the object being used to load the model node
* @return XDataModelLoader object
*/
public XDataModelLoader setModelLoader()
{
return modelLoader;
}
/**
* Sets the object that is used to construct data sources via relfection.
* @param rc the XDataModelLoader object
*/
public void setModelLoader( XDataModelLoader rc )
{
modelLoader = rc;
}
/**
* If the ShowVisualiser property is set to true then show the visualiser
* for debugging
*/
protected void showVisualisation()
{
String sp = currentProject.getStartupParam( "ShowVisualiser" );
if ( ( sp != null ) && ( sp.compareTo( "true" ) == 0 ) ) {
String className = currentProject.getStartupParam( "VisualiseClass" );
try {
XModelVisualiser visualiser = (XModelVisualiser)Class.forName( className.trim() ).newInstance();
visualiser.showDlg( "" );
}
catch ( Exception ex ) {
ex.printStackTrace();
}
}
}
/**
* Read a model from the Reader
* @param r the Reader
*/
public void read( Reader r )
{
XmlElement ele = XmlSource.read( r );
XmlElement dbSrc = null, routeSrc = null, serviceSrc = null;
Vector v = ele.getChildren();
if ( v != null ) {
int numFiles = v.size();
XModel model = currentProject.getModel();
for ( int i = 0; i < numFiles; i++ ) {
try {
XmlElement source = ( XmlElement )v.elementAt( i );
Reader sr = currentProject.getBufferedReader( source.getAttribute( "filename" ), "UTF8" );
if ( sr == null )
continue;
if ( BuildProperties.DEBUG )
DebugLogger.trace( "Datasource: " + source.getAttribute( "filename" ) );
XmlElement src = XmlSource.read( sr );
String type = source.getAttribute( "type" );
if (( type == null ) || ( type.length()==0 ))
loadTable( src, model );
else if ( type.equals( "database" ))
dbSrc = src;
else if ( type.equals( "routing" ))
routeSrc = src;
else if ( type.equals( "service" ))
serviceSrc = src;
else if ( modelLoader != null )
modelLoader.loadModel( type, src, model );
registerDataSource( source );
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Failed to read configuration file" );
ex.printStackTrace();
}
}
try {
if ( routeSrc != null )
loadRoutes( routeSrc, model );
}
catch ( Exception ex1 ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Failed to read routes config file" );
}
try {
if ( serviceSrc != null )
loadService( serviceSrc, model );
}
catch ( Exception ex1 ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Failed to read services config file" );
}
try {
if ( dbSrc != null ) {
loadDatabase( dbSrc, model );
}
}
catch ( Exception ex1 ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Failed to read database config file" );
}
}
showVisualisation();
}
// Design time methods.
/**
* Register the datasource files found within the datasets.xml file
* @param source The path to the discovered file
*/
public void registerDataSource( XmlElement source ){}
/**
* Register a database source element found within the datasets.xml file
* @param source The path to the database file
*/
public void registerDatabaseNode( XmlElement source ){}
/**
* Recursively load the model data
* @param source the source element
* @param model the model for the source element
*/
public void loadDatabase( XmlElement source, XModel model )
{
XmlElement connEle = source.elementAt( 0 );
String dbDriver = connEle.getAttribute( "driver" );
String dbUrl = connEle.getAttribute( "url" );
dbUrl = checkLocalHsqldb( dbDriver, dbUrl );
NamedConnectionManager connMgr = ( NamedConnectionManager )NamedConnectionManager.getInstance();
connMgr = ( NamedConnectionManager )connMgr.reset( connEle.getAttribute( "id" ),
dbDriver,
dbUrl,
connEle.getAttribute( "user" ),
connEle.getAttribute( "pwd" ) );
registerDatabaseNode( connEle );
Vector vRS = source.getChildren();
int rsCount = vRS.size();
int numChildren = model.getNumChildren();
numChildren += rsCount;
model.setNumChildren( numChildren );
for ( int i = 1; i < rsCount; i++ ) {
XmlElement rsEle = ( XmlElement )vRS.elementAt( i );
if ( rsEle.getName().compareTo( "Connection" ) == 0 ) {
String extraDbDriver = rsEle.getAttribute( "driver" );
String extraDbUrl = rsEle.getAttribute( "url" );
extraDbUrl = checkLocalHsqldb( extraDbDriver, extraDbUrl );
connMgr.addConnection( rsEle.getAttribute( "id" ),
extraDbDriver,
extraDbUrl,
rsEle.getAttribute( "user" ),
rsEle.getAttribute( "pwd" ) );
}
else {
DatabaseTableModel tableModel = new DatabaseTableModel( currentProject );
String escapeStr = rsEle.getAttribute( "escape" );
if ( escapeStr != null )
tableModel.setDoesEscapeProcessing( "true".equals( escapeStr ));
tableModel.setName( rsEle.getAttribute( "id" ) );
boolean updateDb = false;
String updateStr = rsEle.getAttribute( "update" );
if ( updateStr != null )
updateDb = updateStr.compareTo( "true" ) == 0;
String sqlStr = rsEle.getAttribute( "sql" );
if (( sqlStr != null ) && ( sqlStr.length() > 0 ))
tableModel.setSqlStatement( sqlStr, rsEle.getAttribute( "conn" ), updateDb );
else {
// We should probably change the name table to from as it equates to the FROM clause
tableModel.setupTable( rsEle.getAttribute( "from" ),
rsEle.getAttribute( "fields" ),
rsEle.getAttribute( "where" ),
rsEle.getAttribute( "conn" ),
updateDb );
String distinctStr = rsEle.getAttribute( "distinct" );
if ( distinctStr != null )
tableModel.setDistinct( distinctStr.equals( "true" ));
tableModel.setOrderField( rsEle.getAttribute( "order" ) );
}
model.append( tableModel );
}
registerDatabaseNode( rsEle );
}
}
/**
* Check for a local database url. For hsqldb this extracts the db files to a
* local directory and returns the modiefied URL
* @param dbDriver the database drivr class
* @param dbUrl the database url
*/
protected String checkLocalHsqldb( String dbDriver, String dbUrl )
{
return checkLocalHsqldb( getClass().getClassLoader(), dbDriver, dbUrl );
}
/**
* Check for a local database url. For hsqldb this extracts the db files to a
* local directory and returns the modiefied URL
* @param cl teh classloader used to try and find the database files
* @param dbDriver the database drivr class
* @param dbUrl the database url
*/
public static String checkLocalHsqldb( ClassLoader cl, String dbDriver, String dbUrl )
{
if ( dbDriver.equals( "org.hsqldb.jdbcDriver" )) {
if ( dbUrl.indexOf( '.' ) < 0 ) {
String dbName = dbUrl.substring( dbUrl.lastIndexOf( ':' ) + 1 );
try {
String fileName = cl.getResource( dbName + ".script" ).getFile();
/** @todo check that the file is not already extracted, up-to-date and in use. */
if ( fileName.indexOf( ".jar" ) > 0 ) {
fileName = LibraryLoader.extractFile( cl, dbName + ".script" );
LibraryLoader.extractFile( cl, dbName + ".properties" );
LibraryLoader.extractFile( cl, dbName + ".data" );
}
return dbUrl.substring( 0, dbUrl.lastIndexOf( ':' ) + 1 ) + fileName.substring( 0, fileName.lastIndexOf( '.' ) );
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.logWarning( "Unable to open the resource file: " + dbName + ".script" );
}
}
}
return dbUrl;
}
/**
* Recursively load the model data
* @param source the source element
* @param model the model for the source element
*/
public void loadService( XmlElement source, XModel model )
{
routeMgr = XRouteManager.setupRouteManager( currentProject );
// Get the 'Services' node
Vector serviceNodes = source.getChildren();
// iterate all of the 'service' children
int serviceCount = serviceNodes.size();
for ( int i = 0; i < serviceCount; i++ ) {
XmlElement eleService = ( XmlElement )serviceNodes.elementAt( i );
String serviceName = eleService.getAttribute( "id" );
if ( BuildProperties.DEBUG ) {
if ( serviceName == null )
DebugLogger.logWarning( "Service (" + i + ") id is null or missing" );
}
String serviceRoute = eleService.getAttribute( "route" );
String serviceImplementationClass = eleService.getAttribute( "server" );
// int argSize = eleService.getChildren().size();
// iterate the 'arg' and 'return' elements of the service
/*
* This is removed for now as it really wasn't doing anything. We should re-introduce it
* but flag the arguments as private or public. If private then only the layer's owner can
* modify the argument.
String[] args = new String[ argSize ];
for ( int argNode = 0; argNode < argSize; argNode++ ) {
// Get the type of all args and only the type of the 'return' element.
String argName, argType;
XmlElement argEle = eleService.elementAt( argNode );
argType = argEle.getAttribute( "type" );
if ( argEle.getName().compareTo( "arg" ) == 0 ) {
argName = argEle.getAttribute( "id" );
args[ argNode ] = argName;
}
}*/
String[] args = new String[ 0 ];
XServiceModelNode node = new XServiceModelNode();
try {
node.setupService( serviceName, routeMgr.getRoute( serviceRoute, serviceImplementationClass ), args );
node.getServiceProxy().setServiceName( serviceName );
model.set( serviceName, node );
}
catch ( XServiceProxyNotFoundException ex ) {
ex.printStackTrace();
}
}
}
/**
* Load the routing information
* @param source the source element
* @param model the model for the source element
*/
public void loadRoutes( XmlElement source, XModel model )
{
// Initialise the XRouteManager
XRouteManager routeMgr = ((XRouteManager)currentProject.getObject( "RouteMgr" ));
if ( routeMgr == null ) {
routeMgr = new XRouteManager( currentProject );
currentProject.setObject( "RouteMgr", routeMgr );
}
Vector routeNodes = source.getChildren();
// iterate all of the 'route' children
int routeCount = routeNodes.size();
for ( int i = 0; i < routeCount; i++ ) {
XmlElement routeElement = ( XmlElement )routeNodes.elementAt( i );
String routeName = routeElement.getAttribute( "id" );
if ( routeName == null )
continue;
// For each route add the layers
int layerCount = routeElement.getChildren().size();
Hashtable[] layers = new Hashtable[ layerCount ];
for ( int layer = 0; layer < layerCount; layer++ ) {
XmlElement layerElement = routeElement.elementAt( layer );
Hashtable layerTable = new Hashtable();
// Put any extra attributes into a hashtable
Enumeration attribs = layerElement.enumerateAttributeNames();
while ( attribs.hasMoreElements() ) {
String attribName = attribs.nextElement().toString();
layerTable.put( attribName, layerElement.getAttribute( attribName ) );
// Add the children of the layer node
int pathCount = layerElement.getChildren().size();
for ( int pathIdx = 0; pathIdx < pathCount; pathIdx++ ) {
XmlElement pathElement = layerElement.elementAt( pathIdx );
Vector pathTable = new Vector();
// Put any extra path attributes into a hashtable
Enumeration pathAttribs = pathElement.enumerateAttributeNames();
while ( pathAttribs.hasMoreElements() ) {
String pathAttribName = (String)pathAttribs.nextElement();
if ( pathAttribName.compareTo( "id" ) == 0 )
pathTable.add( pathElement.getAttribute( "id" ) );
else
; // do nothing with the extra data for now.
}
layerTable.put( new Integer( pathIdx ).toString(), pathTable );
}
}
layers[ layer ] = layerTable;
}
// add the route to the XRouteManager
routeMgr.addRoute( routeName, layers );
}
}
/**
* Output the model to a String and return it
* @param mdl the model to bt output
* @return the String representation of the model
*/
public String getModelXML( XModel mdl )
{
StringWriter sw = new StringWriter();
super.outputModel( sw, mdl );
return sw.toString();
}
}