/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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 <code>ShutdownService</code> implementation * currently registered in the bundle context of the active * <code>OsDependentActivator</code> instance. * <p> * The returned reference to <code>ShutdownService</code> is not being * cached. * </p> * * @return reference to a <code>ShutdownService</code> implementation * currently registered in the bundle context of the active * <code>OsDependentActivator</code> instance */ private ShutdownService getShutdownService() { return(ShutdownService)context.getService( context.getServiceReference(ShutdownService.class.getName())); } /** * Gets a reference to a <code>SystemActivityNotificationsService</code> * implementation currently registered in the bundle context. * <p> * The returned reference to <code>SystemActivityNotificationsService</code> * is not being cached. * </p> * * @param context the bundle context. * @return reference to a <code>SystemActivityNotificationsService</code> * 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; } } }