Package de.danet.an.util

Source Code of de.danet.an.util.EJBUtil$HighLow

/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* $Id: EJBUtil.java 2550 2007-10-22 14:00:54Z  $
*
* $Log$
* Revision 1.7  2006/10/11 09:42:36  drmlipp
* Comepleted adaption to local KeyGen EJB.
*
* Revision 1.6  2006/10/11 09:06:08  drmlipp
* Fixed EJB naming.
*
* Revision 1.5  2006/10/11 07:47:02  drmlipp
* Adapted properties file to new convention.
*
* Revision 1.4  2006/10/07 20:41:34  mlipp
* Merged J2EE 1.4 adaptions from test branch.
*
* Revision 1.3  2006/09/29 12:32:08  drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.2  2005/09/10 21:44:18  mlipp
* Fixed JDK 5.0 warnings.
*
* Revision 1.1.1.3  2004/08/18 15:17:34  drmlipp
* Update to 1.2
*
* Revision 1.19  2004/02/13 10:32:22  lipp
* Added support for local home interfaces.
*
* Revision 1.18  2003/10/27 15:28:51  lipp
* Fixed repeat behaviour.
*
* Revision 1.17  2003/10/27 14:33:37  lipp
* Added debug statements.
*
* Revision 1.16  2003/10/24 11:08:01  lipp
* Added retrievJNDIEntry.
*
* Revision 1.15  2003/10/22 15:58:45  lipp
* Fixed synchronization problem.
*
* Revision 1.14  2003/09/09 13:38:47  lipp
* Added support for local home and business interfaces.
*
* Revision 1.13  2003/06/29 19:50:07  lipp
* Moved primary key generator fropm JDBCUtil to EJBUtil and made some
* fixes.
*
* Revision 1.12  2003/06/27 08:51:47  lipp
* Fixed copyright/license information.
*
* Revision 1.11  2003/05/23 15:42:41  lipp
* Fixed deployment unit dependencies.
*
* Revision 1.10  2003/03/31 16:50:27  huaiyang
* Logging using common-logging.
*
* Revision 1.9  2002/09/26 15:05:13  lipp
* Added method and extended comments.
*
* Revision 1.8  2002/08/30 13:37:04  lipp
* Using Workflow engine facade now.
*
* Revision 1.7  2002/07/29 21:03:57  lipp
* Some JavaDoc fixes.
*
* Revision 1.6  2002/07/24 05:48:16  huaiyang
* javadocs added.
*
* Revision 1.5  2002/07/03 11:02:17  lipp
* New session ejb handling support.
*
* Revision 1.4  2002/02/06 16:02:29  lipp
* Added caching for home interfaces.
*
* Revision 1.3  2001/12/12 14:11:45  lipp
* Added environment lookup.
*
* Revision 1.2  2001/10/25 17:02:13  robert
* javadoc
*
* Revision 1.1  2001/10/25 07:41:23  lipp
* Moved EJBUtil to de.danet.an.util
*
* Revision 1.1  2001/10/21 20:03:00  lipp
* Initial version
*
*/

package de.danet.an.util;

import java.io.IOException;
import java.io.InputStream;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.EJBLocalObject;
import javax.ejb.EJBObject;
import javax.ejb.RemoveException;
import javax.naming.CommunicationException;
import javax.naming.InitialContext;
import javax.naming.InterruptedNamingException;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

/**
* Collection of EJB utilities.
* This class adds common utilitites for an EJB environment.
*/
public class EJBUtil {

    private static final org.apache.commons.logging.Log logger
  = org.apache.commons.logging.LogFactory.getLog(EJBUtil.class);

    /**
     * The initial context used for lookup.
     */
    private static InitialContext initialContext = null;

    /** Size of low key. */
    private static int lowKeySize = 64;

    /** Key generation EJB name. */
    private static String keyGenEjbName = null;

    /** Key generation high table. */
    private static String keyGenTable = null;

    /**
     * Lookup a JNDI entry. This method implements caching as not all
     * application servers implement JNDI efficiently. As a
     * consequence, this method can only be used for read-only entries
     * that do not change over time.
     *
     * @param entry the JNDI name of the entry
     * @return the object found object
     * @throws NamingException if the JNDI lookup fails.
     */
    public static Object lookupJNDIEntry(String entry)
  throws NamingException{
  if (initialContext == null) {
      initialContext = new InitialContext();
  }
  Object objref = null;
  try {   
      objref = initialContext.lookup(entry);
  } catch (NamingException ne) {
      // maybe naming context is no longer valid (e.g. cluster), retry
      initialContext = new InitialContext();
      objref = initialContext.lookup(entry);
  }
  return objref;
    }

    /**
     * Lookup a JNDI entry using {@link #lookupJNDIEntry
     * <code>lookupJNDIEntry</code>}. This should be called from a
     * context where the JNDI name is expected
     * to exist. <code>NamingException</code>s should thus only occur
     * if the service is not available due to dynamic error conditions
     * (network failure etc.). These exceptions are mapped to a
     * <code>ResourceNotAvailable</code> exception.<P>
     *
     * If, nevertheless, the <code>NamingException</code> indicates
     * that the JNDI entry does not exists, we assume a configuration
     * error and throw an <code>IllegalStateException</code>.
     *
     * @param entry the JNDI name
     * @return an EJBHome object
     * @throws ResourceNotAvailableException if a problem occurs that
     * should be temporary.
     * @throws IllegalStateException if the given
     * <code>jndiName</code> is not registered.
     */
    public static Object retrieveJNDIEntry(String entry)
  throws ResourceNotAvailableException, IllegalStateException {
  try {
      return lookupJNDIEntry (entry);
  } catch (CommunicationException e) {
      logger.error (e.getMessage(), e);
      throw new ResourceNotAvailableException (e.getMessage());
  } catch (InterruptedNamingException e) {
      logger.error (e.getMessage(), e);
      throw new ResourceNotAvailableException (e.getMessage());
  } catch (NamingException e) {
      logger.error (e.getMessage(), e);
      throw new IllegalStateException (e.getMessage());
  }
    }

    private static Map homeCache = new HashMap ();

    /**
     * Lookup a home interface using {@link #lookupJNDIEntry
     * <code>lookupJNDIEntry</code>}.
     *
     * The method provides caching of looked up home interface
     * if the JNDI name is global (i.e. does not start with
     * "<code>java:comp/</code>").
     *
     * @param ejbClass the class of the home interface
     * @param jndiName the JNDI name of the home interface
     * @return an EJBHome object
     * @throws NamingException if the JNDI lookup fails.
     */
    public static EJBHome lookupEJBHome(Class ejbClass, String jndiName)
  throws NamingException {
  if (jndiName.startsWith ("java:comp/")) {
      Object objref = lookupJNDIEntry (jndiName);
      return (EJBHome)PortableRemoteObject.narrow (objref, ejbClass);
  }
  synchronized (homeCache) {
      EJBHome ejbHome = (EJBHome)homeCache.get (jndiName);
      if (ejbHome == null) {
    ejbHome = (EJBHome)PortableRemoteObject.narrow
        (lookupJNDIEntry (jndiName), ejbClass);
    homeCache.put (jndiName, ejbHome);
      }
      return ejbHome;
  }
    }

    /**
     * Return an EJB's home interface using {@link #lookupEJBHome
     * <code>lookupEJBHome</code>}. This should be called from a
     * context where the JNDI name of the home interface is expected
     * to exist. <code>NamingException</code>s should thus only occur
     * if the service is not available due to dynamic error conditions
     * (network failure etc.). These exceptions are mapped to a
     * <code>ResourceNotAvailable</code> exception.<P>
     *
     * If, nevertheless, the <code>NamingException</code> indicates
     * that the JNDI entry does not exists, we assume a configuration
     * error and throw an <code>IllegalStateException</code>.
     *
     * @param ejbClass the class of the home interface
     * @param jndiName the JNDI name of the home interface
     * @return an EJBHome object
     * @throws ResourceNotAvailableException if a problem occurs that
     * should be temporary.
     * @throws IllegalStateException if the given
     * <code>jndiName</code> is not registered.
     */
    public static EJBHome retrieveEJBHome(Class ejbClass, String jndiName)
  throws ResourceNotAvailableException, IllegalStateException {
  try {
      return lookupEJBHome (ejbClass, jndiName);
  } catch (CommunicationException e) {
      logger.error (e.getMessage(), e);
      throw new ResourceNotAvailableException (e.getMessage());
  } catch (InterruptedNamingException e) {
      logger.error (e.getMessage(), e);
      throw new ResourceNotAvailableException (e.getMessage());
  } catch (NamingException e) {
      logger.error (e.getMessage(), e);
      throw new IllegalStateException (e.getMessage());
  }
    }

    /**
     * Lookup a local home interface using {@link #lookupJNDIEntry
     * <code>lookupJNDIEntry</code>}.
     *
     * @param ejbClass the class of the home interface
     * @param jndiName the JNDI name of the home interface
     * @return an EJBHome object
     * @throws NamingException if the JNDI lookup fails.
     */
    public static EJBLocalHome lookupEJBLocalHome
  (Class ejbClass, String jndiName)
  throws NamingException {
  Object objref = lookupJNDIEntry (jndiName);
  return (EJBLocalHome)PortableRemoteObject.narrow (objref, ejbClass);
    }

    /**
     * Return an EJB's local home interface using {@link
     * #lookupEJBHome <code>lookupEJBLocalHome</code>}. This should be
     * called from a context where the JNDI name of the home interface
     * is expected to exist. <code>NamingException</code>s should thus
     * only occur if the service is not available due to dynamic error
     * conditions (network failure etc.). These exceptions are mapped
     * to a <code>ResourceNotAvailable</code> exception.<P>
     *
     * If, nevertheless, the <code>NamingException</code> indicates
     * that the JNDI entry does not exists, we assume a configuration
     * error and throw an <code>IllegalStateException</code>.
     *
     * @param ejbClass the class of the home interface
     * @param jndiName the JNDI name of the home interface
     * @return an EJBHome object
     * @throws ResourceNotAvailableException if a problem occurs that
     * should be temporary.
     * @throws IllegalStateException if the given
     * <code>jndiName</code> is not registered.
     */
    public static EJBLocalHome retrieveEJBLocalHome
  (Class ejbClass, String jndiName)
  throws ResourceNotAvailableException, IllegalStateException {
  try {
      return lookupEJBLocalHome (ejbClass, jndiName);
  } catch (CommunicationException e) {
      logger.error (e.getMessage(), e);
      throw new ResourceNotAvailableException (e.getMessage());
  } catch (InterruptedNamingException e) {
      logger.error (e.getMessage(), e);
      throw new ResourceNotAvailableException (e.getMessage());
  } catch (NamingException e) {
      logger.error (e.getMessage(), e);
      throw new IllegalStateException (e.getMessage());
  }
    }

    /**
     * Create a session EJB. This method can be used for the special
     * &mdash; but very common &mdash; case when you want to create a
     * session EJB and the corresponding home interface has a create
     * method without any paramaters.<P>
     *
     * The given home interface is looked up using {@link
     * #retrieveEJBHome <code>retrieveEJBHome</code>} or {@link
     * #retrieveEJBLocalHome <code>retrieveEJBLocalHome</code>} (as
     * appropriate for the given class) and then the create session
     * method is called.
     *
     * @param ejbClass the class of the home interface which must define a
     * <code>create()</code> method.
     * @param jndiName the JNDI name of the home interface
     * @return the new <code>EJBObject</code>.
     * @throws IllegalArgumentException if no <code>create</code>
     * method without arguments exists.
     * @throws ResourceNotAvailableException if a problem occurs that
     * should be temporary.
     */
    public static Object createSession (Class ejbClass, String jndiName)
  throws IllegalArgumentException, ResourceNotAvailableException {
  if (EJBHome.class.isAssignableFrom (ejbClass)) {
      EJBHome home = retrieveEJBHome (ejbClass, jndiName);
      return createSession (home);
  } else {
      EJBLocalHome home = retrieveEJBLocalHome (ejbClass, jndiName);
      return createSession (home);
  }
    }

    /**
     * Create a session EJB. This method can be used for the special -
     * but very common - case when you want to create a session EJB
     * and the corresponding home interface has a create method
     * without any paramaters.<P>
     *
     * @param home the home interface
     * @throws IllegalArgumentException if no <code>create</code>
     * method without arguments exists.
     * @throws ResourceNotAvailableException if a problem occurs that
     * should be temporary.
     * @return the new <code>EJBObject</code>.
     */
    public static EJBObject createSession (EJBHome home)
  throws IllegalArgumentException, ResourceNotAvailableException {
  Class homeClass = null;
  try {
      homeClass = home.getClass();
      Method createMethod = homeClass.getMethod("create", (Class[])null);
      return (EJBObject)createMethod.invoke(home, new Object[0]);
  } catch (NoSuchMethodException nm) {
      throw new IllegalArgumentException
    (homeClass.getName() + " does not define a create() method.");
  } catch (IllegalAccessException ia) {
      throw new IllegalArgumentException
    ("Calling " + homeClass.getName() + ".create() not allowed: "
     + ia.getMessage());
  } catch (InvocationTargetException ie) {
      Throwable tex = ie.getTargetException();
      logger.warn ("Exception in " + homeClass.getName() + ".create():",
       unwrapEJBException(tex));
      logger.warn ("Unwrapped exception is:", tex);
      if ((tex instanceof RemoteException)
    || (tex instanceof CreateException)
    || (tex instanceof EJBException)) {
    // these should be temporary conditions
    throw new ResourceNotAvailableException (tex.getMessage());
      }
      throw new IllegalArgumentException (tex.getMessage());
  }
    }

    /**
     * Create a session EJB. This method can be used for the special -
     * but very common - case when you want to create a session EJB
     * and the corresponding home interface has a create method
     * without any paramaters.<P>
     *
     * @param home the home interface
     * @throws IllegalArgumentException if no <code>create</code>
     * method without arguments exists.
     * @throws ResourceNotAvailableException if a problem occurs that
     * should be temporary.
     * @return the new <code>EJBObject</code>.
     */
    public static EJBLocalObject createSession (EJBLocalHome home)
  throws IllegalArgumentException, ResourceNotAvailableException {
  Class homeClass = null;
  try {
      homeClass = home.getClass();
      Method createMethod = homeClass.getMethod("create", (Class[])null);
      return (EJBLocalObject)createMethod.invoke(home, new Object[0]);
  } catch (NoSuchMethodException nm) {
      throw new IllegalArgumentException
    (homeClass.getName() + " does not define a create() method.");
  } catch (IllegalAccessException ia) {
      throw new IllegalArgumentException
    ("Calling " + homeClass.getName() + ".create() not allowed: "
     + ia.getMessage());
  } catch (InvocationTargetException ie) {
      Throwable tex = ie.getTargetException();
      logger.warn ("Exception in " + homeClass.getName() + ".create():",
       unwrapEJBException(tex));
      logger.warn ("Unwrapped exception is:", tex);
      if ((tex instanceof RemoteException)
    || (tex instanceof CreateException)
    || (tex instanceof EJBException)) {
    // these should be temporary conditions
    throw new ResourceNotAvailableException (tex.getMessage());
      }
      throw new IllegalArgumentException (tex.getMessage());
  }
    }

    /**
     * Convenience method to clean up a session connection after
     * usage. If the given parameter is an instance of
     * <code>EJBObject</code> it simply calls <code>remove()</code> on
     * the given object. If the parameter is <code>null</code>
     * (indicating that the previous session creation propably failed)
     * nothing happens.  If an error occurs when calling
     * <code>remove()</code>, it will be logged as warning.
     * @param handle the &mdash; maybe &mdash; ejb session object.
     */
    public static void removeSession (Object handle) {
  if (handle == null || !(handle instanceof EJBObject)) {
      return;
  }
  try {
      ((EJBObject)handle).remove();
  } catch (RemoveException rex) {
      logger.warn ("Error in remove(): " + rex.getMessage(), rex);
  } catch (RemoteException rex) {
      logger.warn ("Error in remove(): " + rex.getMessage(), rex);
  }
    }

    /**
     * Unwrap the root cuase of an <code>EJBException</code>. The
     * <code>EJBException</code> wrappers often hide the root cause of
     * an exception. This method calls
     * <code>getCausedByException()</code> until it finds an exception
     * that is not of type <code>EJBException</code>. This exception
     * is returned.
     *
     * @param ex the exception to be unwrapped.
     * @return the root cause exception.
     */
    public static Throwable unwrapEJBException (Throwable ex) {
  while (ex instanceof EJBException) {
      ex = ((EJBException)ex).getCausedByException ();
  }
  return ex;
    }

    /**
     * Included database id values of each database tables. If a table is
     * not present it is added.
     */
    private static Map counters = new HashMap();

    private static class HighLow {
  public long high;
  public int low;
    }

    /**
     * Gets the next unique primary key for the given database table
     * observing a minimum value.<P>
     *
     * This method uses a high/low algorithm. The high values are
     * obtained from an EJB that maintains a table in a database.
     * The JNDI name of the ejb used can be set in a properties file
     * "<code>/de.danet.an.util.jdbcKeyGen.properties</code> with the
     * entry "<code>generatorEjbJndiName</code>". <P>
     *
     * The JNDI name defaults to
     * "<code>java:comp/env/ejb/JdbcKeyGenLocal</code>". This reflects the
     * assumption that this method is usually called from an EJB or
     * servlet. Of course, the deployment descriptor of the calling
     * EJB or servlet must include an <code>&lt;ejb-ref&gt;</code>
     * entry that links to the local home of EJB "KeyGen".<P>
     *
     * The table used by the EJB can be set with the property
     * "<code>highKeyTable</code>". It defaults to
     * "<code>KeyGeneratorHighs</code>". This table must be created as:
     * <pre>create table KeyGeneratorHighs (
     *     TabName VARCHAR(50) NOT NULL,
     *     NextKey INTEGER NOT NULL
     * )/</pre><P>
     *
     * Another property that can be set is "<code>highFactor</code>".
     * It determines the number of low keys used before a new
     * high key is requested. This value defaults to 64.
     *
     * @param table the name of the table.
     * @param min the minimum value of the returned key.
     * @return the new primary key.
     * @throws ResourceNotAvailableException if an error occurs.
     */
    public static long newPrimaryKey (String table, long min)
  throws ResourceNotAvailableException {
  try {
      if (keyGenEjbName == null) {
    Properties props = new Properties();
    try {
        InputStream is = JDBCUtil.class.getResourceAsStream
      ("/de.danet.an.util.jdbcKeyGen.properties");
        if (is != null) {
      props.load (is);
        }
    } catch (IOException ex) {
        logger.error ("Cannot read jdbcKeyGen.properties: "
          + ex.getMessage (), ex);
    }
    keyGenTable = props.getProperty
        ("highKeyTable", "KeyGeneratorHighs");
    lowKeySize = Integer.parseInt
        (props.getProperty ("highFactor", "64"));
    keyGenEjbName = props.getProperty
        ("generatorEjbJndiName",
                     "java:comp/env/ejb/JdbcKeyGenLocal");
      }
      // now do the work
      HighLow hl = null;
      synchronized (counters) {
    hl = (HighLow)counters.get (table);
    if (hl == null) {
        hl = new HighLow ();
        hl.low = lowKeySize - 1;
        counters.put (table, hl);
    }
      }
      synchronized (hl) {
    if (logger.isDebugEnabled ()) {
        logger.debug ("Finding new key for " + table
          + " using HighLow " + hl);
    }
    if (hl.low == (lowKeySize - 1)) {
        KeyGenLocal keyGen = (KeyGenLocal)EJBUtil.createSession
      (KeyGenLocalHome.class, keyGenEjbName);
        while (true) {
      try {
          hl.high = keyGen.newHigh
        (keyGenTable, table,
         (min+lowKeySize-1) / lowKeySize);
          break;
      } catch (EJBException e) {
          logger.info ("Problem calling newHigh (will be "
           + "repeated): " + e.getMessage ());
      }
        }
        EJBUtil.removeSession (keyGen);
        hl.low = 0;
    }
    return hl.high * lowKeySize + hl.low++;
      }
  } catch (RemoteException e) {
      logger.error ("Cannot generate key: " + e.getMessage (), e);
      throw new ResourceNotAvailableException (e.getMessage ());
  }
    }

    /**
     * Gets the next unique primary key for the given database table.
     * @param table the name of the table.
     * @return the new primary key.
     * @throws ResourceNotAvailableException if an error occurs.
     * @see #newPrimaryKey(String,long)
     */
    public static long newPrimaryKey (String table)
  throws ResourceNotAvailableException {
  return newPrimaryKey (table, 0);
    }

}
TOP

Related Classes of de.danet.an.util.EJBUtil$HighLow

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.