//
// ERXFrameworkPrincipal.java
// Project ERExtensions
//
// Created by ak on Sat May 04 2002
//
package er.extensions;
import java.lang.reflect.Field;
import org.apache.log4j.Logger;
import com.webobjects.foundation.NSForwardException;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSNotification;
import com.webobjects.foundation.NSNotificationCenter;
import com.webobjects.foundation.NSSelector;
import er.extensions.appserver.ERXApplication;
import er.extensions.eof.ERXConstant;
import er.extensions.foundation.ERXStringUtilities;
/**
* Designated starter class for frameworks, adds support for dependency management.
* <p>
* Allows you to disregard your framework order in the class path (at least where
* startup is concerned, if you override actual classes you still need to take care.)
* <p>
* The <code>initialize()</code> method will be called directly after your principal
* is instantiated.<br>
* The <code>finishInitialization()</code> method will be called when the app finishes
* startup but before it will begin to process requests.
* <p>
* If you define <pre><code>public static Class[] REQUIRES = Class[] {...}</code></pre>
* all the classes (which must be assignable from this class) will get
* loaded before your principal.
* <p>
* NOTE: try to avoid putting code in static initializers. These may lead to
* unpredictable behaviour when launching. Use one of the methods above
* to do what you need to do.
* <p>
* Here is an example:<pre><code>
* public class ExampleFrameworkPrincipal extends ERXFrameworkPrincipal {
*
* public static final Logger log = Logger.getLogger(ExampleFrameworkPrincipal.class);
*
* protected static ExampleFrameworkPrincipal sharedInstance;
*
* public final static Class REQUIRES[] = new Class[] {ERXExtensions.class, ERDirectToWeb.class, ERJavaMail.class};
*
* // Registers the class as the framework principal
* static {
* setUpFrameworkPrincipalClass(ExampleFrameworkPrincipal.class);
* }
*
* public static ExampleFrameworkPrincipal sharedInstance() {
* if (sharedInstance == null) {
* sharedInstance = (ExampleFrameworkPrincipal)sharedInstance(ExampleFrameworkPrincipal.class);
* }
* return sharedInstance;
* }
*
* public void initialize() {
* // code during startup
* }
*
* public void finishInitialization() {
* // Initialized shared data
* }
* }</code></pre>
*/
public abstract class ERXFrameworkPrincipal {
/** logging support */
protected final Logger log = Logger.getLogger(getClass());
/** holds the mapping between framework principals classes and ERXFrameworkPrincipal objects */
protected static final NSMutableDictionary<String, ERXFrameworkPrincipal> initializedFrameworks = new NSMutableDictionary<>();
protected static final NSMutableArray<ERXFrameworkPrincipal> launchingFrameworks = new NSMutableArray<>();
public static class Observer {
/**
* Notification method called when the WOApplication posts
* the notification 'ApplicationDidCreateNotification'. This
* method handles de-registering for notifications and releasing
* any references to observer so that it can be released for
* garbage collection.
* @param n notification that is posted after the WOApplication
* has been constructed, but before the application is
* ready for accepting requests.
*/
public final void willFinishInitialization(NSNotification n) {
NSNotificationCenter.defaultCenter().removeObserver(this, ERXApplication.ApplicationDidCreateNotification, null);
for (ERXFrameworkPrincipal principal : launchingFrameworks) {
principal.finishInitialization();
ERXApplication.log.debug("Finished initialization after launch: " + principal);
}
}
/**
* Notification method called when the WOApplication posts
* the notification 'ApplicationDidFinishInitializationNotification'. This
* method handles de-registering for notifications and releasing
* any references to observer so that it can be released for
* garbage collection.
* @param n notification that is posted after the WOApplication
* has been constructed, but before the application is
* ready for accepting requests.
*/
public final void didFinishInitialization(NSNotification n) {
NSNotificationCenter.defaultCenter().removeObserver(this);
for (ERXFrameworkPrincipal principal : launchingFrameworks) {
principal.didFinishInitialization();
}
}
}
private static Observer observer;
/**
* Gets the shared framework principal instance for a given
* class.
* @param c principal class for a given framework
* @return framework principal initializer
*/
public static<T extends ERXFrameworkPrincipal> T sharedInstance(Class<T> c) {
return (T)initializedFrameworks.objectForKey(c.getName());
}
/**
* Sets up a given framework principal class to receive notification
* when it is safe for the framework to be initialized.
* @param c principal class
*/
public static void setUpFrameworkPrincipalClass(Class c) {
if (initializedFrameworks.objectForKey(c.getName()) != null) {
return;
}
try {
// NSLog.debug.appendln("Loaded items: " + initializedFrameworks);
if(observer == null) {
observer = new Observer();
NSNotificationCenter center = NSNotificationCenter.defaultCenter();
center.addObserver(observer,
new NSSelector("willFinishInitialization", ERXConstant.NotificationClassArray),
// WOApplication.ApplicationWillFinishLaunchingNotification,
ERXApplication.ApplicationDidCreateNotification,
null);
center.addObserver(observer,
new NSSelector("didFinishInitialization", ERXConstant.NotificationClassArray),
// WOApplication.ApplicationWillFinishLaunchingNotification,
ERXApplication.ApplicationDidFinishInitializationNotification,
null);
}
if (initializedFrameworks.objectForKey(c.getName()) == null) {
// NSLog.debug.appendln("Starting up: " + c.getName());
try {
Field f = c.getField("REQUIRES");
Class requires[] = (Class[]) f.get(c);
for (int i = 0; i < requires.length; i++) {
Class requirement = requires[i];
if(initializedFrameworks.objectForKey(requirement.getName()) == null) {
// NSLog.debug.appendln("Loading required: " + requirement.getName());
setUpFrameworkPrincipalClass(requirement);
}
}
} catch (NoSuchFieldException e) {
// nothing
// NSLog.debug.appendln("No requirements: " + c.getName());
} catch (IllegalAccessException e) {
ERXApplication.log.error("Can't read field REQUIRES from " + c.getName() + ", check if it is 'public static Class[] REQUIRES= new Class[] {...}' in this class");
throw NSForwardException._runtimeExceptionForThrowable(e);
}
if(initializedFrameworks.objectForKey(c.getName()) == null) {
ERXFrameworkPrincipal principal = (ERXFrameworkPrincipal)c.newInstance();
initializedFrameworks.setObjectForKey(principal,c.getName());
principal.initialize();
launchingFrameworks.addObject(principal);
ERXApplication.log.debug("Initialized : " + c.getName());
}
} else {
ERXApplication.log.debug("Was already inited: " + c.getName());
}
} catch (InstantiationException e) {
throw NSForwardException._runtimeExceptionForThrowable(e);
} catch (IllegalAccessException e) {
throw NSForwardException._runtimeExceptionForThrowable(e);
}
}
/**
* Called directly after the constructor.
*/
protected void initialize() {
// empty
}
public ERXFrameworkPrincipal() {
// NSLog.debug.appendln("Started initialization: " + getClass().getName());
}
/**
* Overridden by subclasses to provide framework initialization.
*/
public abstract void finishInitialization();
/**
* Overridden by subclasses to finalize framework initialization.
*/
public void didFinishInitialization() {
// Do nothing
}
@Override
public String toString() {
return ERXStringUtilities.lastPropertyKeyInKeyPath(getClass().getName());
}
/**
* <span class="ja">
* 指定フレームワークがインストールされているかどうかを確認します。
*
* @return ある場合には true が戻ります。
* </span>
*/
public static boolean hasFrameworkInstalled(String frameworkName) {
if(ERXStringUtilities.stringIsNullOrEmpty(frameworkName)) {
return false;
}
for (ERXFrameworkPrincipal frameworkPrincipal : ERXFrameworkPrincipal.launchingFrameworks) {
String s = frameworkPrincipal.toString();
if(frameworkName.equalsIgnoreCase(s)) {
return true;
}
}
return false;
}
}