//
// ERCNNotificationCoordinator.java
// Project ERChangeNotificationJMS
//
// Created by tatsuya on Sat Aug 31 2002
//
package er.changenotification;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import com.webobjects.appserver.WOApplication;
import com.webobjects.eoaccess.EODatabaseContext;
import com.webobjects.eoaccess.EOModelGroup;
import com.webobjects.eoaccess.EOUtilities;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.eocontrol.EOObjectStoreCoordinator;
import com.webobjects.foundation.NSLog;
import com.webobjects.foundation.NSNotification;
import com.webobjects.foundation.NSNotificationCenter;
import com.webobjects.foundation.NSSelector;
/**
* ERCNNotificationCoordinator is the primary controller of the change
* notification for enterprise objects. It manages the single connection
* to the JMS server, and creates ERCNPublisher and ERCNSubscriber objects
* and registers them as the observers to handle change notifications.
* <p>
* When the application finishes launching, it checks properties and
* initializes the framework. It can also perform necessary clean-up
* operations when the application is about to terminate.
* <p>
* The framework works transparently and you will not have to call any
* methods on the framework. Just add this framework to application project as
* an external framework and put necessary properties to your property file.
* <p>
* The current implementation supports concurrent request handling (multi
* threaded operations.) It only supports the changes in the default
* EOObjectStoreCoordinator.
* <p>
* Properties: <br>
* Put the following properties into WebObjects.properties file under
* your home directory, or into Properties file and register it under
* the applications project's Resources group.
* <p>
* <pre>
*
* # The host name that the JMS server is running on
* er.changenotification.jms.serverHostName = localhost
*
* # Entities *not* to synchronize
* #er.changenotification.entitiesNotToSynchronize = (TalentPhoto)
* er.changenotification.entitiesNotToSynchronize = ()
*
* # Change types to track; Can contain inserted, updated and deleted.
* er.changenotification.changeTypesToTrack = (inserted, updated, deleted)
*
* # JMS topic name (Destination object) to pass the notifications.
* # Specify one and register it from the OpenJMS administration tool or
* # configuration file.
* er.changenotification.jms.topicName = business logic group 1
*
* # Whether or not the JMS subscriber is durable;
* # prevents to miss change notifications by temporaly
* # network disruptions.
* #
* # false - suggested for both development and deployment
* #
* # Please do *not* set it true, otherwise OpenJMS 0.7.3.1 server
* # will fail with some databases (PostgreSQL, FrontBase, etc.)
* #
* # If it's set to true, you need properly to shut down the applications
* # (e.g. shut down it from JavaMonitor or calling application's
* # terminate() method), otherwise JMS provider will try to keep
* # all changes even after application is shut down.
* #
* er.changenotification.jms.durableSubscribers = false
*
* </pre>
*/
public class ERCNNotificationCoordinator {
static final String LOG_HEADER = "ERChangeNotification: ";
private static final ERCNNotificationCoordinator _coordinator = new ERCNNotificationCoordinator();
private ERCNConfiguration _configuration;
private ERCNConnectionKeeper _connectionKeeper;
private ERCNPublisher _publisher;
private ERCNSubscriber _subscriber;
private boolean _isInitialized = false;
private boolean _isTerminated = false;
static {
NSLog.debug.appendln(LOG_HEADER + "Registering the observer to initialize ERChangeNotification Framework");
NSNotificationCenter.defaultCenter().addObserver(
_coordinator,
new NSSelector("initialize", new Class[] { NSNotification.class } ),
WOApplication.ApplicationDidFinishLaunchingNotification,
null);
}
private ERCNNotificationCoordinator() {
super();
}
public static ERCNNotificationCoordinator coordinator() {
return _coordinator;
}
protected String id() {
WOApplication app = WOApplication.application();
String host = app.host();
Number port = app.port();
String appName = app.name();
return host + ":" + port + "/" + appName;
}
public synchronized void initialize(NSNotification notification) {
if (_isInitialized) return;
NSLog.out.appendln(LOG_HEADER + "Initializing ERChangeNotification framework");
// Create the notification publisher object and register it as the observer for
// the EOObjectStoreCoordinator changes.
_publisher = new ERCNPublisher(this);
NSNotificationCenter.defaultCenter().addObserver(
this,
new NSSelector("publishChange", new Class[] { NSNotification.class } ),
EOObjectStoreCoordinator.ObjectsChangedInStoreNotification,
EOObjectStoreCoordinator.defaultCoordinator());
// Create the notification subscriber object and register it as the observer for
// the distributed change notifications.
_subscriber = new ERCNSubscriber(this);
// Create the connection keeper object and initiate connection.
_connectionKeeper = new ERCNConnectionKeeper(this);
_connectionKeeper.openConnection(ERCNConnectionKeeper.VERBOSE_LOGGING);
if (! _connectionKeeper.isConnected())
_connectionKeeper.initiateRecoveryTask();
NSLog.out.appendln(LOG_HEADER + "Finished initializing ERChangeNotificationJMS framework");
_isInitialized = true;
}
void didConnect(TopicConnection connection) {
_subscriber.subscribe(connection);
}
void didDisconnect(TopicConnection connection) {
_subscriber.unsubscribe();
}
/**
* releases JMS resouces, including closing the connection.
* <p>
* This method is supposed to be called by the application's
* terminate method.
*/
// ENHANCEME: Should remove observers as well.
public synchronized void terminate() {
if (_isTerminated) return;
_connectionKeeper.stopConnection(ERCNConnectionKeeper.VERBOSE_LOGGING);
_publisher.terminate();
_subscriber.terminate();
NSLog.out.appendln(LOG_HEADER + "Closing the JMS connection.");
_connectionKeeper.closeConnection(ERCNConnectionKeeper.VERBOSE_LOGGING);
_connectionKeeper.terminate();
_isTerminated = true;
}
@Override
public void finalize() throws Throwable {
if (! _isTerminated) terminate();
super.finalize();
}
public void publishChange(NSNotification notification) {
_publisher.publishChange(notification);
}
public ERCNConfiguration configuration() {
if (_configuration == null)
_configuration = ERCNConfiguration.getInstance();
return _configuration;
}
public ERCNSubscriberDelegate subscriberDelegate() {
return ERCNSubscriber.delegate();
}
public void setSubscriberDelegate(ERCNSubscriberDelegate delegate) {
ERCNSubscriber.setDelegate(delegate);
}
Topic topic() {
return _connectionKeeper.topic();
}
TopicConnection connection() {
return _connectionKeeper.connection();
}
public boolean isConnected() {
return _connectionKeeper.isConnected();
}
public static EODatabaseContext databaseContextForEntityNamed(String entityName, EOEditingContext editingContext) {
return EOUtilities.databaseContextForModelNamed(editingContext,
EOModelGroup.defaultGroup().entityNamed(entityName).model().name());
}
}