org.apache.jackrabbit.jca.JCAManagedConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jackrabbit-jca Show documentation
Show all versions of jackrabbit-jca Show documentation
A resource adapter for Jackrabbit as specified by JCA 1.0 and 1.5.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.jackrabbit.jca;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This class implements the managed connection for
* this resource adapter.
*/
public class JCAManagedConnection
implements ManagedConnection, ManagedConnectionMetaData {
/**
* The LocalTransactionAdapter wraps the internal XAResource and uses the XA Method's to
* fulfill the LocalTransaction calls.
*/
private static class LocalTransactionAdapter implements javax.resource.spi.LocalTransaction {
/**
* Internal {@link Xid} implementation.
*/
class XidImpl implements Xid {
private final byte[] globalTxId;
public XidImpl(byte[] globalTxId) {
this.globalTxId = globalTxId;
}
/**
* {@inheritDoc}
*/
public int getFormatId() {
return 0;
}
/**
* {@inheritDoc}
*/
public byte[] getBranchQualifier() {
return new byte[0];
}
/**
* {@inheritDoc}
*/
public byte[] getGlobalTransactionId() {
return globalTxId;
}
}
/**
* Global static counter for the internal Xid's
*/
private static AtomicInteger globalCounter = new AtomicInteger();
private XAResource resource;
private Xid xid;
public LocalTransactionAdapter(XAResource xaResource) {
this.resource = xaResource;
}
/**
* {@inheritDoc}
*/
@Override
public void begin() throws ResourceException {
try {
this.xid = new XidImpl(intToByteArray(globalCounter.getAndIncrement()));
resource.start(xid, XAResource.TMNOFLAGS);
} catch (XAException e) {
throw new ResourceException(e.getMessage());
}
}
/**
* {@inheritDoc}
*/
@Override
public void commit() throws ResourceException {
try {
resource.end(xid, XAResource.TMSUCCESS);
resource.commit(xid, true);
} catch (XAException e) {
throw new ResourceException(e.getMessage());
}
}
/**
* {@inheritDoc}
*/
@Override
public void rollback() throws ResourceException {
try {
resource.end(xid, XAResource.TMFAIL);
resource.rollback(xid);
} catch (XAException e) {
throw new ResourceException(e.getMessage());
}
}
/**
* Converts the given int (global transaction id) to a byte[]
*
* @param value
* @return byte[]
*/
private static byte[] intToByteArray(int value) {
byte[] b = new byte[4];
for (int i = 0; i < 4; i++) {
int offset = (b.length - 1 - i) * 8;
b[i] = (byte) ((value >>> offset) & 0xFF);
}
return b;
}
}
/**
* Managed connection factory.
*/
private final JCAManagedConnectionFactory mcf;
/**
* Connection request info.
*/
private final JCAConnectionRequestInfo cri;
/**
* Session instance.
*/
private Session session;
/**
* XAResource instance.
*/
private XAResource xaResource;
/**
* Listeners.
*/
private final LinkedList listeners;
/**
* Handles.
*/
private final LinkedList handles;
/**
* Log writer.
*/
private PrintWriter logWriter;
private LocalTransactionAdapter localTransactionAdapter;
/**
* Construct the managed connection.
*/
public JCAManagedConnection(
JCAManagedConnectionFactory mcf, JCAConnectionRequestInfo cri)
throws ResourceException {
this.mcf = mcf;
this.cri = cri;
this.session = openSession();
this.listeners = new LinkedList();
this.handles = new LinkedList();
if (this.mcf.getBindSessionToTransaction().booleanValue()) {
this.xaResource = new TransactionBoundXAResource(this, (XAResource) session);
} else {
this.xaResource = (XAResource) session;
}
this.localTransactionAdapter = new LocalTransactionAdapter(xaResource);
}
/**
* Create a new session.
*/
private Session openSession() throws ResourceException {
try {
Session session = mcf.getRepository().login(
cri.getCredentials(), cri.getWorkspace());
log("Created session (" + session + ")");
return session;
} catch (RepositoryException e) {
log("Failed to create session", e);
ResourceException exception = new ResourceException(
"Failed to create session: " + e.getMessage());
exception.initCause(e);
throw exception;
}
}
/**
* Return the managed connection factory.
*/
public JCAManagedConnectionFactory getManagedConnectionFactory() {
return mcf;
}
/**
* Return the connection request info.
*/
public JCAConnectionRequestInfo getConnectionRequestInfo() {
return cri;
}
/**
* Get the log writer.
*/
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Set the log writer.
*/
public void setLogWriter(PrintWriter logWriter)
throws ResourceException {
this.logWriter = logWriter;
}
/**
* Creates a new connection handle for the underlying physical
* connection represented by the ManagedConnection instance.
*/
public Object getConnection(Subject subject, ConnectionRequestInfo cri)
throws ResourceException {
JCASessionHandle handle = new JCASessionHandle(this);
addHandle(handle);
return handle;
}
/**
* Destroys the physical connection to the underlying resource manager.
*/
public void destroy()
throws ResourceException {
this.session.logout();
this.handles.clear();
}
/**
* Application server calls this method to force any cleanup on
* the ManagedConnection instance.
*/
public void cleanup()
throws ResourceException {
synchronized (handles) {
this.session.logout();
this.session = openSession();
this.handles.clear();
if (this.mcf.getBindSessionToTransaction().booleanValue() && (this.xaResource instanceof TransactionBoundXAResource)) {
((TransactionBoundXAResource) this.xaResource).rebind((XAResource) session);
} else {
this.xaResource = (XAResource) session;
}
}
}
/**
* Used by the container to change the association of an
* application-level connection handle with a ManagedConneciton instance.
*/
public void associateConnection(Object connection)
throws ResourceException {
JCASessionHandle handle = (JCASessionHandle) connection;
if (handle.getManagedConnection() != this) {
handle.getManagedConnection().removeHandle(handle);
handle.setManagedConnection(this);
addHandle(handle);
}
}
/**
* Returns an javax.transaction.xa.XAresource instance.
*/
public XAResource getXAResource()
throws ResourceException {
return this.xaResource;
}
/**
* Returns an javax.resource.spi.LocalTransaction instance.
*/
public LocalTransaction getLocalTransaction()
throws ResourceException {
return localTransactionAdapter;
}
/**
* Gets the metadata information for this connection's underlying
* EIS resource manager instance.
*/
public ManagedConnectionMetaData getMetaData()
throws ResourceException {
return this;
}
/**
* Close the handle.
*/
public void closeHandle(JCASessionHandle handle) {
if (handle != null) {
removeHandle(handle);
sendClosedEvent(handle);
}
}
/**
* Return the session.
*/
public Session getSession(JCASessionHandle handle) {
synchronized (handles) {
if ((handles.size() > 0) && (handles.get(0) == handle)) {
return session;
} else {
throw new java.lang.IllegalStateException("Inactive logical session handle called");
}
}
}
private String getDescriptor(String key) throws ResourceException {
try {
return mcf.getRepository().getDescriptor(key);
} catch (RepositoryException e) {
log("Failed to access the repository", e);
ResourceException exception = new ResourceException(
"Failed to access the repository: " + e.getMessage());
exception.initCause(e);
throw exception;
}
}
/**
* Return the product name.
*/
public String getEISProductName() throws ResourceException {
return getDescriptor(Repository.REP_NAME_DESC);
}
/**
* Return the product version.
*/
public String getEISProductVersion() throws ResourceException {
return getDescriptor(Repository.REP_VERSION_DESC);
}
/**
* Return number of max connections.
*/
public int getMaxConnections()
throws ResourceException {
return Integer.MAX_VALUE;
}
/**
* Return the user name.
*/
public String getUserName()
throws ResourceException {
return session.getUserID();
}
/**
* Log a message.
*/
public void log(String message) {
log(message, null);
}
/**
* Log a message.
*/
public void log(String message, Throwable exception) {
if (logWriter != null) {
logWriter.println(message);
if (exception != null) {
exception.printStackTrace(logWriter);
}
}
}
/**
* Adds a listener.
*/
public void addConnectionEventListener(ConnectionEventListener listener) {
synchronized (listeners) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
}
/**
* Remove a listener.
*/
public void removeConnectionEventListener(ConnectionEventListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
/**
* Send event.
*/
private void sendEvent(ConnectionEvent event) {
synchronized (listeners) {
for (Iterator i = listeners.iterator(); i.hasNext();) {
ConnectionEventListener listener = i.next();
switch (event.getId()) {
case ConnectionEvent.CONNECTION_CLOSED:
listener.connectionClosed(event);
break;
case ConnectionEvent.CONNECTION_ERROR_OCCURRED:
listener.connectionErrorOccurred(event);
break;
case ConnectionEvent.LOCAL_TRANSACTION_COMMITTED:
listener.localTransactionCommitted(event);
break;
case ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK:
listener.localTransactionRolledback(event);
break;
case ConnectionEvent.LOCAL_TRANSACTION_STARTED:
listener.localTransactionStarted(event);
break;
default:
// Unknown event, skip
}
}
}
}
/**
* Send event.
*/
private void sendEvent(int type, Object handle, Exception cause) {
ConnectionEvent event = new ConnectionEvent(this, type, cause);
if (handle != null) {
event.setConnectionHandle(handle);
}
sendEvent(event);
}
/**
* Send connection closed event.
*/
private void sendClosedEvent(JCASessionHandle handle) {
sendEvent(ConnectionEvent.CONNECTION_CLOSED, handle, null);
}
/**
* Send connection error event.
*/
public void sendrrorEvent(JCASessionHandle handle, Exception cause) {
sendEvent(ConnectionEvent.CONNECTION_ERROR_OCCURRED, handle, cause);
}
/**
* Send transaction committed event.
*/
public void sendTxCommittedEvent(JCASessionHandle handle) {
sendEvent(ConnectionEvent.LOCAL_TRANSACTION_COMMITTED, handle, null);
}
/**
* Send transaction rolledback event.
*/
public void sendTxRolledbackEvent(JCASessionHandle handle) {
sendEvent(ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK, handle, null);
}
/**
* Send transaction started event.
*/
public void sendTxStartedEvent(JCASessionHandle handle) {
sendEvent(ConnectionEvent.LOCAL_TRANSACTION_STARTED, handle, null);
}
/**
* Add a session handle.
*/
private void addHandle(JCASessionHandle handle) {
synchronized (handles) {
handles.addFirst(handle);
}
}
/**
* Remove a session handle.
*/
private void removeHandle(JCASessionHandle handle) {
synchronized (handles) {
handles.remove(handle);
}
}
/**
* Release handles.
*/
void closeHandles() {
synchronized (handles) {
JCASessionHandle[] handlesArray = new JCASessionHandle[handles
.size()];
handles.toArray(handlesArray);
for (int i = 0; i < handlesArray.length; i++) {
this.closeHandle(handlesArray[i]);
}
}
}
}