/*
* Copyright 2003,2004,2005 Colin Crist
*
* 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 hermes.store;
import hermes.Domain;
import hermes.HermesException;
import hermes.MessageFactory;
import hermes.store.jdbc.JDBCConnectionPool;
import hermes.store.schema.JDBCAdapter;
import hermes.util.JMSUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.QueueBrowser;
import javax.naming.NamingException;
import org.apache.commons.dbutils.DbUtils;
import org.apache.log4j.Logger;
/**
* @author colincrist@hermesjms.com
* @version $Id: DefaultMessageStore.java,v 1.2 2005/06/29 11:04:11 colincrist
* Exp $
*/
public class SingleUserMessageStore implements MessageStore {
private static final Logger log = Logger.getLogger(SingleUserMessageStore.class);
private String storeId;
private String jdbcURL;
private JDBCAdapter adapter;
private List<MessageStoreListener> listeners = new ArrayList<MessageStoreListener>();
private MessageFactory defaultFactory = new StoreMessageFactory();
private Set<Destination> destinations = new HashSet<Destination>();
private Map<Destination, Integer> depths = new HashMap<Destination, Integer>();
private JDBCConnectionPool connectionPool;
private ThreadLocal<Connection> writerTL = new ThreadLocal<Connection>();
public SingleUserMessageStore(String storeId, String jdbcURL, boolean doCreate) throws JMSException {
this.storeId = storeId;
this.jdbcURL = jdbcURL;
this.connectionPool = new JDBCConnectionPool(jdbcURL, 2, false);
this.adapter = StoreUtils.getJDBCAdapter(jdbcURL);
try {
Connection connection = connectionPool.get();
if (doCreate) {
adapter.createDatabase(connection);
}
adapter.createStore(connection, storeId);
connection.commit();
destinations.addAll(getDestinationsFromDatabase());
for (Destination d : destinations) {
getDepth(d);
}
} catch (SQLException ex) {
throw new HermesException(ex);
}
}
public String getURL() {
return jdbcURL;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((jdbcURL == null) ? 0 : jdbcURL.hashCode());
result = prime * result + ((storeId == null) ? 0 : storeId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SingleUserMessageStore other = (SingleUserMessageStore) obj;
if (jdbcURL == null) {
if (other.jdbcURL != null)
return false;
} else if (!jdbcURL.equals(other.jdbcURL))
return false;
if (storeId == null) {
if (other.storeId != null)
return false;
} else if (!storeId.equals(other.storeId))
return false;
return true;
}
private Connection getWriterConnection() throws HermesException {
if (writerTL.get() == null) {
writerTL.set(connectionPool.get());
}
return writerTL.get();
}
public String getTooltipText() {
return jdbcURL;
}
public String getId() {
return storeId;
}
public Collection<Destination> getDestinations() throws JMSException {
return destinations;
}
public Collection<Destination> getDestinationsFromDatabase() throws JMSException {
final Connection connection = connectionPool.get();
try {
return adapter.getDestinations(connection, getId());
} catch (SQLException ex) {
throw new HermesException(ex);
} finally {
if (connection != null) {
DbUtils.closeQuietly(connection);
}
}
}
public QueueBrowser visit() throws JMSException {
return visit(defaultFactory, MessageStore.HeaderPolicy.MESSAGEID_AND_DESTINATION);
}
public QueueBrowser visit(Destination d) throws JMSException {
return visit(defaultFactory, d, MessageStore.HeaderPolicy.MESSAGEID_AND_DESTINATION);
}
public QueueBrowser visit(MessageFactory factory, HeaderPolicy headerPolicy) throws JMSException {
final Connection connection = connectionPool.get();
try {
return adapter.getMessages(connection, getId(), factory, headerPolicy);
} catch (SQLException ex) {
throw new HermesException(ex);
}
//
// Connection is closed later by the MessageResultSetHandler...
}
public QueueBrowser visit(MessageFactory factory, Destination d, HeaderPolicy headerPolicy) throws JMSException {
final Connection connection = connectionPool.get();
try {
return adapter.getMessages(connection, getId(), d, factory, headerPolicy);
} catch (SQLException ex) {
throw new HermesException(ex);
}
//
// Connection is closed later by the MessageResultSetHandler...
}
public void delete() throws JMSException {
final Connection connection = getWriterConnection();
try {
adapter.remove(connection, storeId);
} catch (SQLException ex) {
throw new HermesException(ex);
}
}
protected Destination createStoreDestination(Destination from) throws JMSException {
try {
if (from instanceof MessageStoreQueue || from instanceof MessageStoreTopic) {
return from;
} else {
return defaultFactory.getDestination(JMSUtils.getDestinationName(from), Domain.getDomain(from));
}
} catch (NamingException ex) {
throw new HermesException(ex);
}
}
public synchronized void store(Message message) throws JMSException {
try {
adapter.insert(getWriterConnection(), getId(), message);
final Destination from = createStoreDestination(message.getJMSDestination());
if (!destinations.contains(from)) {
destinations.add(from);
for (MessageStoreListener l : listeners) {
l.onDestination(from);
}
}
for (MessageStoreListener l : listeners) {
l.onMessage(message);
}
synchronized (depths) {
if (depths.containsKey(from)) {
depths.put(from, depths.get(from) + 1);
}
}
} catch (SQLException ex) {
throw new HermesException(ex);
}
}
public int getDepth(Destination d) throws JMSException {
d = createStoreDestination(d);
synchronized (depths) {
if (depths.containsKey(d)) {
return depths.get(d);
}
}
final Connection connection = connectionPool.get();
try {
int depth = adapter.getDepth(connection, getId(), d);
synchronized (depths) {
depths.put(d, depth);
}
return depth;
} catch (SQLException e) {
throw new HermesException(e);
} finally {
DbUtils.closeQuietly(connection);
}
}
public void delete(Message message) throws JMSException {
final Connection connection = getWriterConnection();
final Destination destination = createStoreDestination(message.getJMSDestination());
try {
adapter.remove(connection, getId(), message);
for (final MessageStoreListener l : listeners) {
l.onMessageDeleted(message);
}
synchronized (depths) {
if (depths.containsKey(destination)) {
depths.put(destination, depths.get(destination) - 1);
}
}
if (getDepth(destination) == 0) {
destinations.remove(destination);
synchronized (depths) {
depths.remove(destination);
}
for (final MessageStoreListener l : listeners) {
l.onDestinationDeleted(destination);
}
}
} catch (SQLException ex) {
throw new HermesException(ex);
}
}
public void delete(Destination d) throws JMSException {
final Connection connection = getWriterConnection();
try {
adapter.remove(connection, getId(), JMSUtils.getDestinationName(d));
for (final MessageStoreListener l : listeners) {
l.onDestinationDeleted(d);
}
depths.remove(d);
destinations.remove(createStoreDestination(d));
} catch (SQLException ex) {
throw new HermesException(ex);
}
}
public void rollback() throws JMSException {
try {
if (writerTL.get() != null) {
writerTL.get().rollback();
writerTL.get().close();
}
} catch (SQLException e) {
throw new HermesException(e);
} finally {
writerTL.set(null);
}
}
public void checkpoint() throws JMSException {
try {
if (writerTL.get() != null) {
writerTL.get().commit();
writerTL.get().close();
}
} catch (SQLException e) {
throw new HermesException(e);
} finally {
writerTL.set(null);
}
}
public void close() throws JMSException {
try {
getWriterConnection().close();
} catch (SQLException e) {
throw new HermesException(e);
} finally {
writerTL.set(null);
}
}
public void addMessageListener(MessageStoreListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
public void removeMessageListener(MessageStoreListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
@Override
public void update(Message message) throws Exception {
final Connection connection = getWriterConnection();
final Destination destination = createStoreDestination(message.getJMSDestination());
try {
adapter.update(connection, getId(), message);
for (final MessageStoreListener l : listeners) {
l.onMessageChanged(message);
}
} catch (SQLException ex) {
throw new HermesException(ex) ;
}
}
}