From 326a24d8434e4bb3064036a8c0294b870027c8ca Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Mon, 12 Sep 2011 13:35:50 +0000 Subject: [PATCH] Detects ip changes (not link changes) under macosx. Fixes a problem with reconnecting several tcp/tls sip providers. --- .../mac/libsysactivitynotifications.jnilib | Bin 74208 -> 74208 bytes ..._sysactivity_SystemActivityNotifications.m | 11 +- .../netaddr/NetworkConfigurationWatcher.java | 13 +- .../jabber/debugger/SmackPacketDebugger.java | 3 +- .../protocol/sip/ProtocolIconSipImpl.java | 3 + .../impl/protocol/sip/SipStackSharing.java | 153 +++++++++++++++++- .../protocol/sip/sip.provider.manifest.mf | 1 + .../ResourceManagementServiceImpl.java | 2 +- .../DefaultResourcePackActivator.java | 4 +- .../ReconnectPluginActivator.java | 88 ++++++++-- 10 files changed, 246 insertions(+), 32 deletions(-) diff --git a/lib/native/mac/libsysactivitynotifications.jnilib b/lib/native/mac/libsysactivitynotifications.jnilib index ef0cf71c4499ee61d037cdf259dd0e375ce2d18c..a163c34f24a8e0c38116507161452c499b1a03be 100644 GIT binary patch delta 109 zcmaEGnB~D?mJJQ!0!t301YO9zG%4<=SYh(&tfb8o#GlBqcm|Z|ZLTnjaS&j9pq{rd ztmoaajQJjGwcpijep0qa7%V@z#;jwrPPc-$z)N+u1577eEZVQ%Rpp9ZoVq#Y+8Hs3 K89Hybm;eBxlrTd8 delta 109 zcmaEGnB~D?mJJQ!0s=>5Cb!vr?BPyvm?HG^i{0i4;!osQd@}R0H&>X&I0z*FIe9>B zvXn(eINw%1jo*2jpOozp2Fp*bG3(f@)2-kwkpE}*{~f|!{Wta<-eRq!VzN2r+8Hs3 K89Hybm;eA{xG>QG diff --git a/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.m b/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.m index 20ee383f3..d9e2ce6a6 100644 --- a/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.m +++ b/src/native/sysactivity/net_java_sip_communicator_impl_sysactivity_SystemActivityNotifications.m @@ -215,7 +215,7 @@ void scCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) &context); const CFStringRef keys[1] = { - CFSTR("State:/Network/Interface/.*/Link") + CFSTR("State:/Network/Interface/.*/IPv.") }; CFArrayRef watchedKeys = CFArrayCreate(kCFAllocatorDefault, (const void **)keys, @@ -226,7 +226,8 @@ void scCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) watchedKeys)) { CFRelease(watchedKeys); - fprintf(stderr, "SCDynamicStoreSetNotificationKeys() failed: %s", SCErrorString(SCError())); + fprintf(stderr, "SCDynamicStoreSetNotificationKeys() failed: %s", + SCErrorString(SCError())); CFRelease(dynStore); dynStore = NULL; @@ -235,8 +236,10 @@ void scCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) CFRelease(watchedKeys); - rlSrc = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, dynStore, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSrc, kCFRunLoopDefaultMode); + rlSrc = SCDynamicStoreCreateRunLoopSource( + kCFAllocatorDefault, dynStore, 0); + CFRunLoopAddSource( + CFRunLoopGetCurrent(), rlSrc, kCFRunLoopDefaultMode); CFRelease(rlSrc); } } diff --git a/src/net/java/sip/communicator/impl/netaddr/NetworkConfigurationWatcher.java b/src/net/java/sip/communicator/impl/netaddr/NetworkConfigurationWatcher.java index b3c438cdc..172841e9f 100644 --- a/src/net/java/sip/communicator/impl/netaddr/NetworkConfigurationWatcher.java +++ b/src/net/java/sip/communicator/impl/netaddr/NetworkConfigurationWatcher.java @@ -173,6 +173,9 @@ private void initialFireEvents( private void handleNewSystemActivityNotificationsService( SystemActivityNotificationsService newService) { + if(newService == null) + return; + this.systemActivityNotificationsService = newService; if(this.systemActivityNotificationsService @@ -397,16 +400,6 @@ public void activityChanged(SystemActivityEvent event) } else if(event.getEventID() == SystemActivityEvent.EVENT_NETWORK_CHANGE) { - // when there is a net change - // give time for devices to come up/down fully - // before checking with them - synchronized(this) - { - try{ - wait(3000); - }catch(InterruptedException ex){} - } - try { checkNetworkInterfaces(true, 0); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java b/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java index e3747d04c..491006c5c 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java @@ -136,7 +136,8 @@ public void processPacket(Packet packet) try { if(packetLogging.isLoggingEnabled( - PacketLoggingService.ProtocolName.JABBER)) + PacketLoggingService.ProtocolName.JABBER) + && packet != null && connection.getSocket() != null) { packetLogging.logPacket( PacketLoggingService.ProtocolName.JABBER, diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolIconSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolIconSipImpl.java index 5466a5020..5b195d3f4 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolIconSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolIconSipImpl.java @@ -185,6 +185,9 @@ public static byte[] loadIcon(String imagePath) { InputStream is = resources.getImageInputStreamForPath(imagePath); + if(is == null) + return null; + try { icon = new byte[is.available()]; diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java index f331064a0..5ac203cab 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java @@ -18,7 +18,9 @@ import javax.sip.header.*; import javax.sip.message.*; +import net.java.sip.communicator.service.netaddr.event.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; /** @@ -35,7 +37,8 @@ * @author Sebastien Mazy */ public class SipStackSharing - implements SipListener + implements SipListener, + NetworkConfigurationChangeListener { /** * We set a custom parameter in the contact address for registrar accounts, @@ -116,6 +119,9 @@ public class SipStackSharing new AddressResolverImpl(); ((SIPTransactionStack) this.stack) .setAddressResolver(addressResolver); + + SipActivator.getNetworkAddressManagerService() + .addNetworkConfigurationChangeListener(this); } catch(Exception ex) { @@ -1187,4 +1193,149 @@ private boolean applyNonConformanceHacks(RequestEvent event) return false; } + + /** + * List of currently waiting timers that will monitor the protocol provider + * + */ + Map resetListeningPointsTimers + = new HashMap(); + + /** + * Listens for network changes and if we have a down interface + * and we have a tcp/tls provider which is staying for 20 seconds in + * unregistering state, it cannot unregister cause its using the old + * address which is currently down, and we must recreate its listening + * points so it can further reconnect. + * + * @param event the change event. + */ + public void configurationChanged(ChangeEvent event) + { + if(event.isInitial()) + return; + + if(event.getType() == ChangeEvent.ADDRESS_DOWN) + { + for(final ProtocolProviderServiceSipImpl pp : listeners) + { + if(pp.getRegistrarConnection().getTransport() != null + && (pp.getRegistrarConnection().getTransport() + .equals(ListeningPoint.TCP) + || pp.getRegistrarConnection().getTransport() + .equals(ListeningPoint.TLS))) + { + ResetListeningPoint reseter; + synchronized(resetListeningPointsTimers) + { + // we do this only once for transport + if(resetListeningPointsTimers.containsKey( + pp.getRegistrarConnection().getTransport())) + continue; + + reseter = new ResetListeningPoint(pp); + resetListeningPointsTimers.put( + pp.getRegistrarConnection().getTransport(), + reseter); + } + pp.addRegistrationStateChangeListener(reseter); + } + } + } + } + + /** + * If a tcp(tls) provider stays unregistering for a long time after + * connection changed most probably it won't get registered after + * unregistering fails, cause underlying listening point are conncted + * to wrong interfaces. So we will replace them. + */ + private class ResetListeningPoint + extends TimerTask + implements RegistrationStateChangeListener + { + /** + * The time we wait before checking is the provider still unregistering. + */ + private static final int TIME_FOR_PP_TO_UNREGISTER = 20000; + + /** + * The protocol provider we are checking. + */ + private final ProtocolProviderServiceSipImpl protocolProvider; + + /** + * Constructs this task. + * @param pp + */ + ResetListeningPoint(ProtocolProviderServiceSipImpl pp) + { + this.protocolProvider = pp; + } + + /** + * Notified when registration state changed for a provider. + * @param evt + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if(evt.getNewState() == RegistrationState.UNREGISTERING) + { + new Timer().schedule(this, TIME_FOR_PP_TO_UNREGISTER); + } + else + { + protocolProvider.removeRegistrationStateChangeListener(this); + resetListeningPointsTimers.remove( + protocolProvider.getRegistrarConnection().getTransport()); + } + } + + /** + * The real task work, replace listening point. + */ + public void run() + { + // if the provider is still unregistering it most probably won't + // successes until we re-init the LP + if(protocolProvider.getRegistrationState() + == RegistrationState.UNREGISTERING) + { + String transport = protocolProvider.getRegistrarConnection() + .getTransport(); + + ListeningPoint old = getLP(transport); + + try + { + stack.deleteListeningPoint(old); + } + catch(Throwable t) + { + logger.warn("Error replacing ListeningPoint for " + + transport, t); + } + + try + { + ListeningPoint tcpLP = + stack.createListeningPoint( + NetworkUtils.IN_ADDR_ANY + , transport.equals(ListeningPoint.TCP)? + getPreferredClearPort(): getPreferredSecurePort() + , transport); + clearJainSipProvider.addListeningPoint(tcpLP); + } + catch(Throwable t) + { + logger.warn("Error replacing ListeningPoint for " + + protocolProvider.getRegistrarConnection().getTransport(), + t); + } + } + + resetListeningPointsTimers.remove( + protocolProvider.getRegistrarConnection().getTransport()); + } + } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf index aff5a65fb..95d72dcac 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf @@ -46,6 +46,7 @@ Import-Package: org.apache.log4j, net.java.sip.communicator.service.neomedia.format, net.java.sip.communicator.service.hid, net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.service.netaddr.event, net.java.sip.communicator.service.packetlogging, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.protocol.event, diff --git a/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java b/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java index 653f4eb5d..f2ef138a6 100644 --- a/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java +++ b/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java @@ -502,7 +502,7 @@ public InputStream getImageInputStreamForPath(String path) } } - if (path != null) + if (path != null && imagePack != null) return imagePack.getClass().getClassLoader() .getResourceAsStream(path); diff --git a/src/net/java/sip/communicator/plugin/defaultresourcepack/DefaultResourcePackActivator.java b/src/net/java/sip/communicator/plugin/defaultresourcepack/DefaultResourcePackActivator.java index c14367b35..96b874eca 100644 --- a/src/net/java/sip/communicator/plugin/defaultresourcepack/DefaultResourcePackActivator.java +++ b/src/net/java/sip/communicator/plugin/defaultresourcepack/DefaultResourcePackActivator.java @@ -71,7 +71,7 @@ public void start(BundleContext bc) throws Exception new DefaultSettingsPackImpl(); Hashtable setProps = new Hashtable(); - langProps.put(ResourcePack.RESOURCE_NAME, + setProps.put(ResourcePack.RESOURCE_NAME, SettingsPack.RESOURCE_NAME_DEFAULT_VALUE); bundleContext.registerService( SettingsPack.class.getName(), @@ -82,7 +82,7 @@ public void start(BundleContext bc) throws Exception new DefaultSoundPackImpl(); Hashtable sndProps = new Hashtable(); - langProps.put(ResourcePack.RESOURCE_NAME, + sndProps.put(ResourcePack.RESOURCE_NAME, SoundPack.RESOURCE_NAME_DEFAULT_VALUE); bundleContext.registerService( SoundPack.class.getName(), diff --git a/src/net/java/sip/communicator/plugin/reconnectplugin/ReconnectPluginActivator.java b/src/net/java/sip/communicator/plugin/reconnectplugin/ReconnectPluginActivator.java index bd8fabe93..2602439c4 100644 --- a/src/net/java/sip/communicator/plugin/reconnectplugin/ReconnectPluginActivator.java +++ b/src/net/java/sip/communicator/plugin/reconnectplugin/ReconnectPluginActivator.java @@ -434,7 +434,7 @@ public synchronized void configurationChanged(ChangeEvent event) reconnect(pp); } - + needsReconnection.clear(); } @@ -491,7 +491,8 @@ else if(event.getType() == ChangeEvent.IFACE_DOWN) currentlyReconnecting.remove(pp).cancel(); } - unregister(pp); + // don't reconnect just unregister if needed. + unregister(pp, false, null, null); } connectedInterfaces.clear(); @@ -514,8 +515,16 @@ else if(event.getType() == ChangeEvent.IFACE_DOWN) * Unregisters the ProtocolProvider. Make sure to do it in separate thread * so we don't block other processing. * @param pp the protocol provider to unregister. - */ - private void unregister(final ProtocolProviderService pp) + * @param reconnect if the protocol provider does not need unregistering + * shall we trigger reconnect. Its true when call called from + * reconnect. + * @param listener the listener used in reconnect method. + * @param task the task to use for reconnection. + */ + private void unregister(final ProtocolProviderService pp, + final boolean reconnect, + final RegistrationStateChangeListener listener, + final ReconnectTask task) { unregisteredProviders.add(pp); @@ -535,7 +544,26 @@ public void run() RegistrationState.UNREGISTERED) || pp.getRegistrationState().equals( RegistrationState.CONNECTION_FAILED)) + { + if(reconnect) + { + if(listener != null) + pp.removeRegistrationStateChangeListener( + listener); + + if(timer == null || task == null) + return; + + currentlyReconnecting.put(pp, task); + + if (logger.isTraceEnabled()) + logger.trace("Reconnect " + pp + + " after " + task.delay + " ms."); + + timer.schedule(task, task.delay); + } return; + } pp.unregister(); } @@ -713,7 +741,7 @@ else if(evt.getNewState().equals(RegistrationState.UNREGISTERED)) * Method to schedule a reconnect for a protocol provider. * @param pp the provider. */ - private void reconnect(ProtocolProviderService pp) + private void reconnect(final ProtocolProviderService pp) { long delay; @@ -733,16 +761,50 @@ private void reconnect(ProtocolProviderService pp) + Math.random() * RECONNECT_DELAY_MAX)*1000; } - // as we will reconnect, lets unregister - unregister(pp); - - ReconnectTask task = new ReconnectTask(pp); + final ReconnectTask task = new ReconnectTask(pp); task.delay = delay; - currentlyReconnecting.put(pp, task); - if (logger.isTraceEnabled()) - logger.trace("Reconnect " + pp + " after " + delay + " ms."); - timer.schedule(task, delay); + // start registering after the pp has unregistered + RegistrationStateChangeListener listener = + new RegistrationStateChangeListener() + { + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if(evt.getSource() instanceof ProtocolProviderService) + { + if(evt.getNewState().equals( + RegistrationState.UNREGISTERED) + || evt.getNewState().equals( + RegistrationState.CONNECTION_FAILED)) + { + synchronized(this) + { + pp.removeRegistrationStateChangeListener(this); + + if(timer == null) + return; + + currentlyReconnecting.put(pp, task); + + if (logger.isTraceEnabled()) + logger.trace("Reconnect " + pp + + " after " + task.delay + " ms."); + + timer.schedule(task, task.delay); + } + } + else if(evt.getNewState().equals( + RegistrationState.REGISTERED)) + { + pp.removeRegistrationStateChangeListener(this); + } + } + } + }; + pp.addRegistrationStateChangeListener(listener); + + // as we will reconnect, lets unregister + unregister(pp, true, listener, task); } /**