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-2004 (C) Exoffice Technologies Inc. All Rights Reserved. 42 * 43 * $Id: RDBMSAdapter.java,v 1.54 2004/01/08 05:55:07 tanderson Exp $ 44 * 45 * Date Author Changes 46 * $Date jimm Created 47 */ 48 package org.exolab.jms.persistence; 49 50 import java.sql.Connection; 51 import java.sql.Date; 52 import java.sql.PreparedStatement; 53 import java.sql.ResultSet; 54 import java.sql.SQLException; 55 import java.util.Enumeration; 56 import java.util.HashMap; 57 import java.util.Vector; 58 59 import EDU.oswego.cs.dl.util.concurrent.FIFOReadWriteLock; 60 import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock; 61 62 import org.apache.commons.logging.Log; 63 import org.apache.commons.logging.LogFactory; 64 65 import org.exolab.core.foundation.HandleIfc; 66 import org.exolab.jms.authentication.User; 67 import org.exolab.jms.client.JmsDestination; 68 import org.exolab.jms.client.JmsQueue; 69 import org.exolab.jms.client.JmsTopic; 70 import org.exolab.jms.config.ConfigurationManager; 71 import org.exolab.jms.config.DatabaseConfiguration; 72 import org.exolab.jms.config.RdbmsDatabaseConfiguration; 73 import org.exolab.jms.events.EventHandler; 74 import org.exolab.jms.message.MessageImpl; 75 import org.exolab.jms.messagemgr.PersistentMessageHandle; 76 77 78 /*** 79 * This adapter is a wrapper class around the persistency mechanism. 80 * It isolates the client from the working specifics of the database, by 81 * providing a simple straight forward interface. Furure changes to 82 * the database will only require changes to the adapter. 83 * 84 * @version $Revision: 1.54 $ $Date: 2004/01/08 05:55:07 $ 85 * @author <a href="mailto:mourikis@exolab.org">Jim Mourikis</a> 86 */ 87 88 public class RDBMSAdapter 89 extends PersistenceAdapter 90 implements EventHandler { 91 92 /*** 93 * The schema version number. Note this must be incremented whenever 94 * The schema changes. 95 */ 96 public static final String SCHEMA_VERSION = "V0.7.6"; 97 98 /*** 99 * The JDBC ConnectionManager 100 */ 101 private static DBConnectionManager _connectionManager = null; 102 103 /*** 104 * This is the interval that the automatic garbage collector will 105 * execute, if specified. It is specified in seconds. 106 */ 107 private int _gcInterval = 600; 108 109 /*** 110 * This is the block size that is used during purging. 111 */ 112 private int _gcBlockSize = 500; 113 114 /*** 115 * This is used to track the incremental garbage collection. It 116 * tracks the last blocks examined. 117 */ 118 private long _lastTime = 0; 119 120 /*** 121 * This is the thread priority for the GC Thread 122 */ 123 private int _gcThreadPriority = Thread.NORM_PRIORITY; 124 125 /*** 126 * Lock to help prevent deadlocks when administratively removing 127 * destinations, while producers and consumers are actively sending 128 * and receiving messages. It ensures that when a destination is in the 129 * process of being removed, no other changes are occuring on the 130 * messages and message_handles tables. 131 */ 132 private ReadWriteLock _destinationLock = new FIFOReadWriteLock(); 133 134 /*** 135 * This is the event that is fired to initiate garbage collection 136 * in the database 137 */ 138 private static final int COLLECT_DATABASE_GARBAGE_EVENT = 1; 139 140 /*** 141 * The logger 142 */ 143 private static final Log _log = LogFactory.getLog(RDBMSAdapter.class); 144 145 146 /*** 147 * Connects to the given db. 148 * 149 * @throws PersistenceException if a connection cannot be establised to 150 * the database 151 */ 152 RDBMSAdapter(String driver, String url, String userName, String password) 153 throws PersistenceException { 154 155 DatabaseConfiguration dbConfig = 156 ConfigurationManager.getConfig().getDatabaseConfiguration(); 157 RdbmsDatabaseConfiguration config = 158 dbConfig.getRdbmsDatabaseConfiguration(); 159 160 // create the connection manager, and configure it 161 _connectionManager = getConnectionManager(config.getClazz()); 162 _connectionManager.setUser(userName); 163 _connectionManager.setPassword(password); 164 _connectionManager.setDriver(driver); 165 _connectionManager.setURL(url); 166 _connectionManager.setMaxActive(config.getMaxActive()); 167 _connectionManager.setMaxIdle(config.getMaxIdle()); 168 _connectionManager.setMinIdleTime(config.getMinIdleTime()); 169 _connectionManager.setEvictionInterval(config.getEvictionInterval()); 170 _connectionManager.setTestQuery(config.getTestQuery()); 171 _connectionManager.setTestBeforeUse(config.getTestBeforeUse()); 172 173 // initialisze the connection manager 174 _connectionManager.init(); 175 176 Connection connection = null; 177 try { 178 // initialize the various caches and helper classes used to 179 // execute the various SQL. 180 connection = getConnection(); 181 182 String version = getSchemaVersion(connection); 183 if (version == null) { 184 initSchemaVersion(connection); 185 } else if (!version.equals(SCHEMA_VERSION)) { 186 throw new PersistenceException( 187 "Schema needs to be converted from version=" + version 188 + " to version=" + SCHEMA_VERSION 189 + "\nBack up your database, and run 'dbtool -migrate'" 190 + "to convert the schema"); 191 } 192 193 SeedGenerator.initialise(); 194 Destinations.initialise(connection); 195 Consumers.initialise(connection); 196 Messages.initialise(); 197 MessageHandles.initialise(); 198 connection.commit(); 199 Users.initialise(connection); 200 } catch (PersistenceException exception) { 201 SQLHelper.rollback(connection); 202 throw exception; 203 } catch (Exception exception) { 204 throw new PersistenceException( 205 "Failed to initialise database adapter", exception); 206 } finally { 207 SQLHelper.close(connection); 208 209 } 210 211 // check whether we should initiate automatic garbage collection 212 if (dbConfig.hasGarbageCollectionInterval()) { 213 _gcInterval = dbConfig.getGarbageCollectionInterval() * 1000; 214 registerEvent(); 215 } 216 217 if (dbConfig.hasGarbageCollectionBlockSize()) { 218 _gcBlockSize = dbConfig.getGarbageCollectionBlockSize(); 219 } 220 221 if (dbConfig.hasGarbageCollectionThreadPriority()) { 222 _gcThreadPriority = dbConfig.getGarbageCollectionBlockSize(); 223 if (_gcThreadPriority < Thread.MIN_PRIORITY) { 224 _gcThreadPriority = Thread.MIN_PRIORITY; 225 } else if (_gcThreadPriority > Thread.MAX_PRIORITY) { 226 _gcThreadPriority = Thread.MAX_PRIORITY; 227 } 228 } 229 } 230 231 /*** 232 * Close the database if open. 233 */ 234 public void close() { 235 if (SeedGenerator.instance() != null) { 236 SeedGenerator.instance().close(); 237 } 238 239 if (Destinations.instance() != null) { 240 Destinations.instance().close(); 241 } 242 243 if (Consumers.instance() != null) { 244 Consumers.instance().close(); 245 } 246 247 if (Messages.instance() != null) { 248 Messages.instance().close(); 249 } 250 251 if (MessageHandles.instance() != null) { 252 MessageHandles.instance().close(); 253 } 254 255 if (Users.instance() != null) { 256 Users.instance().close(); 257 } 258 } 259 260 // implementation of PersistenceAdapter.getLastId 261 public long getLastId(Connection connection) 262 throws PersistenceException { 263 264 long lastId = -1; 265 boolean successful = false; 266 PreparedStatement query = null; 267 ResultSet result = null; 268 PreparedStatement insert = null; 269 try { 270 query = connection.prepareStatement( 271 "select * from message_id where id = 1"); 272 result = query.executeQuery(); 273 274 if (result.next()) { 275 lastId = result.getInt("maxId"); 276 } else { 277 // first entry create. 278 insert = connection.prepareStatement( 279 "insert into message_id values (?,?)"); 280 insert.setInt(1, 1); 281 insert.setLong(2, 0); 282 insert.executeUpdate(); 283 lastId = 0; 284 } 285 } catch (Exception exception) { 286 throw new PersistenceException("Failed to get last message id", 287 exception); 288 } finally { 289 SQLHelper.close(result); 290 SQLHelper.close(insert); 291 SQLHelper.close(query); 292 } 293 294 return lastId; 295 } 296 297 // implementation of PersistenceAdapter.updateIds 298 public void updateIds(Connection connection, long id) 299 throws PersistenceException { 300 PreparedStatement insert = null; 301 try { 302 insert = connection.prepareStatement( 303 "update message_id set maxId = ? where id = 1"); 304 305 insert.setLong(1, id); 306 insert.executeUpdate(); 307 } catch (Exception exception) { 308 throw new PersistenceException("Failed to update message id", 309 exception); 310 } finally { 311 SQLHelper.close(insert); 312 } 313 } 314 315 // implementation of PersistenceMessage.addMessage 316 public void addMessage(Connection connection, MessageImpl message) 317 throws PersistenceException { 318 319 long start = 0; 320 321 if (_log.isDebugEnabled()) { 322 start = System.currentTimeMillis(); 323 } 324 325 try { 326 _destinationLock.readLock().acquire(); 327 Messages.instance().add(connection, message); 328 } catch (InterruptedException exception) { 329 throw new PersistenceException("Failed to acquire lock", 330 exception); 331 } finally { 332 _destinationLock.readLock().release(); 333 334 if (_log.isDebugEnabled()) { 335 _log.debug("addMessage," + 336 (System.currentTimeMillis() - start)); 337 } 338 } 339 } 340 341 // implementation of PersistenceMessage.addMessage 342 public void updateMessage(Connection connection, MessageImpl message) 343 throws PersistenceException { 344 long start = 0; 345 if (_log.isDebugEnabled()) { 346 start = System.currentTimeMillis(); 347 } 348 349 try { 350 _destinationLock.readLock().acquire(); 351 Messages.instance().update(connection, message); 352 } catch (InterruptedException exception) { 353 throw new PersistenceException("Failed to acquire lock", 354 exception); 355 } finally { 356 _destinationLock.readLock().release(); 357 if (_log.isDebugEnabled()) { 358 _log.debug("updateMessage," + 359 (System.currentTimeMillis() - start)); 360 } 361 } 362 } 363 364 // implementation of PersistenceAdapter.getUnprocessedMessages 365 public Vector getUnprocessedMessages(Connection connection) 366 throws PersistenceException { 367 long start = 0; 368 if (_log.isDebugEnabled()) { 369 start = System.currentTimeMillis(); 370 } 371 372 try { 373 return Messages.instance().getUnprocessedMessages(connection); 374 } finally { 375 if (_log.isDebugEnabled()) { 376 _log.debug("getUnprocessedMessages," + (System.currentTimeMillis() - start)); 377 } 378 } 379 } 380 381 382 // implementation of PersistenceAdapter.removeMessage 383 public void removeMessage(Connection connection, String id) 384 throws PersistenceException { 385 long start = 0; 386 if (_log.isDebugEnabled()) { 387 start = System.currentTimeMillis(); 388 } 389 390 try { 391 _destinationLock.readLock().acquire(); 392 Messages.instance().remove(connection, id); 393 } catch (InterruptedException exception) { 394 throw new PersistenceException("Failed to acquire lock", 395 exception); 396 } finally { 397 _destinationLock.readLock().release(); 398 if (_log.isDebugEnabled()) { 399 _log.debug("removeMessage," + 400 (System.currentTimeMillis() - start)); 401 } 402 } 403 } 404 405 // implementation of PersistenceAdapter.getMessage 406 public MessageImpl getMessage(Connection connection, String id) 407 throws PersistenceException { 408 long start = 0; 409 if (_log.isDebugEnabled()) { 410 start = System.currentTimeMillis(); 411 } 412 413 try { 414 return Messages.instance().get(connection, id); 415 } finally { 416 if (_log.isDebugEnabled()) { 417 _log.debug("getMessage," + (System.currentTimeMillis() - start)); 418 } 419 } 420 } 421 422 // implementation of PersistenceAdapter.getMessages 423 public Vector getMessages(Connection connection, PersistentMessageHandle handle) 424 throws PersistenceException { 425 long start = 0; 426 if (_log.isDebugEnabled()) { 427 start = System.currentTimeMillis(); 428 } 429 430 try { 431 return Messages.instance().getMessages(connection, 432 handle.getDestination().getName(), handle.getPriority(), 433 handle.getAcceptedTime()); 434 } finally { 435 if (_log.isDebugEnabled()) { 436 _log.debug("getMessages," + (System.currentTimeMillis() - start)); 437 } 438 } 439 } 440 441 // implementation of PersistenceAdapter.addMessageHandle 442 public void addMessageHandle(Connection connection, PersistentMessageHandle handle) 443 throws PersistenceException { 444 long start = 0; 445 if (_log.isDebugEnabled()) { 446 start = System.currentTimeMillis(); 447 } 448 449 try { 450 _destinationLock.readLock().acquire(); 451 MessageHandles.instance().addMessageHandle(connection, handle); 452 } catch (InterruptedException exception) { 453 throw new PersistenceException("Failed to acquire lock", 454 exception); 455 } finally { 456 _destinationLock.readLock().release(); 457 if (_log.isDebugEnabled()) { 458 _log.debug("addMessageHandle," + (System.currentTimeMillis() - start)); 459 } 460 } 461 } 462 463 // implementation of PersistenceAdapter.updateMessageHandle 464 public void updateMessageHandle(Connection connection, PersistentMessageHandle handle) 465 throws PersistenceException { 466 long start = 0; 467 if (_log.isDebugEnabled()) { 468 start = System.currentTimeMillis(); 469 } 470 471 try { 472 _destinationLock.readLock().acquire(); 473 MessageHandles.instance().updateMessageHandle(connection, handle); 474 } catch (InterruptedException exception) { 475 throw new PersistenceException("Failed to acquire lock", 476 exception); 477 } finally { 478 _destinationLock.readLock().release(); 479 if (_log.isDebugEnabled()) { 480 _log.debug("updateMessageHandle," + (System.currentTimeMillis() - start)); 481 } 482 } 483 } 484 485 // implementation of PersistenceAdapter.removeMessageHandle 486 public void removeMessageHandle(Connection connection, PersistentMessageHandle handle) 487 throws PersistenceException { 488 long start = 0; 489 if (_log.isDebugEnabled()) { 490 start = System.currentTimeMillis(); 491 } 492 493 try { 494 _destinationLock.readLock().acquire(); 495 MessageHandles.instance().removeMessageHandle(connection, handle); 496 } catch (InterruptedException exception) { 497 throw new PersistenceException("Failed to acquire lock", 498 exception); 499 } finally { 500 _destinationLock.readLock().release(); 501 if (_log.isDebugEnabled()) { 502 _log.debug("removeMessageHandle," + (System.currentTimeMillis() - start)); 503 } 504 } 505 } 506 507 // implementation of PersistenceAdapter.getMessageHandles 508 public Vector getMessageHandles(Connection connection, 509 JmsDestination destination, String name) 510 throws PersistenceException { 511 long start = 0; 512 if (_log.isDebugEnabled()) { 513 start = System.currentTimeMillis(); 514 } 515 516 try { 517 return MessageHandles.instance().getMessageHandles(connection, 518 destination.getName(), name); 519 } finally { 520 if (_log.isDebugEnabled()) { 521 _log.debug("getMessageHandles," 522 + (System.currentTimeMillis() - start)); 523 } 524 } 525 } 526 527 // implementation of PersistenceAdapter.addDurableConsumer 528 public void addDurableConsumer(Connection connection, String topic, 529 String consumer) 530 throws PersistenceException { 531 532 try { 533 _destinationLock.readLock().acquire(); 534 Consumers.instance().add(connection, topic, consumer); 535 } catch (InterruptedException exception) { 536 throw new PersistenceException("Failed to acquire lock", 537 exception); 538 } finally { 539 _destinationLock.readLock().release(); 540 } 541 } 542 543 // implementation of PersistenceAdapter.removeDurableConsumer 544 public void removeDurableConsumer(Connection connection, String consumer) 545 throws PersistenceException { 546 547 try { 548 _destinationLock.readLock().acquire(); 549 Consumers.instance().remove(connection, consumer); 550 } catch (InterruptedException exception) { 551 throw new PersistenceException("Failed to acquire lock", 552 exception); 553 } finally { 554 _destinationLock.readLock().release(); 555 } 556 } 557 558 // implementation of PersistenceAdapter.getDurableConsumers 559 public Enumeration getDurableConsumers(Connection connection, String topic) 560 throws PersistenceException { 561 return Consumers.instance().getDurableConsumers(topic).elements(); 562 } 563 564 // implementation of PersistenceAdapter.getAllDurableConsumers 565 public HashMap getAllDurableConsumers(Connection connection) 566 throws PersistenceException { 567 568 return Consumers.instance().getAllDurableConsumers(); 569 } 570 571 // implementation of PersistenceAdapter.durableConsumerExists 572 public boolean durableConsumerExists(Connection connection, String name) 573 throws PersistenceException { 574 575 return Consumers.instance().exists(name); 576 } 577 578 // implementation of PersistenceAdapter.addDestination 579 public void addDestination(Connection connection, String name, 580 boolean queue) 581 throws PersistenceException { 582 583 JmsDestination destination = (queue) 584 ? (JmsDestination) new JmsQueue(name) 585 : (JmsDestination) new JmsTopic(name); 586 587 // create the destination. If the destination is also 588 // a queue create a special consumer for it. 589 try { 590 _destinationLock.readLock().acquire(); 591 Destinations.instance().add(connection, destination); 592 if (queue) { 593 Consumers.instance().add(connection, name, name); 594 } 595 } catch (InterruptedException exception) { 596 throw new PersistenceException("Failed to acquire lock", 597 exception); 598 } finally { 599 _destinationLock.readLock().release(); 600 } 601 } 602 603 // implementation of PersistenceAdapter.removeDestination 604 public void removeDestination(Connection connection, String name) 605 throws PersistenceException { 606 607 JmsDestination destination = Destinations.instance().get(name); 608 if (destination != null) { 609 try { 610 _destinationLock.writeLock().acquire(); 611 Destinations.instance().remove(connection, destination); 612 } catch (InterruptedException exception) { 613 throw new PersistenceException("Failed to acquire lock", 614 exception); 615 } finally { 616 _destinationLock.writeLock().release(); 617 } 618 } 619 } 620 621 // implementation of PersistenceAdapter.getAllDestinations 622 public Enumeration getAllDestinations(Connection connection) 623 throws PersistenceException { 624 625 return Destinations.instance().getDestinations().elements(); 626 } 627 628 // implementation of PersistenceAdapter.checkDestination 629 public boolean checkDestination(Connection connection, String name) 630 throws PersistenceException { 631 632 return (Destinations.instance().get(name) != null); 633 } 634 635 // implementation of getQueueMessageCount 636 public int getQueueMessageCount(Connection connection, String name) 637 throws PersistenceException { 638 639 return MessageHandles.instance().getMessageCount( 640 connection, name, name); 641 } 642 643 // implementation of PersistenceAdapter.getQueueMessageCount 644 public int getDurableConsumerMessageCount(Connection connection, 645 String destination, String name) 646 throws PersistenceException { 647 648 return MessageHandles.instance().getMessageCount(connection, 649 destination, name); 650 } 651 652 // implementation of PersistenceAdapter.getQueueMessageCount 653 public void removeExpiredMessages(Connection connection) 654 throws PersistenceException { 655 656 Messages.instance().removeExpiredMessages(connection); 657 } 658 659 // implementation of PersistenceAdapter.removeExpiredMessageHandles 660 public void removeExpiredMessageHandles(Connection connection, 661 String consumer) 662 throws PersistenceException { 663 664 MessageHandles.instance().removeExpiredMessageHandles(connection, 665 consumer); 666 } 667 668 // implementation of PersistenceAdapter.getNonExpiredMessages 669 public Vector getNonExpiredMessages(Connection connection, 670 JmsDestination destination) 671 throws PersistenceException { 672 673 return Messages.instance().getNonExpiredMessages( 674 connection, destination); 675 } 676 677 // implementation of EventHandler.getHandle 678 public HandleIfc getHandle() { 679 return null; 680 } 681 682 // implementation of EventHandler.handleEvent 683 public void handleEvent(int event, Object callback, long time) { 684 // disabled, as per bug 816895 - Exception in purgeMessages 685 // if (event == COLLECT_DATABASE_GARBAGE_EVENT) { 686 // // collect garbage now, but before doing so change the thread 687 // // priority to low. 688 // try { 689 // Thread.currentThread().setPriority(_gcThreadPriority); 690 // purgeMessages(); 691 // } finally { 692 // Thread.currentThread().setPriority(Thread.NORM_PRIORITY); 693 // registerEvent(); 694 // } 695 // } 696 } 697 698 /*** 699 * Return a connection to the database from the pool of connections. It 700 * will throw an PersistenceException if it cannot retrieve a connection. 701 * The client should close the connection normally, since the pool is a 702 * connection event listener. 703 * 704 * @return Connection - a pooled connection or null 705 * @exception PersistenceException - if it cannot retrieve a connection 706 */ 707 public Connection getConnection() 708 throws PersistenceException { 709 return _connectionManager.getConnection(); 710 } 711 712 /*** 713 * Return a reference to the DBConnectionManager 714 * 715 * @return DBConnectionManager 716 */ 717 public DBConnectionManager getDBConnectionManager() { 718 return _connectionManager; 719 } 720 721 public void addUser(Connection connection, User user) 722 throws PersistenceException { 723 Users.instance().add(connection, user); 724 } 725 726 public Enumeration getAllUsers(Connection connection) 727 throws PersistenceException { 728 return Users.instance().getAllUsers(connection).elements(); 729 } 730 731 public User getUser(Connection connection, User user) 732 throws PersistenceException { 733 return Users.instance().get(connection, user); 734 } 735 736 public void removeUser(Connection connection, User user) 737 throws PersistenceException { 738 Users.instance().remove(connection, user); 739 } 740 741 public void updateUser(Connection connection, User user) 742 throws PersistenceException { 743 Users.instance().update(connection, user); 744 } 745 746 /*** 747 * Incrementally purge all processed messages from the database. 748 * @TODO this needs to be revisited. See bug 816895 749 * - existing expired messages are purged at startup 750 * - messages received that subsequently expire while the server is 751 * running are removed individually. 752 * - not clear how the previous implementation ever worked. 753 * The Messages.getMessageIds() method returns all messages, not 754 * just those processed, nor is it clear that the processed flag 755 * is ever non-zero. 756 * The current implementation (as a fix for bug 816895 - Exception in 757 * purgeMessages) simply delegates to removeExpiredMessages() 758 * 759 * @return the number of messages deleted 760 */ 761 public synchronized int purgeMessages() { 762 int deleted = 0; 763 764 Connection connection = null; 765 try { 766 connection = getConnection(); 767 removeExpiredMessages(connection); 768 connection.commit(); 769 } catch (Exception exception) { 770 _log.error("Exception in purgeMessages", exception); 771 } finally { 772 SQLHelper.close(connection); 773 } 774 return 0; 775 776 // if (connection == null) { 777 // return 0; 778 // } 779 780 // // we have a valid connection so we can proceed 781 // try { 782 // long stime = System.currentTimeMillis(); 783 // HashMap msgids = Messages.instance().getMessageIds( 784 // connection, _lastTime, _gcBlockSize); 785 786 // // if there are no messages then reset the last time to 787 // // 0 and break; 788 // if (msgids.size() > 0) { 789 // // find the minimum and maximum..we can improve the way we 790 // // do this. 791 // Iterator iter = msgids.values().iterator(); 792 // long min = -1; 793 // long max = -1; 794 795 // while (iter.hasNext()) { 796 // Long id = (Long) iter.next(); 797 // if ((min == -1) && 798 // (max == -1)) { 799 // min = id.longValue(); 800 // max = id.longValue(); 801 // } 802 803 // if (id.longValue() < min) { 804 // min = id.longValue(); 805 // } else if (id.longValue() > max) { 806 // max = id.longValue(); 807 // } 808 // } 809 810 // // set the last time for the next iteration unless the 811 // // the size of the msgids is less than the gcBlockSize. 812 // // If the later is the case then reset the last time. 813 // // This is in preparation for the next pass through this 814 // // method. 815 // if (msgids.size() < _gcBlockSize) { 816 // _lastTime = 0; 817 // } else { 818 // _lastTime = max; 819 // } 820 821 // // now iterate through the message list and delete the 822 // // messages that do not have corresponding handles. 823 // Vector hdlids = MessageHandles.instance().getMessageIds(connection, min, max); 824 // iter = msgids.keySet().iterator(); 825 // while (iter.hasNext()) { 826 // String id = (String) iter.next(); 827 // if (!hdlids.contains(id)) { 828 // // this message is not referenced by anyone so we can 829 // // delete it 830 // Messages.instance().remove(connection, id); 831 // deleted++; 832 // } 833 // } 834 // connection.commit(); 835 // } else { 836 // // reset the lastTime 837 // _lastTime = 0; 838 // } 839 // _log.debug("DBGC Deleted " + deleted + " messages and took " 840 // + (System.currentTimeMillis() - stime) + 841 // "ms to complete."); 842 // } catch (Exception exception) { 843 // try { 844 // connection.rollback(); 845 // } catch (Exception nested) { 846 // // ignore this exception 847 // } 848 // _log.error("Exception in purgeMessages", exception); 849 // deleted = 0; 850 // } finally { 851 // try { 852 // connection.close(); 853 // } catch (Exception nested) { 854 // // ignore 855 // } 856 // } 857 // 858 // return deleted; 859 } 860 861 /*** 862 * Get the schema version 863 * 864 * @param connection the connection to use 865 * @return the schema version, or null, if no version has been initialised 866 * @throws PersistenceException for any related persistence exception 867 */ 868 private String getSchemaVersion(Connection connection) 869 throws PersistenceException { 870 871 String version = null; 872 PreparedStatement query = null; 873 ResultSet result = null; 874 try { 875 query = connection.prepareStatement( 876 "select * from system_data where id = 1"); 877 result = query.executeQuery(); 878 if (result.next()) { 879 version = result.getString("version"); 880 } 881 } catch (SQLException exception) { 882 throw new PersistenceException( 883 "Failed to get the schema version", exception); 884 } finally { 885 SQLHelper.close(result); 886 SQLHelper.close(query); 887 888 } 889 return version; 890 } 891 892 /*** 893 * Initialise the schema version 894 * 895 * @param connection the connection to use 896 */ 897 private void initSchemaVersion(Connection connection) 898 throws PersistenceException { 899 900 _log.info("Initialising schema version " + SCHEMA_VERSION); 901 PreparedStatement insert = null; 902 try { 903 insert = connection.prepareStatement( 904 "insert into system_data values (?,?,?)"); 905 insert.setInt(1, 1); 906 insert.setString(2, SCHEMA_VERSION); 907 insert.setDate(3, new Date(System.currentTimeMillis())); 908 insert.executeUpdate(); 909 910 } catch (SQLException exception) { 911 throw new PersistenceException( 912 "Failed to initialise schema version", exception); 913 } finally{ 914 SQLHelper.close(insert); 915 } 916 } 917 918 /*** 919 * Register an event to collect and remove processed messages with the 920 * {@link BasicEventManager} 921 */ 922 private void registerEvent() { 923 // try { 924 // disabled, as per bug 816895 - Exception in purgeMessages 925 // BasicEventManager.instance().registerEventRelative( 926 // new Event(COLLECT_DATABASE_GARBAGE_EVENT, this, null), 927 // _gcInterval); 928 // } catch (IllegalEventDefinedException exception) { 929 // _log.error("registerEvent failed", exception); 930 // } 931 } 932 933 /*** 934 * Creates a {@link DBConnectionManager} using its fully qualified class 935 * name 936 * 937 * @param className the fully qualified class name 938 * @throws PersistenceException if it cannot be created 939 */ 940 private DBConnectionManager getConnectionManager(String className) 941 throws PersistenceException { 942 943 DBConnectionManager result = null; 944 Class clazz = null; 945 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 946 try { 947 if (loader != null) { 948 clazz = loader.loadClass(className); 949 } 950 } catch (ClassNotFoundException ignore) { 951 } 952 try { 953 if (clazz == null) { 954 clazz = Class.forName(className); 955 } 956 } catch (ClassNotFoundException exception) { 957 throw new PersistenceException( 958 "Failed to locate connection manager implementation: " 959 + className, exception); 960 } 961 962 try { 963 result = (DBConnectionManager) clazz.newInstance(); 964 } catch (Exception exception) { 965 throw new PersistenceException( 966 "Failed to create connection manager", exception); 967 } 968 969 return result; 970 } 971 972 }

This page was automatically generated by Maven