Implements support for launch parameters

Implements support for handling SIP URIs as launch parameters
Adds a lock mechanism to prevent from running multiple instances of SIP Communicator
Adds a mechanism for a second instance of SC to pass its launch 
    parameters to a one that's already running.
Adds a DefaultSecurityAuthority class (in use by the systray and SIP URI handler)
cusax-fix
Emil Ivov 17 years ago
parent 45288c7024
commit 88a0036d18

@ -514,6 +514,12 @@
<target name="run" depends="-deploy-os-specific-bundles"
description="Starts felix and runs sip-comunicator gui (use latest build).">
<!-- we allow users to pass command line args using the "args" system
property. However we need to manually set tha prop to an empty
string here or otherwise the application would get an argument with
the value ${args}-->
<property name="args" value=""/>
<!-- forking prevents from debugging -->
<java classname="net.java.sip.communicator.launcher.SIPCommunicator"
fork="true"
@ -550,6 +556,9 @@
<env key="LD_LIBRARY_PATH" path="${ld.library.path}"/>
<env key="PATH" path="${path}"/>
<env key="DYLD_LIBRARY_PATH" path="${dyld.library.path}"/>
<!-- pass to SC args that have been specified by the user -->
<arg line="${args}"/>
</java>
</target>
@ -623,7 +632,7 @@
</target>
<!-- - - - - - - - - - - - - - BUNDLE BUILDING TARGETS - - - - - - - - - -->
<!-- - - - - - - - - - - - - - BUNDLE BUILDING TARGETS - - - - - - - - -->
<!--ALL BUNDLES-->
<target name="bundles"
depends="bundle-sc-launcher,bundle-util,bundle-configuration,bundle-configuration-slick,
@ -659,7 +668,8 @@
bundle-updatecheckplugin,
bundle-dict,bundle-plugin-dictaccregwizz,
bundle-plugin-simpleaccreg,bundle-plugin-generalconfig,
bundle-plugin-googletalkaccregwizz"/>
bundle-plugin-googletalkaccregwizz,bundle-argdelegation-service,
bundle-argdelegation"/>
<!--BUNDLE-SC-LAUNCHER-->
<target name="bundle-sc-launcher">
@ -754,7 +764,15 @@
</target>
<!--BUNDLE-UTIL-->
<target name="bundle-util">
<target name="bundle-util" depends="version">
<!-- Create a properties file that the arg handler could use
to determine SC's version -->
<echo file="${dest}/net/java/sip/communicator/util/launchutils/version.properties"
message="APPLICATION_NAME=SIP Communicator${line.separator}" />
<echo file="${dest}/net/java/sip/communicator/util/launchutils/version.properties"
message="APPLICATION_VERSION=${sip-communicator.version}${line.separator}"
append="true"/>
<!-- Create the util.jar-->
<jar compress="false" destfile="${bundles.dest}/util.jar"
manifest="${src}/net/java/sip/communicator/util/util.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/util"
@ -1685,7 +1703,7 @@ javax.swing.event, javax.swing.border"/>
</jar>
</target>
<!-- BUNDLE-JFontChooser -->
<target name="bundle-jfontchooserlib">
<!-- Creates a bundle containing the jfontchooser lib.-->
<jar compress="false" destfile="${bundles.dest}/jfontchooserlib.jar"
@ -1693,4 +1711,24 @@ javax.swing.event, javax.swing.border"/>
<zipfileset src="${lib.noinst}/jfontchooser-1.0.5.jar" prefix=""/>
</jar>
</target>
<!-- BUNDLE Arg Delegation Service -->
<target name="bundle-argdelegation-service">
<!-- Creates a bundle for the notifications.-->
<jar compress="false" destfile="${bundles.dest}/argdelegation-service.jar"
manifest="${src}/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/service/argdelegation/"
prefix="net/java/sip/communicator/service/argdelegation"/>
</jar>
</target>
<!-- BUNDLE Arg Delegation Implementation-->
<target name="bundle-argdelegation">
<!-- Creates a bundle for the notifications.-->
<jar compress="false" destfile="${bundles.dest}/argdelegation.jar"
manifest="${src}/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/impl/argdelegation/"
prefix="net/java/sip/communicator/impl/argdelegation/"/>
</jar>
</target>
</project>

@ -41,7 +41,8 @@ org.osgi.framework.system.packages= org.osgi.framework; ; version=1.3.0, \
org.xml.sax.helpers; \
javax.crypto; \
javax.crypto.spec; \
javax.crypto.interfaces;
javax.crypto.interfaces; \
net.java.sip.communicator.util.launchutils
felix.auto.start.10= reference:file:lib/bundle/org.apache.felix.bundlerepository-1.0.0.jar \
reference:file:lib/bundle/org.apache.felix.servicebinder-0.9.0-SNAPSHOT.jar
@ -68,7 +69,8 @@ felix.auto.start.42= \
reference:file:sc-bundles/defaultresources.jar
felix.auto.start.45= \
reference:file:sc-bundles/ui-service.jar \
reference:file:sc-bundles/ui-service.jar \
reference:file:sc-bundles/argdelegation-service.jar \
reference:file:sc-bundles/version.jar \
reference:file:sc-bundles/version-impl.jar \
reference:file:sc-bundles/branding.jar
@ -130,7 +132,6 @@ felix.auto.start.67= \
reference:file:sc-bundles/contactinfo.jar \
reference:file:sc-bundles/accountinfo.jar \
reference:file:sc-bundles/chatalerter.jar \
reference:file:sc-bundles/shutdown.jar \
reference:file:sc-bundles/autoaway.jar \
reference:file:sc-bundles/keybindingChooser.jar \
reference:file:sc-bundles/generalconfig.jar \
@ -139,6 +140,12 @@ felix.auto.start.67= \
felix.auto.start.70= \
reference:file:sc-bundles/simpleaccreg.jar
felix.auto.start.75= \
reference:file:sc-bundles/argdelegation.jar
felix.auto.start.80= \
reference:file:sc-bundles/shutdown.jar
# Uncomment the following lines if you want to run the architect viewer
# bundle.
#oscar.auto.start.100= \

@ -43,7 +43,7 @@ net.java.sip.communicator.util.FileHandler.level = FINEST
# Limit the message that are printed on the console to FINEST and above (all).
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.level = WARNING
java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter
@ -61,20 +61,7 @@ ymsg.network.level = INFO
net.sf.cindy.impl.level = INFO
# But we want everything coming from the sip-comm
net.java.sip.communicator.impl.level = INFO
net.java.sip.communicator.impl.protocol.level = INFO
net.java.sip.communicator.impl.shutdown.level = INFO
net.java.sip.communicator.impl.contactlist.level = INFO
net.java.sip.communicator.slick.level = INFO
net.java.sip.communicator.impl.level = INFO
net.java.sip.communicator.service.level = INFO
net.java.sip.communicator.util.level = INFO
net.java.sip.communicator.service.configuration.level = INFO
net.java.sip.communicator.impl.configuration.level = INFO
net.java.sip.communicator.impl.history.level = INFO
net.java.sip.communicator.impl.gui.level = INFO
net.java.sip.communicator.impl.protocol.zeroconf.jmdns.level = WARNING
net.java.sip.communicator.impl.media.level = WARNING
net.java.sip.communicator.level = WARNING
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:

@ -2,19 +2,7 @@
# environment when running automated testing.
# A list of all tests that should be run by the test target of the project.
net.java.sip.communicator.slick.runner.TEST_LIST=ConfigurationServiceLick \
MetaContactListServiceLick \
NetworkAddressManagerServiceLick \
FileAccessServiceLick \
HistoryServiceLick \
SlicklessTests \
MsgHistoryServiceLick \
CallHistoryServiceLick \
JabberProtocolProviderSlick \
YahooProtocolProviderSlick \
MsnProtocolProviderSlick \
GibberishProtocolProviderServiceLick \
RssProtocolProviderServiceLick
net.java.sip.communicator.slick.runner.TEST_LIST=JabberProtocolProviderSlick
# MediaServiceLick \
# SipProtocolProviderServiceLick \

@ -0,0 +1,94 @@
/*
* 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.argdelegation;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.version.*;
import net.java.sip.communicator.util.launchutils.*;
import org.osgi.framework.*;
/**
* Activates the <tt>ArgDelegationService</tt> and registers a URI delegation
* peer with the util package arg manager so that we would be notified when the
* application receives uri arguments.
*
* @author Emil Ivov
*/
public class ArgDelegationActivator
implements BundleActivator
{
/**
* A reference to the bundle context that is currently in use.
*/
private static BundleContext bundleContext = null;
/**
* A reference to the delegation peer implementation that is currently
* handling uri arguments.
*/
private UriDelegationPeerImpl delegationPeer = null;
/**
* A reference to the <tt>UIService</tt> currently in use in
* SIP Communicator.
*/
private static UIService uiService = null;
/**
* Starts the arg delegation bundle and registers the delegationPeer with
* the util package URI manager.
*
* @param bc a reference to the currently active bundle context.
*/
@Override
public void start(BundleContext bc) throws Exception
{
bundleContext = bc;
delegationPeer = new UriDelegationPeerImpl(bc);
bc.addServiceListener(delegationPeer);
//register our instance of delegation peer.
LaunchArgHandler.getInstance().setDelegationPeer(delegationPeer);
}
/**
* Unsets the delegation peer instance that we set when we start this
* bundle.
*
* @param bc an instance of the currently valid bundle context.
*/
@Override
public void stop(BundleContext bc) throws Exception
{
uiService = null;
bc.removeServiceListener(delegationPeer);
delegationPeer = null;
LaunchArgHandler.getInstance().setDelegationPeer(null);
}
/**
* Returns a reference to an UIService implementation currently registered
* in the bundle context or null if no such implementation was found.
*
* @return a reference to an UIService implementation currently registered
* in the bundle context or null if no such implementation was found.
*/
public static UIService getUIService()
{
if(uiService == null)
{
ServiceReference versionServiceReference
= bundleContext.getServiceReference(
UIService.class.getName());
uiService = (UIService)bundleContext
.getService(versionServiceReference);
}
return uiService;
}
}

@ -0,0 +1,186 @@
/*
* 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.argdelegation;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.argdelegation.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.version.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.launchutils.*;
/**
* Implements the <tt>UriDelegationPeer</tt> interface from our argument handler
* utility. We use this handler to relay arguments to URI handlers that have
* been registered from other services such as the SIP provider for example.
*
* @author Emil Ivov
*/
public class UriDelegationPeerImpl
implements UriDelegationPeer, ServiceListener
{
private static final Logger logger =
Logger.getLogger(UriDelegationPeerImpl.class);
/**
* The list of uriHandlers that we are currently aware of.
*/
private Map<String, UriHandler> uriHandlers
= new Hashtable<String, UriHandler>();
/**
* Creates an instance of this peer and scans <tt>bundleContext</tt> for all
* existing <tt>UriHandler</tt>s
*
* @param bundleContext a reference to a currently valid instance of a
* bundle context.
*/
public UriDelegationPeerImpl(BundleContext bundleContext)
{
ServiceReference[] uriHandlerRefs = null;
synchronized (uriHandlers)
{
try
{
uriHandlerRefs = bundleContext.getServiceReferences(
UriHandler.class.getName(), null);
}
catch (InvalidSyntaxException exc)
{
// this shouldn't happen because we aren't using a filter
// but let's log just the same.
logger.info("An error occurred while retrieving UriHandlers",
exc);
return;
}
if(uriHandlerRefs == null)
{
//none URI handlers are registered at this point. Some might
//come later.
return;
}
for (ServiceReference uriHandlerRef : uriHandlerRefs)
{
UriHandler uriHandler = (UriHandler) bundleContext
.getService(uriHandlerRef);
uriHandlers.put(uriHandler.getProtocol(), uriHandler);
}
}
}
/**
* Listens for <tt>UriHandlers</tt> that are registered in the bundle
* context after we had started so that we could add them to the list
* of currently known handlers.
*
* @param event the event containing the newly (un)registered service.
*/
public void serviceChanged(ServiceEvent event)
{
synchronized (uriHandlers)
{
BundleContext bc = event.getServiceReference().getBundle()
.getBundleContext();
Object service = bc.getService(event.getServiceReference());
//we are only interested in UriHandler-s
if(!(service instanceof UriHandler) )
{
return;
}
if (event.getType() == ServiceEvent.MODIFIED
|| event.getType() == ServiceEvent.REGISTERED)
{
UriHandler uriHandler = (UriHandler) bc.getService(event
.getServiceReference());
uriHandlers.put(uriHandler.getProtocol(), uriHandler);
}
else if (event.getType() == ServiceEvent.UNREGISTERING)
{
UriHandler uriHandler = (UriHandler) bc.getService(event
.getServiceReference());
if(uriHandlers.get(uriHandler.getProtocol()) == uriHandler)
uriHandlers.remove(uriHandler.getProtocol());
}
}
}
/**
* Relays <tt>uirArg</tt> to the corresponding handler or shows an error
* message in case no handler has been registered for the corresponding
* protocol.
*
* @param uriArg the uri that we've been passed and that we'd like to
* delegate to the corresponding provider.
*/
public void handleUri(String uriArg)
{
logger.trace("Handling URI: " + uriArg);
//first parse the uri and determine the scheme/protocol
//the parsing is currently a bit oversimplified so we'd probably need
//to revisit it at some point.
int colonIndex = uriArg.indexOf(":");
if( colonIndex == -1)
{
//no scheme, we don't know how to handle the URI
ArgDelegationActivator.getUIService().getPopupDialog()
.showMessagePopupDialog(
"Could not determine how to handle: " + uriArg
+ ".\nNo protocol scheme found.",
"Error handling URI",
PopupDialog.ERROR_MESSAGE);
return;
}
String scheme = uriArg.substring(0, colonIndex);
UriHandler handler = uriHandlers.get(scheme);
//if handler is null we need to tell the user.
if(handler == null)
{
logger.trace("Couldn't open " + uriArg
+ "No handler found for protocol"+ scheme);
ArgDelegationActivator.getUIService().getPopupDialog()
.showMessagePopupDialog(
"\"" + scheme + "\" URIs are currently not supported.",
"Error handling URI",
PopupDialog.ERROR_MESSAGE);
return;
}
//we're all set. let's do the handling now.
try
{
handler.handleUri(uriArg);
}
//catch every possible exception
catch(Throwable thr)
{
ArgDelegationActivator.getUIService().getPopupDialog()
.showMessagePopupDialog(
"Error handling " + uriArg,
"Error handling URI",
PopupDialog.ERROR_MESSAGE);
logger.error("Failed to handle \""+ uriArg +"\"", thr);
}
}
}

@ -0,0 +1,13 @@
Bundle-Activator: net.java.sip.communicator.impl.argdelegation.ArgDelegationActivator
Bundle-Name: Argument Delegation
Bundle-Description: A bundle that delegates invocation arguments to register handler services
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: org.osgi.framework,
net.java.sip.communicator.util,
net.java.sip.communicator.util.launchutils,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.argdelegation,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.gui

@ -559,6 +559,25 @@ public ExportedWindow getAuthenticationWindow(
isUserNameEditable);
}
/**
* Returns a default implementation of the <tt>SecurityAuthority</tt>
* interface that can be used by non-UI components that would like to launch
* the registration process for a protocol provider. Initially this method
* was meant for use by the systray bundle and the protocol URI handlers.
*
* @param protocolProvider the <tt>ProtocolProviderService</tt> for which
* the authentication window is about.
*
* @return a default implementation of the <tt>SecurityAuthority</tt>
* interface that can be used by non-UI components that would like to launch
* the registration process for a protocol provider.
*/
public SecurityAuthority getDefaultSecurityAuthority(
ProtocolProviderService protocolProvider)
{
return new DefaultSecurityAuthority(protocolProvider);
}
/**
* Returns the LoginManager.
* @return the LoginManager

@ -0,0 +1,106 @@
/*
* 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.gui.main.login;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Utility class that can be used in cases where components other than the main
* user interface may need to launch provider registration. At the time I am
* writing this, the <tt>DefaultSecurityAuthority</tt> is being used by the
* systray and
*
* @author Emil Ivov
*/
public class DefaultSecurityAuthority
implements SecurityAuthority
{
private boolean isUserNameEditable = false;
/**
* The provider that this authority would be responsible for.
*/
private ProtocolProviderService provider = null;
/**
* Creates this authority for a particular provider.
*/
public DefaultSecurityAuthority(ProtocolProviderService provider)
{
this.provider = provider;
}
/**
* Used to login to the protocol providers
*
* @param realm the realm that the credentials are needed for
* @param userCredentials the values to propose the user by default
* @return The Credentials associated with the speciefied realm
*/
public UserCredentials obtainCredentials(
String realm,
UserCredentials userCredentials)
{
return obtainCredentials( realm,
userCredentials,
SecurityAuthority.AUTHENTICATION_REQUIRED);
}
/**
* Used to login to the protocol providers
*
* @param realm the realm that the credentials are needed for
* @param userCredentials the values to propose the user by default
* @param reasonCode the reason for which we're asking for credentials
* @return The Credentials associated with the speciefied realm
*/
public UserCredentials obtainCredentials(
String realm,
UserCredentials userCredentials,
int reasonCode)
{
ExportedWindow loginWindow
= GuiActivator.getUIService()
.getAuthenticationWindow(provider,
realm,
userCredentials,
isUserNameEditable);
loginWindow.setVisible(true);
return userCredentials;
}
/**
* Sets the userNameEditable property, which should indicate to the
* implementations of this interface if the user name could be changed
* by user or not.
*
* @param isUserNameEditable indicates if the user name could be changed
* by user in the implementation of this interface.
*/
public void setUserNameEditable(boolean isUserNameEditable)
{
this.isUserNameEditable = isUserNameEditable;
}
/**
* Indicates if the user name is currently editable, i.e. could be
* changed by user or not.
*
* @return <tt>true</tt> if the user name could be changed and
* <tt>false</tt> otherwise.
*/
public boolean isUserNameEditable()
{
return isUserNameEditable;
}
}

@ -155,6 +155,14 @@ public Call createCall(Contact callee) throws OperationFailedException
private synchronized CallSipImpl createOutgoingCall(Address calleeAddress)
throws OperationFailedException
{
if(!protocolProvider.isRegistered())
{
throw new OperationFailedException(
"The protocol provider should be registered "
+"before placing an outgoing call.",
OperationFailedException.PROVIDER_NOT_REGISTERED);
}
// create the invite request
Request invite = createInviteRequest(calleeAddress);

@ -27,7 +27,8 @@ public class ProtocolProviderFactorySipImpl
/**
* The table that we store our accounts in.
*/
private Hashtable registeredAccounts = new Hashtable();
private Hashtable<AccountID, ServiceRegistration> registeredAccounts
= new Hashtable<AccountID, ServiceRegistration>();
/**
* Constructs a new instance of the ProtocolProviderFactorySipImpl.
@ -63,9 +64,9 @@ public ServiceReference getProviderForAccount(AccountID accountID)
* @return a copy of the llist containing all accounts currently installed
* in the protocol provider.
*/
public ArrayList getRegisteredAccounts()
public ArrayList<AccountID> getRegisteredAccounts()
{
return new ArrayList(registeredAccounts.keySet());
return new ArrayList<AccountID>(registeredAccounts.keySet());
}
/**

@ -312,6 +312,9 @@ public class ProtocolProviderServiceSipImpl
*/
private ProtocolIconSipImpl protocolIcon;
/**
* The presence status set supported by this provider
*/
private SipStatusEnum sipStatusEnum;
/**

@ -10,6 +10,7 @@
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@ -32,9 +33,12 @@ public class SipActivator
= null;
private static MediaService mediaService = null;
private static VersionService versionService = null;
private static UIService uiService = null;
private static ProtocolProviderFactorySipImpl sipProviderFactory = null;
private static UriHandlerSipImpl uriHandler = null;
/**
* Called when this bundle is started so the Framework can perform the
* bundle-specific activities necessary to start this bundle.
@ -63,6 +67,8 @@ public void start(BundleContext context) throws Exception
sipProviderFactory,
hashtable);
uriHandler = new UriHandlerSipImpl(sipProviderFactory);
logger.debug("SIP Protocol Provider Factory ... [REGISTERED]");
}
@ -169,6 +175,26 @@ public static VersionService getVersionService()
return versionService;
}
/**
* Returns a reference to the UIService implementation currently registered
* in the bundle context or null if no such implementation was found.
*
* @return a reference to a UIService implementation currently registered
* in the bundle context or null if no such implementation was found.
*/
public static UIService getUIService()
{
if(uiService == null)
{
ServiceReference uiServiceReference
= bundleContext.getServiceReference(
UIService.class.getName());
uiService = (UIService)bundleContext
.getService(uiServiceReference);
}
return uiService;
}
/**
* Called when this bundle is stopped so the Framework can perform the
* bundle-specific activities necessary to stop the bundle.

@ -0,0 +1,185 @@
/*
* 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.protocol.sip;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.logging.Level;
import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Allows SIP communicator to create SIP accounts without a registrar. We use
* this class as a replacement of the SipRegistrarConnection for accounts that
* do not have a configured registrar.
*
* @author Emil Ivov
*/
public class SipRegistrarlessConnection
extends SipRegistrarConnection
{
private static final Logger logger =
Logger.getLogger(SipRegistrarlessConnection.class);
/**
* A reference to the sip provider that created us.
*/
private ProtocolProviderServiceSipImpl sipProvider = null;
/**
* Keeps our current registration state.
*/
private RegistrationState currentRegistrationState
= RegistrationState.UNREGISTERED;
/**
* Creates a new instance of this class.
*
* @param sipProviderCallback a reference to the
* ProtocolProviderServiceSipImpl instance that created us.
*
*/
public SipRegistrarlessConnection(
ProtocolProviderServiceSipImpl sipProviderCallback)
{
this.sipProvider = sipProviderCallback;
}
/**
* Simply sets the state of the connection to REGISTERED without doing
* anything else.
*
* @throws OperationFailedException never thrown
*/
@Override
void register()
throws OperationFailedException
{
setRegistrationState(RegistrationState.REGISTERED,
RegistrationStateChangeEvent.REASON_USER_REQUEST,
null);
}
/**
* Simply sets the state of the connection to UNREGISTERED without doing
* anything else.
*
* @throws OperationFailedException never thrown.
*/
@Override
public void unregister() throws OperationFailedException
{
setRegistrationState(RegistrationState.UNREGISTERED,
RegistrationStateChangeEvent.REASON_USER_REQUEST,
null);
}
/**
* Returns the state of this connection.
*
* @return a RegistrationState instance indicating the state of our
* registration with the corresponding registrar.
*/
@Override
public RegistrationState getRegistrationState()
{
return currentRegistrationState;
}
/**
* Sets our registration state to <tt>newState</tt> and dispatches an event
* through the protocol provider service impl.
* <p>
* @param newState a reference to the RegistrationState that we're currently
* detaining.
* @param reasonCode one of the REASON_XXX error codes specified in
* {@link RegistrationStateChangeEvent}.
* @param reason a reason String further explaining the reasonCode.
*/
@Override
public void setRegistrationState(RegistrationState newState,
int reasonCode,
String reason)
{
if( currentRegistrationState.equals(newState) )
{
return;
}
RegistrationState oldState = currentRegistrationState;
this.currentRegistrationState = newState;
sipProvider.fireRegistrationStateChanged(
oldState, newState, reasonCode, reason);
}
/**
* Returns the address of this connection's registrar.
*
* @return the InetAddress of our registrar server.
*/
@Override
public InetAddress getRegistrarAddress()
{
try
{
return InetAddress.getByAddress("2001:1890:1112:1::20",
new byte[]{(byte) 20, (byte) 01, (byte) 18, (byte) 90,
(byte) 11, (byte) 11, (byte) 12, (byte) 00,
(byte) 01, (byte) 00, (byte) 00, (byte) 00,
(byte) 00, (byte) 00, (byte) 00, (byte) 20});
} catch (UnknownHostException ex)
{
logger.error("Failed to generate a dummy registrar addr", ex);
return null;
}
}
/**
* Returns the listening point that should be used for communication with our
* current registrar.
*
* @return the listening point that should be used for communication with our
* current registrar.
*/
@Override
public ListeningPoint getRegistrarListeningPoint()
{
return sipProvider.getDefaultListeningPoint();
}
/**
* Returns a string representation of this connection instance
* instance including information that would permit to distinguish it among
* other sip listeners when reading a log file.
* <p>
* @return a string representation of this operation set.
*/
@Override
public String toString()
{
String className = getClass().getName();
try
{
className = className.substring(className.lastIndexOf('.') + 1);
}
catch (Exception ex)
{
// we don't want to fail in this method because we've messed up
//something with indexes, so just ignore.
}
return className + "-[dn=" + sipProvider.getOurDisplayName()
+" addr="+sipProvider.getOurSipAddress() + "]";
}
}

@ -0,0 +1,524 @@
/*
* 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.protocol.sip;
import java.text.*;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.impl.systray.*;
import net.java.sip.communicator.service.argdelegation.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* The sip implementation of the URI handler. This class handles sip URIs by
* trying to establish a call to them.
*
* @author Emil Ivov
*/
public class UriHandlerSipImpl
implements UriHandler,
ServiceListener
{
private static final Logger logger =
Logger.getLogger(UriHandlerSipImpl.class);
/**
* The protocol provider factory that created us.
*/
private ProtocolProviderFactory protoFactory = null;
/**
* A reference to the OSGi registration we create with this handler.
*/
private ServiceRegistration ourServiceRegistration = null;
/**
* The object that we are using to synchronize our service registration.
*/
private Object registrationLock = new Object();
/**
* Creates an instance of this uri handler, so that it would start handling
* URIs by passing them to the providers registered by <tt>protoFactory</tt>.
*
* @param parentProvider the provider that created us.
*
* @throws NullPointerException if <tt>protoFactory</tt> is <tt>null</tt>.
*/
protected UriHandlerSipImpl(ProtocolProviderFactory protoFactory)
throws NullPointerException
{
if(protoFactory == null)
{
throw new NullPointerException(
"The ProtocolProviderFactory that a UriHandler is created with "
+ " cannot be null.");
}
this.protoFactory = protoFactory;
//we listen for service events so that we can disable ourselves in
//case our protocol factory decides to leave.
SipActivator.bundleContext.addServiceListener(this);
registerHandlerService();
}
/**
* Registers this UriHandler with the bundle context so that it could
* start handling URIs
*/
private void registerHandlerService()
{
synchronized(registrationLock)
{
if (ourServiceRegistration != null)
{
// ... we are already registered (this is probably
// happening during startup)
return;
}
Hashtable<String, String> registrationProperties
= new Hashtable<String, String>();
registrationProperties.put(UriHandler.PROTOCOL_PROPERTY,
getProtocol());
ourServiceRegistration = SipActivator.bundleContext
.registerService(UriHandler.class.getName(), this,
registrationProperties);
}
}
/**
* Unregisters this UriHandler from the bundle context.
*/
private void unregisterHandlerService()
{
synchronized(registrationLock)
{
ourServiceRegistration.unregister();
ourServiceRegistration = null;
}
}
/**
* Returns the protocol that this handler is responsible for or "sip" in
* other words.
*
* @return the "sip" string to indicate that this handler is responsible
* for handling "sip" uris.
*/
public String getProtocol()
{
return "sip";
}
/**
* Parses the specified URI and creates a call with the currently active
* telephony operation set.
*
* @param uri the SIP URI that we have to call.
*/
public void handleUri(String uri)
{
ProtocolProviderService provider;
try
{
provider = selectHandlingProvider(uri);
}
catch (OperationFailedException exc)
{
// The operation has been canceled by the user. Bail out.
logger.trace("User canceled handling of uri " + uri);
return;
}
//if provider is null then we need to tell the user to create an account
if(provider == null)
{
showErrorMessage(
"You need to configure at least one "
+ "SIP" +" account \n"
+"to be able to call " + uri,
null);
return;
}
OperationSetBasicTelephony telephonyOpSet
= (OperationSetBasicTelephony) provider
.getOperationSet(OperationSetBasicTelephony.class);
try
{
telephonyOpSet.createCall(uri);
}
catch (OperationFailedException exc)
{
//make sure that we prompt for registration only if it is really
//required by the provider.
if(exc.getErrorCode()
== OperationFailedException.PROVIDER_NOT_REGISTERED)
{
promptForRegistration(uri, provider);
}
showErrorMessage("Failed to create a call to " + uri, exc);
}
catch (ParseException exc)
{
showErrorMessage(
uri + " does not appear to be a valid SIP address",
exc);
}
}
/**
* Informs the user that they need to be registered before placing calls
* and asks them whether they would like us to do it for them.
*
* @param uri the uri that the user would like us to call after registering.
* @param provider the provider that we may have to reregister.
*/
private void promptForRegistration(String uri,
ProtocolProviderService provider)
{
int answer = SipActivator.getUIService()
.getPopupDialog().showConfirmPopupDialog(
"You need to be online in order to make a call and your "
+ "account is currently offline. Do want to connect now?",
"Account is currently offline",
PopupDialog.YES_NO_OPTION);
if(answer == PopupDialog.YES_OPTION)
{
new ProtocolRegistrationThread(uri, provider).start();
}
}
/**
* The point of implementing a service listener here is so that we would
* only register our own uri handling service and thus only handle URIs
* while the factory is available as an OSGi service. We remove ourselves
* when our factory unregisters its service reference.
*
* @param event the OSGi <tt>ServiceEvent</tt>
*/
public void serviceChanged(ServiceEvent event)
{
Object sourceService = SipActivator.bundleContext
.getService(event.getServiceReference());
//ignore anything but our protocol factory.
if( ! (sourceService instanceof ProtocolProviderFactorySipImpl)
|| (sourceService != protoFactory))
{
return;
}
if(event.getType() == ServiceEvent.REGISTERED)
{
//our factory has just been registered as a service ...
registerHandlerService();
}
else if(event.getType() == ServiceEvent.UNREGISTERING)
{
//our factory just died - seppuku.
unregisterHandlerService();
}
else if(event.getType() == ServiceEvent.MODIFIED)
{
//we don't care.
}
}
/**
* Uses the <tt>UIService</tt> to show an error <tt>message</tt> and log
* and <tt>exception</tt>.
*
* @param message the message that we'd like to show to the user.
* @param exc the exception that we'd like to log
*/
private void showErrorMessage(String message, Exception exc)
{
SipActivator.getUIService().getPopupDialog().showMessagePopupDialog(
message,
"Failed to create call!",
PopupDialog.ERROR_MESSAGE);
logger.error(message, exc);
}
/**
* We use this class when launching a provider registration by ourselves in
* order to track for provider registration states and retry uri handling,
* once the provider is registered.
*
*/
private class ProtocolRegistrationThread
extends Thread
implements SecurityAuthority,
RegistrationStateChangeListener
{
private boolean isUserNameEditable = false;
private ProtocolProviderService handlerProvider = null;
/**
* The URI that we'd need to re-call.
*/
private String uri = null;
/**
* Configures this thread register our parent provider and re-attempt
* connection to the specified <tt>uri</tt>.
*
* @param uri the uri that we need to handle.
* @param handlerProvider the provider that we are going to make
* register and that we are going to use to handle the <tt>uri</tt>.
*/
public ProtocolRegistrationThread(
String uri,
ProtocolProviderService handlerProvider)
{
super("UriHandlerProviderRegistrationThread:uri=" + uri);
this.uri = uri;
this.handlerProvider = handlerProvider;
}
/**
* Used to login to the protocol providers
*
* @param realm the realm that the credentials are needed for
* @param userCredentials the values to propose the user by default
* @return The Credentials associated with the speciefied realm
*/
public UserCredentials obtainCredentials(
String realm,
UserCredentials userCredentials)
{
return obtainCredentials( realm,
userCredentials,
SecurityAuthority.AUTHENTICATION_REQUIRED);
}
/**
* Used to login to the protocol providers
*
* @param realm the realm that the credentials are needed for
* @param userCredentials the values to propose the user by default
* @param reasonCode the reason for which we're asking for credentials
* @return The Credentials associated with the speciefied realm
*/
public UserCredentials obtainCredentials(
String realm,
UserCredentials userCredentials,
int reasonCode)
{
ExportedWindow loginWindow
= SystrayActivator.getUIService()
.getAuthenticationWindow(handlerProvider,
realm,
userCredentials,
isUserNameEditable);
loginWindow.setVisible(true);
return userCredentials;
}
/**
* Sets the userNameEditable property, which should indicate to the
* implementations of this interface if the user name could be changed
* by user or not.
*
* @param isUserNameEditable indicates if the user name could be changed
* by user in the implementation of this interface.
*/
public void setUserNameEditable(boolean isUserNameEditable)
{
this.isUserNameEditable = isUserNameEditable;
}
/**
* Indicates if the user name is currently editable, i.e. could be
* changed by user or not.
*
* @return <tt>true</tt> if the user name could be changed and
* <tt>false</tt> otherwise.
*/
public boolean isUserNameEditable()
{
return isUserNameEditable;
}
/**
* Starts the registration process, ads this class as a registration
* listener and then tries to rehandle the uri this thread was initiaded
* with.
*/
@Override
public void run()
{
handlerProvider.addRegistrationStateChangeListener(this);
try
{
handlerProvider.register(this);
}
catch (OperationFailedException exc)
{
logger.error("Failed to manually register provider.");
logger.warn(exc.getMessage(), exc);
}
}
/**
* If the parent provider passes into the registration state, the method
* re-handles the URI that this thread was initiated with. The method
* would only rehandle the uri if the event shows successful
* registration. It would ignore intermediate states such as
* REGISTERING. Disconnection and failure events would simply cause this
* listener to remove itself from the list of registration listeners.
*
* @param evt the <tt>RegistrationStateChangeEvent</tt> that this
* thread was initiated with.
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
if (evt.getNewState() == RegistrationState.REGISTERED)
{
Thread uriRehandleThread = new Thread()
{
public void run()
{
handleUri(uri);
}
};
uriRehandleThread.setName("UriRehandleThread:uri="+uri);
uriRehandleThread.start();
}
//we're only interested in a single event so we stop listening
//(unless this was a REGISTERING notification)
if(evt.getNewState() == RegistrationState.REGISTERING)
return;
handlerProvider.removeRegistrationStateChangeListener(this);
}
}
/**
* Returns the default provider that we are supposed to handle URIs through
* or null if there aren't any. Depending on the implementation this
* method may require user intervention so make sure you don't rely on
* a quick outcome when calling it.
*
* @param uri the uri that we'd like to handle with the provider that we are
* about to select.
*
* @return the provider that we should handle URIs through.
*
* @throws OperationFailedException with code <tt>OPERATION_CANCELED</tt>
* if the users.
*/
public ProtocolProviderService selectHandlingProvider(String uri)
throws OperationFailedException
{
ArrayList<AccountID> registeredAccounts
= protoFactory.getRegisteredAccounts();
//if we don't have any providers - return null.
if(registeredAccounts.size() == 0)
{
return null;
}
//if we only have one provider - select it
if(registeredAccounts.size() == 1)
{
ServiceReference providerReference
= protoFactory.getProviderForAccount(registeredAccounts.get(0));
ProtocolProviderService provider = (ProtocolProviderService)
SipActivator.getBundleContext().getService(providerReference);
return provider;
}
//otherwise - ask the user.
ArrayList<ProviderComboBoxEntry> providers
= new ArrayList<ProviderComboBoxEntry>();
for (AccountID accountID : registeredAccounts)
{
ServiceReference providerReference
= protoFactory.getProviderForAccount(accountID);
ProtocolProviderService provider = (ProtocolProviderService)
SipActivator.getBundleContext().getService(providerReference);
providers.add( new ProviderComboBoxEntry( provider ) );
}
Object result = SipActivator.getUIService().getPopupDialog()
.showInputPopupDialog(
"Please select the account that you would like \n"
+ "to use to call "
+ uri,
"Account Selection",
PopupDialog.OK_CANCEL_OPTION,
providers.toArray(),
providers.get(0));
if( result == null)
{
throw new OperationFailedException(
"Operation cancelled",
OperationFailedException.OPERATION_CANCELED);
}
return ((ProviderComboBoxEntry)result).provider;
}
/**
* A class that we use to wrap providers before showing them to the user
* through a selection popup dialog from the UIService.
*/
private class ProviderComboBoxEntry
{
public ProtocolProviderService provider;
public ProviderComboBoxEntry(ProtocolProviderService provider)
{
this.provider = provider;
}
/**
* Returns a human readable <tt>String</tt> representing the
* provider encapsulated by this class.
*
* @return a human readable string representing the provider.
*/
@Override
public String toString()
{
return provider.getAccountID().getAccountAddress();
}
}
}

@ -8,6 +8,8 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.util,
net.java.sip.communicator.util.xml,
net.java.sip.communicator.service.argdelegation,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.configuration.event,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.protocol,

@ -19,19 +19,17 @@
* continue its execution during this operation.
*
* @author Nicolas Chamouard
* @author Emil Ivov
*/
public class ProviderRegistration
extends Thread
implements SecurityAuthority
{
/**
* The protocol provider to whom we want to register
*/
private ProtocolProviderService protocolProvider;
private boolean isUserNameEditable = false;
/**
* The logger for this class.
*/
@ -53,8 +51,10 @@ public ProviderRegistration(ProtocolProviderService protocolProvider)
*/
public void run()
{
try {
protocolProvider.register(this);
try
{
protocolProvider.register(SystrayActivator.getUIService()
.getDefaultSecurityAuthority(protocolProvider));
}
catch (OperationFailedException ex)
{
@ -86,69 +86,4 @@ else if (errorCode == OperationFailedException
}
}
}
/**
* Used to login to the protocol providers
*
* @param realm the realm that the credentials are needed for
* @param userCredentials the values to propose the user by default
* @param reasonCode the reason for which we're asking for credentials
* @return The Credentials associated with the speciefied realm
*/
public UserCredentials obtainCredentials(
String realm,
UserCredentials userCredentials,
int reasonCode)
{
ExportedWindow loginWindow
= SystrayActivator.getUIService()
.getAuthenticationWindow(protocolProvider,
realm,
userCredentials,
isUserNameEditable);
loginWindow.setVisible(true);
return userCredentials;
}
/**
* Used to login to the protocol providers
*
* @param realm the realm that the credentials are needed for
* @param userCredentials the values to propose the user by default
* @return The Credentials associated with the speciefied realm
*/
public UserCredentials obtainCredentials(
String realm,
UserCredentials userCredentials)
{
return obtainCredentials( realm,
userCredentials,
SecurityAuthority.AUTHENTICATION_REQUIRED);
}
/**
* Sets the userNameEditable property, which should indicate to the
* implementations of this interface if the user name could be changed by
* user or not.
*
* @param isUserNameEditable indicates if the user name could be changed by
* user in the implementation of this interface.
*/
public void setUserNameEditable(boolean isUserNameEditable)
{
this.isUserNameEditable = isUserNameEditable;
}
/**
* Indicates if the user name is currently editable, i.e. could be changed
* by user or not.
*
* @return <code>true</code> if the user name could be changed,
* <code>false</code> - otherwise.
*/
public boolean isUserNameEditable()
{
return isUserNameEditable;
}
}

@ -9,6 +9,8 @@
import java.awt.*;
import java.io.*;
import net.java.sip.communicator.util.launchutils.*;
import org.apache.felix.main.*;
/**
@ -16,13 +18,14 @@
*
* @author Yana Stamcheva
* @author Lubomir Marinov
* @author Emil Ivov
*/
public class SIPCommunicator
{
private static final String PNAME_SC_HOME_DIR_LOCATION =
public static final String PNAME_SC_HOME_DIR_LOCATION =
"net.java.sip.communicator.SC_HOME_DIR_LOCATION";
private static final String PNAME_SC_HOME_DIR_NAME =
public static final String PNAME_SC_HOME_DIR_NAME =
"net.java.sip.communicator.SC_HOME_DIR_NAME";
/**
@ -68,6 +71,51 @@ else if (osName.startsWith("Windows"))
return;
}
//first - pass the arguments to our arg handler
LaunchArgHandler argHandler = LaunchArgHandler.getInstance();
int argHandlerRes = argHandler.handleArgs(args);
if ( argHandlerRes == LaunchArgHandler.ACTION_EXIT
|| argHandlerRes == LaunchArgHandler.ACTION_ERROR)
{
System.exit(argHandler.getErrorCode());
}
//lock our config di so that we would only have a single instance of
//sip communicator, no matter how many times we start it (use mainly
//for handling sip: uris after starting the application)
if ( argHandlerRes != LaunchArgHandler.ACTION_CONTINUE_MULTIINSTANCE )
{
SipCommunicatorLock lock = new SipCommunicatorLock();
int lockResult = lock.tryLock(args);
if( lockResult == SipCommunicatorLock.LOCK_ERROR )
{
System.err.print("Failed to lock SIP Communicator's "
+"configuration directory.\n"
+"Try launching with the --multiple param.");
System.exit(SipCommunicatorLock.LOCK_ERROR);
}
else if(lockResult == SipCommunicatorLock.ALREADY_STARTED)
{
System.err.print(
"SIP Communicator is already running and will "
+"handle your parameters (if any).\n"
+"Launch with the --multiple param to override this "
+"behaviour.");
//we exit with succes because for the user that's what it is.
System.exit(SipCommunicatorLock.SUCCESS);
}
else if(lockResult == SipCommunicatorLock.SUCCESS)
{
//Successfully locked, continue as normal.
}
}
//there was no error, continue;
Main.main(args);
}
@ -81,7 +129,6 @@ else if (osName.startsWith("Windows"))
*/
private static void setScHomeDir(String osName)
{
/*
* Though we'll be setting the SC_HOME_DIR_* property values depending
* on the OS running the application, we have to make sure we are

@ -0,0 +1,35 @@
/*
* 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.argdelegation;
/**
* This interface is meant to be implemented by all bundles that wish to handle
* URIs passed as invocation arguments.
*
* @author Emil Ivov <emcho at sip-communicator.org>
*/
public interface UriHandler
{
/**
* The name of the property that we use in the service registration
* properties to store a protocol name when registering <tt>UriHandler</tt>s
*/
public static final String PROTOCOL_PROPERTY = "ProtocolName";
/**
* Returns the protocol that this handler is responsible for.
*/
public String getProtocol();
/**
* Handles/opens the URI.
*
* @param uri the URI that the handler has to open.
*/
public void handleUri(String uri);
}

@ -0,0 +1,5 @@
Bundle-Name: Argument Delegation Service
Bundle-Description: A service that allows bundles to register as handlers for specific command line arguments
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Export-Package: net.java.sip.communicator.service.argdelegation

@ -193,6 +193,22 @@ public ExportedWindow getAuthenticationWindow(
UserCredentials userCredentials,
boolean isUserNameEditable);
/**
* Returns a default implementation of the <tt>SecurityAuthority</tt>
* interface that can be used by non-UI components that would like to launch
* the registration process for a protocol provider. Initially this method
* was meant for use by the systray bundle and the protocol URI handlers.
*
* @param protocolProvider the <tt>ProtocolProviderService</tt> for which
* the authentication window is about.
*
* @return a default implementation of the <tt>SecurityAuthority</tt>
* interface that can be used by non-UI components that would like to launch
* the registration process for a protocol provider.
*/
public SecurityAuthority getDefaultSecurityAuthority(
ProtocolProviderService protocolProvider);
/**
* Returns an iterator over a set of windowID-s. Each <tt>WindowID</tt>
* points to a window in the current UI implementation. Each

@ -121,6 +121,11 @@ public class OperationFailedException
*/
public static final int AUTHENTICATION_CANCELED = 15;
/**
* Indicates that the operation has been canceled by the user.
*/
public static final int OPERATION_CANCELED = 16;
/**
* The error code of the exception
*/

@ -254,7 +254,7 @@ public abstract void modifyAccount(
* @return a copy of the list containing the <tt>AccountID</tt>s of all
* accounts currently registered in this protocol provider.
*/
public abstract ArrayList getRegisteredAccounts();
public abstract ArrayList<AccountID> getRegisteredAccounts();
/**
* Returns the ServiceReference for the protocol provider corresponding to

@ -0,0 +1,404 @@
/*
* 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.util.launchutils;
import java.util.*;
import net.java.sip.communicator.util.*;
import java.io.*;
/**
* The <tt>LauncherArgHandler</tt> class handles invocation arguments that have
* been passed to us when running SIP Communicator. The class supports a fixed
* set of options and also allows for registration of delegates.
*
* @author Emil Ivov <emcho at sip-communicator.org>
*/
public class LaunchArgHandler
{
private static final Logger logger =
Logger.getLogger(LaunchArgHandler.class);
/**
* The name of the property that contains the location of the SC
* configuration directory.
*/
private static final String PNAME_SC_HOME_DIR_LOCATION =
"net.java.sip.communicator.SC_HOME_DIR_LOCATION";
/**
* The name of the property that contains the name of the SC configuration
* directory.
*/
private static final String PNAME_SC_HOME_DIR_NAME =
"net.java.sip.communicator.SC_HOME_DIR_NAME";
/**
* Returned by the <tt>handleArgs</tt> methods when the arguments that have
* been parsed do not require for SIP Communicator to be started and the
* Launcher is supposed to exit. That could happen when "SIP Communicator"
* is launched with a --version argument for example or when trying to
* run the application after an instance was already launched.
*/
public static final int ACTION_EXIT = 0;
/**
* Returned by the <tt>handleArgs</tt> methods when all arguments have been
* parsed and the SIP Communicator launch can continue.
*/
public static final int ACTION_CONTINUE = 1;
/**
* Returned by the <tt>handleArgs</tt> method when parsing the arguments
* has failed or if no arguments were passed and an instance of SC was
* already launched. If this is the code returned by handleArgs, then the
* <tt>getErrorCode</tt> method would return an error code indicating what
* the error was.
*/
public static final int ACTION_ERROR = 2;
/**
* Returned by the <tt>handleArgs</tt> methods when all arguments have been
* successfully parsed and one of them indicates that the user has requested
* a multi instance launch.
*/
public static final int ACTION_CONTINUE_MULTIINSTANCE = 3;
/**
* The error code returned when we couldn't parse one of the options.
*/
public static final int ERROR_CODE_UNKNOWN_ARG = 1;
/**
* The error code returned when we try to launch SIP Communicator while
* there is already a running instance and there were no arguments that we
* forward to that instance.
*/
public static final int ERROR_CODE_ALREADY_STARTED = 2;
/**
* The error code that we return when we fail to create a directory that has
* been specified with the -c|--config option.
*/
public static final int ERROR_CODE_CREATE_DIR_FAILED = 3;
/**
* The property name containing the name of the application
* (e.g. SIP Communicator)
*/
private static final String PNAME_APPLICATION_NAME = "APPLICATION_NAME";
/**
* The property name containing the current version.
*/
private static final String PNAME_VERSION = "APPLICATION_VERSION";
/**
* The name of the file containing version properties for use with the
* argument handler.
*/
private static final String VERSION_PROPERTIES = "version.properties";
/**
* The errorCode identifying the error that occurred last time
* <tt>handleArgs</tt> was called.
*/
private int errorCode = 0;
/**
* A reference to the instance of the
*/
private UriArgManager uriArgManager = new UriArgManager();
/**
* The singleton instance of this handler.
*/
private static LaunchArgHandler argHandler = null;
private Properties versionProperties = new Properties();
/**
* Creates the sole instance of this class;
*/
private LaunchArgHandler()
{
try
{
versionProperties.load(
getClass().getResourceAsStream(VERSION_PROPERTIES));
}
catch(IOException exc)
{
//no need to worry the user, so only print if we're in FINEST
logger.trace("Couldn't open version.properties");
}
}
/**
* Creates a singleton instance of the LauncherArgHandler if necessary and
* returns a reference to it.
*
* @return the singleton instance of the LauncherArgHandler.
*/
public static LaunchArgHandler getInstance()
{
if(argHandler == null)
{
argHandler = new LaunchArgHandler();
}
return argHandler;
}
/**
* Does the actual argument handling.
*
* @param args the arguments the way we have received them from the main()
* method.
*
* @return one of the ACTION_XXX fields defined here, intended to indicate
* to the caller they action that they are supposed as a result of the arg
* handling.
*/
public int handleArgs(String[] args)
{
int returnAction = ACTION_CONTINUE;
for(int i = 0; i < args.length; i++)
{
logger.trace("handling arg " + i);
if (args[i].equals("--version") || args[i].equals("-v"))
{
handleVersionArg();
//we're supposed to exit after printing version info
returnAction = ACTION_EXIT;
break;
}
else if (args[i].equals("--help") || args[i].equals("-h"))
{
handleHelpArg();
//we're supposed to exit after printing the help message
returnAction = ACTION_EXIT;
break;
}
else if (args[i].equals("--debug") || args[i].equals("-d"))
{
handleDebugArg(args[i]);
continue;
}
else if (args[i].startsWith("--config="))
{
returnAction = handleConfigArg(args[i]);
if(returnAction == ACTION_ERROR)
break;
else
continue;
}
else if (args[i].equals("-c"))
{
//make sure we have at least one more argument left.
if( i == args.length - 1)
{
System.out.println("The \"-c\" option expects a directory parameter.");
returnAction = ACTION_ERROR;
break;
}
handleConfigArg(args[++i]);
continue;
}
else if (args[i].equals("--multiple") || args[i].equals("-m"))
{
handleMultipleArg(args[i]);
continue;
}
//if this is the last arg and it's not an option then it's probably
//an URI
else if ( i == args.length - 1
&& !args[i].startsWith("-"))
{
handleUri(args[i]);
}
else
{
handleUnknownArg(args[i]);
errorCode = ERROR_CODE_UNKNOWN_ARG;
returnAction = ACTION_ERROR;
break;
}
}
return returnAction;
}
/**
* Passes <tt>uriArg</tt> to our uri manager for handling.
*
* @param arg the uri that we'd like to pass to
*/
private void handleUri(String uri)
{
logger.trace("Handling uri "+ uri);
uriArgManager.handleUri(uri);
}
/**
* Instructs SIP Communicator to print logging messages to the console.
*/
private void handleDebugArg(String arg)
{
System.out.println("Option " + arg + " is not yet implemented!");
}
/**
* Instructs SIP Communicator to allow for more than a single running
* instance.
*/
private void handleMultipleArg(String arg)
{
System.out.println("Option " + arg + " is not yet implemented!");
}
/**
* Instructs SIP Communicator to allow for more than a single running
* instance.
*
* @return either ACTION_ERROR or ACTION_CONTINUE depending on whether or
* not parsing the option went fine.
*/
private int handleConfigArg(String configArg)
{
if (configArg.startsWith("--config="))
{
configArg = configArg.substring("--config=".length());
}
File configDir = new File(configArg);
configDir.mkdirs();
if(!configDir.isDirectory())
{
System.out.println("Failed to create directory " + configArg);
errorCode = ERROR_CODE_CREATE_DIR_FAILED;
return ACTION_ERROR;
}
System.setProperty(PNAME_SC_HOME_DIR_LOCATION, configDir.getParent());
System.setProperty(PNAME_SC_HOME_DIR_NAME, configDir.getName());
return ACTION_CONTINUE;
}
/**
* Prints the name and the version of this application. This method uses the
* version.properties file which is created by ant during the build process.
* If this file does not exist the method would print a default name and
* version string.
*/
private void handleVersionArg()
{
String name = getApplicationName();
String version = getVersion();
if (name == null || name.trim().length() == 0)
{
name = "SIP Communicator";
}
if (version == null || version.trim().length() == 0)
{
version = "build.by.SVN";
}
System.out.println(name + " " + version);
}
/**
* Returns the version of the SIP Communicator instance that we are
* currently running.
*
* @return a String containing the version of the SC instance we are
* currently running.
*/
private String getVersion()
{
String version = versionProperties.getProperty(PNAME_VERSION);
return version == null
? "build.by.SVN"
: version;
}
/**
* Returns the name of the application. That should be SIP Communicator
* most of the time but who knows ..
*
* @return the name of the application (i.e. SIP Communicator until we
* change our name some day.)
*/
private String getApplicationName()
{
String name = versionProperties.getProperty(PNAME_APPLICATION_NAME);
return name == null
? "SIP Communicator"
: name;
}
/**
* Prints an error message and then prints the help message.
*/
public void handleUnknownArg(String arg)
{
System.out.println("Unknown argument: " + arg);
handleHelpArg();
}
/**
* Prints a help message containing usage instructions and descriptions of
* all options currently supported by SIP Communicator.
*/
public void handleHelpArg()
{
handleVersionArg();
System.out.println("Usage: sip-communicator [OPTIONS] [uri-to-call]");
System.out.println("");
System.out.println(" -c, --config=DIR use DIR for config files");
System.out.println(" -d, --debug print debugging messages to stdout");
System.out.println(" -h, --help display this help message and exit");
System.out.println(" -m, --multiple do not ensure single instance");
System.out.println(" -v, --version display the current version and exit");
}
/**
* Returns an error code that could help identify an error when
* <tt>handleArgs</tt> returns ACTION_ERROR or 0 if everything went fine.
*
* @return an error code that could help identify an error when
* <tt>handleArgs</tt> returns ACTION_ERROR or 0 if everything went fine.
*/
public int getErrorCode()
{
return errorCode;
}
/**
* Sets the <tt>delegationPeer</tt> that would be handling all URIs passed
* as command line arguments to SIP Communicator.
*
* @param delegationPeer the <tt>delegationPeer</tt> that should handle URIs
* or <tt>null</tt> if we'd like to unset a previously set peer.
*/
public void setDelegationPeer(UriDelegationPeer delegationPeer)
{
this.uriArgManager.setDelegationPeer(delegationPeer);
}
}

@ -0,0 +1,784 @@
/*
* 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.util.launchutils;
import java.io.*;
import java.net.*;
import java.util.*;
import net.java.sip.communicator.launcher.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.launchutils.*;
/**
* This class is used to prevent from running multiple instances of SIP
* Communicator. The class binds a socket somewhere on the localhost domain and
* records its socket address in the SIP Communicator configuration directory.
*
* All following instances of SIP Communicator (and hence this class) will look
* for this record in the configuration directory and try to connect to the
* original instance through the socket address in there.
*
* @author Emil Ivov
*/
public class SipCommunicatorLock extends Thread
{
private static final Logger logger = Logger
.getLogger(SipCommunicatorLock.class);
/**
* Indicates that something went wrong. More information will probably be
* available in the console ... if anyone cares at all.
*/
public static final int LOCK_ERROR = 300;
/**
* Returned by the soft start method to indicate that we have successfully
* started and locked the configuration directory.
*/
public static final int SUCCESS = 0;
/**
* Returned by the soft start method to indicate that an instance of SIP
* Communicator has been already started and we should exit. This return
* code also indicates that all arguments were passed to that new instance.
*/
public static final int ALREADY_STARTED = 301;
/**
* The name of the file that we use to store the address and port that this
* lock is bound on.
*/
private static final String LOCK_FILE_NAME = ".lock";
/**
* The name of the property that we use to store the address that we bind on
* in this class.
*/
private static final String PNAME_LOCK_ADDRESS = "lockAddress";
/**
* The name of the property that we use to store the address that we bind on
* in this class.
*/
private static final String PNAME_LOCK_PORT = "lockPort";
/**
* The header preceding each of the arguments that we toss around between
* instances of SIP Communicator
*/
private static final String ARGUMENT = "Argument";
/**
* The name of the header that contains the number of arguments that we send
* from one instance to another.
*/
private static final String ARG_COUNT = "Arg-Count";
/**
* The name of the header that contains any error messages resulting from
* remote argument handling.
*/
private static final String ERROR_ARG = "ERROR";
/**
* The carriage return, line feed sequence (\r\n).
*/
private static final String CRLF = "\r\n";
/**
* The number of milliseconds that we should wait for a remote SC instance
* to come back to us.
*/
private long LOCK_COMMUNICATION_DELAY = 50;
/**
* The socket that we use for cross instance lock and communication.
*/
private ServerSocket instanceServerSocket = null;
/**
* Tries to lock the configuration directory. If lock-ing is not possible
* because a previous instance is already running, then it transmits the
* list of args to that running instance.
* <p>
* There are three possible outcomes of this method. 1. We lock
* successfully; 2. We fail to lock because another instance of SIP
* Communicator is already running; 3. We fail to lock for some unknown
* error. Each of these cases is represented by an error code returned as a
* result.
*
* @param args
* the array of arguments that we are to submit in case an
* instance of SIP Communicator has already been started.
*
* @return an error or success code indicating the outcome of the lock
* operation.
*/
public int tryLock(String[] args)
{
// first check whether we have a file.
File lockFile = getLockFile();
if (lockFile.exists())
{
InetSocketAddress lockAddress = readLockFile(lockFile);
if (lockAddress != null)
{
// we have a valid lockAddress and hence possibly an already
// running instance of SC. Try to communicate with it.
if (interInstanceConnect(lockAddress, args) == SUCCESS)
{
return ALREADY_STARTED;
}
}
// our lockFile is probably stale and left from a previous instance.
// or an instance that is still running but is not responding.
lockFile.delete();
}
// if we get here then this means that we should go for a real lock
// initialization
// create a new socket,
// right the bind address in the file
try
{
lockFile.createNewFile();
}
catch (IOException e)
{
logger.error("Failed to create lock file", e);
}
lockFile.deleteOnExit();
return lock(lockFile);
}
/**
* Locks the configuration directory by binding our lock socket and
* recording the lock file into the configuration directory. Returns SUCCESS
* if everything goes well and ERROR if something fails. This method does
* not return the ALREADY_RUNNING code as it is assumed that this has
* already been checked before calling this method.
*
* @param lockFile
* the file that we should use to lock the configuration
* directory.
*
* @return the SUCCESS or ERROR codes defined by this class.
*/
private int lock(File lockFile)
{
InetAddress lockAddress = getRandomBindAddress();
if (lockAddress == null)
{
return LOCK_ERROR;
}
int port = getRandomPortNumber();
InetSocketAddress serverSocketAddress = new InetSocketAddress(
lockAddress, port);
writeLockFile(lockFile, serverSocketAddress);
startLockServer(serverSocketAddress);
return SUCCESS;
}
/**
* Creates and binds a socket on <tt>lockAddress</tt> and then starts a
* <tt>LockServer</tt> instance so that we would start interacting with
* other instances of SIP Communicator that are trying to start.
*
* @return the <tt>ERROR</tt> code if something goes wrong and
* <tt>SUCCESS</tt> otherwise.
*/
private int startLockServer(InetSocketAddress localAddress)
{
try
{
// check config directory
instanceServerSocket = new ServerSocket();
}
catch (IOException exc)
{
// Just checked the impl and this doesn't seem to ever be thrown
// .... ignore ...
logger.error("Couldn't create server socket", exc);
return LOCK_ERROR;
}
try
{
instanceServerSocket.bind(localAddress, 16);// Why 16? 'cos I say
// so.
}
catch (IOException exc)
{
logger.error("Couldn't create server socket", exc);
return LOCK_ERROR;
}
LockServer lockServ = new LockServer(instanceServerSocket);
lockServ.start();
return SUCCESS;
}
/**
* Returns a randomly chosen socket address using a loopback interface (or
* another one in case the loopback is not available) that we should bind
* on.
*
* @return an InetAddress (most probably a loopback) that we can use to bind
* our semaphore socket on.
*/
private InetAddress getRandomBindAddress()
{
NetworkInterface loopback;
try
{
// find a loopback interface
Enumeration<NetworkInterface> interfaces;
try
{
interfaces = NetworkInterface.getNetworkInterfaces();
}
catch (SocketException exc)
{
// I don't quite understand why this would happen ...
logger.error(
"Failed to obtain a list of the local interfaces.",
exc);
return null;
}
loopback = null;
while (interfaces.hasMoreElements())
{
NetworkInterface iface = interfaces.nextElement();
if (iface.isLoopback())
{
loopback = iface;
break;
}
}
// if we didn't find a loopback (unlikely but possible)
// return the first available interface on this machine
if (loopback == null)
{
loopback = NetworkInterface.getNetworkInterfaces()
.nextElement();
}
}
catch (SocketException exc)
{
// I don't quite understand what could possibly cause this ...
logger.error("Could not find the loopback interface", exc);
return null;
}
// get the first address on the loopback.
InetAddress addr = loopback.getInetAddresses().nextElement();
return addr;
}
/**
* Returns a random port number that we can use to bind a socket on.
*
* @return a random port number that we can use to bind a socket on.
*/
private int getRandomPortNumber()
{
return (int) (Math.random() * 64509) + 1025;
}
/**
* Parses the <tt>lockFile</tt> into a standard Properties Object and
* verifies it for completeness. The method also tries to validate the
* contents of <tt>lockFile</tt> and asserts presence of all properties
* mandated by this version.
*
* @param lockFile
* the file that we are to parse.
*
* @return the <tt>SocketAddress</tt> that we should use to communicate with
* a possibly already running version of SIP Communicator.
*/
private InetSocketAddress readLockFile(File lockFile)
{
Properties lockProperties = new Properties();
try
{
lockProperties.load(new FileInputStream(lockFile));
}
catch (Exception exc)
{
logger.error("Failed to read lock properties.", exc);
return null;
}
String lockAddressStr = lockProperties.getProperty(PNAME_LOCK_ADDRESS);
if (lockAddressStr == null)
{
logger.error("Lock file contains no lock address.");
return null;
}
String lockPort = lockProperties.getProperty(PNAME_LOCK_PORT);
if (lockPort == null)
{
logger.error("Lock file contains no lock port.");
return null;
}
InetAddress lockAddress = findLocalAddress(lockAddressStr);
if (lockAddress == null)
{
logger.error(lockAddressStr + " is not a valid local address.");
return null;
}
int port;
try
{
port = Integer.parseInt(lockPort);
}
catch (NumberFormatException exc)
{
logger.error(lockPort + " is not a valid port number.", exc);
return null;
}
InetSocketAddress lockSocketAddress = new InetSocketAddress(
lockAddress, port);
return lockSocketAddress;
}
/**
* Records our <tt>lockAddress</tt> into <tt>lockFile</tt> using the
* standard properties format.
*
* @param lockFile
* the file that we should store the address in.
* @param lockAddress
* the address that we have to record.
*
* @return <tt>SUCCESS</tt> upon success and <tt>ERROR</tt> if we fail to
* store the file.
*/
private int writeLockFile(File lockFile, InetSocketAddress lockAddress)
{
Properties lockProperties = new Properties();
lockProperties.setProperty(PNAME_LOCK_ADDRESS, lockAddress.getAddress()
.getHostAddress());
lockProperties.setProperty(PNAME_LOCK_PORT, Integer
.toString(lockAddress.getPort()));
try
{
lockProperties.store(new FileOutputStream(lockFile),
"SIP Communicator lock file. This file will be automatically"
+ "removed when execution of SIP Communicator terminates.");
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
logger.error("Failed to create lock file.", e);
return LOCK_ERROR;
}
return SUCCESS;
}
/**
* Returns a reference to the file that we should be using to lock SIP
* Communicator's home directory, whether it exists or not.
*
* @return a reference to the file that we should be using to lock SIP
* Communicator's home directory.
*/
private File getLockFile()
{
String homeDirLocation = System
.getProperty(SIPCommunicator.PNAME_SC_HOME_DIR_LOCATION);
String homeDirName = System
.getProperty(SIPCommunicator.PNAME_SC_HOME_DIR_NAME);
String fileSeparator = System.getProperty("file.separator");
String fullLockFileName = homeDirLocation + fileSeparator + homeDirName
+ fileSeparator + LOCK_FILE_NAME;
return new File(fullLockFileName);
}
/**
* Returns an <tt>InetAddress</tt> instance corresponding to
* <tt>addressStr</tt> or <tt>null</tt> if no such address exists on the
* local interfaces.
*
* @param addressStr
* the address string that we are trying to resolve into an
* <tt>InetAddress</tt>
*
* @return an <tt>InetAddress</tt> instance corresponding to
* <tt>addressStr</tt> or <tt>null</tt> if none of the local
* interfaces has such an address.
*/
private InetAddress findLocalAddress(String addressStr)
{
Enumeration<NetworkInterface> ifaces;
try
{
ifaces = NetworkInterface.getNetworkInterfaces();
}
catch (SocketException exc)
{
logger.error(
"Could not extract the list of local intefcaces.",
exc);
return null;
}
// loop through local interfaces
while (ifaces.hasMoreElements())
{
NetworkInterface iface = ifaces.nextElement();
Enumeration<InetAddress> addreses = iface.getInetAddresses();
// loop iface addresses
while (addreses.hasMoreElements())
{
InetAddress addr = addreses.nextElement();
if (addr.getHostAddress().equals(addressStr))
return addr;
}
}
return null;
}
/**
* Initializes a client TCP socket, connects if to <tt>sockAddr</tt> and
* sends all <tt>args</tt> to it.
*
* @param sockAddr the address that we are to connect to.
* @param args the args that we need to send to <tt>sockAddr</tt>.
*
* @return <tt>SUCCESS</tt> upond success and <tt>ERROR</tt> if anything
* goes wrong.
*/
private int interInstanceConnect(InetSocketAddress sockAddr, String[] args)
{
try
{
Socket interInstanceSocket = new Socket(sockAddr.getAddress(),
sockAddr.getPort());
LockClient lockClient = new LockClient(interInstanceSocket);
lockClient.start();
PrintStream printStream = new PrintStream(interInstanceSocket
.getOutputStream());
printStream.print(ARG_COUNT + "=" + args.length + CRLF);
for (int i = 0; i < args.length; i++)
{
printStream.print(ARGUMENT + "=" + args[i] + CRLF);
}
lockClient.waitForReply(LOCK_COMMUNICATION_DELAY);
//NPEs are handled in catch so no need to check whether or not we
//actually have a reply.
String serverReadArgCountStr = lockClient.message
.substring((ARG_COUNT + "=").length());
int serverReadArgCount = Integer.parseInt(serverReadArgCountStr);
logger.debug("Server read " + serverReadArgCount + " args.");
if(serverReadArgCount != args.length)
return LOCK_ERROR;
printStream.flush();
printStream.close();
interInstanceSocket.close();
}
//catch IOExceptions, NPEs and NumberFormatExceptions here.
catch (Exception e)
{
logger.debug("Failed to connect to a running sc instance.");
return LOCK_ERROR;
}
return SUCCESS;
}
/**
* We use this thread to communicate with an already running instance of SIP
* Communicator. This thread will listen for a reply to a message that we've
* sent to the other instance. We will wait for this message for a maximum
* of <tt>runDuration</tt> milliseconds and then consider the remote
* instance dead.
*/
private class LockClient extends Thread
{
/**
* The <tt>String</tt> that we've read from the socketInputStream
*/
public String message = null;
/**
* The socket that this <tt>LockClient</tt> is created to read from.
*/
private Socket interInstanceSocket = null;
/**
* Creates a <tt>LockClient</tt> that should read whatever data we
* receive on <tt>sockInputStream</tt>.
*
* @param commSocket
* the socket that this client should be reading from.
*/
public LockClient(Socket commSocket)
{
super(LockClient.class.getName());
setDaemon(true);
this.interInstanceSocket = commSocket;
}
/**
* Blocks until a reply has been received or until run<tt>Duration</tt>
* milliseconds had passed.
*
* @param runDuration the number of seconds to wait for a reply from
* the remote instance
*/
public void waitForReply(long runDuration)
{
try
{
synchronized(this)
{
//return if we have already received a message.
if(message != null)
return;
wait(runDuration);
}
logger.debug("Done waiting. Will close socket");
interInstanceSocket.close();
}
catch (Exception exception)
{
logger.error("Failed to close our inter instance input stream",
exception);
}
}
/**
* Simply collects everything that we read from the InputStream that
* this <tt>InterInstanceCommunicationClient</tt> was created with.
*/
public void run()
{
try
{
BufferedReader lineReader = new BufferedReader(
new InputStreamReader(interInstanceSocket
.getInputStream()));
//we only need to read a single line and then bail out.
message = lineReader.readLine();
logger.debug("Message is " + message);
synchronized(this)
{
notifyAll();
}
}
catch (IOException exc)
{
// does not necessarily mean something is wrong. Could be
// that we got tired of waiting and want to quit.
logger.info("An IOException is thrown while reading sock", exc);
}
}
}
/**
* We start this thread when running SIP Communicator as a means of
* notifying others that this is
*/
private class LockServer extends Thread
{
private boolean keepAccepting = true;
/**
* The socket that we use for cross instance lock and communication.
*/
private ServerSocket lockSocket = null;
/**
* Creates an instance of this <tt>LockServer</tt> wrapping the
* specified <tt>serverSocket</tt>. It is expected that the serverSocket
* will be already bound and ready to accept.
*
* @param serverSocket
* the serverSocket that we should use for inter instance
* communication.
*/
public LockServer(ServerSocket serverSocket)
{
super(LockServer.class.getName());
setDaemon(true);
this.lockSocket = serverSocket;
}
public void run()
{
try
{
while (keepAccepting)
{
Socket instanceSocket = lockSocket.accept();
new LockServerConnectionProcessor(instanceSocket).start();
}
}
catch (Exception exc)
{
logger.warn("Someone tried ", exc);
}
}
}
/**
* We use this thread to handle individual messages in server side inter
* instance communication.
*/
private class LockServerConnectionProcessor extends Thread
{
/**
* The socket that we will be using to communicate with the fellow SIP
* Communicator instance..
*/
private Socket connectionSocket = null;
/**
* Creates an instance of <tt>LockServerConnectionProcessor</tt> that
* would handle parameters received through the
* <tt>connectionSocket</tt>.
*
* @param connectedSocket
* the socket that we will be using to read arguments from
* the remote SIP Communicator instance.
*/
public LockServerConnectionProcessor(Socket connectionSocket)
{
this.connectionSocket = connectionSocket;
}
/**
* Starts reading messages arriving through the connection socket.
*/
public void run()
{
InputStream is;
PrintWriter printer;
try
{
is = connectionSocket.getInputStream();
printer = new PrintWriter(connectionSocket
.getOutputStream());
}
catch (IOException exc)
{
logger.warn("Failed to read arguments from another SC instance",
exc);
return;
}
ArrayList<String> argsList = new ArrayList<String>();
logger.debug("Handling incoming connection");
int argCount = 1024;
try
{
while (true)
{
BufferedReader lineReader = new BufferedReader(
new InputStreamReader(is));
String line = lineReader.readLine();
logger.debug(line);
if (line.startsWith(ARG_COUNT))
{
argCount = Integer.parseInt(line
.substring((ARG_COUNT + "=").length()));
}
else if (line.startsWith(ARGUMENT))
{
String arg = line.substring((ARGUMENT + "=").length());
argsList.add(arg);
}
else
{
// ignore unknown headers.
}
if (argCount <= argsList.size())
break;
}
// first tell the remote application that everything went OK
// and end the connection so that it could exit
printer.print(ARG_COUNT + "=" + argCount + CRLF);
printer.close();
connectionSocket.close();
// now let's handle what we've got
String[] args = new String[argsList.size()];
LaunchArgHandler.getInstance().handleArgs(
argsList.toArray(args));
}
catch (IOException exc)
{
logger.info("An IOException is thrown while "
+ "processing remote args", exc);
printer.print(ERROR_ARG + "=" + exc.getMessage());
}
}
}
}

@ -0,0 +1,84 @@
/*
* 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.util.launchutils;
import java.util.*;
import net.java.sip.communicator.util.*;
/**
* The <tt>UriArgManager</tt> implements an utility for handling URIs that have
* been passed as command line arguments. The class maintains a list of
* registered delegates that do the actual URI handling. The UriArgDelegator
* is previewed for use with SIP Communicator argdelegation service. It would
* therefore record all URIs until the corresponding DelegationPeer has been
* registered with the UriArgManager.
*
* @author Emil Ivov
*/
class UriArgManager
{
private static final Logger logger = Logger.getLogger(UriArgManager.class);
/**
* The delegation peer that we pass arguments to. This peer is going to
* get set only after Felix starts and all its services have been properly
* loaded.
*/
private UriDelegationPeer uriDelegationPeer = null;
/**
* We use this list to store arguments that we have been asked to handle
* before we had a registered delegation peer.
*/
private List<String> recordedArgs = new LinkedList<String>();
/**
* Passes the <tt>uriArg</tt> to the uri delegation peer or, in case
* no peer is currently registered, stores it and keeps it until one
* appears.
*
* @param uriArg the uri argument that we'd like to delegate to our peer.
*/
protected void handleUri(String uriArg)
{
synchronized(recordedArgs)
{
if(uriDelegationPeer == null)
{
recordedArgs.add(uriArg);
return;
}
}
uriDelegationPeer.handleUri(uriArg);
}
/**
* Sets a delegation peer that we can now use to pass arguments to and
* makes it handle all arguments that have been already registered.
*
* @param delegationPeer the delegation peer that we can use to deliver
* command line URIs to.
*/
public void setDelegationPeer(UriDelegationPeer delegationPeer)
{
synchronized(recordedArgs)
{
logger.trace("Someone set a delegationPeer. "
+"Will dispatch "+ recordedArgs.size() +" args");
this.uriDelegationPeer = delegationPeer;
for (String arg : recordedArgs)
{
logger.trace("Dispatching arg: " + arg);
uriDelegationPeer.handleUri(arg);
}
recordedArgs.clear();
}
}
}

@ -0,0 +1,26 @@
/*
* 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.util.launchutils;
/**
* The <tt>UriDelegationPeer</tt> is used as a mechanism to pass arguments from
* the UriArgManager which resides in "launcher space" to our argument
* delegation service implementation that lives as an osgi bundle. An instance
* of this peer is created from within the argument delegation service impl
* and is registered with the UriArgManager.
*
* @author Emil Ivov
*/
public interface UriDelegationPeer
{
/**
* Handles <tt>uriArg</tt> in whatever way it finds fit.
*
* @param uriArg the uri argument that this delegate has to handle.
*/
public void handleUri(String uriArg);
}
Loading…
Cancel
Save