| 
/** Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
 package com.opensymphony.module.sequence;
 
 import java.util.HashMap;
 
 /* ====================================================================
 * The OpenSymphony Software License, Version 1.1
 *
 * (this license is derived and fully compatible with the Apache Software
 * License - see http://www.apache.org/LICENSE.txt)
 *
 * Copyright (c) 2001 The OpenSymphony Group. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        OpenSymphony Group (http://www.opensymphony.com/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "OpenSymphony" and "The OpenSymphony Group"
 *    must not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [email protected] .
 *
 * 5. Products derived from this software may not be called "OpenSymphony"
 *    or "OSCore", nor may "OpenSymphony" or "OSCore" appear in their
 *    name, without prior written permission of the OpenSymphony Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 */
 import java.util.Map;
 
 import javax.ejb.*;
 
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
 
 import javax.rmi.PortableRemoteObject;
 
 
 /**
 * SequenceGenerator Session EJB implementation.
 *
 * <p>Utilizes the HIGH/LOW OID strategy as described at
 * <a href="http://www.ambysoft.com/mappingObjects.html">
 *  http://www.ambysoft.com/mappingObjects.html </a>.</p>
 *
 * @ejb.bean
 *  type="Stateless"
 *  name="SequenceGenerator"
 *  view-type="remote"
 *  transaction-type="Container"
 *
 * @ejb.ejb-ref
 *  ejb-name="Sequence"
 *  view-type="local"
 *
 * @ejb.transaction type="Supports"
 * @ejb.permission unchecked="true"
 *
 * @author <a href="mailto:[email protected]">Joe Walnes</a>
 * @author <a href="mailto:[email protected]">Hani Suleiman</a>
 * @version $Revision: 130 $
 */
 public class SequenceGeneratorEJB implements SessionBean {
 //~ Instance fields ////////////////////////////////////////////////////////
 
 /**
 * Map of sequenceNames to appropriate MemorySequence's.
 */
 private Map sequenceStore;
 
 /**
 * Home for Sequence Entity
 */
 private SequenceLocalHome sequenceHome;
 private SessionContext context;
 
 /**
 * Increment size (size of HIGH block).
 */
 private int increment;
 
 /**
 * Number of times to attempt to retry.
 */
 private int retry;
 
 /**
 * Time (in milliseconds) to pause between retries.
 */
 private int retryPause;
 
 //~ Methods ////////////////////////////////////////////////////////////////
 
 /**
 * @ejb.interface-method
 * @ejb.transaction type="Required"
 */
 public long getCount(String sequenceName) {
 try {
 // Allow a sequence for null.
 if (sequenceName == null) {
 sequenceName = "";
 }
 
 // Get appropriate memorySequence
 MemorySequence memorySequence = getMemorySequence(sequenceName);
 
 // If LOW count has cycled through increment, get next HIGH count
 if ((memorySequence.last % increment) == 0) {
 getNextHighCount(memorySequence);
 }
 
 // Increment LOW count and return value.
 return memorySequence.last++;
 } catch (Exception e) {
 // Kick up a fuss about anything that went wrong.
 throw new EJBException(e);
 }
 }
 
 /**
 * Session context set. Lookup Home interface for Sequence Entity
 */
 public void setSessionContext(SessionContext context) {
 this.context = context;
 
 try {
 InitialContext ctx = new InitialContext();
 sequenceHome = (SequenceLocalHome) PortableRemoteObject.narrow(ctx.lookup(SequenceLocalHome.COMP_NAME), SequenceLocalHome.class);
 } catch (NamingException e) {
 throw new EJBException(e);
 }
 }
 
 public void ejbActivate() {
 }
 
 /**
 * Session created. Initialize.
 * @ejb.create-method
 */
 public void ejbCreate() throws CreateException {
 // Get values from env
 increment = getIntEnv("increment", 10);
 retry = getIntEnv("retry", 5);
 retryPause = getIntEnv("retryPause", 100);
 
 // Initialise sequenceStore
 sequenceStore = new HashMap();
 }
 
 public void ejbPassivate() {
 }
 
 public void ejbRemove() {
 }
 
 /**
 * Get an env-entry value that is an int.
 *
 * @param envName Name of env-entry to retrieve
 * @param defaultValue Value it should have if it is not found.
 */
 private int getIntEnv(String envName, int defaultValue) {
 try {
 return ((Integer) PortableRemoteObject.narrow(new InitialContext().lookup(envName), Integer.class)).intValue();
 } catch (Exception e) {
 return defaultValue;
 }
 }
 
 /**
 * Get the MemorySequence for a given sequenceName. Try to get from
 * sequenceStore first - if that fails, find or create appropriate
 * entity and add to sequenceStore.
 *
 * @param sequenceName Name of the sequence to lookup
 * @exception javax.ejb.CreateException thrown if cannot create new sequence
 */
 private MemorySequence getMemorySequence(String sequenceName) throws CreateException {
 // Get the MemorySequence for the given sequenceName from the sequenceStore
 MemorySequence memorySequence = (MemorySequence) sequenceStore.get(sequenceName);
 
 // If the MemorySequence was not already in the sequenceStore
 if (memorySequence == null) {
 // Create a new MemorySequence
 memorySequence = new MemorySequence();
 
 try {
 // Lookup 'real' sequence and assign to memorySequence
 memorySequence.sequence = sequenceHome.findByPrimaryKey(sequenceName);
 } catch (FinderException e) {
 // ... if 'real' sequence not found, create it.
 memorySequence.sequence = sequenceHome.create(sequenceName);
 }
 
 // Put memorySequence into sequenceStore
 sequenceStore.put(sequenceName, memorySequence);
 }
 
 return memorySequence;
 }
 
 /**
 * Increment the <code>last</code> field of a memoryStore to the next
 * HIGH count for the actual <code>Sequence</code>. Seeing as this is
 * quite an important operation and other Entities are dependant on it
 * working, if there is a failure it will make a few retries.
 *
 * @param memorySequence MemorySequence to obtain next HIGH count for
 */
 private void getNextHighCount(MemorySequence memorySequence) {
 // Start looping and keep track of how many retryAttempts.
 for (int retryAttempt = 0; true; retryAttempt++) {
 try {
 // Attempt to get next HIGH count for memorySequence
 memorySequence.last = memorySequence.sequence.getCount(increment);
 
 // Success! - break out of loop
 break;
 } catch (Exception tre) {
 // Unsuccessful - worry not...
 if (retryAttempt < retry) {
 // If we have still got another retry, pause then continue loop
 try {
 Thread.sleep(retryPause);
 } catch (InterruptedException ie) {
 }
 
 continue;
 } else {
 // If we're out of retries, give up and throw EJBException
 throw new EJBException(tre);
 }
 }
 }
 }
 
 //~ Inner Classes //////////////////////////////////////////////////////////
 
 /**
 * This classrepresents an in-memory sequence. (The LOW bit of
 * a HIGH/LOW counter). It has a reference to the HIGH sequence
 * (that stored in the database), and a LOW in-memory count.
 */
 private class MemorySequence {
 SequenceLocal sequence;
 long last;
 }
 }
 |