diff --git a/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXApplication.java b/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXApplication.java index c21bfbc60e6..e8ee8be0bfe 100644 --- a/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXApplication.java +++ b/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXApplication.java @@ -15,9 +15,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.net.BindException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.net.URLConnection; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; @@ -44,6 +46,7 @@ import com.webobjects.appserver.WOAction; import com.webobjects.appserver.WOActionResults; +import com.webobjects.appserver.WOAdaptor; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; @@ -98,6 +101,7 @@ import er.extensions.foundation.ERXArrayUtilities; import er.extensions.foundation.ERXCompressionUtilities; import er.extensions.foundation.ERXConfigurationManager; +import er.extensions.foundation.ERXExceptionUtilities; import er.extensions.foundation.ERXPatcher; import er.extensions.foundation.ERXProperties; import er.extensions.foundation.ERXRuntimeUtilities; @@ -150,6 +154,7 @@ * @property er.extensions.ERXApplication.useEditingContextUnlocker * @property er.extensions.ERXApplication.useSessionStoreDeadlockDetection * @property er.extensions.ERXComponentActionRedirector.enabled + * @property er.extensions.ERXApplication.allowMultipleDevInstances */ public abstract class ERXApplication extends ERXAjaxApplication implements ERXGracefulShutdown.GracefulApplication { @@ -821,6 +826,49 @@ public static void main(String argv[], Class applicationClass) { WOApplication.main(argv, applicationClass); } + /** + *

Terminates a different instance of the same application that may already be running.
+ * Only in dev mode.

+ *

Set the property "er.extensions.ERXApplication.allowMultipleDevInstances" to "true" if + * you need to run multiple instances in dev mode.

+ * + * @return true if a previously running instance was stopped. + */ + private static boolean stopPreviousDevInstance() { + if (!isDevelopmentModeSafe() || + ERXProperties.booleanForKeyWithDefault("er.extensions.ERXApplication.allowMultipleDevInstances", false)) { + return false; + } + + String appUrl; + if (!(application().wasMainInvoked())) { + return false; + } else if (application().isDirectConnectEnabled()) { + appUrl = application().cgiAdaptorURL().replace("/cgi", ":" + application().port() + "/cgi"); + appUrl += "/" + application().name() + ".woa"; + } else { + appUrl = application().cgiAdaptorURL() + "/" + application().name() + ".woa/-" + application().port(); + } + + URL url; + try { + appUrl = appUrl + "/" + application().directActionRequestHandlerKey() + "/stop"; + url = new URL(appUrl); + + log.debug("Stopping previously running instance of " + application().name()); + + URLConnection connection = url.openConnection(); + connection.getContent(); + + Thread.sleep(2000); // really only necessary for direct connect mode + + return true; + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + /** * Utility class to track down duplicate items in the class path. Reports * duplicate packages and packages that are present in different versions. @@ -2617,6 +2665,19 @@ public NSArray> additionalAdaptors() { return additionalAdaptors; } + @Override + public WOAdaptor adaptorWithName(String aClassName, NSDictionary anArgsDictionary) { + try { + return super.adaptorWithName(aClassName, anArgsDictionary); + } catch (NSForwardException e) { + Throwable rootCause = ERXExceptionUtilities.getMeaningfulThrowable(e); + if ((rootCause instanceof BindException) && stopPreviousDevInstance()) { + return super.adaptorWithName(aClassName, anArgsDictionary); + } + throw e; + } + } + protected void _debugValueForDeclarationNamed(WOComponent component, String verb, String aDeclarationName, String aDeclarationType, String aBindingName, String anAssociationDescription, Object aValue) { if (aValue instanceof String) { StringBuffer stringbuffer = new StringBuffer(((String) aValue).length() + 2); diff --git a/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXDirectAction.java b/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXDirectAction.java index 14b5f41edfb..32cb2bf3296 100644 --- a/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXDirectAction.java +++ b/Frameworks/Core/ERExtensions/Sources/er/extensions/appserver/ERXDirectAction.java @@ -503,4 +503,18 @@ public T pageWithName(Class componentClass) { return (T) super.pageWithName(componentClass.getName()); } + public WOActionResults stopAction() { + WOResponse response = new WOResponse(); + response.setHeader("text/plain", "Content-Type"); + + if (ERXApplication.isDevelopmentModeSafe()) { + WOApplication.application().terminate(); + response.setContent("OK"); + } else { + response.setStatus(401); + } + + return response; + } + }