diff --git a/lib/native/windows-64/sysactivitynotifications.dll b/lib/native/windows-64/sysactivitynotifications.dll index 1a301e6db..160c21bb2 100644 Binary files a/lib/native/windows-64/sysactivitynotifications.dll and b/lib/native/windows-64/sysactivitynotifications.dll differ diff --git a/lib/native/windows/sysactivitynotifications.dll b/lib/native/windows/sysactivitynotifications.dll index be2376660..64a526ec7 100644 Binary files a/lib/native/windows/sysactivitynotifications.dll and b/lib/native/windows/sysactivitynotifications.dll differ diff --git a/resources/install/build.xml b/resources/install/build.xml index 6c2eafafc..71b782088 100644 --- a/resources/install/build.xml +++ b/resources/install/build.xml @@ -439,6 +439,12 @@ + + + felix.auto.start.14= reference:file:sc-bundles/windows-clean-shutdown.jar + + diff --git a/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.cpp b/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.cpp index 67658e643..e4a2b80dc 100644 --- a/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.cpp +++ b/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.cpp @@ -396,6 +396,18 @@ HRESULT callback(UINT Msg, WPARAM wParam, LPARAM lParam) return TRUE; } } + else if (Msg == WM_QUERYENDSESSION) + { + notify(net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_QUERY_ENDSESSION); + return TRUE; + } + else if (Msg == WM_ENDSESSION && wParam == TRUE) + { + // we fire the message only if we are really ending the session + // is wParam is False means someone has canceled the shutdown/logoff + notify(net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_ENDSESSION); + return TRUE; + } jvm->DetachCurrentThread(); diff --git a/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.h b/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.h index 885035eae..7c5d55a79 100644 --- a/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.h +++ b/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.h @@ -29,7 +29,10 @@ extern "C" { #define net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_NETWORK_CHANGE 9L #undef net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_DNS_CHANGE #define net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_DNS_CHANGE 10L - +#undef net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_QUERY_ENDSESSION +#define net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_QUERY_ENDSESSION 11L +#undef net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_ENDSESSION +#define net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications_NOTIFY_ENDSESSION 12L /* * Class: net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications * Method: allocAndInit diff --git a/src/net/java/sip/communicator/impl/shutdowntimeout/ShutdownTimeout.java b/src/net/java/sip/communicator/impl/shutdowntimeout/ShutdownTimeout.java index e97cfb4ce..ea8cac8e1 100644 --- a/src/net/java/sip/communicator/impl/shutdowntimeout/ShutdownTimeout.java +++ b/src/net/java/sip/communicator/impl/shutdowntimeout/ShutdownTimeout.java @@ -29,10 +29,16 @@ public class ShutdownTimeout private static final Logger logger = Logger.getLogger(ShutdownTimeout.class); + /** + * The system property which can be used to set custom timeout. + */ + public static String SHUTDOWN_TIMEOUT_PROP = + "org.jitsi.shutdown.SHUTDOWN_TIMEOUT"; + /** * The number of miliseconds that we wait before we force a shutdown. */ - public static final long SHUTDOWN_TIMEOUT = 15000;//ms + public static final long SHUTDOWN_TIMEOUT_DEFAULT = 3000;//ms /** * The code that we exit with if the application is not down in 15 seconds. @@ -72,11 +78,31 @@ public void run() { synchronized(this) { - try{ + try + { + + long shutDownTimeout = SHUTDOWN_TIMEOUT_DEFAULT; + + // check for custom value available through system + // property + try + { + String shutdownCustomValue = + System.getProperty(SHUTDOWN_TIMEOUT_PROP); + + if(shutdownCustomValue != null + && shutdownCustomValue.length() > 0) + { + shutDownTimeout = + Long.valueOf(shutdownCustomValue); + } + } + catch(Throwable t){} + if (logger.isTraceEnabled()) logger.trace("Starting shutdown countdown of " - + SHUTDOWN_TIMEOUT + "ms."); - wait(SHUTDOWN_TIMEOUT); + + shutDownTimeout + "ms."); + wait(shutDownTimeout); logger.error("Failed to gently shutdown. Forcing exit."); System.exit(SYSTEM_EXIT_CODE); }catch (InterruptedException ex){ diff --git a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityEventDispatcher.java b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityEventDispatcher.java index 8e091a541..4255eb7a1 100644 --- a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityEventDispatcher.java +++ b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityEventDispatcher.java @@ -116,6 +116,25 @@ protected void fireSystemActivityEvent(SystemActivityEvent evt) fireSystemActivityEvent(evt, 0); } + /** + * Delivers the specified event to all registered listeners. Without + * using the thread, but delivering them in the calling thread. + * + * @param evt the SystemActivityEvent that we'd like delivered to + * all registered message listeners. + */ + protected void fireSystemActivityEventCurrentThread(SystemActivityEvent evt) + { + List listenersCopy = new ArrayList + (listeners); + for (int i = 0; i < listenersCopy.size(); i++) + { + fireSystemActivityEvent( + evt, + listenersCopy.get(i)); + } + } + /** * Delivers the specified event to all registered listeners. * diff --git a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java index b7682c564..4f207e6de 100644 --- a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java +++ b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java @@ -68,6 +68,18 @@ public class SystemActivityNotifications */ public static final int NOTIFY_DNS_CHANGE = 10; + /** + * Notifies for start of process of ending desktop session, + * logoff or shutdown. + */ + public static final int NOTIFY_QUERY_ENDSESSION = 11; + + /** + * All processes have been informed about ending session, now notify for + * the actual end session. + */ + public static final int NOTIFY_ENDSESSION = 12; + /** * The logger. */ diff --git a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java index e8891addd..67ec0987a 100644 --- a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java +++ b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java @@ -278,6 +278,30 @@ public void notify(int type) SystemActivityEvent.EVENT_DNS_CHANGE); break; } + case SystemActivityNotifications.NOTIFY_QUERY_ENDSESSION : + { + // both events QUERY_ENDSESSION and ENDSESSION + // depend on the result one after another + // we don't put them in new thread in order to give control + // in the bundles using this events. + evt = new SystemActivityEvent(this, + SystemActivityEvent.EVENT_QUERY_ENDSESSION); + eventDispatcher.fireSystemActivityEventCurrentThread(evt); + + return; + } + case SystemActivityNotifications.NOTIFY_ENDSESSION : + { + // both events QUERY_ENDSESSION and ENDSESSION + // depend on the result one after another + // we don't put them in new thread in order to give control + // in the bundles using this events. + evt = new SystemActivityEvent(this, + SystemActivityEvent.EVENT_ENDSESSION); + eventDispatcher.fireSystemActivityEventCurrentThread(evt); + + return; + } } if (evt != null) diff --git a/src/net/java/sip/communicator/plugin/windowscleanshutdown/CleanShutdownActivator.java b/src/net/java/sip/communicator/plugin/windowscleanshutdown/CleanShutdownActivator.java new file mode 100644 index 000000000..7c64243a4 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/windowscleanshutdown/CleanShutdownActivator.java @@ -0,0 +1,224 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.windowscleanshutdown; + +import net.java.sip.communicator.service.shutdown.*; +import net.java.sip.communicator.service.sysactivity.*; +import net.java.sip.communicator.service.sysactivity.event.*; +import net.java.sip.communicator.util.*; +import org.osgi.framework.*; + +import java.util.concurrent.*; + +/** + * Tries to cleanly close the application on shutdown/logoff. The events used + * here are only available on windows. + * + * If the application is still running once end session event is received + * and we have give it time (currently 3 sec.) we System.exit() the application. + * + * @author Emil Ivov + */ +public class CleanShutdownActivator + implements BundleActivator, ServiceListener +{ + private static final Logger logger + = Logger.getLogger(CleanShutdownActivator.class); + + /** + * Used to wait for stop. + */ + final CountDownLatch synchShutdown = new CountDownLatch(1); + + /** + * Our context. + */ + private BundleContext context; + + /** + * The system activity service. + */ + SystemActivityNotificationsService sysActivityService = null; + + /** + * Bundle activator start method. + * + * @throws Exception If this method throws an exception + * (which won't happen). + */ + public void start(final BundleContext context) + throws Exception + { + this.context = context; + + logger.info("Starting the CleanShutdown service."); + + handleNewSystemActivityNotificationsService( + getSystemActivityNotificationsService(context)); + + // if missing will wait for it + if(sysActivityService == null) + context.addServiceListener(this); + } + + /** + * Called when this bundle is stopped so the Framework can perform the + * bundle-specific activities necessary to stop the bundle. + * + * @param context The execution context of the bundle being stopped. + * @throws Exception If this method throws an exception, the bundle is + * still marked as stopped, and the Framework will remove the bundle's + * listeners, unregister all services registered by the bundle, and + * release all services used by the bundle. + */ + public void stop(BundleContext context) + throws Exception + { + // stop received. + synchShutdown.countDown(); + } + + /** + * Gets a reference to a ShutdownService implementation + * currently registered in the bundle context of the active + * OsDependentActivator instance. + *

+ * The returned reference to ShutdownService is not being + * cached. + *

+ * + * @return reference to a ShutdownService implementation + * currently registered in the bundle context of the active + * OsDependentActivator instance + */ + private ShutdownService getShutdownService() + { + return(ShutdownService)context.getService( + context.getServiceReference(ShutdownService.class.getName())); + } + + /** + * Gets a reference to a SystemActivityNotificationsService + * implementation currently registered in the bundle context. + *

+ * The returned reference to SystemActivityNotificationsService + * is not being cached. + *

+ * + * @param context the bundle context. + * @return reference to a SystemActivityNotificationsService + * implementation currently registered in the bundle context. + */ + public static SystemActivityNotificationsService + getSystemActivityNotificationsService(BundleContext context) + { + ServiceReference ref = + context.getServiceReference( + SystemActivityNotificationsService.class.getName()); + + if(ref == null) + return null; + else + return + (SystemActivityNotificationsService) + context.getService(ref); + } + + + /** + * Saves the reference for the service and + * add a listener if the desired events are supported. Or start + * the checking thread otherwise. + * @param newService the service + */ + private void handleNewSystemActivityNotificationsService + (SystemActivityNotificationsService newService) + { + sysActivityService = newService; + + if(newService != null) + newService.addSystemActivityChangeListener( + new SystemActivityChangeListener() + { + public void activityChanged(SystemActivityEvent event) + { + if(event.getEventID() + == SystemActivityEvent.EVENT_QUERY_ENDSESSION) + { + // instruct the shutdown timeout to + // wait only 3 secs. + System.setProperty( + "org.jitsi.shutdown.SHUTDOWN_TIMEOUT", + "3000"); + + getShutdownService().beginShutdown(); + + // just wait a moment, or till we are stopped + try + { + synchronized(this) + { + synchShutdown.await(1500, + TimeUnit.MILLISECONDS); + } + } + catch(Throwable t) + {} + } + else if(event.getEventID() + == SystemActivityEvent.EVENT_ENDSESSION) + { + try + { + // wait till we are stopped or forced stopped + synchShutdown.await(); + } + catch(Throwable t) + {} + } + } + }); + } + + /** + * When new SystemActivityNotificationsService + * is registered we add needed listeners. + * + * @param serviceEvent ServiceEvent + */ + public void serviceChanged(ServiceEvent serviceEvent) + { + ServiceReference serviceRef = serviceEvent.getServiceReference(); + + // if the event is caused by a bundle being stopped, we don't want to + // know we are shutting down + if (serviceRef.getBundle().getState() == Bundle.STOPPING) + { + return; + } + + Object sService = context + .getService(serviceRef); + + if(sService instanceof SystemActivityNotificationsService) + { + switch (serviceEvent.getType()) + { + case ServiceEvent.REGISTERED: + handleNewSystemActivityNotificationsService( + (SystemActivityNotificationsService)sService); + break; + case ServiceEvent.UNREGISTERING: + //((SystemActivityNotificationsService)sService) + // .removeSystemActivityChangeListener(this); + break; + } + + return; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/windowscleanshutdown/cleanshutdown.manifest.mf b/src/net/java/sip/communicator/plugin/windowscleanshutdown/cleanshutdown.manifest.mf new file mode 100644 index 000000000..25db59ff8 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/windowscleanshutdown/cleanshutdown.manifest.mf @@ -0,0 +1,12 @@ +Bundle-Activator: net.java.sip.communicator.plugin.windowscleanshutdown.CleanShutdownActivator +Bundle-Name: CleanShutdownBundle +Bundle-Description: A bundle that makes sure that when closed Jitsi will exit cleanly. +Bundle-Vendor: jitsi.org +Bundle-Version: 0.0.1 +System-Bundle: yes +Import-Package: org.osgi.framework, + org.jitsi.service.configuration, + net.java.sip.communicator.service.shutdown, + net.java.sip.communicator.service.sysactivity, + net.java.sip.communicator.service.sysactivity.event, + net.java.sip.communicator.util diff --git a/src/net/java/sip/communicator/service/sysactivity/event/SystemActivityEvent.java b/src/net/java/sip/communicator/service/sysactivity/event/SystemActivityEvent.java index 49494b047..36f85b9b0 100644 --- a/src/net/java/sip/communicator/service/sysactivity/event/SystemActivityEvent.java +++ b/src/net/java/sip/communicator/service/sysactivity/event/SystemActivityEvent.java @@ -86,6 +86,19 @@ public class SystemActivityEvent */ public static final int EVENT_DNS_CHANGE = 12; + /** + * Informing that the machine is logging of or shutting down. + */ + public static final int EVENT_QUERY_ENDSESSION = 13; + + /** + * The log off or shutdown is in process for us, no matter + * what other process has replied, whether one of them has canceled + * or not the current end of session. It's like that cause we have answered + * that we will shutdown. + */ + public static final int EVENT_ENDSESSION = 14; + /** * The type of the event. */