/* * 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.schema; import hermes.Domain; import hermes.Hermes; import hermes.MessageFactory; import hermes.browser.HermesBrowser; import hermes.impl.DefaultXMLHelper; import hermes.impl.XMLHelper; import hermes.store.MessageStore; import hermes.store.MessageStoreFolder; import hermes.store.MessageStoreQueue; import hermes.store.MessageStoreTopic; import hermes.store.jdbc.MessageResultSetHandler; import hermes.util.JMSUtils; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Collection; import java.util.UUID; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.QueueBrowser; import javax.swing.ProgressMonitor; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; import org.apache.log4j.Logger; import org.apache.tools.ant.filters.StringInputStream; /** * @author colincrist@hermesjms.com * @version $Id: DefaultJDBCAdapter.java,v 1.2 2005/06/29 11:04:10 colincrist * Exp $ */ public class DefaultJDBCAdapter implements JDBCAdapter { private static final Logger log = Logger.getLogger(DefaultJDBCAdapter.class); private XMLHelper xmlHelper = new DefaultXMLHelper(); private int maxMessageSize = 1024 * 1024; private int maxDestinationSize = 5000 ; private Statements statements; public DefaultJDBCAdapter() throws IOException { try { Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); } catch (ClassNotFoundException e) { log.error("default Derby JDBC driver not loaded: " + e.getMessage(), e); } } public void setStatements(Statements statements) { this.statements = statements; } public Collection<String> getStores(Connection connection) throws SQLException { final QueryRunner runner = new QueryRunner(); final ArrayList<String> stores = new ArrayList<String>(); Hermes.ui.getDefaultMessageSink().add("Getting message stores...."); runner.query(connection, statements.getStoresStatement(), new ResultSetHandler() { public Object handle(ResultSet rs) throws SQLException { while (rs.next()) { stores.add(rs.getString(1)); } return stores; } }); Hermes.ui.getDefaultMessageSink().add("Getting message stores.... done."); return stores; } public void remove(Connection connection, String storeId) throws SQLException { final QueryRunner runner = new QueryRunner(); for (final String statment : statements.getRemoveStoreStatements()) { runner.update(connection, statment, new Object[] { storeId }); } } private synchronized String getNextMessageId(String storeId) { return "ID:" + storeId + "-" + UUID.randomUUID() ; } @Override public void update(Connection connection, String id, Message message) throws SQLException, JMSException { final String messageAsXMLString = xmlHelper.toXML(message); final InputStream messageAsXML = new StringInputStream(messageAsXMLString); final PreparedStatement pstmt = connection.prepareStatement("update messages set message = ? where messageid = ?"); pstmt.setString(1, message.getJMSMessageID()) ; pstmt.setAsciiStream(2, messageAsXML, messageAsXMLString.length()) ; pstmt.execute() ; pstmt.close() ; } public void insert(Connection connection, String storeId, Message message) throws SQLException, JMSException { final String destinationName = message.getJMSDestination() == null ? "default" : JMSUtils.getDestinationName(message.getJMSDestination()); final Domain domain = message.getJMSDestination() == null ? Domain.QUEUE : Domain.getDomain(message.getJMSDestination()); final String messageAsXMLString = xmlHelper.toXML(message); final InputStream messageAsXML = new StringInputStream(messageAsXMLString); final String messageId = getNextMessageId(storeId); // // DBUtils does not seem to correctly deal with CLOBS, so we have to use // normal JDBC... // // runner.update(connection, "insert into messages values (?, ?)", new // Object[] { message.getJMSMessageID(), messageAsXML }); final PreparedStatement pstmt = connection.prepareStatement("insert into messages values (?, ?)"); pstmt.setString(1, messageId); pstmt.setAsciiStream(2, messageAsXML, messageAsXMLString.length()); pstmt.execute(); pstmt.close(); final QueryRunner runner = new QueryRunner(); runner.update(connection, "insert into stores values (?, ?, ?, ?)", new Object[] { storeId, destinationName, domain.getId(), messageId }); } public void remove(Connection connection, String storeId, String destination) throws SQLException, JMSException { final QueryRunner runner = new QueryRunner(); if (runner.update(connection, "delete from messages where messageid in (select messageid from stores where storeid=? and destination=?)", new Object[] { storeId, destination }) > 0) { runner.update(connection, "delete from stores where storeid=? and destination=?", new Object[] { storeId, destination }); } } public void remove(Connection connection, String storeId, Message message) throws SQLException, JMSException { final QueryRunner runner = new QueryRunner(); if (runner.update(connection, "delete from stores where storeid=? and messageid=?", new Object[] { storeId, message.getJMSMessageID() }) > 0) { runner.update(connection, "delete from messages where messageid=?", new Object[] { message.getJMSMessageID() }); } else { throw new SQLException("No message id=" + message.getJMSMessageID() + " exists in store=" + storeId); } } public Collection<Destination> getDestinations(Connection connection, String storeId) throws SQLException, JMSException { final Collection<Destination> destinations = new ArrayList<Destination>(); final QueryRunner runner = new QueryRunner(); Hermes.ui.getDefaultMessageSink().add("Getting message store destinations...."); runner.query(connection, "select distinct destination, domain from stores where storeId=? ", new Object[] { storeId }, new ResultSetHandler() { public Object handle(ResultSet rs) throws SQLException { while (rs.next()) { final Domain domain = Domain.getDomain(rs.getInt(2)) ; if (domain.equals(Domain.QUEUE)) { destinations.add( new MessageStoreQueue(rs.getString(1))); } else if (domain.equals(Domain.TOPIC)) { destinations.add(new MessageStoreTopic(rs.getString(1))) ; } else if (domain.equals(Domain.FOLDER)) { destinations.add(new MessageStoreFolder(rs.getString(1))) ; } } return destinations; } }); Hermes.ui.getDefaultMessageSink().add("Getting message store folders.... done."); return destinations; } private PreparedStatement createPreparedStatement(Connection connection, String sql, Object[] params) throws SQLException { final PreparedStatement stmt = connection.prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { if (params[i] != null) { stmt.setObject(i + 1, params[i]); } else { stmt.setNull(i + 1, Types.OTHER); } } } return stmt; } public QueueBrowser getMessages(Connection connection, String storeId, Destination destination, MessageFactory messageFactory, MessageStore.HeaderPolicy headerPolicy) throws SQLException, JMSException { final QueryRunner runner = new QueryRunner(); final PreparedStatement stmt = createPreparedStatement( connection, "select stores.destination, stores.domain, messages.message, messages.messageid from stores, messages where stores.storeId=? and destination=? and stores.messageid = messages.messageid order by messages.messageid", new Object[] { storeId, messageFactory.getDestinationName(destination) }); return new MessageResultSetHandler(connection, stmt, messageFactory, headerPolicy); } public QueueBrowser getMessages(Connection connection, String storeId, final MessageFactory messageFactory, MessageStore.HeaderPolicy headerPolicy) throws SQLException, JMSException { final QueryRunner runner = new QueryRunner(); final PreparedStatement stmt = createPreparedStatement( connection, "select stores.destination, stores.domain, messages.message, messages.messageid from stores, messages where stores.storeId=? and stores.messageid = messages.messageid order by messages.messageid", new Object[] { storeId }); return new MessageResultSetHandler(connection, stmt, messageFactory, headerPolicy); } private void executeStatements(Connection connection, String[] statements) throws SQLException { final StringBuffer message = new StringBuffer(); ProgressMonitor progressMonitor = null; if (HermesBrowser.getBrowser() != null) { progressMonitor = new ProgressMonitor(HermesBrowser.getBrowser(), "Initialising message stores... ", "Connecting...", 0, statements.length); progressMonitor.setMillisToDecideToPopup(100); progressMonitor.setMillisToPopup(400); } final QueryRunner runner = new QueryRunner(); for (int i = 0; i < statements.length; i++) { try { log.debug("executing: " + statements[i]); if (progressMonitor != null) { progressMonitor.setProgress(statements.length); progressMonitor.setNote("Executing statement " + i + " of " + statements.length); } runner.update(connection, statements[i]); } catch (SQLException ex) { log.error(ex.getMessage()); } } } public void createStore(Connection connection, String storeId) throws SQLException { try { final QueryRunner runner = new QueryRunner(); runner.update(connection, "insert into storeInfo values ( ? )", new Object[] { storeId }); } catch (SQLException ex) { log.debug("swallowing " + ex.getMessage()); } } public void recreateDatabase(Connection connection) throws SQLException { executeStatements(connection, statements.getDeleteDatabaseStatements()); connection.commit(); createDatabase(connection); } public void createDatabase(Connection connection) throws SQLException { Hermes.ui.getDefaultMessageSink().add("Initialising message stores..."); executeStatements(connection, statements.getCreateDatabaseStatements(maxMessageSize, maxDestinationSize)); connection.commit(); Hermes.ui.getDefaultMessageSink().add("Initialising message stores... done."); } public int getDepth(Connection connection, String storeId, Destination destination) throws SQLException, JMSException { final QueryRunner runner = new QueryRunner(); return (Integer) runner.query(connection, statements.getDepthStatement(), new Object[] { storeId, JMSUtils.getDestinationName(destination) }, new ResultSetHandler() { public Object handle(ResultSet rs) throws SQLException { rs.next(); return rs.getInt(1); } }); } }