/**
* Speedo: an implementation of JDO compliant personality on top of JORM generic
* I/O sub-system.
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library 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 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Release: 1.0
*
* Created on 2004-02-18
* @author fmillevi@yahoo.com
*
*/
package org.objectweb.speedo.j2eedo.web;
import java.io.IOException;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.NotSupportedException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import org.objectweb.speedo.j2eedo.common.PMHolder;
import org.objectweb.speedo.j2eedo.bo.DatabaseImpl;
import org.objectweb.speedo.j2eedo.ejb.StoreServicesLocal;
import org.objectweb.speedo.j2eedo.ejb.StoreServicesLocalHome;
import org.objectweb.speedo.j2eedo.ejb.StoreServicesRemote;
import org.objectweb.speedo.j2eedo.ejb.StoreServicesRemoteHome;
import org.objectweb.util.monolog.Monolog;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
/**
* is an extended of the former servlet MainServlet. This class implements the
* two methods used to execute the resquest through a Session Bean or directly
* from the {@link org.objectweb.speedo.j2eedo.bo.DatabaseImpl DataBaseImpl} class.</li>
* </ul>
*
* @author fmillevi@yahoo.com
*
*/
public class ApplicationServlet extends HttpServlet {
/**
* The constant <code>PARAMETER_ACTION</code> defines the HTTP
* requests paramater <b>"action"</b> use to know
* {@link org.objectweb.speedo.j2eedo.bo.DatabaseImpl#actionArray which action} to do.
*/
final public static String PARAMETER_ACTION = "action";
// define the class name parameter
/**
* The constant <code>WITHOUT_TRANSACTION_PARAMETER</code> is used
* to retrive the HTTP request parameter <b>"withoutTrans"</b> used by the servlet
* to know if a transaction must be start at the servlet level.
*/
public final static String WITH_TRANSACTION_PARAMETER = "withTrans";
/**
* The constant <code>TYPE_CONTAINER_PARAMETER</code> is used
* to retrive the HTTP request parameter <b>"container"</b> used by the servlet
* to know if the action must be done through a Session Bean Container.
*/
public final static String TYPE_CONTAINER_PARAMETER = "container";
public final static String LOCAL_SESSION_BEAN_PARAMETER = "localsession";
/**
* The constant <code>WITH_GETPM_PARAMETER</code> is used
* to retrive the HTTP request parameter <b>"withGetPM"</b> used by the servlet
* to know if the persistence manager must be get by the servelt.
*/
public final static String JDOTX = "jdotx";
private final static String PMF_JNDI_NAME = "speedo";
protected static Logger logger = null;
protected PersistenceManagerFactory persistenceManagerFactory = null;
protected StoreServicesLocalHome lstoreServicesLH;
protected StoreServicesRemoteHome lstoreServicesRH;
/**
* This servlet executes current request:<ul>
* <li>Gets the Persistence manager when required (false by default),</li>
* <li>Starts a local transaction when requested (false by default),</li>
* <li>Calls the dedicated action directly or through a Session Bean Container
* (direct call by default),</li>
* <li>Close the PM when initialized at the servlet level.</li>
* </ul>
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
boolean withTransaction = getParameterValue(
req, WITH_TRANSACTION_PARAMETER, false);
boolean isJdoTx = getParameterValue(
req, JDOTX, true);
boolean useSessionBean = getParameterValue(
req, TYPE_CONTAINER_PARAMETER, false);
boolean localSessionBean = getParameterValue(
req, LOCAL_SESSION_BEAN_PARAMETER, false);
String action = req.getParameter(ApplicationServlet.PARAMETER_ACTION);
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Invoke Servlet: "
+ "\n\t-withTransaction=" + withTransaction
+" \n\t-isJdoTx=" + isJdoTx
+ "\n\t-useSessionBean=" + useSessionBean
+ "\n\t-localSessionBean=" + localSessionBean
+ "\n\t-action=" + action
);
}
UserTransaction jtatx = null;
PMHolder pmh = new PMHolder(persistenceManagerFactory);
Transaction jdotx = null;
if (withTransaction) {
if (isJdoTx && (!useSessionBean || localSessionBean)) {
logger.log(BasicLevel.DEBUG, "fetch JDO PersistenceManager");
jdotx = pmh.getPersistenceManager().currentTransaction();
logger.log(BasicLevel.DEBUG, "Begin JDO Transaction (local)");
jdotx.begin();
} else {
try {
jtatx = (UserTransaction) getObjectFromContext(
null, "java:comp/UserTransaction");
} catch (Exception e) {
logger.log(BasicLevel.ERROR, "Cannot lookup java:comp/UserTransaction: ", e);
throw new ServletException(e);
}
logger.log(BasicLevel.DEBUG, "Begin JTA Transaction");
try {
jtatx.begin();
} catch (Exception e) {
logger.log(BasicLevel.ERROR, "Cannot begin JTA transaction", e);
throw new ServletException(e);
}
}
}
try {
if (useSessionBean) {
//The action will be done by the Session bean
if (localSessionBean) {
//Invoke the session bean through the local interface
logger.log(BasicLevel.DEBUG, "Use local session bean");
StoreServicesLocal lstoreServices = null;
try {
lstoreServices = lstoreServicesLH.create();
} catch (CreateException e) {
logger.log(BasicLevel.ERROR, "Error during the creation of Local Session", e);
throw new ServletException(e);
}
try {
ServletOutputStream out = resp.getOutputStream();
out.println(lstoreServices.doAction(pmh, action));
} finally {
//release the session bean from the pool
lstoreServices.remove();
}
} else {
//Invoke the session bean through the Remote interface
logger.log(BasicLevel.DEBUG, "Use remote session bean");
StoreServicesRemote lstoreServices = null;
try {
lstoreServices = lstoreServicesRH.create();
} catch (CreateException e) {
logger.log(BasicLevel.ERROR, "Error during the creation of Local Session", e);
throw new ServletException(e);
}
try {
ServletOutputStream out = resp.getOutputStream();
out.println(lstoreServices.doAction(action));
} finally {
//release the session bean from the pool
lstoreServices.remove();
}
}
} else {
logger.log(BasicLevel.DEBUG, "Use direct call");
ServletOutputStream out = resp.getOutputStream();
String outStr = DatabaseImpl.instance.doAction(action, false, pmh);
out.println(outStr);
}
if (jtatx != null) {
logger.log(BasicLevel.DEBUG, "Commit the JTA Transaction");
jtatx.commit();
} else if (jdotx != null && jdotx.isActive()) {
logger.log(BasicLevel.DEBUG, "Commit the local JDOTransactionItf");
jdotx.commit();
}
} catch (Exception e) {
if (jtatx != null) {
logger.log(BasicLevel.WARN, "Rolling back the JTA Transaction due to an error: ", e);
try {
jtatx.rollback();
} catch (Exception jtae) {
logger.log(BasicLevel.ERROR, "Cannot rollback JTA transaction", jtae);
}
} else if (jdotx != null && jdotx.isActive()) {
logger.log(BasicLevel.WARN, "Rolling back the JDO Transaction due to an error: ", e);
jdotx.rollback();
} else {
logger.log(BasicLevel.WARN, "An error has occured: ", e);
}
throw new ServletException(e);
} finally {
if (jdotx != null) {
pmh.closePersistenceManager();
}
}
}
public void init() throws ServletException {
super.init();
// get the logger
logger = Monolog.initialize().getLogger(getClass().getName());
//get the PersistenceManagerFactory
persistenceManagerFactory = (PersistenceManagerFactory)
getObjectFromContext(null, PMF_JNDI_NAME);
try {
getStoreServicesRemoteHome();
} catch (ServletException e) {
logger.log(BasicLevel.WARN, "No StoreServicesRemoteHome found: " + e.getMessage());
}
try {
getStoreServicesLocalHome();
} catch (ServletException e) {
logger.log(BasicLevel.WARN, "No StoreServicesLocalHome found: " + e.getMessage());
}
}
/**
* get the StoreServicesLocalHome.
*/
private StoreServicesLocalHome getStoreServicesLocalHome() throws ServletException {
if (lstoreServicesLH == null) {
lstoreServicesLH = (StoreServicesLocalHome)
getObjectFromContext(null, "java:comp/env/ejb/StoreServicesHomeLocal");
}
return lstoreServicesLH;
}
/**
* get the StoreServicesRemoteHome.
*/
private StoreServicesRemoteHome getStoreServicesRemoteHome() throws ServletException {
if (lstoreServicesRH == null) {
lstoreServicesRH = (StoreServicesRemoteHome)
getObjectFromContext(null, "ejb/storeService");
}
return lstoreServicesRH;
}
/**
* Get an object from JNDI
* @param ictx is the InitialContext to use. If this parameter is null
* a new context is created and closed
* @param name is the JNDI name of the searched object
* @return the object (never null)
* @throws ServletException if the object cannot be found.
*/
private static Object getObjectFromContext(InitialContext ictx, String name) throws ServletException {
boolean hasToCloseContext = false;
if (ictx == null) {
try {
ictx = new InitialContext();
hasToCloseContext = true;
} catch (NamingException e) {
logger.log(BasicLevel.ERROR,
"Impossible to get the InitialContext: ", e);
throw new ServletException(
"Impossible to get the InitialContext: " + e.getMessage());
}
}
try {
Object o = ictx.lookup(name);
if (o == null) {
throw new ServletException("'" + name + "' has not been found from JNDI");
}
return o;
} catch (ServletException e) {
throw e;
} catch (NameNotFoundException e) {
logger.log(BasicLevel.WARN, "Impossible to find " + name +": " + e.getMessage());
return null;
} catch (Exception e) {
logger.log(BasicLevel.ERROR, "Impossible to find " + name +": ", e);
return null;
} finally {
if (hasToCloseContext && ictx != null) {
try {
ictx.close();
} catch (NamingException ne) {
}
}
}
}
/**
* Get the value of a prameter from a HttpServletRequest instance
* @param req is the HttpServletRequest
* @param paramName is the name of the parameter
* @param defaultValue is the default value to return if the parameter
* is not defined in the HttpServletRequest
*/
private static boolean getParameterValue(HttpServletRequest req,
String paramName,
boolean defaultValue) {
try {
String val = req.getParameter(paramName);
return val == null ? defaultValue : Boolean.valueOf(val).booleanValue();
} catch (Exception e) {
return defaultValue;
}
}
}