/*
* $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/mem/AbstractTransientStore.java,v 1.3.2.1 2004/09/28 11:34:23 luetzkendorf Exp $
* $Revision: 1.3.2.1 $
* $Date: 2004/09/28 11:34:23 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.slide.store.mem;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.transaction.memory.ConflictException;
import org.apache.commons.transaction.memory.TransactionalMapWrapper;
import org.apache.commons.transaction.memory.jca.MapXAResource;
import org.apache.commons.transaction.util.LoggerFacade;
import org.apache.slide.common.AbstractServiceBase;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceConnectionFailedException;
import org.apache.slide.common.ServiceDisconnectionFailedException;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.common.ServiceResetFailedException;
import org.apache.slide.util.logger.Logger;
import org.apache.slide.util.logger.TxLogger;
/**
* Base class for the stores that what to store its data transient in a
* map.
*
* <p>Implementations access the backing map only via {@link #put(Object, Object)},
* {@link #get(Object)} and {@link #remove(Object)}.
*
* <p>The backed map can be any class that is derived from
* {@link org.apache.commons.transaction.memory.TransactionalMapWrapper}.
*
* <p>The class may be configured in the <code>domain.xml</code> using the
* parameter <code>map-classname</code>.
* Default is {@link org.apache.commons.transaction.memory.OptimisticMapWrapper}.
*
*
*/
public abstract class AbstractTransientStore extends AbstractServiceBase {
static final String MAP_IMPL_PARAMETER = "map-classname";
static final String MAP_IMPL_PARAMETER_DEFAULT =
"org.apache.commons.transaction.memory.OptimisticMapWrapper";
private Map parameters = null;
private boolean isConnected = false;
/**
* The map containing the stored content.
*/
private TransactionalMapWrapper store = null;
/**
* The XAResource to delegate all XA requests.
*/
private MapXAResource xaResource = null;
// ----------------------------------------------------- XAResource Mathods
public void start(Xid xid, int flags)
throws XAException
{
debug("start {0} {1}", xid, Integer.toString(flags));
this.xaResource.start(xid, flags);
}
public void commit(Xid xid, boolean onePhase)
throws XAException
{
debug("commit {0} {1}", xid, onePhase ? "true" : "false");
try {
this.xaResource.commit(xid, onePhase);
}
catch (ConflictException e) {
this.xaResource.rollback(xid);
// TODO it would be great if we could throw something
// that would (on the webdav layer) leads to a 409 Conflict
// instead of 500 Internal Server Error
throw new XAException(XAException.XA_RBOTHER); // ??
}
}
public int prepare(Xid xid)
throws XAException
{
debug("prepare {0}", xid);
return this.xaResource.prepare(xid);
}
public void end(Xid xid, int flags)
throws XAException
{
debug("end {0} {1}", xid, Integer.toString(flags));
this.xaResource.end(xid, flags);
}
public void rollback(Xid xid)
throws XAException
{
debug("rollback {0}", xid);
this.xaResource.rollback(xid);
}
public Xid[] recover(int flag)
throws XAException
{
debug("recover {0}", Integer.toString(flag));
return this.xaResource.recover(flag);
}
public void forget(Xid xid)
throws XAException
{
debug("forget {0}", xid);
this.xaResource.forget(xid);
}
public int getTransactionTimeout()
throws XAException
{
return this.xaResource.getTransactionTimeout();
}
public boolean setTransactionTimeout(int seconds)
throws XAException
{
return this.xaResource.setTransactionTimeout(seconds);
}
public boolean isSameRM(XAResource xares)
throws XAException
{
return this == xares;
}
// ------------------------------------- Store accessors for implementations
protected void put(Object key, Object value) {
this.store.put(key, value);
}
protected Object get(Object key) {
return this.store.get(key);
}
protected Object remove(Object key) {
return this.store.remove(key);
}
// ------------------------------------------------ ContextTuple InnerClass
public void setParameters(Hashtable parameters)
throws ServiceParameterErrorException,
ServiceParameterMissingException
{
this.parameters = new HashMap(parameters);
}
protected String getParameter(String name) {
if (this.parameters != null) {
return (String)this.parameters.get(name);
} else {
throw new IllegalStateException("Parameter not yet set!");
}
}
public void initialize(NamespaceAccessToken token)
throws ServiceInitializationFailedException
{
super.initialize(token);
TxLogger txLogger = new TxLogger(getLogger(), LogChannel());
String param = (String)this.parameters.get(MAP_IMPL_PARAMETER);
if (param == null) {
param = MAP_IMPL_PARAMETER_DEFAULT;
}
try {
info("Initializing {0} using {1}.", getClass().getName(), param);
Map toBeWrapped = new HashMap();
Class mapClass = Class.forName(param);
try {
Class[] ctorFormalArgs = { Map.class, LoggerFacade.class };
Constructor ctor = mapClass.getConstructor(ctorFormalArgs);
Object[] ctorArgs = { toBeWrapped, txLogger };
this.store = (TransactionalMapWrapper)ctor.newInstance(ctorArgs);
}
catch (NoSuchMethodException e) {
// try next
try {
Class[] ctorFormalArgs = { Map.class };
Constructor ctor = mapClass.getConstructor(ctorFormalArgs);
Object[] ctorArgs = { toBeWrapped };
this.store = (TransactionalMapWrapper)ctor.newInstance(ctorArgs);
}
catch (NoSuchMethodException ee) {
error("Initialization error: ", ee);
throw new ServiceInitializationFailedException(this, ee);
}
}
}
catch (ClassNotFoundException e) {
error("Initialization error: ", e);
throw new ServiceInitializationFailedException(this, e);
}
catch (InstantiationException e) {
error("Initialization error: ", e);
throw new ServiceInitializationFailedException(this, e);
}
catch (IllegalAccessException e) {
error("Initialization error: ", e);
throw new ServiceInitializationFailedException(this, e);
}
catch (InvocationTargetException e) {
error("Initialization error: ", e);
throw new ServiceInitializationFailedException(this, e);
}
catch (ClassCastException e) {
error("Initialization error: ", e);
throw new ServiceInitializationFailedException(this,
"class in parameter '" + MAP_IMPL_PARAMETER + "' must " +
"be derived from TransactionalMapWrapper");
}
this.xaResource = new MapXAResource(this.store);
// FIXME can't set logging because methods are not public, and
// ctor doesn't take a loggerFacade
}
public void connect() throws ServiceConnectionFailedException {
this.isConnected = true;
}
public void disconnect() throws ServiceDisconnectionFailedException {
this.isConnected = false;
}
public void reset() throws ServiceResetFailedException {
}
public boolean isConnected() throws ServiceAccessException {
return this.isConnected;
}
// -------------------------------------------------------------------
protected String LogChannel() {
return LOG_CHANNEL;
}
protected void debug(String msg, Object o) {
if (this.getLogger().isEnabled(Logger.DEBUG)) {
Object[] args = { o };
this.getLogger().log(MessageFormat.format(msg, args),
LogChannel(), Logger.DEBUG);
}
}
protected void debug(String msg, Object o1, Object o2) {
if (this.getLogger().isEnabled(Logger.DEBUG)) {
Object[] args = { o1, o2 };
this.getLogger().log(MessageFormat.format(msg, args),
LogChannel(), Logger.DEBUG);
}
}
private void info(String msg, Object o1, Object o2) {
if (this.getLogger().isEnabled(Logger.INFO)) {
Object[] args = { o1, o2 };
this.getLogger().log(MessageFormat.format(msg, args),
LogChannel(), Logger.INFO);
}
}
private void error(String msg, Throwable t) {
if (this.getLogger().isEnabled(Logger.INFO)) {
this.getLogger().log(msg, t,
LogChannel(), Logger.INFO);
}
}
/**
* Empty enumeration.
*/
protected static Enumeration EMPTY_ENUM = new Enumeration() {
public boolean hasMoreElements() {
return false;
}
public Object nextElement() {
throw new NoSuchElementException();
}
};
/**
* Enumeration wrapper for an Iterator.
*/
protected static class IteratorEnum implements Enumeration {
private Iterator iterator;
public IteratorEnum(Iterator iterator){
this.iterator = iterator;
}
public boolean hasMoreElements() {
return this.iterator.hasNext();
}
public Object nextElement() {
return this.iterator.next();
}
}
}