/* Copyright 2004-2014 Jim Voris
*
* 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 com.qumasoft.qvcslib;
import com.qumasoft.qvcslib.requestdata.ClientRequestTransactionBeginData;
import com.qumasoft.qvcslib.requestdata.ClientRequestTransactionEndData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A class to manage the outstanding transactions for the client. It is a singleton.
*
* @author Jim Voris
*/
public final class ClientTransactionManager {
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib");
private static final int STARTING_TRANSACTION_ID = 1000;
private static final ClientTransactionManager CLIENT_TRANSACTION_MANAGER = new ClientTransactionManager();
private final List<TransactionInProgressListenerInterface> transactionInProgressListeners;
private final Map<Integer, TransactionIdentifier> transactionIDMap;
private int nextTransactionID;
/**
* Creates a new instance of WorkfileDigestDictionary.
*/
private ClientTransactionManager() {
this.transactionIDMap = Collections.synchronizedMap(new HashMap<Integer, TransactionIdentifier>());
this.transactionInProgressListeners = Collections.synchronizedList(new ArrayList<TransactionInProgressListenerInterface>());
nextTransactionID = STARTING_TRANSACTION_ID;
}
/**
* Get the singleton instance.
*
* @return the singleton instance.
*/
public static ClientTransactionManager getInstance() {
return CLIENT_TRANSACTION_MANAGER;
}
private int getNextTransactionID() {
return nextTransactionID++;
}
/**
* Create the transaction id for a given server.
* @param serverName the server name.
* @return a client transaction id.
*/
public int createTransactionIdentifier(String serverName) {
int transactionID = getNextTransactionID();
TransactionIdentifier transactionIdentifier = new TransactionIdentifier(serverName, transactionID);
Integer integerTransactionID = Integer.valueOf(transactionID);
transactionIDMap.put(integerTransactionID, transactionIdentifier);
return transactionID;
}
/**
* If the server connection goes away, we need to discard any pending transactions associated with that server. If as a result of discarding those transactions, the global
* transaction count goes to zero, we need to let any listeners know that there are no remaining open transactions (so the progress bar will go away, for example).
* @param transportProxy identify the connection for which we discard transactions.
*/
public synchronized void discardServerTransactions(TransportProxyInterface transportProxy) {
LOGGER.log(Level.INFO, "discarding transactions for: [" + transportProxy.getServerProperties().getServerName() + "]");
String serverName = transportProxy.getServerProperties().getServerName();
Iterator it = transactionIDMap.values().iterator();
while (it.hasNext()) {
TransactionIdentifier transactionIdentifier = (TransactionIdentifier) it.next();
if (transactionIdentifier.getServerName().equals(serverName)) {
it.remove();
}
}
LOGGER.log(Level.INFO, "discarding transactions for: [" + transportProxy.getServerProperties().getServerName() + "]. Remaining transaction count: ["
+ getOpenTransactionCount() + "]");
if (getOpenTransactionCount() == 0) {
it = transactionInProgressListeners.iterator();
while (it.hasNext()) {
TransactionInProgressListenerInterface listener = (TransactionInProgressListenerInterface) it.next();
LOGGER.log(Level.INFO, "Setting transaction in progress to false for listener: [" + listener.getClass().getSimpleName() + "]");
listener.setTransactionInProgress(false);
}
}
}
/**
* Set a begin transaction message to the server.
* @param transportProxy the server connection.
* @return the client transaction id.
*/
public int sendBeginTransaction(TransportProxyInterface transportProxy) {
int transactionID = 0;
if (transportProxy != null) {
String serverName = transportProxy.getServerProperties().getServerName();
transactionID = createTransactionIdentifier(serverName);
ClientRequestTransactionBeginData beginTransactionData = new ClientRequestTransactionBeginData();
beginTransactionData.setTransactionID(transactionID);
beginTransactionData.setServerName(serverName);
transportProxy.write(beginTransactionData);
}
return transactionID;
}
/**
* Set a begin transaction message to the server.
* @param transportProxy the server connection.
* @param transactionID the transaction id.
* @return the client transaction id.
*/
public int sendBeginTransaction(TransportProxyInterface transportProxy, int transactionID) {
if (transportProxy != null) {
QumaAssert.isTrue(getTransactionIdentifier(transactionID) != null);
String serverName = transportProxy.getServerProperties().getServerName();
ClientRequestTransactionBeginData beginTransactionData = new ClientRequestTransactionBeginData();
beginTransactionData.setTransactionID(transactionID);
beginTransactionData.setServerName(serverName);
transportProxy.write(beginTransactionData);
}
return transactionID;
}
/**
* Send a begin transaction message to the server.
* @param serverProperties the server properties.
* @return the transaction id.
*/
public int sendBeginTransaction(final ServerProperties serverProperties) {
TransportProxyInterface transportProxy = TransportProxyFactory.getInstance().getTransportProxy(serverProperties);
return sendBeginTransaction(transportProxy);
}
/**
* Send an end transaction message to the server.
* @param transportProxy the server connection.
* @param transactionID the transaction id.
*/
public void sendEndTransaction(TransportProxyInterface transportProxy, int transactionID) {
if ((transportProxy != null) && (transactionID != 0)) {
String serverName = transportProxy.getServerProperties().getServerName();
ClientRequestTransactionEndData endTransactionData = new ClientRequestTransactionEndData();
endTransactionData.setTransactionID(transactionID);
endTransactionData.setServerName(serverName);
transportProxy.write(endTransactionData);
}
}
/**
* Send an end transaction message to the server.
* @param serverProperties the server properties.
* @param transactionID the transaction id.
*/
public void sendEndTransaction(final ServerProperties serverProperties, int transactionID) {
TransportProxyInterface transportProxy = TransportProxyFactory.getInstance().getTransportProxy(serverProperties);
sendEndTransaction(transportProxy, transactionID);
}
/**
* Mark the start of a transaction on a given server. As a side effect, we let listeners know that a transaction is in progress.
* @param serverName the name of the server.
* @param transactionID the transaction id for this transaction.
*/
public synchronized void beginTransaction(String serverName, int transactionID) {
LOGGER.log(Level.INFO, "Begin transaction for serverName: [" + serverName + "] transactionID: [" + transactionID + "]");
Integer integerTransactionID = Integer.valueOf(transactionID);
TransactionIdentifier transactionIdentifier = transactionIDMap.get(integerTransactionID);
if (transactionIdentifier == null) {
createTransactionIdentifier(serverName, transactionID);
}
Iterator<TransactionInProgressListenerInterface> it = transactionInProgressListeners.iterator();
while (it.hasNext()) {
TransactionInProgressListenerInterface listener = it.next();
listener.setTransactionInProgress(true);
}
}
/**
* Mark the start of a transaction on a given server. This method will also let listeners know that a transaction is in progress.
* @param serverName the name of the server.
* @return the transaction id.
*/
public synchronized int beginTransaction(String serverName) {
int transactionID = createTransactionIdentifier(serverName);
LOGGER.log(Level.INFO, "Begin transaction for serverName: [" + serverName + "] transactionID: [" + transactionID + "]");
Iterator<TransactionInProgressListenerInterface> it = transactionInProgressListeners.iterator();
while (it.hasNext()) {
TransactionInProgressListenerInterface listener = it.next();
listener.setTransactionInProgress(true);
}
return transactionID;
}
/**
* End the given transaction.
* @param serverName the server name.
* @param transactionID the transaction id.
*/
public synchronized void endTransaction(String serverName, int transactionID) {
Integer integerTransactionID = Integer.valueOf(transactionID);
transactionIDMap.remove(integerTransactionID);
int openTransactionCount = getOpenTransactionCount();
if (openTransactionCount == 0) {
Iterator<TransactionInProgressListenerInterface> it = transactionInProgressListeners.iterator();
while (it.hasNext()) {
TransactionInProgressListenerInterface listener = it.next();
listener.setTransactionInProgress(false);
}
}
LOGGER.log(Level.INFO, "End transaction for serverName: [" + serverName + "] transactionID: [" + transactionID + "]");
}
/**
* Add a transaction in progress listener. The listener receives notifications for the start and end of transactions.
* @param listener a transaction in progress listener.
*/
public void addTransactionInProgressListener(TransactionInProgressListenerInterface listener) {
transactionInProgressListeners.add(listener);
}
/**
* Remove a transaction in progress listener. The listener will no longer be notified when a client transaction starts or completes.
* @param listener the listener to remove.
*/
public void removeTransactionInProgressListener(TransactionInProgressListenerInterface listener) {
transactionInProgressListeners.remove(listener);
}
/**
* Get the number of open transactions.
* @return the number of open transactions.
*/
public int getOpenTransactionCount() {
return transactionIDMap.size();
}
private TransactionIdentifier createTransactionIdentifier(String serverName, int transactionID) {
TransactionIdentifier transactionIdentifier = new TransactionIdentifier(serverName, transactionID);
Integer integerTransactionID = Integer.valueOf(transactionID);
transactionIDMap.put(integerTransactionID, transactionIdentifier);
return transactionIdentifier;
}
private TransactionIdentifier getTransactionIdentifier(int transactionID) {
return transactionIDMap.get(Integer.valueOf(transactionID));
}
class TransactionIdentifier {
private final String serverName;
private final Integer transactionID;
TransactionIdentifier(String server, Integer transID) {
serverName = server;
transactionID = transID;
}
String getServerName() {
return serverName;
}
Integer getTransactionID() {
return transactionID;
}
}
}