View Javadoc
1 /*** 2 * Redistribution and use of this software and associated documentation 3 * ("Software"), with or without modification, are permitted provided 4 * that the following conditions are met: 5 * 6 * 1. Redistributions of source code must retain copyright 7 * statements and notices. Redistributions must also contain a 8 * copy of this document. 9 * 10 * 2. Redistributions in binary form must reproduce the 11 * above copyright notice, this list of conditions and the 12 * following disclaimer in the documentation and/or other 13 * materials provided with the distribution. 14 * 15 * 3. The name "Exolab" must not be used to endorse or promote 16 * products derived from this Software without prior written 17 * permission of Exoffice Technologies. For written permission, 18 * please contact info@exolab.org. 19 * 20 * 4. Products derived from this Software may not be called "Exolab" 21 * nor may "Exolab" appear in their names without prior written 22 * permission of Exoffice Technologies. Exolab is a registered 23 * trademark of Exoffice Technologies. 24 * 25 * 5. Due credit should be given to the Exolab Project 26 * (http://www.exolab.org/). 27 * 28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 39 * OF THE POSSIBILITY OF SUCH DAMAGE. 40 * 41 * Copyright 2000-2001,2003 (C) Exoffice Technologies Inc. All Rights Reserved. 42 */ 43 44 package org.exolab.jms.persistence; 45 46 import java.sql.Connection; 47 import java.sql.PreparedStatement; 48 import java.sql.ResultSet; 49 import java.sql.SQLException; 50 import java.util.Date; 51 import java.util.HashMap; 52 import java.util.Iterator; 53 import java.util.Vector; 54 55 import javax.jms.JMSException; 56 import javax.sql.DataSource; 57 58 import org.apache.commons.logging.Log; 59 import org.apache.commons.logging.LogFactory; 60 61 import org.exolab.jms.client.JmsDestination; 62 import org.exolab.jms.client.JmsTopic; 63 64 65 /*** 66 * This class provides persistency for ConsumerState objects 67 * in an RDBMS database 68 * 69 * @version $Revision: 1.9 $ $Date: 2003/08/07 13:33:06 $ 70 * @author <a href="mailto:tima@intalio.com">Tim Anderson</a> 71 * @see org.exolab.jms.persistence.ConsumerState 72 * @see org.exolab.jms.persistence.RDBMSAdapter 73 */ 74 class Consumers { 75 76 /*** 77 * A cache for all durable consumers 78 */ 79 private HashMap _consumers; 80 81 /*** 82 * A refernce to the singleton instance of this class 83 */ 84 private static Consumers _instance; 85 86 /*** 87 * Monitor used to synchronize access to the initialization of 88 * the singleton 89 */ 90 private static final Boolean block = new Boolean(true); 91 92 /*** 93 * The name of the column that uniquely identifies the consumer 94 */ 95 private static final String CONSUMER_ID_SEED = "consumerId"; 96 97 /*** 98 * The name of the table that maintains a list of message handles 99 * per consumer 100 */ 101 private static final String CONSUMER_MESSAGE = "message_handles"; 102 103 /*** 104 * The logger 105 */ 106 private static final Log _log = LogFactory.getLog(Consumers.class); 107 108 109 /*** 110 * Returns the singleton instance. 111 * 112 * Note that initialise() must have been invoked first for this 113 * to return a valid instance. 114 * 115 * @return Consumers the singleton instance 116 */ 117 public static Consumers instance() { 118 return _instance; 119 } 120 121 /*** 122 * Initialise the singleton instance 123 * 124 * @param Connection - the connection to use 125 * @return Consumers - singleton instance 126 * @throws PersistenceException - if the call cannot complete 127 */ 128 public static Consumers initialise(Connection connection) 129 throws PersistenceException { 130 131 if (_instance == null) { 132 synchronized (block) { 133 if (_instance == null) { 134 _instance = new Consumers(); 135 _instance.load(connection); 136 } 137 } 138 } 139 return _instance; 140 } 141 142 /*** 143 * Add a new durable consumer to the database if it does not already 144 * exist. A durable consumer is specified by a destination name and 145 * a consumer name. 146 * <p> 147 * The destination must resolve to a valid JmsDestination object 148 * 149 * @param connection - the database connection to use 150 * @param destination - the name of the destination 151 * @param consumer - the name of the consumer 152 * @throws PersistenceException - if the consumer cannot be added 153 */ 154 public synchronized void add(Connection connection, String dest, 155 String consumer) 156 throws PersistenceException { 157 158 JmsDestination destination = null; 159 Destinations singleton = Destinations.instance(); 160 long destinationId = 0; 161 162 synchronized (singleton) { 163 destination = singleton.get(dest); 164 if (destination == null) { 165 raise("add", consumer, dest, "destination is invalid"); 166 } 167 destinationId = singleton.getId(dest); 168 } 169 170 // check that for a topic the consumer name is not the same as the 171 // destination name 172 if ((destination instanceof JmsTopic) && 173 (consumer.equals(dest))) { 174 raise("add", consumer, dest, 175 "The consumer name and destination name cannot be the same"); 176 } 177 178 // get the next id from the seed table 179 long consumerId = SeedGenerator.instance().next(connection, 180 CONSUMER_ID_SEED); 181 182 PreparedStatement insert = null; 183 try { 184 insert = connection.prepareStatement( 185 "insert into consumers values (?,?,?,?)"); 186 187 long created = (new Date()).getTime(); 188 insert.setString(1, consumer); 189 insert.setLong(2, destinationId); 190 insert.setLong(3, consumerId); 191 insert.setLong(4, created); 192 insert.executeUpdate(); 193 194 Consumer map = new Consumer(consumer, consumerId, 195 destinationId, created); 196 197 // check to see if the durable consumer already exists. If it 198 // does then do not add it but signal and error 199 if (!_consumers.containsKey(consumer)) { 200 _consumers.put(consumer, map); 201 } else { 202 _log.error("Durable consumer with name " + consumer 203 + " already exists."); 204 } 205 } catch (Exception exception) { 206 throw new PersistenceException( 207 "Failed to add consumer, destination=" + dest + 208 ", name=" + consumer, exception); 209 } finally { 210 SQLHelper.close(insert); 211 } 212 } 213 214 /*** 215 * Remove a consumer from the database. If the destination is of 216 * type queue then the destination name and the consumer name are 217 * identical. 218 * 219 * @param connection - the connection to use 220 * @param name - the consumer name 221 * @throws PersistenceException - if the consumer cannot be removed 222 */ 223 public synchronized void remove(Connection connection, String name) 224 throws PersistenceException { 225 226 PreparedStatement delete = null; 227 228 // locate the consumer 229 Consumer map = (Consumer) _consumers.get(name); 230 if (map == null) { 231 raise("remove", name, "consumer does not exist"); 232 } 233 234 try { 235 delete = connection.prepareStatement( 236 "delete from consumers where name=?"); 237 delete.setString(1, name); 238 delete.executeUpdate(); 239 240 // now delete all the corresponding handles in the consumer 241 // message table 242 remove(CONSUMER_MESSAGE, map.consumerId, connection); 243 244 // remove the consumer from the local cache 245 _consumers.remove(name); 246 } catch (SQLException exception) { 247 throw new PersistenceException( 248 "Failed to remove consumer=" + name, exception); 249 } finally { 250 SQLHelper.close(delete); 251 } 252 } 253 254 /*** 255 * Return the id of the durable consumer. 256 * 257 * @param connection - the database connection to use 258 * @param name - consumer name 259 * @return the consumer identity 260 */ 261 public synchronized long getConsumerId(String name) { 262 Consumer map = (Consumer) _consumers.get(name); 263 return (map != null) ? map.consumerId : 0; 264 } 265 266 /*** 267 * Return true if a consumer exists 268 * 269 * @param name - the consumer name 270 */ 271 public synchronized boolean exists(String name) { 272 return (_consumers.get(name) != null); 273 } 274 275 /*** 276 * Returns a list of consumer names associated with a topic 277 * 278 * @param topic - the topic to query 279 */ 280 public synchronized Vector getDurableConsumers(String destination) { 281 Vector result = new Vector(); // vector for legacy reasons 282 long destinationId = Destinations.instance().getId(destination); 283 if (destinationId != 0) { 284 Iterator iter = _consumers.values().iterator(); 285 while (iter.hasNext()) { 286 Consumer map = (Consumer) iter.next(); 287 if (map.destinationId == destinationId) { 288 result.add(map.name); 289 } 290 } 291 } 292 293 return result; 294 } 295 296 /*** 297 * Return a map of consumer names to destinations names. 298 * 299 * @return HashMap - list of all durable consumers 300 */ 301 public synchronized HashMap getAllDurableConsumers() { 302 HashMap result = new HashMap(); 303 304 Iterator iter = _consumers.values().iterator(); 305 while (iter.hasNext()) { 306 Consumer map = (Consumer) iter.next(); 307 JmsDestination dest = Destinations.instance().get( 308 map.destinationId); 309 310 if (dest instanceof JmsTopic) { 311 result.put(map.name, dest.getName()); 312 } 313 } 314 315 return result; 316 } 317 318 /*** 319 * Return the consumer name corresponding to the specified identity 320 * 321 * @param id - the consumer identity 322 */ 323 public synchronized String getConsumerName(long id) { 324 String name = null; 325 Iterator iter = _consumers.values().iterator(); 326 327 while (iter.hasNext()) { 328 Consumer map = (Consumer) iter.next(); 329 if (map.consumerId == id) { 330 name = map.name; 331 break; 332 } 333 } 334 335 return name; 336 } 337 338 /*** 339 * Deallocates resources owned or referenced by the instance 340 */ 341 public synchronized void close() { 342 _consumers.clear(); 343 _consumers = null; 344 345 _instance = null; 346 } 347 348 /*** 349 * Removes all cached consumer details for a given destination 350 * 351 * @param destinationId the Id of the destination 352 */ 353 protected synchronized void removeCached(long destinationId) { 354 Object[] list = _consumers.values().toArray(); 355 for (int i = 0; i < list.length; i++) { 356 Consumer map = (Consumer) list[i]; 357 if (map.destinationId == destinationId) { 358 _consumers.remove(map.name); 359 } 360 } 361 } 362 363 /*** 364 * Constructor 365 */ 366 private Consumers() { 367 _consumers = new HashMap(); 368 } 369 370 /*** 371 * Load the cache during init time. It needs to get access to the 372 * TransactionService and the DatabaseService so that it can get 373 * get access to a transaction and a database connection. This method 374 * reads <b>all the consumers</b> into memory. 375 * <p> 376 * If there is any problem completing this operation then the method 377 * will throw a PersistenceException 378 * 379 * @param connection - the connection to use 380 * @throws PersistenceException - if the load fails 381 */ 382 private void load(Connection connection) 383 throws PersistenceException { 384 385 PreparedStatement select = null; 386 ResultSet set = null; 387 try { 388 select = connection.prepareStatement("select * from consumers"); 389 set = select.executeQuery(); 390 391 while (set.next()) { 392 String name = set.getString("name"); 393 long consumerId = set.getLong("consumerId"); 394 long destinationId = set.getLong("destinationId"); 395 long created = set.getLong("created"); 396 Consumer map = new Consumer(name, consumerId, destinationId, 397 created); 398 _consumers.put(name, map); 399 } 400 } catch (SQLException exception) { 401 throw new PersistenceException("Failed to retrieve consumers", 402 exception); 403 } finally { 404 SQLHelper.close(set); 405 SQLHelper.close(select); 406 } 407 } 408 409 /*** 410 * Remove all the rows in the specified table with the corresponding 411 * consumer identity. 412 * 413 * @param table - the table to destroy 414 * @param consumerId - the target consumerId 415 * @param connection - the database connection to use 416 * @throws SQLException - thrown on any error 417 */ 418 private void remove(String table, long consumerId, Connection connection) 419 throws SQLException { 420 421 PreparedStatement delete = null; 422 try { 423 delete = connection.prepareStatement( 424 "delete from " + table + " where consumerId=?"); 425 delete.setLong(1, consumerId); 426 delete.executeUpdate(); 427 } finally { 428 SQLHelper.close(delete); 429 } 430 } 431 432 /*** 433 * Raise a PersistenceException with the specified parameters 434 * 435 * @param operation - operation that failed 436 * @param name - corresponding consumert name 437 * @param destination - corresponding destination 438 * @param reason - the reason for the exception 439 */ 440 private void raise(String operation, String name, String destination, 441 String reason) 442 throws PersistenceException { 443 throw new PersistenceException("Cannot " + operation + " consumer=" + 444 name + ", destination=" + destination + ": " + reason); 445 } 446 447 /*** 448 * Raise a PersistenceException with the specified parameters 449 * 450 * @param operation - operation that failed 451 * @param name - corresponding consumert name 452 * @param reason - the reasone for the exception 453 */ 454 private void raise(String operation, String name, String reason) 455 throws PersistenceException { 456 throw new PersistenceException("Cannot " + operation + " consumer=" + 457 name + ": " + reason); 458 } 459 460 /*** 461 * This is an internal class that is used to store consumer entries 462 */ 463 private class Consumer { 464 465 /*** 466 * The name of the consumer 467 */ 468 public String name; 469 470 /*** 471 * The unique consumer identity 472 */ 473 public long consumerId; 474 475 /*** 476 * The identity of the destination that this durable consumer is 477 * subscribed too 478 */ 479 public long destinationId; 480 481 /*** 482 * The time that this durable consumer was created 483 */ 484 public long created; 485 486 487 public Consumer(String name, long consumerId, long destinationId, 488 long created) { 489 490 this.name = name; 491 this.consumerId = consumerId; 492 this.destinationId = destinationId; 493 this.created = created; 494 } 495 496 public String getKey() { 497 return name; 498 } 499 } 500 }

This page was automatically generated by Maven