Java tutorial
/* * Copyright (C) NetStruxr, Inc. All rights reserved. * * This software is published under the terms of the NetStruxr * Public Software License version 0.5, a copy of which has been * included with this distribution in the LICENSE.NPL file. */ package er.extensions; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOSession; import com.webobjects.eoaccess.EOAttribute; import com.webobjects.eoaccess.EODatabase; import com.webobjects.eoaccess.EODatabaseContext; import com.webobjects.eoaccess.EOEntity; import com.webobjects.eoaccess.EOModelGroup; import com.webobjects.eoaccess.EOQualifierSQLGeneration; import com.webobjects.eoaccess.EOQualifierSQLGeneration.Support; import com.webobjects.eoaccess.EORelationship; import com.webobjects.eoaccess.EOSQLExpression; import com.webobjects.eoaccess.EOUtilities; import com.webobjects.eoaccess.ERXEntityDependencyOrderingDelegate; import com.webobjects.eoaccess.ERXModel; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.eocontrol.EOFetchSpecification; import com.webobjects.eocontrol.EOKeyValueQualifier; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eocontrol.EOSharedEditingContext; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSBundle; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSForwardException; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSLog; import com.webobjects.foundation.NSNotification; import com.webobjects.foundation.NSNotificationCenter; import com.webobjects.foundation.NSSelector; import com.webobjects.jdbcadaptor.JDBCAdaptorException; import er.extensions.appserver.ERXApplication; import er.extensions.appserver.ERXSession; import er.extensions.eof.ERXAdaptorChannelDelegate; import er.extensions.eof.ERXConstant; import er.extensions.eof.ERXDatabaseContext; import er.extensions.eof.ERXDatabaseContextDelegate; import er.extensions.eof.ERXDatabaseContextMulticastingDelegate; import er.extensions.eof.ERXEC; import er.extensions.eof.ERXEOAccessUtilities; import er.extensions.eof.ERXEntityClassDescription; import er.extensions.eof.ERXGenericRecord; import er.extensions.eof.ERXModelGroup; import er.extensions.eof.ERXObjectStoreCoordinatorPool; import er.extensions.eof.ERXSharedEOLoader; import er.extensions.eof.qualifiers.ERXFullTextQualifier; import er.extensions.eof.qualifiers.ERXFullTextQualifierSupport; import er.extensions.eof.qualifiers.ERXPrimaryKeyListQualifier; import er.extensions.eof.qualifiers.ERXRegExQualifier; import er.extensions.eof.qualifiers.ERXToManyQualifier; import er.extensions.formatters.ERXSimpleHTMLFormatter; import er.extensions.foundation.ERXArrayUtilities; import er.extensions.foundation.ERXConfigurationManager; import er.extensions.foundation.ERXFileUtilities; import er.extensions.foundation.ERXMutableURL; import er.extensions.foundation.ERXPatcher; import er.extensions.foundation.ERXProperties; import er.extensions.foundation.ERXRuntimeUtilities; import er.extensions.foundation.ERXStringUtilities; import er.extensions.foundation.ERXSystem; import er.extensions.foundation.ERXValueUtilities; import er.extensions.jdbc.ERXJDBCAdaptor; import er.extensions.localization.ERXLocalizer; import er.extensions.logging.ERXLogger; import er.extensions.partials.ERXPartialInitializer; import er.extensions.qualifiers.ERXFalseQualifier; import er.extensions.qualifiers.ERXFalseQualifierSupport; import er.extensions.qualifiers.ERXTrueQualifier; import er.extensions.qualifiers.ERXTrueQualifierSupport; import er.extensions.remoteSynchronizer.ERXRemoteSynchronizer; import er.extensions.validation.ERXValidationFactory; /** * Principal class of the ERExtensions framework. This class * will be loaded at runtime when the ERExtensions bundle is * loaded (even before the Application constructor is called) * This class has a boat-load of stuff in it that will hopefully * be finding better homes in the future. This class serves as * the initialization point of this framework, look in the static * initializer to see all the stuff that is initially setup when * this class is loaded. This class also has a boat load of * string, array and EOF utilities as well as the factory methods * for creating editing contexts with the default delegates set. */ public class ERXExtensions extends ERXFrameworkPrincipal { /** Notification name, posted before object will change in an editing context */ public final static String objectsWillChangeInEditingContext = "ObjectsWillChangeInEditingContext"; /** Notification name, posted before EOAdaptor debug logging will change its setting. */ public final static String eoAdaptorLoggingWillChangeNotification = "EOAdaptorLoggingWillChange"; /** logging support */ private static Logger _log; private static boolean _initialized; public ERXExtensions() { } /** holds the default model group */ protected volatile ERXModelGroup defaultModelGroup; /** * Delegate method for the {@link EOModelGroup} class delegate. * @return a fixed ERXModelGroup */ public EOModelGroup defaultModelGroup() { if (defaultModelGroup == null) { synchronized (ERXModel._ERXGlobalModelLock) { if (defaultModelGroup == null) { String defaultModelGroupClassName = ERXProperties .stringForKey("er.extensions.defaultModelGroupClassName"); if (defaultModelGroupClassName == null) { defaultModelGroup = new ERXModelGroup(); } else { try { defaultModelGroup = Class.forName(defaultModelGroupClassName) .asSubclass(ERXModelGroup.class).newInstance(); } catch (Exception e) { throw new RuntimeException("Failed to create custom ERXModelGroup subclass '" + defaultModelGroupClassName + "'.", e); } } defaultModelGroup.loadModelsFromLoadedBundles(); } } } return defaultModelGroup; } /** * Configures the framework. All the bits and pieces that need * to be configured are configured, those that need to happen * later are delayed by registering an observer for notifications * that are posted when the application is finished launching. * This public observer is used to perform basic functions in * response to notifications. Specifically it handles * configuring the adapator context so that SQL debugging can * be enabled and disabled on the fly throgh the log4j system. * Handling cleanup issues when sessions timeout, i.e. releasing * all references to editing contexts created for that session. * Handling call all of the <code>did*</code> methods on * {@link ERXGenericRecord} subclasses after an editing context * has been saved. This delegate is also responsible for configuring * {@link ERXValidationFactory}. * This delegate is configured when this framework is loaded. */ @Override protected void initialize() { NSNotificationCenter.defaultCenter().addObserver(this, new NSSelector("bundleDidLoad", ERXConstant.NotificationClassArray), ERXApplication.AllBundlesLoadedNotification, null); } public void bundleDidLoad(NSNotification n) { if (_initialized) return; _initialized = true; try { // This will load any optional configuration files, // ensures that WOOutputPath's was processed with this @@ // variable substitution. WOApplication uses WOOutputPath in // its constructor so we need to modify it before calling // the constructor. ERXConfigurationManager.defaultManager().initialize(); EOModelGroup.setClassDelegate(this); ERXSystem.updateProperties(); // AK: enable this when we're ready // WOEncodingDetector.sharedInstance().setFallbackEncoding(CharEncoding.UTF_8); // GN: configure logging with optional custom subclass of ERXLogger String className = ERXProperties.stringForKey("er.extensions.erxloggerclass"); if (className != null) { Class loggerClass = Class.forName(className); Method method = loggerClass.getDeclaredMethod(ERXLogger.CONFIGURE_LOGGING_WITH_SYSTEM_PROPERTIES, (Class[]) null); method.invoke(loggerClass, (Object[]) null); } else { // default behaviour: ERXLogger.configureLoggingWithSystemProperties(); } ERXArrayUtilities.initialize(); // False by default if (ERXValueUtilities .booleanValue(System.getProperty(ERXSharedEOLoader.PatchSharedEOLoadingPropertyKey))) { ERXSharedEOLoader.patchSharedEOLoading(); } ERXExtensions.configureAdaptorContextRapidTurnAround(this); ERXJDBCAdaptor.registerJDBCAdaptor(); if (EODatabaseContext.defaultDelegate() == null) { if (ERXProperties.booleanForKey( ERXEntityDependencyOrderingDelegate.ERXEntityDependencyOrderingDelegateActiveKey)) { ERXDatabaseContextMulticastingDelegate .addDefaultDelegate(new ERXEntityDependencyOrderingDelegate()); ERXDatabaseContextMulticastingDelegate .addDefaultDelegate(ERXDatabaseContextDelegate.defaultDelegate()); } else { EODatabaseContext.setDefaultDelegate(ERXDatabaseContextDelegate.defaultDelegate()); } } ERXAdaptorChannelDelegate.setupDelegate(); NSNotificationCenter.defaultCenter().addObserver(this, new NSSelector("sharedEditingContextWasInitialized", ERXConstant.NotificationClassArray), EOSharedEditingContext.DefaultSharedEditingContextWasInitializedNotification, null); ERXEntityClassDescription.registerDescription(); ERXPartialInitializer.registerModelGroupListener(); } catch (Exception e) { throw NSForwardException._runtimeExceptionForThrowable(e); } } /** * This method is called when the application has finished * launching. Here is where log4j is configured for rapid * turn around and the validation template system is configured. */ @Override public void finishInitialization() { ERXJDBCAdaptor.registerJDBCAdaptor(); // AK: we now setup the properties three times. At startup, in ERX.init // and here. Note that this sucks beyond belief, as this will produce // unforeseen results in several cases, but it's the only way to set up // all parts of the property handling. The first install only loads plain // and user props the second has no good way to set up the main bundle and this one // comes too late for static inits ERXConfigurationManager.defaultManager().loadConfiguration(); ERXProperties.populateSystemProperties(); ERXConfigurationManager.defaultManager().configureRapidTurnAround(); ERXLocalizer.initialize(); ERXValidationFactory.defaultFactory().configureFactory(); // update configuration with system properties that might depend // on others like // log4j.appender.SQL.File=@@loggingBasePath@@/@@port@@.sql // loggingBasePath=/var/log/@@name@@ // name and port are resolved via WOApplication.application() // ERXLogger.configureLoggingWithSystemProperties(); _log = Logger.getLogger(ERXExtensions.class); ERXProperties.pathsForUserAndBundleProperties(true); try { // MS: initialize these with Class.forName(Whatever.class.getName()) because the .class class literal does not trigger static initializers to run in 1.5, // which means that the sql generation support classes are not registered registerSQLSupportForSelector(new NSSelector(ERXPrimaryKeyListQualifier.IsContainedInArraySelectorName), EOQualifierSQLGeneration.Support .supportForClass(Class.forName(ERXPrimaryKeyListQualifier.class.getName()))); registerSQLSupportForSelector(new NSSelector(ERXToManyQualifier.MatchesAllInArraySelectorName), EOQualifierSQLGeneration.Support .supportForClass(Class.forName(ERXToManyQualifier.class.getName()))); registerSQLSupportForSelector(new NSSelector(ERXRegExQualifier.MatchesSelectorName), EOQualifierSQLGeneration.Support .supportForClass(Class.forName(ERXRegExQualifier.class.getName()))); registerSQLSupportForSelector(new NSSelector(ERXFullTextQualifier.FullTextContainsSelectorName), EOQualifierSQLGeneration.Support .supportForClass(Class.forName(ERXFullTextQualifier.class.getName()))); } catch (Throwable t) { throw NSForwardException._runtimeExceptionForThrowable(t); } EOQualifierSQLGeneration.Support.setSupportForClass(new ERXFullTextQualifierSupport(), ERXFullTextQualifier.class); EOQualifierSQLGeneration.Support.setSupportForClass(new ERXFalseQualifierSupport(), ERXFalseQualifier.class); EOQualifierSQLGeneration.Support.setSupportForClass(new ERXTrueQualifierSupport(), ERXTrueQualifier.class); // ERXObjectStoreCoordinatorPool has a static initializer, so just load the class if // the configuration setting exists if (ERXRemoteSynchronizer.remoteSynchronizerEnabled() || ERXProperties.booleanForKey("er.extensions.ERXDatabaseContext.activate")) { String className = ERXProperties.stringForKeyWithDefault("er.extensions.ERXDatabaseContext.className", ERXDatabaseContext.class.getName()); Class c = ERXPatcher.classForName(className); if (c == null) { throw new IllegalStateException( "er.extensions.ERXDatabaseContext.className not found: " + className); } EODatabaseContext.setContextClassToRegister(c); } ERXObjectStoreCoordinatorPool.initializeIfNecessary(); } private static Map<String, Support> _qualifierKeys; public static synchronized void registerSQLSupportForSelector(NSSelector selector, EOQualifierSQLGeneration.Support support) { if (_qualifierKeys == null) { _qualifierKeys = new HashMap<String, Support>(); EOQualifierSQLGeneration.Support old = EOQualifierSQLGeneration.Support .supportForClass(EOKeyValueQualifier.class); EOQualifierSQLGeneration.Support.setSupportForClass(new KeyValueQualifierSQLGenerationSupport(old), EOKeyValueQualifier.class); } _qualifierKeys.put(selector.name(), support); } /** * Support class that listens for EOKeyValueQualifiers that have a selector * that was registered and uses their support instead. * You'll use this mainly to bind queryOperators in display groups. * @author ak */ public static class KeyValueQualifierSQLGenerationSupport extends EOQualifierSQLGeneration.Support { private EOQualifierSQLGeneration.Support _old; public KeyValueQualifierSQLGenerationSupport(EOQualifierSQLGeneration.Support old) { _old = old; } private EOQualifierSQLGeneration.Support supportForQualifier(EOQualifier qualifier) { EOQualifierSQLGeneration.Support support = null; if (qualifier instanceof EOKeyValueQualifier) { synchronized (_qualifierKeys) { support = _qualifierKeys.get(((EOKeyValueQualifier) qualifier).selector().name()); } } if (support == null) { support = _old; } return support; } @Override public String sqlStringForSQLExpression(EOQualifier eoqualifier, EOSQLExpression e) { try { return supportForQualifier(eoqualifier).sqlStringForSQLExpression(eoqualifier, e); } catch (JDBCAdaptorException ex) { ERXExtensions._log.error("Failed to generate sql string for qualifier " + eoqualifier + " on entity " + e.entity() + "."); throw ex; } } @Override public EOQualifier schemaBasedQualifierWithRootEntity(EOQualifier eoqualifier, EOEntity eoentity) { EOQualifier result = supportForQualifier(eoqualifier).schemaBasedQualifierWithRootEntity(eoqualifier, eoentity); return result; } @Override public EOQualifier qualifierMigratedFromEntityRelationshipPath(EOQualifier eoqualifier, EOEntity eoentity, String s) { return supportForQualifier(eoqualifier).qualifierMigratedFromEntityRelationshipPath(eoqualifier, eoentity, s); } } /** * This method is called everytime the configuration file * is changed. This allows for turning SQL debugging on and * off at runtime. * @param n notification posted when the configuration file * changes. */ public void configureAdaptorContext(NSNotification n) { ERXExtensions.configureAdaptorContext(); } /** * This method is called for the following notification * {@link EOSharedEditingContext#DefaultSharedEditingContextWasInitializedNotification} * * @param n the notification. */ public void sharedEditingContextWasInitialized(NSNotification n) { EOSharedEditingContext sec = EOSharedEditingContext.defaultSharedEditingContext(); ERXEC._factory().setDefaultDelegateOnEditingContext(sec, true); } /** logging support for the adaptor channel */ public static Logger adaptorLogger; /** logging support for shared object loading */ public static Logger sharedEOadaptorLogger; /** flag to indicate if adaptor channel logging is enabled */ private static Boolean adaptorEnabled; /** * flag to indicate if rapid turn around is enabled for the * adaptor channel logging. */ private static boolean _isConfigureAdaptorContextRapidTurnAround = false; /** * Configures the passed in observer to register a call back * when the configuration file is changed. This allows one to * change a logger's setting and have that changed value change * the NSLog setting to log the generated SQL. This method is * called as part of the framework initialization process. * @param anObserver object to register the call back with. */ // FIXME: This shouldn't be enabled when the application is in production. // FIXME: Now that all of the logging has been centralized, we should just be able // to do something like this, but much more generic, i.e. have a mapping // between logger names and NSLog groups, for example // com.webobjects.logging.DebugGroupSQLGeneration we should // be able to get the last part of the logger name and look up that log group and turn public static void configureAdaptorContextRapidTurnAround(Object anObserver) { if (!_isConfigureAdaptorContextRapidTurnAround) { // This allows enabling from the log4j system. adaptorLogger = Logger.getLogger("er.transaction.adaptor.EOAdaptorDebugEnabled"); sharedEOadaptorLogger = Logger.getLogger("er.transaction.adaptor.EOSharedEOAdaptorDebugEnabled"); if ((adaptorLogger.isDebugEnabled() && !NSLog .debugLoggingAllowedForGroups(NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess)) || ERXProperties.booleanForKey("EOAdaptorDebugEnabled")) { NSLog.allowDebugLoggingForGroups(NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess); NSLog.setAllowedDebugLevel(NSLog.DebugLevelInformational); } adaptorEnabled = NSLog.debugLoggingAllowedForGroups( NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess) ? Boolean.TRUE : Boolean.FALSE; // Allows rapid turn-around of adaptor debugging. NSNotificationCenter.defaultCenter().addObserver(anObserver, new NSSelector("configureAdaptorContext", ERXConstant.NotificationClassArray), ERXConfigurationManager.ConfigurationDidChangeNotification, null); _isConfigureAdaptorContextRapidTurnAround = true; } } /** * This method is called by the delegate when the configuration * file is changed. It's sole purpose is to map a logging logger * to a debug group. Hopefully in the future we will have a more * generic solution. */ public static void configureAdaptorContext() { Boolean targetState = null; if (adaptorLogger != null) { if (adaptorLogger.isDebugEnabled() && !adaptorEnabled.booleanValue()) { targetState = Boolean.TRUE; } else if (!adaptorLogger.isDebugEnabled() && adaptorEnabled.booleanValue()) { targetState = Boolean.FALSE; } if (targetState != null) { setAdaptorLogging(targetState.booleanValue()); } } } /** * Returns the current state of EOAdaptor logging. */ public static boolean adaptorLogging() { return NSLog.debugLoggingAllowedForGroups(NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess); } /** * Turn EOAdaptor logging on and off. * @param onOff */ public static void setAdaptorLogging(boolean onOff) { Boolean targetState = Boolean.valueOf(onOff); if (NSLog.debugLoggingAllowedForGroups( NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess) != targetState.booleanValue()) { // Post a notification to give us a hook to perform other operations necessary to get logging going, e.g. change Logger settings, etc. NSNotificationCenter.defaultCenter() .postNotification(new NSNotification(eoAdaptorLoggingWillChangeNotification, targetState)); if (targetState.booleanValue()) { NSLog.allowDebugLoggingForGroups(NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess); } else { NSLog.refuseDebugLoggingForGroups(NSLog.DebugGroupSQLGeneration | NSLog.DebugGroupDatabaseAccess); } } if (adaptorLogger != null) { if (targetState.booleanValue()) { adaptorLogger.info("Adaptor debug on"); } else { adaptorLogger.info("Adaptor debug off"); } } adaptorEnabled = targetState; } /** * @deprecated Please use ERXStringUtilities.removeHTMLTagsFromString(String) directly */ public static String removeHTMLTagsFromString(String s) { return ERXStringUtilities.removeHTMLTagsFromString(s); } /** * Forces the garbage collector to run. The * max loop parameter determines the maximum * number of times to run the garbage collector * if the memory footprint is still going down. * In normal cases you would just need to call * this method with the parameter 1. If called * with the parameter 0 the garbage collector * will continue to run until no more free memory * is available to collect. <br/> * <br/> * Note: This can be a very costly operation and * should only be used in extreme circumstances. * @param maxLoop maximum times to run the garbage * collector. Passing in 0 will cause the * collector to run until all free objects * have been collected. */ public static void forceGC(int maxLoop) { if (_log.isDebugEnabled()) _log.debug("Forcing full Garbage Collection"); Runtime runtime = Runtime.getRuntime(); long isFree = runtime.freeMemory(); long wasFree; int i = 0; do { wasFree = isFree; runtime.gc(); isFree = runtime.freeMemory(); i++; } while (isFree > wasFree && (maxLoop <= 0 || i < maxLoop)); runtime.runFinalization(); //TODO: should this be inside the loop? } /** * Capitalizes the given string. * @param s string to capitalize * @return capitalized string if the first char is a * lowercase character. * @deprecated use {@link er.extensions.foundation.ERXStringUtilities#capitalize(String)} */ @Deprecated public static String capitalize(String s) { return ERXStringUtilities.capitalize(s); } /** * Pluralizes a given string for a given language. * See {@link ERXLocalizer} for more information. * @param s string to pluralize * @param howMany number of its * @param language target language * @return plurified string * @deprecated use {@link er.extensions.localization.ERXLocalizer#localizerForLanguage(String)} then {@link er.extensions.localization.ERXLocalizer#plurifiedString(String, int)} */ @Deprecated public static String plurify(String s, int howMany, String language) { return ERXLocalizer.localizerForLanguage(language).plurifiedString(s, howMany); } /** * A safe comparison method that first checks to see * if either of the objects are <code>null</code> before comparing * them with the <code>equals</code> method.<br/> * <br/> * Note that if both objects are <code>null</code> then they will * be considered equal. * @param v1 first object * @param v2 second object * @return <code>true</code> if they are equal, <code>false</code> if not * @deprecated use {@link ObjectUtils#equals(Object, Object)} instead */ @Deprecated public static boolean safeEquals(Object v1, Object v2) { return v1 == v2 || (v1 != null && v2 != null && v1.equals(v2)); } /** * A safe different comparison method that first checks to see * if either of the objects are <code>null</code> before comparing * them with the <code>equals</code> method.<br/> * <br/> * Note that if both objects are <code>null</code> then they will * be considered equal. * @param v1 first object * @param v2 second object * @return <code>true</code> if they are not equal, <code>false</code> if they are * @deprecated use {@link ObjectUtils#equals(Object, Object)} instead */ @Deprecated public static boolean safeDifferent(Object v1, Object v2) { return v1 != v2 && (v1 == null || v2 == null || !v1.equals(v2)); } /** * Tests if a given string object can be parsed into * an integer. * @param s string to be parsed * @return if the string can be parsed into an int * @deprecated use {@link er.extensions.foundation.ERXStringUtilities#stringIsParseableInteger(String)} */ @Deprecated public static boolean stringIsParseableInteger(String s) { try { Integer.parseInt(s); return true; } catch (NumberFormatException e) { return false; } } /** * Returns an integer from a parsable string. If the * string can not be parsed then 0 is returned. * @param s string to be parsed. * @return int from the string or 0 if un-parsable. * @deprecated use {@link er.extensions.foundation.ERXValueUtilities#intValue(Object)} */ @Deprecated public static int intFromParseableIntegerString(String s) { try { int x = Integer.parseInt(s); return x; } catch (NumberFormatException e) { return 0; } } /** * Replaces a given string by another string in a string. * @param s1 string to be replaced * @param s2 to be inserted * @param s string to have the replacement done on it * @return string after having all of the replacement done. * @deprecated use {@link StringUtils#replace(String, String, String)} instead */ @Deprecated public static String substituteStringByStringInString(String s1, String s2, String s) { NSArray a = NSArray.componentsSeparatedByString(s, s1); return a != null ? a.componentsJoinedByString(s2) : s; } /** * Method used to retrieve the shared instance of the * html formatter. * @return shared instance of the html formatter * @deprecated use {@link er.extensions.formatters.ERXSimpleHTMLFormatter#formatter()} */ @Deprecated public static ERXSimpleHTMLFormatter htmlFormatter() { return ERXSimpleHTMLFormatter.formatter(); } /** * This method handles 3 different cases * * 1. keyPath is a single key and represents a relationship * --> addObjectToBothSidesOfRelationshipWithKey * 2. keyPath is a single key and does NOT represents a relationship * 3. keyPath is a real key path: break it up, navigate to the last atom * --> back to 1. or 2. * @param to enterprise object that is having objects added to it * @param from enterprise object that is providing the objects * @param keyPath that specifies the relationship on the to object * to add the objects to. */ // MOVEME: ERXEOFUtilities // FIXME: Should ensure local instances of objects public static void addObjectToBothSidesOfPotentialRelationshipFromObjectWithKeyPath(EOEnterpriseObject to, EOEnterpriseObject from, String keyPath) { if (from != null) { if (keyPath.indexOf('.') != -1) { // we have a key path String partialKeyPath = ERXStringUtilities.keyPathWithoutLastProperty(keyPath); from = (EOEnterpriseObject) from.valueForKeyPath(partialKeyPath); keyPath = ERXStringUtilities.lastPropertyKeyInKeyPath(keyPath); } //if the key is not a keyPath we can check if the key is actually a relationship EOEntity e = ERXEOAccessUtilities.entityNamed(from.editingContext(), from.entityName()); EORelationship r = e.relationshipNamed(keyPath); if (r != null) //if the key correspond to a relationship from.addObjectToBothSidesOfRelationshipWithKey(to, keyPath); else from.takeValueForKeyPath(to, keyPath); } } /** * Returns the byte array for a given file. * @param f file to get the bytes from * @throws IOException if things go wrong * @return byte array of the file. * @deprecated use {@link er.extensions.foundation.ERXFileUtilities#bytesFromFile(File)} */ @Deprecated public static byte[] bytesFromFile(File f) throws IOException { return ERXFileUtilities.bytesFromFile(f); } /** * Returns a string from the file using the default * encoding. * @param f file to read * @throws IOException if things go wrong * @return string representation of that file. * @deprecated use {@link er.extensions.foundation.ERXFileUtilities#stringFromFile(File)} */ @Deprecated public static String stringFromFile(File f) throws IOException { return ERXFileUtilities.stringFromFile(f); } /** * Returns a string from the file using the specified * encoding. * @param f file to read * @param encoding to be used, null will use the default * @throws IOException if things go wrong * @return string representation of the file. * @deprecated user {@link er.extensions.foundation.ERXFileUtilities#stringFromFile(File, String)} */ @Deprecated public static String stringFromFile(File f, String encoding) throws IOException { return ERXFileUtilities.stringFromFile(f, encoding); } /** * Determines the last modification date for a given file * in a framework. Note that this method will only test for * the global resource not the localized resources. * @param fileName name of the file * @param frameworkName name of the framework, null or "app" * for the application bundle * @return the <code>lastModified</code> method of the file object * @deprecated use {@link er.extensions.foundation.ERXFileUtilities#lastModifiedDateForFileInFramework(String, String)} */ @Deprecated public static long lastModifiedDateForFileInFramework(String fileName, String frameworkName) { return ERXFileUtilities.lastModifiedDateForFileInFramework(fileName, frameworkName); } /** * Reads a file in from the file system and parses it as if it were a property list. * @param fileName name of the file * @param aFrameWorkName name of the framework, null or * 'app' for the application bundle. * @return de-serialized object from the plist formatted file * specified. * @deprecated use {@link er.extensions.foundation.ERXFileUtilities#readPropertyListFromFileInFramework(String, String)} */ @Deprecated public static Object readPropertyListFromFileinFramework(String fileName, String aFrameWorkName) { return ERXFileUtilities.readPropertyListFromFileInFramework(fileName, aFrameWorkName); } /** * Reads a file in from the file system for the given set * of languages and parses the file as if it were a property list. * @param fileName name of the file * @param aFrameWorkName name of the framework, null or * 'app' for the application bundle. * @param languageList language list search order * @return de-serialized object from the plist formatted file * specified. * @deprecated use {@link er.extensions.foundation.ERXFileUtilities#readPropertyListFromFileInFramework(String, String, NSArray)} */ @Deprecated public static Object readPropertyListFromFileInFramework(String fileName, String aFrameWorkName, NSArray languageList) { return ERXFileUtilities.readPropertyListFromFileInFramework(fileName, aFrameWorkName, languageList); } /** * For a given enterprise object and key path, will return what * the key 'unit' returns from the userInfo dictionary of the * last property of the key path's EOAttribute or EORelationship. * The userInfo dictionary can be edited via EOModeler, it is that * open book looking icon when looking at either an attribute or * relationship.<br/> * <br/> * For example if the userInfo dictionary or the attribute 'speed' on the * entity Car contained the key-value pair unit=mph, then this method * would be able to resolve that unit given either of these keypaths:<br/> * <code>userInfoUnit(aCar, "speed");<br/> * userInfoUnit(aDrive, "toCar.speed");</code></br> * Units can be very useful for adding meta information to particular * attributes and relationships in models. The ERDirectToWeb framework * adds support for displaying units. * @param object to resolve the key-path from * @param key path off of the object * @return unit information stored in the userInfo dictionary */ // ENHANCEME: Should be more generic for resolving any key off of the userInfo // dictionary. // ENHANCEME: Should also support defaulting to the same attribute in the parent entity // if it isn't found or possibly defaulting to the entity's userInfo itself // MOVEME: ERXEOFUtilities public static String userInfoUnit(EOEnterpriseObject object, String key) { // return the unit stored in the userInfo dictionary of the appropriate EOAttribute EOEntity entity = null; String lastKey = null; String result = null; if (key.indexOf(".") == -1) { String entityName = object.entityName(); entity = ERXEOAccessUtilities.entityNamed(object.editingContext(), entityName); lastKey = key; } else { String partialKeyPath = ERXStringUtilities.keyPathWithoutLastProperty(key); EOEnterpriseObject objectForPropertyDisplayed = (EOEnterpriseObject) object .valueForKeyPath(partialKeyPath); if (objectForPropertyDisplayed != null) { entity = ERXEOAccessUtilities.entityNamed(object.editingContext(), objectForPropertyDisplayed.entityName()); lastKey = ERXStringUtilities.lastPropertyKeyInKeyPath(key); } } if (entity != null && lastKey != null) { EOAttribute a = entity.attributeNamed(lastKey); NSDictionary userInfo = null; if (a != null) userInfo = a.userInfo(); else { EORelationship r = entity.relationshipNamed(lastKey); if (r != null) userInfo = r.userInfo(); } result = (String) (userInfo != null ? userInfo.valueForKey("unit") : null); } return result; } /** * Resolves a given user info unit string for a given object. * User info values are stored in an EOAttibute or EORelationship's * userInfo dictionary. * See the method {@link #userInfoUnit(EOEnterpriseObject, String) userInfoUnit} for * a better description of getting values out of the userInfo * dictionary. This method deals with resolving dynamic userInfo * keys. These keys need to start with the '@@' symbol. For instance * if you have the user info value '@unit' off of an attribute for the * entity Movie, then you can either pass in a Movie object or a * different object with a prefix key path to a movie object.<br/> * @param userInfoUnitString string to be resolved, needs to start with * '@@'. This keypath will be evaluated against either the object * if no prefixKeyPath is specified or the object returned by * the prefixKeyPath being evaluated against the object passed in. * @param object to resolve either the user info unit or the prefixKeyPath. * @param prefixKeyPath used as a prefix for the unit resolution. * @return the resolved unit from the object. */ public static String resolveUnit(String userInfoUnitString, EOEnterpriseObject object, String prefixKeyPath) { // some of our units (stored in the user info) take the form of @project.unit // this method resolves the @keyPath.. if (userInfoUnitString != null && userInfoUnitString.indexOf("@") > -1) { String keyPath = userInfoUnitString.substring(1); String PropertyKeyWithoutLastProperty = ERXStringUtilities.keyPathWithoutLastProperty(prefixKeyPath); EOEnterpriseObject objectForPropertyDisplayed = null; if (PropertyKeyWithoutLastProperty != null) { objectForPropertyDisplayed = object != null ? (EOEnterpriseObject) object.valueForKeyPath(PropertyKeyWithoutLastProperty) : null; } else { objectForPropertyDisplayed = object; } userInfoUnitString = objectForPropertyDisplayed != null ? (String) objectForPropertyDisplayed.valueForKeyPath(keyPath) : null; } return userInfoUnitString; } /** * Refreshes all of the objects for an array of entity names. * @param names array of shared entity names */ // FIXME: Uses default model group. // MOVEME: ERXEOFUtilities public static void refreshSharedObjectsWithNames(NSArray names) { for (Enumeration e = names.objectEnumerator(); e.hasMoreElements();) refreshSharedObjectsWithName((String) e.nextElement()); } private static NSSelector _sharedEntityDataWasRefreshedSelector = new NSSelector( "sharedEntityDataWasRefreshed"); /** * Refreshes all of the shared enterprise objects for a given shared entity, * then notifies the entity's class by calling the static method * sharedEntityDataWasRefreshed() if the shared entity implements it. * * @param entityName name of the shared entity */ // FIXME: Uses default model group, and default shared editing context. // MOVEME: ERXEOFUtilities public static void refreshSharedObjectsWithName(String entityName) { if (entityName == null) { throw new IllegalArgumentException( "Entity name argument is null for method: refreshSharedObjectsWithName"); } EOSharedEditingContext sharedEC = EOSharedEditingContext.defaultSharedEditingContext(); sharedEC.lock(); try { EOEntity entity = ERXEOAccessUtilities.entityNamed(sharedEC, entityName); if (entity == null) { _log.warn("Attempting to refresh a non-existent (or not accessible) EO: " + entityName); return; } //if entity caches objects, clear out the cache if (entity.cachesObjects()) { EODatabaseContext databaseContext = EOUtilities.databaseContextForModelNamed(sharedEC, entity.model().name()); EODatabase database = databaseContext.database(); database.invalidateResultCacheForEntityNamed(entityName); } NSArray fetchSpecNames = entity.sharedObjectFetchSpecificationNames(); int count = (fetchSpecNames != null) ? fetchSpecNames.count() : 0; if (count > 0) { //same check as ERXEOAccessUtilities.entityWithNamedIsShared(), but avoids duplicate work for (int index = 0; index < count; ++index) { String oneFetchSpecName = (String) fetchSpecNames.objectAtIndex(index); EOFetchSpecification fs = entity.fetchSpecificationNamed(oneFetchSpecName); if (fs != null) { fs.setRefreshesRefetchedObjects(true); sharedEC.bindObjectsWithFetchSpecification(fs, oneFetchSpecName); } } //notify the entity class, if it wants to know String className = entity.className(); Class entityClass = Class.forName(className); if (_sharedEntityDataWasRefreshedSelector.implementedByClass(entityClass)) { _sharedEntityDataWasRefreshedSelector.invoke(entityClass); } } else { _log.warn("Attempting to refresh a non-shared EO: " + entityName); } } catch (Exception e) { throw new NSForwardException(e, "Exception while refreshing shared objects for entity named " + entityName); } finally { sharedEC.unlock(); } } /** Holds a reference to the random object */ // FIXME: Not a thread safe object, access should be synchronized. private static Random _random = new Random(); /** * This method can be used with Direct Action URLs to make sure * that the browser will reload the page. This is done by * adding the parameter [? | &]r=random_number to the end of the * url. * @param daURL a url to add the randomization to. * @return url with the addition of the randomization key */ // FIXME: Should check to make sure that the key 'r' isn't already present in the url. public static String randomizeDirectActionURL(String daURL) { synchronized (_random) { int r = _random.nextInt(); char c = daURL.indexOf('?') == -1 ? '?' : '&'; return daURL + c + "r=" + r; } } /** * This method can be used with Direct Action URLs to make sure * that the browser will reload the page. This is done by * adding the parameter [? | &]r=random_number to the end of the * url. * @param daURL a url to add the randomization to. */ // FIXME: Should check to make sure that the key 'r' isn't already present in the url. public static void addRandomizeDirectActionURL(StringBuffer daURL) { synchronized (_random) { int r = _random.nextInt(); char c = '?'; for (int i = 0; i < daURL.length(); i++) { if (daURL.charAt(i) == '?') { c = '&'; break; } } daURL.append(c); daURL.append("r="); daURL.append(r); } } /** * Adds the session ID for a given session to a given URL. * @param url URL string to add session ID form value to. * @param session session object * @return URL with the addition of session ID form value * @deprecated use {@link #addSessionIdFormValue(String, WOSession)} */ @Deprecated public static String addWosidFormValue(String url, WOSession session) { return addSessionIdFormValue(url, session); } /** * Adds the session ID for a given session to a given URL. * * @param urlString * URL string to add session ID form value to * @param session * session object * @return URL with the addition of session ID form value */ public static String addSessionIdFormValue(String urlString, WOSession session) { if (urlString == null || session == null) { _log.warn("not adding session ID: url=" + (urlString != null ? urlString : "<null>") + " session=" + (session != null ? session : "<null>")); return urlString; } String sessionIdKey = WOApplication.application().sessionIdKey(); try { ERXMutableURL url = new ERXMutableURL(urlString); if (!url.containsQueryParameter(sessionIdKey)) { url.setQueryParameter(sessionIdKey, session.sessionID()); } return url.toExternalForm(); } catch (MalformedURLException e) { _log.error("invalid URL string: " + urlString, e); } return urlString; } /** * @deprecated Use {@link er.extensions.foundation.ERXStringUtilities#cleanString} * @param newString * @param toBeCleaneds * @return results of ERXStringUtilities.cleanString */ public static String cleanString(String newString, NSArray<String> toBeCleaneds) { return ERXStringUtilities.cleanString(newString, toBeCleaneds); } /** * Uses the <code>setObjectForKey</code> method of the {@link WOSession} * class to push a Boolean object onto the session for a given key. * Note this is not using key value coding, meaning you don't need * to have a boolean instance variable corresponding to the given * key on your session object. This flag can be retrieved using * the method <code>booleanFlagOnSessionForKeyWithDefault</code>. * @param s session object on which to set the boolean flag * @param key to be used in the session's dictionary * @param newValue boolean value to be set on the session */ public static void setBooleanFlagOnSessionForKey(WOSession s, String key, boolean newValue) { s.setObjectForKey(newValue ? Boolean.TRUE : Boolean.FALSE, key); } /** * Retrieves a value from the session's dictionary and evaluates * that object using the <code>booleanValue</code> method of * {@link ERXValueUtilities}. If there is no object corresponding * to the key passed in, then the default value is returned. The * usual way in which boolean values are set on the session object * is by using the method <code>setBooleanFlagOnSessionForKey</code> * in this class. * @param s session object to retrieve the boolean flag from * @param key that the boolean is stored under * @param defaultValue value to be returned if the object in the * dictionary is null * @return boolean value of the object stored in the session's dictionary * for the given key. */ public static boolean booleanFlagOnSessionForKeyWithDefault(WOSession s, String key, boolean defaultValue) { return s.objectForKey(key) != null ? ERXValueUtilities.booleanValue(s.objectForKey(key)) : defaultValue; } /** * Sets the current session for this thread. This is called * from {@link ERXSession}'s awake and sleep methods. * @param session that is currently active for this thread. * @deprecated use ERXSession.setSession(session) instead */ @Deprecated public synchronized static void setSession(ERXSession session) { ERXSession.setSession(session); } /** * Returns the current session object for this thread. * @return current session object for this thread * @deprecated use ERXSession.session() instead */ @Deprecated public synchronized static ERXSession session() { return ERXSession.session(); } /** * Constructs a unique key based on a context. * A method used by the preferences mechanism from ERDirectToWeb which * needs to be here because it is shared by ERDirectToWeb and ERCoreBusinessLogic. * * @param key preference key * @param context most likely a d2wContext object * @return a unique preference key for storing and retrieving preferences */ // FIXME: Needs to find a better home. public static String userPreferencesKeyFromContext(String key, NSKeyValueCoding context) { StringBuilder result = new StringBuilder(key); result.append('.'); String pc = (String) context.valueForKey("pageConfiguration"); if (pc == null || pc.length() == 0) { String en = "_All_"; EOEntity e = (EOEntity) context.valueForKey("entity"); if (e != null) en = e.name(); result.append("__"); result.append(context.valueForKey("task")); result.append('_'); result.append(en); } else { result.append(pc); } return result.toString(); } /** * Frees all of the resources associated with a given * process and then destroys the process. * @param p process to destroy */ public static void freeProcessResources(Process p) { if (p != null) { try { if (p.getInputStream() != null) p.getInputStream().close(); if (p.getOutputStream() != null) p.getOutputStream().close(); if (p.getErrorStream() != null) p.getErrorStream().close(); p.destroy(); } catch (IOException e) { } } } /** * Determines if a given object implements a method given * the name and the array of input parameters. * Note that this doesn't quite check the method signature * since the method return type is not checked. * * @param object to determine if it implements a method * @param methodName name of the method * @param parameters array of parameters * @return if the object implements a method with the given name * and class parameters */ public static boolean objectImplementsMethod(Object object, String methodName, Class[] parameters) { boolean implementsMethod = false; for (Enumeration e = (new NSArray(object.getClass().getMethods())).objectEnumerator(); e .hasMoreElements();) { Method m = (Method) e.nextElement(); if (m.getName().equals(methodName) && Arrays.equals(m.getParameterTypes(), parameters)) { implementsMethod = true; break; } } return implementsMethod; } /** * Initializes your WOApplication programmatically (for use in test cases and main methods) with * the assumption that the current directory is your main bundle URL. * * @param applicationSubclass your Application subclass * @param args the commandline arguments for your application */ public static void initApp(Class applicationSubclass, String[] args) { try { File woaFolder = new File(".").getCanonicalFile(); if (!woaFolder.getName().endsWith(".woa")) { if (new File(woaFolder, ".project").exists()) { File buildFolder = new File(new File(woaFolder, "build"), woaFolder.getName() + ".woa"); if (buildFolder.exists()) { woaFolder = buildFolder; } else { File distFolder = new File(new File(woaFolder, "dist"), woaFolder.getName() + ".woa"); if (distFolder.exists()) { woaFolder = distFolder; } else { //Bundle-less builds. Yay! //throw new IllegalArgumentException("You must run your application from a .woa folder to call this method."); } } } } ERXExtensions.initApp(null, woaFolder.toURI().toURL(), applicationSubclass, args); } catch (IOException e) { throw new NSForwardException(e); } } /** * Initializes your WOApplication programmatically (for use in test cases and main methods). * * @param mainBundleName the name of your main bundle * @param applicationSubclass your Application subclass * @param args the commandline arguments for your application */ public static void initApp(String mainBundleName, Class applicationSubclass, String[] args) { ERXExtensions.initApp(mainBundleName, null, applicationSubclass, args); } private static boolean _appInitialized = false; /** * Initializes your WOApplication programmatically (for use in test cases and main methods). * * @param mainBundleName the name of your main bundle (or null to use mainBundleURL) * @param mainBundleURL the URL to your main bundle (ignored if mainBundleName is set) * @param applicationSubclass your Application subclass * @param args the commandline arguments for your application */ public static void initApp(String mainBundleName, URL mainBundleURL, Class applicationSubclass, String[] args) { if (_appInitialized) { return; } try { ERXApplication.setup(args); if (mainBundleURL != null) { System.setProperty("webobjects.user.dir", new File(mainBundleURL.getFile()).getCanonicalPath()); } // Odds are you are only using this method for test cases and development mode System.setProperty("er.extensions.ERXApplication.developmentMode", "true"); ERXApplication.primeApplication(mainBundleName, mainBundleURL, applicationSubclass.getName()); //NSNotificationCenter.defaultCenter().postNotification(new NSNotification(ERXApplication.ApplicationDidCreateNotification, WOApplication.application())); } catch (IOException e) { throw new NSForwardException(e); } _appInitialized = true; } /** * Initializes Wonder EOF programmatically (for use in test cases and main methods). You do * not need to call this method if you already called initApp. This is lighter-weight than * initApp, and tries to just get enough configured to make EOF work properly. This method * assumes you are running your app from a .woa folder. * * <p>This is equivalent to calling <code>initEOF(new File("."), args)</code>.</p> * * @param args the commandline arguments for your application * @throws IllegalArgumentException if the current dir or mainBundleFolder is not a *.woa bundle. */ public static void initEOF(final String[] args) { ERXExtensions.initEOF(new File("."), args); } /** * Initializes Wonder EOF programmatically (for use in test cases and main methods). You do * not need to call this method if you already called initApp. This is lighter-weight than * initApp, and tries to just get enough configured to make EOF work properly. * * <p>This is equivalent to calling <code>initEOF(mainBundleFolder, args, true, true, true)</code>.</p> * * @param mainBundleFolder the folder of your main bundle * @param args the commandline arguments for your application * @throws IllegalArgumentException if the current dir or mainBundleFolder is not a *.woa bundle. */ public static void initEOF(final File mainBundleFolder, final String[] args) { initEOF(mainBundleFolder, args, true, true, true); } /** * <p> * Initializes Wonder EOF programmatically (for use in test cases and main methods). You do * not need to call this method if you already called initApp. This is lighter-weight than * initApp, and tries to just get enough configured to make EOF work properly. This method is also, * unlike {@link #initEOF(String[])} or {@link #initEOF(File, String[])}, not so restrictive as to * require the name of the bundle be a <code>*.woa</code>, and nor does it require <code>*.framework</code> * as the name of the bundle. * </p> * <p> * It can therefore be useful for, and used with, folder, jar or war * bundles -- whichever bundle is referenced by <code>mainBundleURI</code> -- so long as it is * bundle-like in content rather than by name. For NSBundle, this usually just requires a Resources folder * within the bundle. * </p> * <p> * For example, if you're build tool compiles sources and puts Resources under <code>target/classes</code> * you can call this method via <code>ERXExtensions.initEOF(new File(projectDir, "target/classes"), args, true)</code>. * </p> * <p><b>Note 1:</b> * this will set the system property <code>webobjects.user.dir</code> to the canonical path of the * given bundle uri if, and only if, the bundle uri points to a directory and is schema is <code>file</code>. * </p> * <p><b>Note 2:</b> * this will set NSBundle's mainBundle to the referenced bundle loaded via * {@link er.extensions.foundation.ERXRuntimeUtilities#loadBundleIfNeeded(File)} if found. * </p> * * <p>This is equivalent to calling <code>initEOF(mainBundleFolder, args, assertsBundleExists, false, true)</code>.</p> * * @param mainBundleFile the archive file or directory of your main bundle * @param args the commandline arguments for your application * @param assertsBundleExists ensures that the bundle exists and is loaded * @throws NSForwardException if the given bundle doesn't satisfy the given assertions or * ERXRuntimeUtilities.loadBundleIfNeeded or ERXApplication.setup fails. * @see #initEOF(File, String[], boolean, boolean, boolean) */ public static void initEOF(final File mainBundleFile, final String[] args, boolean assertsBundleExists) { initEOF(mainBundleFile, args, assertsBundleExists, false, true); } private static boolean _eofInitialized = false; private static final Lock _eofInitializeLock = new ReentrantLock(); /** * <p> * Initializes Wonder EOF programmatically (for use in test cases and main methods). You do * not need to call this method if you already called initApp. This is lighter-weight than * initApp, and tries to just get enough configured to make EOF work properly. This method is also, * unlike {@link #initEOF(String[])} or {@link #initEOF(File, String[])}, not so restrictive as to * require the name of the bundle be a <code>*.woa</code>, and nor does it require <code>*.framework</code> * as the name of the bundle. * </p> * <p> * It can therefore be useful for, and used with, folder, jar or war * bundles -- whichever bundle is referenced by <code>mainBundleURI</code> -- so long as it is * bundle-like in content rather than by name. For NSBundle, this usually just requires a Resources folder * within the bundle. * </p> * <p> * For example, if you're build tool compiles sources and puts Resources under <code>target/classes</code> * you can call this method via <code>ERXExtensions.initEOF(new File(projectDir, "target/classes").toURI(), args)</code>. * </p> * <p><b>Note 1:</b> * this will set the system property <code>webobjects.user.dir</code> to the canonical path of the * given bundle uri if, and only if, the bundle uri points to a directory and is schema is <code>file</code>. * </p> * <p><b>Note 2:</b> * this will set NSBundle's mainBundle to the referenced bundle loaded via * {@link er.extensions.foundation.ERXRuntimeUtilities#loadBundleIfNeeded(File)} if found. * </p> * * @param mainBundleFile the archive file or directory of your main bundle * @param args the commandline arguments for your application * @param assertsBundleExists ensures that the bundle exists and is loaded * @param assertsBundleIsWOApplicationFolder ensures that the bundle referenced by mainBundleFile, or the current dir if fallbackToUserDirAsBundle is true, is a <code>*.woa</code> bundle folder. * @param fallbackToUserDirAsBundle falls back to current dir if the mainBundleFile does not exist * @throws NSForwardException if the given bundle doesn't satisfy the given assertions or * ERXRuntimeUtilities.loadBundleIfNeeded or ERXApplication.setup fails. * @see er.extensions.foundation.ERXRuntimeUtilities#loadBundleIfNeeded(File) * @see NSBundle#_setMainBundle(NSBundle) * @see er.extensions.appserver.ERXApplication#setup(String[]) * @see #bundleDidLoad(NSNotification) */ public static void initEOF(final File mainBundleFile, String[] args, boolean assertsBundleExists, boolean assertsBundleIsWOApplicationFolder, boolean fallbackToUserDirAsBundle) { _eofInitializeLock.lock(); try { if (!_eofInitialized) { try { File bundleFile = mainBundleFile; if (assertsBundleIsWOApplicationFolder) { if (!bundleFile.exists() || !bundleFile.getName().endsWith(".woa")) { bundleFile = new File(".").getCanonicalFile(); if (!bundleFile.exists() || !bundleFile.getName().endsWith(".woa")) { throw new IllegalArgumentException( "Assertion failure. You must run your application from the .woa folder to call this method."); } } } if (assertsBundleExists) { if (bundleFile == null || !bundleFile.exists()) { if (fallbackToUserDirAsBundle) { bundleFile = new File(".").getCanonicalFile(); } else { throw new IllegalArgumentException( "Assertion failure. The main bundle is required to exist to call this method."); } } } if (bundleFile != null && bundleFile.isDirectory()) { System.setProperty("webobjects.user.dir", bundleFile.getCanonicalPath()); } NSBundle mainBundle = null; try { mainBundle = ERXRuntimeUtilities.loadBundleIfNeeded(bundleFile); if (mainBundle == null) { throw new IllegalArgumentException("The main bundle failed to load."); } NSBundle._setMainBundle(mainBundle); NSLog.debug.appendln("initEOF setting main bundle to " + mainBundle); } catch (Exception e) { if (assertsBundleExists) { throw e; } } ERXApplication.setup(args); ERXFrameworkPrincipal.sharedInstance(ERXExtensions.class).bundleDidLoad(null); } catch (Exception e) { throw new NSForwardException(e); } _eofInitialized = true; } } finally { _eofInitializeLock.unlock(); } } }