org.apache.sandesha2.storage.jdbc.PersistentStorageManager.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sandesha2.storage.jdbc.PersistentStorageManager.java

Source

/*
 * 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.sandesha2.storage.jdbc;

import java.io.*;
import java.util.HashMap;
import java.sql.*;

import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.description.ModuleConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.sandesha2.SandeshaException;
import org.apache.sandesha2.policy.SandeshaPolicyBean;
import org.apache.sandesha2.polling.PollingManager;
import org.apache.sandesha2.storage.SandeshaStorageException;
import org.apache.sandesha2.storage.StorageManager;
import org.apache.sandesha2.storage.Transaction;
import org.apache.sandesha2.storage.beanmanagers.InvokerBeanMgr;
import org.apache.sandesha2.storage.beanmanagers.RMDBeanMgr;
import org.apache.sandesha2.storage.beanmanagers.RMSBeanMgr;
import org.apache.sandesha2.storage.beanmanagers.SenderBeanMgr;
import org.apache.sandesha2.util.SandeshaUtil;
import org.apache.sandesha2.Sandesha2Constants;
import org.apache.sandesha2.workers.Invoker;
import org.apache.sandesha2.workers.SandeshaThread;
import org.apache.sandesha2.workers.Sender;

/**
 * A Storage Manager implementation for managing Sandesha2 beans.
 * <p/>
 * Needs this parameter in module.xml or axis2.xml :
 * <p/>
 * db.driver            JDBC Driver class name
 * db.connectionstring   JDBC connection string
 * db.user            Data Base user name
 * db.password         Data Base user password
 * <p/>
 * Transactions are supposed to be attached to a thread (see inMemoryStorageManager)
 * hence the ThreadLocal threadTransaction variable (instead of the transactions HashMap
 * used by inMemoryStorageManager).
 * <p/>
 * MessageContexts are stored in a HashMap, as in inMemoryStorageManager, AND in DataBase
 * as backup in case of failure.
 */

public class PersistentStorageManager extends StorageManager {

    private Connection dbConnection = null;
    private String dbConnectionString = null;
    private String dbDriver = null;
    private String dbUser = null;
    private String dbPassword = null;
    private PersistentRMSBeanMgr pRMSBeanMgr = null;
    private PersistentRMDBeanMgr pRMDBeanMgr = null;
    private PersistentSenderBeanMgr pSenderBeanMgr = null;
    private PersistentInvokerBeanMgr pInvokerBeanMgr = null;
    private Sender sender = null;
    private Invoker invoker = null;
    private PollingManager pollingManager = null;
    private boolean useSerialization = false;
    private HashMap<String, MessageContext> storageMap = null;
    private static ThreadLocal<Transaction> threadTransaction = null;
    private static final Log log = LogFactory.getLog(PersistentStorageManager.class);

    private TransactionLock transactionLock;

    public SandeshaThread getInvoker() {
        return invoker;
    }

    public PollingManager getPollingManager() {
        return pollingManager;
    }

    public SandeshaThread getSender() {
        return sender;
    }

    public PersistentStorageManager(ConfigurationContext context) throws SandeshaException {
        super(context);
        log.info("create PersistentStorageManager");
        storageMap = new HashMap<String, MessageContext>();
        threadTransaction = new ThreadLocal<Transaction>();
        pRMSBeanMgr = new PersistentRMSBeanMgr(this);
        pRMDBeanMgr = new PersistentRMDBeanMgr(this);
        pSenderBeanMgr = new PersistentSenderBeanMgr(this);
        pInvokerBeanMgr = new PersistentInvokerBeanMgr(this);
        sender = new Sender();
        // Note that while inOrder is a global property we can decide if we need the
        // invoker thread at this point. If we change this to be a sequence-level
        // property then we'll need to revisit this.
        SandeshaPolicyBean policy = SandeshaUtil.getPropertyBean(context.getAxisConfiguration());
        useSerialization = policy.isUseMessageSerialization();
        if (policy.isInOrder())
            invoker = new Invoker();
        if (policy.isEnableMakeConnection())
            pollingManager = new PollingManager();
        ModuleConfiguration mc = context.getAxisConfiguration().getModuleConfig("sandesha2");
        if (mc != null) {
            Parameter param = mc.getParameter("db.connectionstring");
            if (param != null) {
                dbConnectionString = (String) param.getValue();
                log.debug(param.getName() + "=" + dbConnectionString);
            }
            param = mc.getParameter("db.driver");
            if (param != null) {
                dbDriver = (String) param.getValue();
                log.debug(param.getName() + "=" + dbDriver);
            }
            param = mc.getParameter("db.user");
            if (param != null) {
                dbUser = (String) param.getValue();
                log.debug(param.getName() + "=" + dbUser);
            }
            param = mc.getParameter("db.password");
            if (param != null) {
                dbPassword = (String) param.getValue();
                log.debug(param.getName() + "=" + dbPassword);
            }
        }

        this.transactionLock = new TransactionLock();
    }

    public void shutdown() {
        if (dbConnection != null) {
            try {
                dbConnection.close();
                dbConnection = null;
            } catch (Exception ex) {
            }
        }
        super.shutdown();
    }

    public InvokerBeanMgr getInvokerBeanMgr() {
        return pInvokerBeanMgr;
    }

    public RMDBeanMgr getRMDBeanMgr() {
        return pRMDBeanMgr;
    }

    public RMSBeanMgr getRMSBeanMgr() {
        return pRMSBeanMgr;
    }

    public SenderBeanMgr getSenderBeanMgr() {
        return pSenderBeanMgr;
    }

    public boolean requiresMessageSerialization() {
        return useSerialization;
    }

    public boolean hasUserTransaction(MessageContext msg) {
        // Answer to : Is there a user transaction in play ?
        // but what is a 'user transaction' ?
        return false;
    }

    public Transaction getTransaction() {

        Transaction transaction = (Transaction) threadTransaction.get();
        if (transaction == null) {
            this.acquireTransactionLock();
            transaction = new JDBCTransaction(this);
            threadTransaction.set(transaction);
        } else {
            // We don't want to overwrite or return an existing transaction, as someone
            // else should decide if we commit it or not. If we get here then we probably
            // have a bug.
            if (log.isDebugEnabled())
                log.debug("Possible re-used transaction: ");
            transaction = null;
        }
        return transaction;
    }

    public void removeTransaction() {
        threadTransaction.set(null);
    }

    /**
     * Returns the connection attached to the current transaction if exists
     * or the "common" connection.
     *
     * @return Data Base Connection
     */
    public Connection getDbConnection() {
        JDBCTransaction transaction = (JDBCTransaction) threadTransaction.get();
        if (transaction == null)
            return dbConnection;
        return transaction.getDbConnection();
    }

    public void initStorage(AxisModule moduleDesc) throws SandeshaStorageException {
        log.info("init PersistentStorageManager");
        if (dbConnectionString == null) {
            if (moduleDesc.getParameter("db.connectionstring") != null) {
                dbConnectionString = (String) moduleDesc.getParameter("db.connectionstring").getValue();
            }
        }

        if (dbDriver == null) {
            if (moduleDesc.getParameter("db.driver") != null) {
                dbDriver = (String) moduleDesc.getParameter("db.driver").getValue();
            }
        }

        if (dbUser == null) {
            if (moduleDesc.getParameter("db.user") != null) {
                dbUser = (String) moduleDesc.getParameter("db.user").getValue();
            }
        }

        if (dbPassword == null) {
            if (moduleDesc.getParameter("db.password") != null) {
                dbPassword = (String) moduleDesc.getParameter("db.password").getValue();
            }
        }
        if (dbConnectionString == null || dbDriver == null)
            throw new SandeshaStorageException("Can't proceed. Needed properties are not set.");

        dbConnection = dbConnect();
    }

    public Connection dbConnect() throws SandeshaStorageException {
        try {
            Class.forName(dbDriver);
            return DriverManager.getConnection(dbConnectionString, dbUser, dbPassword);
        } catch (Exception ex) {
            log.error("Unable to create DB connection ", ex);
            throw new SandeshaStorageException(ex);
        }
    }

    public MessageContext retrieveMessageContext(String key, ConfigurationContext configContext)
            throws SandeshaStorageException {
        log.debug("Enter retrieveMessageContext for key " + key);
        /**/
        if (storageMap.containsKey(key)) {
            log.debug("retrieveMessageContext get from cache");
            return (MessageContext) storageMap.get(key);
        }
        /**/
        try {
            Statement stmt = getDbConnection().createStatement();
            /**/
            ResultSet rs = stmt.executeQuery("select * from wsrm_msgctx where ctx_key='" + key + "'");
            rs.next();
            MessageContext msgCtx = new MessageContext();
            msgCtx.readExternal(new ObjectInputStream(rs.getBinaryStream("ctx")));
            msgCtx.activate(configContext);
            msgCtx.setProperty(Sandesha2Constants.POST_FAILURE_MESSAGE, Sandesha2Constants.VALUE_TRUE);
            rs.close();
            stmt.close();
            log.debug("RetrieveMessageContext get from DB");
            return msgCtx;
        } catch (Exception ex) {
            log.error("RetrieveMessageContext exception " + ex);
            throw new SandeshaStorageException(ex);
        }
    }

    synchronized public void storeMessageContext(String key, MessageContext msgContext)
            throws SandeshaStorageException {
        if (log.isDebugEnabled())
            log.debug("Enter storeMessageContext for key " + key + " context " + msgContext);
        storageMap.put(key, msgContext);
        try {
            PreparedStatement pstmt = getDbConnection()
                    .prepareStatement("insert into wsrm_msgctx(ctx_key,ctx)values(?,?)");
            pstmt.setString(1, key);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            msgContext.writeExternal(oos);
            oos.close();
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            pstmt.setBinaryStream(2, bais, bais.available());
            pstmt.execute();
            pstmt.close();
        } catch (Exception ex) {
            throw new SandeshaStorageException(ex);
        }
    }

    synchronized public void updateMessageContext(String key, MessageContext msgContext)
            throws SandeshaStorageException {
        if (log.isDebugEnabled())
            log.debug("updateMessageContext key : " + key);
        storageMap.put(key, msgContext);
        PreparedStatement pstmt = null;
        try {
            pstmt = getDbConnection()
                    .prepareStatement("update wsrm_msgctx set ctx=?" + "where ctx_key='" + key + "'");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            msgContext.writeExternal(oos);
            oos.close();
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            pstmt.setBinaryStream(1, bais, bais.available());
            pstmt.executeQuery();
            pstmt.close();
        } catch (Exception ex) {
            throw new SandeshaStorageException("Exception in updateMessageContext", ex);
        }
    }

    public void removeMessageContext(String key) throws SandeshaStorageException {
        if (log.isDebugEnabled())
            log.debug("removeMessageContext key : " + key);
        try {
            Statement stmt = getDbConnection().createStatement();
            MessageContext messageInCache = (MessageContext) storageMap.get(key);
            if (messageInCache != null)
                storageMap.remove(key);
            stmt.executeUpdate("delete from wsrm_msgctx where ctx_key='" + key + "'");
            stmt.close();
        } catch (Exception ex) {
            throw new SandeshaStorageException("Exception in removeMessageContext", ex);
        }
    }

    public void acquireTransactionLock() {
        synchronized (this.transactionLock) {
            if (this.transactionLock.isAcquried()) {
                try {
                    this.transactionLock.wait();
                } catch (InterruptedException e) {
                }
                // keep on waiting until acquireTransactionLock gain
                this.acquireTransactionLock();
            } else {
                this.transactionLock.lock();
            }
        }
    }

    public void releaseTransactionLock() {
        synchronized (this.transactionLock) {
            this.transactionLock.release();
        }
    }

}