/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.jdo.castor;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Enumeration;
import java.net.URL;
import javax.management.*;
import javax.naming.spi.ObjectFactory;
import javax.naming.Referenceable;
import javax.naming.Reference;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.NameNotFoundException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.DataObjects;
import org.exolab.castor.jdo.JDO;
import org.exolab.castor.jdo.DatabaseNotFoundException;
import org.exolab.castor.jdo.PersistenceException;
import org.exolab.castor.persist.spi.LogInterceptor;
import org.exolab.castor.xml.Unmarshaller;
import org.jboss.logging.util.LoggerPluginWriter;
import org.jboss.system.ServiceMBeanSupport;
/**
* Castor JDO support.
*
* @jmx:mbean name="jboss:type=Service,service=JDO,flavor=Castor"
* extends="org.jboss.system.ServiceMBean"
*
* @version <tt>$Revision: 81038 $</tt>
* @author Oleg Nitz (on@ibis.odessa.ua)
*/
public class CastorJDOImpl
extends ServiceMBeanSupport
implements DataObjects, ObjectFactory, Referenceable, Serializable,
CastorJDOImplMBean, MBeanRegistration, LogInterceptor
{
private String _jndiName;
private String _dbConf;
private String _dbUrl;
private JDO _jdo = new JDO();
private String _dataSourceName;
private static HashMap _instances = new HashMap();
private transient PrintWriter writer;
/**
* Do JDO classes should be loader by the global class loader
* (the same class loader as Castor classes)?
*/
private boolean _commonClassPath;
/*
* True if user prefer all reachable object to be stored automatically.
* False (default) if user want only dependent object to be stored.
*/
private boolean _autoStore = false;
/*
* True if user prefers application-server database pooling.
* False (default) if user wants a new connection for each invocation of
* getDatabase().
*/
private boolean _dbPooling = false;
public CastorJDOImpl() {
}
protected ObjectName getObjectName(MBeanServer server, ObjectName name)
throws javax.management.MalformedObjectNameException
{
if (name == null) {
return new ObjectName(OBJECT_NAME+",name="+_jndiName);
}
return name;
}
protected void startService() throws Exception
{
org.exolab.castor.jdo.conf.Database database;
Unmarshaller unm;
int pos;
Method m;
boolean debug = log.isDebugEnabled();
// Bind in JNDI
bind(new InitialContext(), "java:/" + _jndiName, this);
// Determine complete URL to _dbConf file. It is likely a relative path.
URL confUrl = Thread.currentThread().getContextClassLoader().getResource( _dbConf );
if ( null == confUrl )
{
FileNotFoundException e = new FileNotFoundException(
"CastorJDOImpl.startService(): Unable to resolve Configuration attribute to URL = "
+ _dbConf );
log.error( "CastorJDOImpl.startService(): Unable to find " + _dbConf + " file.", e );
}
_dbUrl = confUrl.toString();
_jdo.setTransactionManager("java:/TransactionManager");
_jdo.setConfiguration( _dbUrl );
unm = new Unmarshaller(org.exolab.castor.jdo.conf.Database.class);
database = (org.exolab.castor.jdo.conf.Database) unm.unmarshal(new InputSource( _dbUrl ));
_jdo.setDatabaseName(database.getName());
if (database.getJndi() != null) {
_dataSourceName = database.getJndi().getName();
}
// Older Castor versions older don't have these methods,
// we'll use reflection for backward compatibility
//_jdo.setAutoStore(_autoStore);
//_jdo.setDatabasePooling(_dbpooling);
try {
// 0.9.4
m = _jdo.getClass().getMethod("setAutoStore",
new Class[] {boolean.class});
m.invoke(_jdo, new Object[] {new Boolean(_autoStore)});
} catch (Exception ex) {
if (debug)
log.debug("couldn't invoke setAutoStore()");
}
try {
// 0.9.3
m = _jdo.getClass().getMethod("setDatabasePooling",
new Class[] {boolean.class});
m.invoke(_jdo, new Object[] {new Boolean(_dbPooling)});
} catch (Exception ex) {
if (debug)
log.debug("couldn't invoke setDatabasePooling()");
}
_instances.put(_jndiName, this);
if (debug)
log.debug("DataObjects factory for " + _dataSourceName + " bound to " + _jndiName);
}
protected void stopService() throws Exception
{
// Unbind from JNDI
InitialContext ctx = new InitialContext();
try {
ctx.unbind("java:/" + _jndiName);
}
finally {
ctx.close();
}
}
/**
* Copied from Jetty.java. Used to find resource within .sar.
*/
public URL findResourceInJar( String name )
{
URL url = null;
try
{
url = getClass().getClassLoader().getResource( name );
}
catch ( Exception e )
{
log.error( "Could not find resource: " + name, e );
}
return url;
}
// CastorJDOImplMBean implementation ---------------------------
/**
* @jmx:managed-attribute
*/
public void setJndiName(String jndiName) {
_jndiName = jndiName;
}
/**
* @jmx:managed-attribute
*/
public String getJndiName() {
return _jndiName;
}
/**
* @jmx:managed-attribute
*/
public void setConfiguration(String dbConf) {
_dbConf = dbConf;
}
/**
* @jmx:managed-attribute
*/
public String getConfiguration() {
return _dbConf;
}
/**
* @jmx:managed-attribute
*/
public String getConfigurationURL() {
return _dbUrl;
}
/**
* @jmx:managed-attribute
*/
public void setLockTimeout(int lockTimeout) {
_jdo.setLockTimeout(lockTimeout);
}
/**
* @jmx:managed-attribute
*/
public int getLockTimeout() {
return _jdo.getLockTimeout();
}
/**
* @jmx:managed-attribute
*/
public void setLoggingEnabled(boolean loggingEnabled) {
_jdo.setLogInterceptor(loggingEnabled ? this : null);
}
/**
* @jmx:managed-attribute
*/
public boolean getLoggingEnabled() {
return (_jdo.getLogInterceptor() != null);
}
/**
* @jmx:managed-attribute
*/
public void setCommonClassPath(boolean commonClassPath) {
_commonClassPath = commonClassPath;
}
/**
* @jmx:managed-attribute
*/
public boolean getCommonClassPath() {
return _commonClassPath;
}
/**
* @jmx:managed-attribute
*
* @param autoStore True if user prefer all reachable object to be stored automatically.
* False if user want only dependent object to be stored.
*/
public void setAutoStore( boolean autoStore ) {
_autoStore = autoStore;
}
/**
* @jmx:managed-attribute
*
* @return if the next Database instance will be set to autoStore.
*/
public boolean isAutoStore() {
return _autoStore;
}
/**
* True if user prefers to use application server database pools.
* False if user wants a new connection for each call to getDatabase().
*
* @jmx:managed-attribute
*/
public void setDatabasePooling(boolean dbPooling) {
_dbPooling = dbPooling;
}
/**
* Return true if the Database instance uses the application server pooling.
*
* @jmx:managed-attribute
*/
public boolean isDatabasePooling() {
return _dbPooling;
}
// DataObjects implementation ----------------------------------
public Database getDatabase()
throws DatabaseNotFoundException, PersistenceException
{
Method m;
if (_commonClassPath) {
_jdo.setClassLoader(null);
} else {
_jdo.setClassLoader(Thread.currentThread().getContextClassLoader());
}
return _jdo.getDatabase();
}
public void setDescription(String description) {
_jdo.setDescription(description);
}
public String getDescription() {
return _jdo.getDescription();
}
// Referenceable implementation ----------------------------------
public Reference getReference() {
return new Reference(getClass().getName(), getClass().getName(), null);
}
// ObjectFactory implementation ----------------------------------
public Object getObjectInstance(Object obj,
Name name,
Context nameCtx,
Hashtable environment)
throws Exception
{
return _instances.get(name.toString());
}
// Private -------------------------------------------------------
private void bind(Context ctx, String name, Object val)
throws NamingException
{
// Bind val to name in ctx, and make sure that all intermediate contexts exist
Name n = ctx.getNameParser("").parse(name);
while (n.size() > 1)
{
String ctxName = n.get(0);
try
{
ctx = (Context)ctx.lookup(ctxName);
} catch (NameNotFoundException e)
{
ctx = ctx.createSubcontext(ctxName);
}
n = n.getSuffix(1);
}
ctx.bind(n.get(0), val);
}
// LogInterceptor implementation for Castor 0.8 ----------------------
public void loading(Class objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Loading " + objClass.getName() + " (" + identity + ")" );
}
public void creating(Class objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Creating " + objClass.getName() + " (" + identity + ")" );
}
public void removing(Class objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Removing " + objClass.getName() + " (" + identity + ")" );
}
public void storing(Class objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Storing " + objClass.getName() + " (" + identity + ")" );
}
// LogInterceptor implementation for Castor 0.9 ----------------------
public void loading(Object objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Loading " + objClass + " (" + identity + ")" );
}
public void creating(Object objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Creating " + objClass + " (" + identity + ")" );
}
public void removing(Object objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Removing " + objClass + " (" + identity + ")" );
}
public void storing(Object objClass, Object identity) {
if (log.isDebugEnabled())
log.debug( "Storing " + objClass + " (" + identity + ")" );
}
// LogInterceptor implementation - the rest part --------------------
public void storeStatement(String statement) {
log.debug(statement);
}
public void queryStatement(String statement) {
log.debug(statement);
}
public void message(String message) {
log.debug(message);
}
public void exception(Exception except) {
log.error("Exception", except);
}
public PrintWriter getPrintWriter()
{
if (writer == null)
{
writer = new LoggerPluginWriter(log.getLoggerPlugin ());
}
return writer;
}
}