/*
* 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.ejb.plugins.cmp.jdbc;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.ejb.FinderException;
import org.jboss.deployment.DeploymentException;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCAutomaticQueryMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCDeclaredQueryMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCDynamicQLQueryMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCJBossQLQueryMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQlQueryMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
import org.jboss.logging.Logger;
import org.jboss.metadata.MetaData;
import org.w3c.dom.Element;
/**
* Maintains a map from a query method to query command.
*
* @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
* @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
* @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
* @author <a href="mailto:shevlandj@kpi.com.au">Joe Shevland</a>
* @author <a href="mailto:justin@j-m-f.demon.co.uk">Justin Forder</a>
* @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
* @version $Revision: 81030 $
*/
public final class JDBCQueryManager
{
private static final String FIND_BY_PK = "findByPrimaryKey";
private static final String EJB_FIND = "ejbFind";
private static final String FIND_ALL = "findAll";
private static final String FIND_BY = "findBy";
private final Map knownQueries = new HashMap();
private final JDBCStoreManager manager;
public JDBCQueryManager(JDBCStoreManager manager)
{
this.manager = manager;
}
public static final Class getQLCompiler(Element query, JDBCEntityMetaData entity)
throws DeploymentException
{
String compiler = MetaData.getOptionalChildContent(query, "ql-compiler");
Class impl;
if(compiler == null || compiler.trim().length() == 0)
{
impl = entity.getQLCompiler();
}
else
{
try
{
impl = TCLAction.UTIL.getContextClassLoader().loadClass(compiler);
}
catch(ClassNotFoundException e)
{
throw new DeploymentException("Failed to load compiler implementation: " + compiler);
}
}
return impl;
}
public static final QLCompiler getInstance(Class impl, Catalog catalog)
throws DeploymentException
{
if(impl == null)
{
throw new DeploymentException("ql-compiler is not specified.");
}
final Constructor constructor;
try
{
constructor = impl.getConstructor(new Class[]{Catalog.class});
}
catch(NoSuchMethodException e)
{
throw new DeploymentException("Compiler class does not have a constructor which takes " + Catalog.class.getName());
}
try
{
return (QLCompiler)constructor.newInstance(new Object[]{catalog});
}
catch(Exception e)
{
throw new DeploymentException("Failed to create an instance of " + impl.getName() + ": " + e.getMessage(), e);
}
}
public JDBCQueryCommand getQueryCommand(Method queryMethod)
throws FinderException
{
JDBCQueryCommand queryCommand = (JDBCQueryCommand)knownQueries.get(queryMethod);
if(queryCommand == null)
{
throw new FinderException("Unknown query: " + queryMethod);
}
return queryCommand;
}
public void start() throws DeploymentException
{
Logger log = Logger.getLogger(
this.getClass().getName() +
"." +
manager.getMetaData().getName());
JDBCCommandFactory factory = manager.getCommandFactory();
Class homeClass = manager.getContainer().getHomeClass();
Class localHomeClass = manager.getContainer().getLocalHomeClass();
//
// findByPrimaryKey
//
JDBCEntityBridge entity = (JDBCEntityBridge) manager.getEntityBridge();
if(homeClass != null)
{
try
{
// try to get the finder method on the home interface
Method method = homeClass.getMethod(FIND_BY_PK, new Class[]{entity.getPrimaryKeyClass()});
JDBCQueryMetaData findByPKMD = manager.getMetaData().getQueryMetaDataForMethod(method);
JDBCReadAheadMetaData readAhead = (findByPKMD == null ?
entity.getMetaData().getReadAhead() : findByPKMD.getReadAhead());
// got it add it to known finders
JDBCQueryMetaData q = new JDBCAutomaticQueryMetaData(
method,
readAhead,
entity.getMetaData().getQLCompiler(),
false
);
knownQueries.put(method, factory.createFindByPrimaryKeyQuery(q));
if(log.isDebugEnabled())
log.debug("Added findByPrimaryKey query command for home interface");
} catch(NoSuchMethodException e)
{
throw new DeploymentException("Home interface does not have a findByPrimaryKey method");
}
}
if(localHomeClass != null)
{
Method method;
try
{
// try to get the finder method on the local home interface
method = localHomeClass.getMethod(FIND_BY_PK, new Class[] { entity.getPrimaryKeyClass() });
} catch(NoSuchMethodException e)
{
throw new DeploymentException("Local home interface does " +
"not have the method findByPrimaryKey(" +
entity.getPrimaryKeyClass().getName() + ")");
}
// got it add it to known finders
JDBCQueryMetaData findByPKMD = manager.getMetaData().getQueryMetaDataForMethod(method);
JDBCReadAheadMetaData readAhead = (findByPKMD == null ?
entity.getMetaData().getReadAhead() : findByPKMD.getReadAhead());
JDBCQueryMetaData q = new JDBCAutomaticQueryMetaData(method, readAhead, entity.getMetaData().getQLCompiler(), false);
knownQueries.put(method, factory.createFindByPrimaryKeyQuery(q));
if(log.isDebugEnabled())
log.debug("Added findByPrimaryKey query command for local home interface");
}
//
// Custom finders - Overrides defined and automatic finders.
//
Class ejbClass = manager.getMetaData().getEntityClass();
Method[] customMethods = ejbClass.getMethods();
for (int i = 0; i < customMethods.length; i++)
{
Method m = customMethods[i];
String methodName = m.getName();
if(methodName.startsWith(EJB_FIND))
{
String interfaceName = 'f' + methodName.substring(4);
if(homeClass != null)
{
try
{
// try to get the finder method on the home interface
Method interfaceMethod = homeClass.getMethod(
interfaceName,
m.getParameterTypes());
// got it add it to known finders
knownQueries.put(interfaceMethod, new JDBCCustomFinderQuery(manager, m));
if(log.isDebugEnabled())
log.debug("Added custom finder " + methodName + " on home interface");
} catch(NoSuchMethodException e)
{
// this is ok method may not be defined on this interface
}
}
if(localHomeClass != null)
{
try
{
// try to get the finder method on the local home interface
Method interfaceMethod = localHomeClass.getMethod(
interfaceName,
m.getParameterTypes());
// got it add it to known finders
knownQueries.put(interfaceMethod, new JDBCCustomFinderQuery(manager, m));
if(log.isDebugEnabled())
log.debug("Added custom finder " + methodName + " on local home interface");
} catch(NoSuchMethodException e)
{
// this is ok method may not be defined on this interface
}
}
}
}
//
// Defined finders - Overrides automatic finders.
//
Iterator definedFinders = manager.getMetaData().getQueries().iterator();
while(definedFinders.hasNext())
{
JDBCQueryMetaData q = (JDBCQueryMetaData)definedFinders.next();
if(!knownQueries.containsKey(q.getMethod()) )
{
if(q instanceof JDBCJBossQLQueryMetaData)
{
knownQueries.put(q.getMethod(), factory.createJBossQLQuery(q));
} else if(q instanceof JDBCDynamicQLQueryMetaData)
{
knownQueries.put(q.getMethod(), factory.createDynamicQLQuery(q));
} else if(q instanceof JDBCDeclaredQueryMetaData)
{
knownQueries.put(q.getMethod(), factory.createDeclaredSQLQuery(q));
} else if(q instanceof JDBCQlQueryMetaData)
{
knownQueries.put(q.getMethod(), factory.createEJBQLQuery(q));
}
}
}
//
// Automatic finders - The last resort
//
if(homeClass != null)
{
addAutomaticFinders(manager, homeClass.getMethods(), log);
}
if(localHomeClass != null)
{
addAutomaticFinders(manager, localHomeClass.getMethods(), log);
}
}
public void clear()
{
this.knownQueries.clear();
}
private void addAutomaticFinders(
JDBCStoreManager manager,
Method[] homeMethods,
Logger log) throws DeploymentException
{
JDBCCommandFactory factory = manager.getCommandFactory();
JDBCEntityBridge entity = (JDBCEntityBridge) manager.getEntityBridge();
for (int i = 0; i < homeMethods.length; i++)
{
Method method = homeMethods[i];
if(!knownQueries.containsKey(method))
{
String name = method.getName();
if(name.equals(FIND_ALL))
{
JDBCQueryMetaData q = new JDBCAutomaticQueryMetaData(
method,
entity.getMetaData().getReadAhead(),
entity.getMetaData().getQLCompiler(), false
);
knownQueries.put(method, factory.createFindAllQuery(q));
}
else if(name.startsWith(FIND_BY) && !name.equals(FIND_BY_PK))
{
try
{
JDBCQueryMetaData q = new JDBCAutomaticQueryMetaData(
method,
entity.getMetaData().getReadAhead(),
entity.getMetaData().getQLCompiler(), false);
knownQueries.put(method, factory.createFindByQuery(q));
} catch (IllegalArgumentException e)
{
log.debug("Could not create the finder " + name +
", because no matching CMP field was found.");
}
}
}
}
}
}