Adds and activates reconnect plugin. Some fixes to protocols in order to work with the plungin (removing all tries to reconnect as they are now controlled from outside).

cusax-fix
Damian Minkov 16 years ago
parent fd5c3ed8b6
commit 3553f9ad0e

@ -909,7 +909,7 @@
bundle-filehistory,bundle-metahistory,bundle-metahistory-slick,
bundle-plugin-facebookaccregwizz,
bundle-bouncycastle,bundle-plugin-otr,bundle-plugin-iptelaccregwizz,
bundle-contactsource"/>
bundle-contactsource,bundle-plugin-reconnect"/>
<!--BUNDLE-SC-LAUNCHER-->
<target name="bundle-sc-launcher">
@ -2325,4 +2325,14 @@ org.apache.http.util"/>
prefix="net/java/sip/communicator/service/contactsource"/>
</jar>
</target>
<!-- BUNDLE-PLUGIN-RECONNECT -->
<target name="bundle-plugin-reconnect">
<!-- Creates a bundle for the reconnect plugin.-->
<jar compress="false" destfile="${bundles.dest}/reconnectplugin.jar"
manifest="${src}/net/java/sip/communicator/plugin/reconnectplugin/reconnectplugin.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/plugin/reconnectplugin"
prefix="net/java/sip/communicator/plugin/reconnectplugin"/>
</jar>
</target>
</project>

@ -76,6 +76,7 @@ felix.auto.start.52= \
reference:file:sc-bundles/protocol-gibberish.jar \
reference:file:sc-bundles/protocol-ssh.jar \
reference:file:sc-bundles/netaddr.jar \
reference:file:sc-bundles/reconnectplugin.jar \
reference:file:sc-bundles/protocol-zeroconf.jar \
reference:file:sc-bundles/protocol-irc.jar \
reference:file:sc-bundles/protocol-dict.jar

@ -401,7 +401,7 @@
<entry key="last_version" value="${sip-communicator.version}" />
<entry key="download_link"
value="http://download.sip-communicator.org/nightly/windows/${package.name}-${sip-communicator.version}.exe" />
<entry key="changes_html" value="changes.html" />
<entry key="changes_html" value="updates/index.html" />
</propertyfile>
<!--

@ -190,28 +190,29 @@ else if (evt.getReasonCode() == RegistrationStateChangeEvent
logger.trace(evt.getReason());
}
else if (newState.equals(RegistrationState.CONNECTION_FAILED))
{
String msgText = GuiActivator.getResources().getI18NString(
"service.gui.CONNECTION_FAILED_MSG",
new String[]
{ accountID.getUserID(),
accountID.getService() });
int result = new MessageDialog(
null,
GuiActivator.getResources().getI18NString("service.gui.ERROR"),
msgText,
GuiActivator.getResources().getI18NString("service.gui.RETRY"),
false).showDialog();
if (result == MessageDialog.OK_RETURN_CODE)
{
this.login(protocolProvider);
}
logger.trace(evt.getReason());
}
// CONNECTION_FAILED events are now dispatched in reconnect plugin
// else if (newState.equals(RegistrationState.CONNECTION_FAILED))
// {
// String msgText = GuiActivator.getResources().getI18NString(
// "service.gui.CONNECTION_FAILED_MSG",
// new String[]
// { accountID.getUserID(),
// accountID.getService() });
//
// int result = new MessageDialog(
// null,
// GuiActivator.getResources().getI18NString("service.gui.ERROR"),
// msgText,
// GuiActivator.getResources().getI18NString("service.gui.RETRY"),
// false).showDialog();
//
// if (result == MessageDialog.OK_RETURN_CODE)
// {
// this.login(protocolProvider);
// }
//
// logger.trace(evt.getReason());
// }
else if (newState.equals(RegistrationState.EXPIRED))
{
String msgText = GuiActivator.getResources().getI18NString(

@ -13,6 +13,7 @@
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.netaddr.event.*;
import net.java.sip.communicator.util.*;
import net.java.stun4j.*;
import net.java.stun4j.client.*;
@ -115,6 +116,11 @@ public class NetworkAddressManagerServiceImpl
*/
public static final int DEFAULT_STUN_SERVER_PORT = 3478;
/**
* A thread which periodically scans network interfaces and reports
* changes in network configuration.
*/
private NetworkConfigurationWatcher networkConfigurationWatcher = null;
/**
* Initializes this network address manager service implementation and
@ -221,6 +227,8 @@ public void stop()
configurationService
.removeVetoableChangeListener( PROP_STUN_SERVER_PORT, this);
if(networkConfigurationWatcher != null)
networkConfigurationWatcher.stop();
}
finally
{
@ -808,5 +816,30 @@ public DatagramSocket createDatagramSocket(InetAddress laddr,
+ minPort + " and " + (port -1));
}
/**
* Adds new <tt>NetworkConfigurationChangeListener</tt> which will
* be informed for network configuration changes.
* @param listener the listener.
*/
public void addNetworkConfigurationChangeListener(
NetworkConfigurationChangeListener listener)
{
if(networkConfigurationWatcher == null)
networkConfigurationWatcher = new NetworkConfigurationWatcher();
networkConfigurationWatcher
.addNetworkConfigurationChangeListener(listener);
}
/**
* Remove <tt>NetworkConfigurationChangeListener</tt>.
* @param listener the listener.
*/
public void removeNetworkConfigurationChangeListener(
NetworkConfigurationChangeListener listener)
{
if(networkConfigurationWatcher != null)
networkConfigurationWatcher
.removeNetworkConfigurationChangeListener(listener);
}
}

@ -0,0 +1,313 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.netaddr;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import net.java.sip.communicator.service.netaddr.event.*;
import net.java.sip.communicator.util.*;
/**
* Periodically checks the current network interfaces to track changes
* and fire events on those changes.
*
* @author Damian Minkov
*/
public class NetworkConfigurationWatcher
implements Runnable
{
/**
* Our class logger.
*/
private static Logger logger =
Logger.getLogger(NetworkConfigurationWatcher.class);
/**
* Interval between check of network configuration.
*/
private static final int CHECK_INTERVAL = 3000; // 3 sec.
/**
* Listers for network configuration changes.
*/
private final List<NetworkConfigurationChangeListener> listeners =
new ArrayList<NetworkConfigurationChangeListener>();
/**
* Whether current thread is running.
*/
private boolean isRunning = false;
/**
* Adds new <tt>NetworkConfigurationChangeListener</tt> which will
* be informed for network configuration changes.
* @param listener the listener.
*/
void addNetworkConfigurationChangeListener(
NetworkConfigurationChangeListener listener)
{
synchronized(listeners)
{
listeners.add(listener);
}
if(!isRunning)
{
isRunning = true;
new Thread(this).start();
}
}
/**
* Remove <tt>NetworkConfigurationChangeListener</tt>.
* @param listener the listener.
*/
void removeNetworkConfigurationChangeListener(
NetworkConfigurationChangeListener listener)
{
synchronized(listeners)
{
listeners.remove(listener);
}
}
/**
* Main loop of this thread.
*/
public void run()
{
long last = 0;
boolean isAfterStandby = false;
List<NetworkInterface> activeInterfaces =
new ArrayList<NetworkInterface>();
while(isRunning)
{
long curr = System.currentTimeMillis();
if(!isAfterStandby && last != 0)
isAfterStandby = (last + CHECK_INTERVAL + 100 - curr) < 0;
try
{
Enumeration<NetworkInterface> e =
NetworkInterface.getNetworkInterfaces();
boolean networkIsUP = activeInterfaces.size() > 0;
List<NetworkInterface> currentActiveInterfaces =
new ArrayList<NetworkInterface>();
while (e.hasMoreElements())
{
NetworkInterface networkInterface = e.nextElement();
if(isInterfaceLoopback(networkInterface))
continue;
// if interface is up and has some valid(non-local) address
if(isInterfaceUp(networkInterface)
&& hasValidAddress(networkInterface))
{
currentActiveInterfaces.add(networkInterface);
}
}
List<NetworkInterface> inactiveActiveInterfaces =
new ArrayList<NetworkInterface>(activeInterfaces);
inactiveActiveInterfaces.removeAll(currentActiveInterfaces);
// fire that interface has gone down
for (int i = 0; i < inactiveActiveInterfaces.size(); i++)
{
NetworkInterface iface = inactiveActiveInterfaces.get(i);
if(!containsInterfaceWithName(
currentActiveInterfaces, iface.getName()))
{
fireChangeEvent(new ChangeEvent(iface,
ChangeEvent.IFACE_DOWN, isAfterStandby));
activeInterfaces.remove(iface);
}
}
// now we leave with only with the new and up interfaces
// in currentActiveInterfaces list
currentActiveInterfaces.removeAll(activeInterfaces);
// fire that interface has gone up
for (int i = 0; i < currentActiveInterfaces.size(); i++)
{
NetworkInterface iface = currentActiveInterfaces.get(i);
fireChangeEvent(new ChangeEvent(iface,
ChangeEvent.IFACE_UP, isAfterStandby));
activeInterfaces.add(iface);
}
// fire that network has gone up
if(!networkIsUP && activeInterfaces.size() > 0)
{
isAfterStandby = false;
}
last = curr;
} catch (SocketException e)
{
logger.error("Error checking network interfaces", e);
}
synchronized(this)
{
try{
wait(CHECK_INTERVAL);
}
catch (Exception e){}
}
}
}
/**
* Fire ChangeEvent.
* @param evt the event to fire.
*/
private void fireChangeEvent(ChangeEvent evt)
{
synchronized(listeners)
{
for (int i = 0; i < listeners.size(); i++)
{
NetworkConfigurationChangeListener nCChangeListener
= listeners.get(i);
try
{
nCChangeListener.configurationChanged(evt);
} catch (Throwable e)
{
logger.warn("Error delivering event:" + evt + ", to:"
+ nCChangeListener);
}
}
}
}
/**
* Stop current running thread.
*/
void stop()
{
synchronized(this)
{
isRunning = false;
notifyAll();
}
}
/**
* Checks whether the supplied network interface name is in the list.
* @param ifaces the list of interfaces.
* @param name the name to check.
* @return whether name is found in the list of interfaces.
*/
private boolean containsInterfaceWithName(
List<NetworkInterface> ifaces, String name)
{
for (int i = 0; i < ifaces.size(); i++)
{
NetworkInterface networkInterface = ifaces.get(i);
if(networkInterface.getName().equals(name))
return true;
}
return false;
}
/**
* Whether the supplied interface has a valid non-local address.
* @param iface interface.
* @return has a valid address.
*/
private static boolean hasValidAddress(NetworkInterface iface)
{
Enumeration<InetAddress> as =
iface.getInetAddresses();
while (as.hasMoreElements())
{
InetAddress inetAddress = as.nextElement();
if(inetAddress.isLinkLocalAddress())
continue;
return true;
}
return false;
}
/**
* Determines whether or not the <tt>iface</tt> interface is a loopback
* interface. We use this method as a replacement to the
* <tt>NetworkInterface.isLoopback()</tt> method that only comes with
* java 1.6.
*
* @param iface the interface that we'd like to determine as loopback or not.
*
* @return true if <tt>iface</tt> contains at least one loopback address
* and <tt>false</tt> otherwise.
*/
public static boolean isInterfaceLoopback(NetworkInterface iface)
{
try
{
Method method = iface.getClass().getMethod("isLoopback");
return ((Boolean)method.invoke(iface, new Object[]{}))
.booleanValue();
}
catch(Throwable t)
{
//apparently we are not running in a JVM that supports the
//is Loopback method. we'll try another approach.
}
Enumeration<InetAddress> addresses = iface.getInetAddresses();
return addresses.hasMoreElements()
&& addresses.nextElement().isLoopbackAddress();
}
/**
* Determines, if possible, whether or not the <tt>iface</tt> interface is
* up. We use this method so that we could use {@link
* java.net.NetworkInterface}'s <tt>isUp()</tt> when running a JVM that
* supports it and return a default value otherwise.
*
* @param iface the interface that we'd like to determine as Up or Down.
*
* @return <tt>false</tt> if <tt>iface</tt> is known to be down and
* <tt>true</tt> if the <tt>iface</tt> is Up or in case we couldn't
* determine.
*/
public static boolean isInterfaceUp(NetworkInterface iface)
{
try
{
Method method = iface.getClass().getMethod("isUp");
return ((Boolean)method.invoke(iface)).booleanValue();
}
catch(Throwable t)
{
//apparently we are not running in a JVM that supports the
//isUp method. returning default value.
}
return true;
}
}

@ -9,3 +9,4 @@ Import-Package: net.java.sip.communicator.service.configuration,
net.java.sip.communicator.util,
org.osgi.framework,
Export-Package: net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.service.netaddr.event

@ -605,14 +605,6 @@ public void fireRegistrationStateChanged( RegistrationState oldState,
int reasonCode,
String reason)
{
if(newState.equals(RegistrationState.CONNECTION_FAILED) &&
isRegistered())
{
// if for some reason (keep alive failed) and connection is
// still connected disconneted
unregister();
}
lastRegistrationState = newState;
super.fireRegistrationStateChanged(

@ -10,6 +10,7 @@
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.sf.jml.*;
import net.sf.jml.impl.*;
import net.sf.jml.net.*;
@ -25,15 +26,30 @@
public class EventManager
extends SessionAdapter
{
/**
* The class logger.
*/
private static final Logger logger = Logger.getLogger(EventManager.class);
/**
* Whether we are connected.
*/
private boolean connected = false;
/**
* The timer for monitoring connection.
*/
private Timer connectionTimer;
/**
* Event listeners.
*/
private final List<MsnContactListEventListener> listeners
= new Vector<MsnContactListEventListener>();
/**
* The messenger.
*/
private final BasicMessenger msnMessenger;
/**
@ -143,6 +159,11 @@ else if(incoming instanceof IncomingQNG)
}
}
/**
* Called when there was timeout on the connection.
* @param socketSession
* @throws Exception
*/
public void sessionTimeout(Session socketSession) throws Exception
{
Timer connectionTimer;
@ -164,8 +185,11 @@ public void run()
{
if(!connected && msnProvider.isRegistered())
{
msnProvider.unregister(false);
msnProvider.reconnect(SecurityAuthority.CONNECTION_FAILED);
msnProvider.fireRegistrationStateChanged(
msnProvider.getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
RegistrationStateChangeEvent.REASON_NOT_SPECIFIED,
null);
}
}
}, 20000);

@ -22,6 +22,9 @@
public class OperationSetTypingNotificationsMsnImpl
extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceMsnImpl>
{
/**
* This class logger.
*/
private static final Logger logger =
Logger.getLogger(OperationSetTypingNotificationsMsnImpl.class);
@ -31,6 +34,9 @@ public class OperationSetTypingNotificationsMsnImpl
*/
private OperationSetPersistentPresenceMsnImpl opSetPersPresence = null;
/**
* The messenger.
*/
private MsnMessenger messenger = null;
/**
@ -99,12 +105,23 @@ public void sendTypingNotification(Contact notifiedContact, int typingState)
void setMessenger(MsnMessenger messenger)
{
this.messenger = messenger;
messenger.addMessageListener(new TypingListener());
if(messenger != null)
messenger.addMessageListener(new TypingListener());
}
/**
* Listens for typing notifications coming from the protocol.
*/
private class TypingListener
extends MsnAdapter
{
/**
* Control message may indicate typing notification.
* @param switchboard
* @param message
* @param contact
*/
public void controlMessageReceived(MsnSwitchboard switchboard,
MsnControlMessage message,
MsnContact contact)

@ -26,9 +26,15 @@
public class ProtocolProviderServiceMsnImpl
extends AbstractProtocolProviderService
{
/**
* Logger of this class
*/
private static final Logger logger
= Logger.getLogger(ProtocolProviderServiceMsnImpl.class);
/**
* The lib messenger.
*/
private MsnMessenger messenger = null;
/**
@ -46,8 +52,14 @@ public class ProtocolProviderServiceMsnImpl
*/
private SecurityAuthority authority = null;
/**
* Operation set for persistent presence.
*/
private OperationSetPersistentPresenceMsnImpl persistentPresence = null;
/**
* Operation set for typing notifications.
*/
private OperationSetTypingNotificationsMsnImpl typingNotifications = null;
/**
@ -125,6 +137,7 @@ void reconnect(int reasonCode)
/**
* Connects and logins to the server
* @param authority SecurityAuthority
* @param reasonCode
* @throws OperationFailedException if login parameters
* as server port are not correct
*/
@ -234,6 +247,9 @@ void unregister(boolean fireEvent)
{
if((messenger != null) && !logoutReceived)
messenger.logout();
persistentPresence.setMessenger(null);
typingNotifications.setMessenger(null);
}
if(fireEvent)
@ -378,6 +394,10 @@ public void fireRegistrationStateChanged(RegistrationState oldState,
private class MsnConnectionListener
implements MsnMessengerListener
{
/**
* Fired when login has completed.
* @param msnMessenger
*/
public void loginCompleted(MsnMessenger msnMessenger)
{
logger.trace("loginCompleted " + msnMessenger.getActualMsnProtocol());
@ -388,6 +408,10 @@ public void loginCompleted(MsnMessenger msnMessenger)
null);
}
/**
* Fire when lib logs out.
* @param msnMessenger
*/
public void logout(MsnMessenger msnMessenger)
{
logger.trace("logout");
@ -400,6 +424,11 @@ public void logout(MsnMessenger msnMessenger)
}
}
/**
* Fired when an exception has occurred.
* @param msnMessenger
* @param throwable
*/
public void exceptionCaught(MsnMessenger msnMessenger,
Throwable throwable)
{
@ -430,8 +459,6 @@ else if(throwable instanceof SocketException)
}
else if(throwable instanceof UnknownHostException)
{
unregister(false);
fireRegistrationStateChanged(
getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
@ -484,6 +511,22 @@ else if(throwable instanceof MsnProtocolException)
{
logger.error("Error in Msn lib ", throwable);
if(throwable instanceof LoginException)
{
MsnActivator.getProtocolProviderFactory().
storePassword(getAccountID(), null);
fireRegistrationStateChanged(
getRegistrationState(),
RegistrationState.AUTHENTICATION_FAILED,
RegistrationStateChangeEvent
.REASON_AUTHENTICATION_FAILED,
null);
// We try to reconnect and ask user to retype
// password.
reconnect(SecurityAuthority.WRONG_PASSWORD);
}
// We don't want to disconnect on any error, that's why we're
// commenting the following lines for now.
//

@ -70,6 +70,11 @@ public class ServerStoredContactListMsnImpl
private Vector<String> skipAddEvent = new Vector<String>();
/**
* Contact list listener.
*/
ContactListListener contactListListener = null;
/**
* Creates a ServerStoredContactList wrapper for the specified BuddyList.
*
@ -917,7 +922,7 @@ public void contactListInitCompleted(MsnMessenger messenger)
parentOperationSet.earlyStatusesDispatch();
// retreive offline messages
msnProvider.getMessenger().retreiveOfflineMessages();
messenger.retreiveOfflineMessages();
}
public void contactStatusChanged(MsnMessenger messenger,
@ -1268,6 +1273,18 @@ public void loggingFromOtherLocation()
*/
void setMessenger(MsnMessenger messenger)
{
if(messenger == null)
{
if(contactListModManager != null)
contactListModManager.removeModificationListener(
contactListModListenerImpl);
this.contactListModManager = null;
if(contactListListener != null)
this.messenger.removeContactListListener(contactListListener);
this.contactListListener = null;
this.messenger = null;
return;
}
this.messenger = messenger;
contactListModManager =
@ -1276,7 +1293,8 @@ void setMessenger(MsnMessenger messenger)
contactListModManager.
addModificationListener(contactListModListenerImpl);
messenger.addContactListListener(new ContactListListener());
contactListListener = new ContactListListener();
messenger.addContactListListener(contactListListener);
}
/**

@ -23,9 +23,15 @@
public class ProtocolProviderServiceYahooImpl
extends AbstractProtocolProviderService
{
/**
* This class logger.
*/
private static final Logger logger =
Logger.getLogger(ProtocolProviderServiceYahooImpl.class);
/**
* The current yahoo session.
*/
private YahooSession yahooSession = null;
/**
@ -48,8 +54,14 @@ public class ProtocolProviderServiceYahooImpl
*/
private SecurityAuthority authority = null;
/**
* The persistent presence operations set.
*/
private OperationSetPersistentPresenceYahooImpl persistentPresence = null;
/**
* Typing notifications operations set.
*/
private OperationSetTypingNotificationsYahooImpl typingNotifications = null;
/**
@ -58,6 +70,10 @@ public class ProtocolProviderServiceYahooImpl
private ProtocolIconYahooImpl yahooIcon
= new ProtocolIconYahooImpl();
/**
* The connection listener.
*/
private YahooConnectionListener connectionListener = null;
/**
* Returns the state of the registration of this protocol provider
@ -103,7 +119,6 @@ public void register(final SecurityAuthority authority)
* @param authority SecurityAuthority
* @param authReasonCode the authentication reason code, which should
* indicate why are making an authentication request
* @throws XMPPException if we cannot connect to the server - network problem
* @throws OperationFailedException if login parameters
* as server port are not correct
*/
@ -152,7 +167,8 @@ private void connectAndLogin( SecurityAuthority authority,
}
yahooSession = new YahooSession();
yahooSession.addSessionListener(new YahooConnectionListener());
connectionListener = new YahooConnectionListener();
yahooSession.addSessionListener(connectionListener);
try
{
@ -266,6 +282,12 @@ void unregister(boolean fireEvent)
try
{
if(connectionListener != null && yahooSession != null)
{
yahooSession.removeSessionListener(connectionListener);
connectionListener = null;
}
if((yahooSession != null)
&& (yahooSession.getSessionStatus() == StatusConstants.MESSAGING))
yahooSession.logout();
@ -422,8 +444,7 @@ public void fireRegistrationStateChanged( RegistrationState oldState,
int reasonCode,
String reason)
{
if(newState.equals(RegistrationState.UNREGISTERED) ||
newState.equals(RegistrationState.CONNECTION_FAILED))
if(newState.equals(RegistrationState.UNREGISTERED))
{
unregister(false);
yahooSession = null;
@ -441,10 +462,11 @@ private class YahooConnectionListener
{
/**
* Yahoo has logged us off the system, or the connection was lost
**/
*
* @param ev the event
*/
public void connectionClosed(SessionEvent ev)
{
unregister(true);
if(isRegistered())
fireRegistrationStateChanged(
getRegistrationState(),
@ -452,6 +474,10 @@ public void connectionClosed(SessionEvent ev)
RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
}
/**
* Some exception has occurred in stack.
* @param ev
*/
public void inputExceptionThrown(SessionExceptionEvent ev)
{
if(ev.getException() instanceof YMSG9BadFormatException)

@ -0,0 +1,581 @@
/*
* SIP Communicator, 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.reconnectplugin;
import java.net.*;
import java.util.*;
import java.util.ArrayList;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.netaddr.event.*;
import net.java.sip.communicator.service.notification.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* Activates the reconnect plug-in.
*
* @author Damian Minkov
*/
public class ReconnectPluginActivator
implements BundleActivator,
ServiceListener,
NetworkConfigurationChangeListener,
RegistrationStateChangeListener
{
/**
* Logger of this class
*/
private static final Logger logger
= Logger.getLogger(ReconnectPluginActivator.class);
/**
* The current BundleContext.
*/
private static BundleContext bundleContext = null;
/**
* The ui service.
*/
private static UIService uiService;
/**
* Notification service.
*/
private static NotificationService notificationService;
/**
* Network address manager service will inform us for changes in
* network configuration.
*/
private NetworkAddressManagerService networkAddressManagerService = null;
/**
* Holds every protocol provider which is can be reconnected and
* a list of the available and up interfaces when the provider was
* registered. When a provider is unregistered it is removed
* from this collection.
*/
private Map<ProtocolProviderService, List<String>> autoReconnEnabledProviders =
new Hashtable<ProtocolProviderService, List<String>>();
/**
* Holds the currently reconnecting providers and their reconnect tasks.
* When they get connected they are removed from this collection.
*/
private Map<ProtocolProviderService, ReconnectTask> currentlyReconnecting =
new Hashtable<ProtocolProviderService, ReconnectTask>();
/**
* If network is down we save here the providers which need to be reconnected.
*/
private Set<ProtocolProviderService> needsReconnection =
new HashSet<ProtocolProviderService>();
/**
* A list of providers on which we have called unregister. This is a
* way to differ our unregister calls from calls coming from user, wanting
* to stop all reconnects.
*/
private List<ProtocolProviderService> unregisteredProviders
= new ArrayList<ProtocolProviderService>();
/**
* A list of currently connected interfaces. If empty network is down.
*/
private Set<String> connectedInterfaces = new HashSet<String>();
/**
* Timer for scheduling all reconnect operations.
*/
private Timer timer = null;
/**
* Start of the delay interval when starting a reconnect.
*/
private static final int RECONNECT_DELAY_MIN = 8; // sec
/**
* The end of the interval for the initial reconnect.
*/
private static final int RECONNECT_DELAY_MAX = 30; // sec
/**
* Max value for growing the reconnect delay, all subsequent reconnects
* use this maximum delay.
*/
private static final int MAX_RECONNECT_DELAY = 300; // sec
/**
* Network notifications event type.
*/
public static final String NETWORK_NOTIFICATIONS = "NetowrkNotifications";
/**
* Starts this bundle
*
* @param bundleContext BundleContext
* @throws Exception
*/
public void start(BundleContext bundleContext) throws Exception
{
try
{
logger.logEntry();
ReconnectPluginActivator.bundleContext = bundleContext;
}
finally
{
logger.logExit();
}
bundleContext.addServiceListener(this);
if(timer == null)
timer = new Timer("Reconnect timer");
ServiceReference serviceReference = bundleContext.getServiceReference(
NetworkAddressManagerService.class.getName());
this.networkAddressManagerService =
(NetworkAddressManagerService)bundleContext
.getService(serviceReference);
this.networkAddressManagerService
.addNetworkConfigurationChangeListener(this);
ServiceReference[] protocolProviderRefs = null;
try
{
protocolProviderRefs = bundleContext.getServiceReferences(
ProtocolProviderService.class.getName(), null);
}
catch (InvalidSyntaxException ex)
{
// this shouldn't happen since we're providing no parameter string
// but let's log just in case.
logger.error(
"Error while retrieving service refs", ex);
return;
}
// in case we found any
if (protocolProviderRefs != null)
{
logger.debug("Found "
+ protocolProviderRefs.length
+ " already installed providers.");
for (int i = 0; i < protocolProviderRefs.length; i++)
{
ProtocolProviderService provider
= (ProtocolProviderService) bundleContext
.getService(protocolProviderRefs[i]);
this.handleProviderAdded(provider);
}
}
}
/**
* Stop the bundle. Nothing to stop for now.
* @param bundleContext
* @throws Exception
*/
public void stop(BundleContext bundleContext)
throws Exception
{
if(timer != null)
{
timer.cancel();
timer = null;
}
}
/**
* Returns the <tt>UIService</tt> obtained from the bundle context.
*
* @return the <tt>UIService</tt> obtained from the bundle context
*/
public static UIService getUIService()
{
if (uiService == null)
{
ServiceReference uiReference =
bundleContext.getServiceReference(UIService.class.getName());
uiService =
(UIService) bundleContext
.getService(uiReference);
}
return uiService;
}
/**
* Returns the <tt>NotificationService</tt> obtained from the bundle context.
*
* @return the <tt>NotificationService</tt> obtained from the bundle context
*/
public static NotificationService getNotificationService()
{
if (notificationService == null)
{
ServiceReference serviceReference = bundleContext
.getServiceReference(NotificationService.class.getName());
notificationService = (NotificationService) bundleContext
.getService(serviceReference);
notificationService.registerDefaultNotificationForEvent(
NETWORK_NOTIFICATIONS,
NotificationService.ACTION_POPUP_MESSAGE,
null,
null);
}
return notificationService;
}
/**
* When new protocol provider 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
if (serviceRef.getBundle().getState() == Bundle.STOPPING)
{
return;
}
Object sService = bundleContext.getService(serviceRef);
logger.trace("Received a service event for: " +
sService.getClass().getName());
if(sService instanceof NetworkAddressManagerService)
{
switch (serviceEvent.getType())
{
case ServiceEvent.REGISTERED:
if(this.networkAddressManagerService != null)
break;
this.networkAddressManagerService =
(NetworkAddressManagerService)sService;
networkAddressManagerService
.addNetworkConfigurationChangeListener(this);
break;
case ServiceEvent.UNREGISTERING:
((NetworkAddressManagerService)sService)
.removeNetworkConfigurationChangeListener(this);
break;
}
return;
}
// we don't care if the source service is not a protocol provider
if (!(sService instanceof ProtocolProviderService))
return;
logger.debug("Service is a protocol provider.");
switch (serviceEvent.getType())
{
case ServiceEvent.REGISTERED:
this.handleProviderAdded((ProtocolProviderService)sService);
break;
case ServiceEvent.UNREGISTERING:
this.handleProviderRemoved( (ProtocolProviderService) sService);
break;
}
}
/**
* Add listeners to newly registered protocols.
*
* @param provider ProtocolProviderService
*/
private void handleProviderAdded(ProtocolProviderService provider)
{
logger.debug("Adding protocol provider " + provider.getProtocolName());
if(provider instanceof ProtocolProviderService)
{
provider.addRegistrationStateChangeListener(this);
}
}
/**
* Stop listening for events as the provider is removed.
*
* @param provider the ProtocolProviderService that has been unregistered.
*/
private void handleProviderRemoved(ProtocolProviderService provider)
{
if(provider instanceof ProtocolProviderService)
{
provider.removeRegistrationStateChangeListener(this);
}
}
/**
* Fired when a change has occurred in the computer network configuration.
*
* @param event the change event.
*/
public synchronized void configurationChanged(ChangeEvent event)
{
if(!(event.getSource() instanceof NetworkInterface))
return;
NetworkInterface iface = (NetworkInterface)event.getSource();
if(event.getType() == ChangeEvent.IFACE_UP)
{
// no connection so one is up, lets connect
if(connectedInterfaces.size() == 0)
{
Iterator<ProtocolProviderService> iter =
needsReconnection.iterator();
while (iter.hasNext())
{
ProtocolProviderService pp = iter.next();
if(currentlyReconnecting.containsKey(pp))
{
// now lets cancel it and schedule it again
// so it will use this iface
currentlyReconnecting.get(pp).cancel();
currentlyReconnecting.remove(pp);
}
reconnect(pp);
}
needsReconnection.clear();
}
connectedInterfaces.add(iface.getName());
}
else if(event.getType() == ChangeEvent.IFACE_DOWN)
{
connectedInterfaces.remove(iface.getName());
// one is down and at least one more is connected
if(connectedInterfaces.size() > 0)
{
// lets reconnect all that was connected when this one was
// available, cause they maybe using it
Iterator<Map.Entry<ProtocolProviderService, List<String>>> iter =
autoReconnEnabledProviders.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry<ProtocolProviderService, List<String>> entry
= iter.next();
if(entry.getValue().contains(iface.getName()))
{
ProtocolProviderService pp = entry.getKey();
// hum someone is reconnecting, lets cancel and
// schedule it again
if(currentlyReconnecting.containsKey(pp))
{
currentlyReconnecting.get(pp).cancel();
currentlyReconnecting.remove(pp);
}
reconnect(pp);
}
}
}
else
{
// we must disconnect every pp and put all to be need of reconnecting
needsReconnection.addAll(autoReconnEnabledProviders.keySet());
Iterator<ProtocolProviderService> iter =
needsReconnection.iterator();
while (iter.hasNext())
{
ProtocolProviderService pp = iter.next();
try
{
unregisteredProviders.add(pp);
pp.unregister();
} catch (Exception e)
{
logger.error("Cannot unregister provider", e);
}
}
connectedInterfaces.clear();
logger.trace("Network is down!");
getNotificationService().fireNotification(
NETWORK_NOTIFICATIONS,
"Network is down!",
"",
null,
null);
}
}
}
/**
* The method is called by a <code>ProtocolProviderService</code>
* implementation whenever a change in the registration state of the
* corresponding provider had occurred.
*
* @param evt the event describing the status change.
*/
public synchronized void registrationStateChanged(RegistrationStateChangeEvent evt)
{
// we don't care about protocol providers that don't support
// reconnection
if(!(evt.getSource() instanceof ProtocolProviderService))
return;
ProtocolProviderService pp = (ProtocolProviderService)evt.getSource();
if(evt.getNewState().equals(RegistrationState.CONNECTION_FAILED))
{
// if this pp is already in needsReconnection, it means
// we got conn failed cause the pp has tried to unregister
// with sending network packet
// but this unregister is scheduled from us so skip
if(needsReconnection.contains(pp))
return;
if(connectedInterfaces.size() == 0)
needsReconnection.add(pp);
else
{
// network is up but something happen and cannot reconnect
// strange lets try again after some time
reconnect(pp);
}
// unregister can finish and with connection failed,
// the protocol is unable to unregister
unregisteredProviders.remove(pp);
}
else if(evt.getNewState().equals(RegistrationState.REGISTERED))
{
autoReconnEnabledProviders.put(
(ProtocolProviderService)evt.getSource(),
new ArrayList<String>(connectedInterfaces));
currentlyReconnecting.remove(pp);
}
else if(evt.getNewState().equals(RegistrationState.UNREGISTERED))
{
autoReconnEnabledProviders.remove(
(ProtocolProviderService)evt.getSource());
if(!unregisteredProviders.contains(pp)
&& currentlyReconnecting.containsKey(pp))
{
currentlyReconnecting.remove(pp).cancel();
}
unregisteredProviders.remove(pp);
}
}
/**
* Method to schedule a reconnect for a protocol provider.
* @param pp the provider.
*/
private void reconnect(ProtocolProviderService pp)
{
long delay;
if(currentlyReconnecting.containsKey(pp))
{
delay = currentlyReconnecting.get(pp).delay;
// we never stop trying
//if(delay == MAX_RECONNECT_DELAY*1000)
// return;
delay = Math.min(delay * 2, MAX_RECONNECT_DELAY*1000);
}
else
{
delay = (long)(RECONNECT_DELAY_MIN
+ Math.random() * RECONNECT_DELAY_MAX)*1000;
}
// as we will reconnect, lets unregister
try
{
unregisteredProviders.add(pp);
pp.unregister();
} catch (OperationFailedException e)
{
logger.error("Cannot unregister provider", e);
}
ReconnectTask task = new ReconnectTask(pp);
task.delay = delay;
currentlyReconnecting.put(pp, task);
logger.trace("Reconnect " + pp + " after " + delay + " ms.");
timer.schedule(task, delay);
}
/**
* The task executed by the timer when time for reconnect comes.
*/
private class ReconnectTask
extends TimerTask
{
/**
* The provider to reconnect.
*/
private ProtocolProviderService provider;
/**
* The delay with which was this task scheduled.
*/
private long delay;
/**
* Creates the task.
* @param provider
*/
public ReconnectTask(ProtocolProviderService provider)
{
this.provider = provider;
}
/**
* Reconnects the provider.
*/
public void run()
{
try
{
logger.trace("Start reconnecting!");
provider.register(
getUIService().getDefaultSecurityAuthority(provider));
} catch (OperationFailedException ex)
{
logger.error("cannot reregister provider will keep going", ex);
}
}
}
}

@ -0,0 +1,17 @@
Bundle-Activator: net.java.sip.communicator.plugin.reconnectplugin.ReconnectPluginActivator
Bundle-Name: ReconnectPlugin
Bundle-Description: A bundle that implements the Reconnect Plugin Package.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.service.netaddr.event,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.notification,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
net.java.sip.communicator.util.swing

@ -9,6 +9,8 @@
import java.net.*;
import java.io.*;
import net.java.sip.communicator.service.netaddr.event.*;
/**
* The NetworkAddressManagerService takes care of problems such as
* @author Emil Ivov
@ -119,4 +121,19 @@ public DatagramSocket createDatagramSocket(InetAddress laddr,
IOException,
BindException;
/**
* Adds new <tt>NetworkConfigurationChangeListener</tt> which will
* be informed for network configuration changes.
* @param listener the listener.
*/
public void addNetworkConfigurationChangeListener(
NetworkConfigurationChangeListener listener);
/**
* Remove <tt>NetworkConfigurationChangeListener</tt>.
* @param listener the listener.
*/
public void removeNetworkConfigurationChangeListener(
NetworkConfigurationChangeListener listener);
}

@ -0,0 +1,100 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.netaddr.event;
/**
* A ChangeEvent is fired on change of the network configuration of the computer.
*
* @author Damian Minkov
*/
public class ChangeEvent
extends java.util.EventObject
{
/**
* Event type for interface going up.
*/
public static final int IFACE_DOWN = 0;
/**
* Event type for interface going down.
*/
public static final int IFACE_UP = 1;
/**
* The type of the current event.
*/
private int type = -1;
/**
* Whether this event is after computer have been suspended.
*/
private boolean standby = false;
/**
* Creates event.
* @param source the source of the event.
* @param type the type of the event.
*/
public ChangeEvent(Object source, int type)
{
super(source);
this.type = type;
}
/**
* Creates event.
* @param source the source of the event.
* @param type the type of the event.
* @param standby is the event after a suspend of the computer.
*/
public ChangeEvent(Object source, int type, boolean standby)
{
this(source, type);
this.standby = standby;
}
/**
* The type of this event.
* @return the type
*/
public int getType()
{
return type;
}
/**
* Whether this event is after suspend of the computer.
* @return the standby
*/
public boolean isStandby()
{
return standby;
}
/**
* Overrides toString method.
* @return string representing the event.
*/
@Override
public String toString()
{
StringBuilder buff = new StringBuilder();
buff.append("ChangeEvent ");
switch(type)
{
case IFACE_DOWN: buff.append("Interface down"); break;
case IFACE_UP: buff.append("Interface up"); break;
}
buff.append(", standby=" + standby);
return buff.toString();
}
}

@ -0,0 +1,22 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.netaddr.event;
/**
* Listens for network changes in the computer configuration.
*
* @author Damian Minkov
*/
public interface NetworkConfigurationChangeListener
{
/**
* Fired when a change has occurred in the computer network configuration.
*
* @param event the change event.
*/
public void configurationChanged(ChangeEvent event);
}
Loading…
Cancel
Save