aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Ivov <emcho@jitsi.org>2008-09-06 10:53:24 +0000
committerEmil Ivov <emcho@jitsi.org>2008-09-06 10:53:24 +0000
commit88a0036d18b21387db6d0053bce7acf86a471d34 (patch)
tree63101e39a2ccbdcc65074da3e91bf7121cddead2
parent45288c70242e5875cf44984b3b6e9bcca84ca3f8 (diff)
downloadjitsi-88a0036d18b21387db6d0053bce7acf86a471d34.zip
jitsi-88a0036d18b21387db6d0053bce7acf86a471d34.tar.gz
jitsi-88a0036d18b21387db6d0053bce7acf86a471d34.tar.bz2
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)
-rw-r--r--build.xml68
-rw-r--r--lib/felix.client.run.properties19
-rw-r--r--lib/logging.properties17
-rw-r--r--lib/testing.properties14
-rw-r--r--src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java94
-rw-r--r--src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java186
-rw-r--r--src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf13
-rw-r--r--src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java78
-rw-r--r--src/net/java/sip/communicator/impl/gui/UIServiceImpl.java27
-rwxr-xr-xsrc/net/java/sip/communicator/impl/gui/main/MainFrame.java78
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java106
-rw-r--r--src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java208
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java8
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java11
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java7
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java32
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java185
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java524
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf2
-rw-r--r--src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java75
-rw-r--r--src/net/java/sip/communicator/launcher/SIPCommunicator.java59
-rw-r--r--src/net/java/sip/communicator/service/argdelegation/UriHandler.java35
-rw-r--r--src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf5
-rw-r--r--src/net/java/sip/communicator/service/gui/UIService.java102
-rw-r--r--src/net/java/sip/communicator/service/protocol/OperationFailedException.java7
-rw-r--r--src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java28
-rw-r--r--src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java404
-rw-r--r--src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java784
-rw-r--r--src/net/java/sip/communicator/util/launchutils/UriArgManager.java84
-rw-r--r--src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java26
30 files changed, 2907 insertions, 379 deletions
diff --git a/build.xml b/build.xml
index b018739..943443f 100644
--- a/build.xml
+++ b/build.xml
@@ -358,7 +358,7 @@
<replace file="${src}/net/java/sip/communicator/impl/version/NightlyBuildID.java"
token="build.id" value="${build.label}"/>
</target>
-
+
<!-- SIP Communicator Version -->
<target name="version" depends="-pre-version">
<!-- Recompile ant task classes-->
@@ -378,7 +378,7 @@
<sip-communicator-version property="sip-communicator.version" />
- <echo message="SIP Communicator version ${sip-communicator.version}" />
+ <echo message="SIP Communicator version ${sip-communicator.version}" />
</target>
<!--INIT-->
@@ -418,7 +418,7 @@
<!--internal-target- prepare to run a single Service Impl Compatibility Kit -->
<!-- extract the simple Test class name. -->
<basename property="simple.test.names" file="${test.name}"/>
- <echo message="single test prepared: ${simple.test.names}" />
+ <echo message="single test prepared: ${simple.test.names}" />
</target>
<!--internal-target- prepare to run all known Service Impl Compatibility Kit -->
@@ -457,7 +457,7 @@
<!-- Tell the slick runner about TestSutes we've preregistered. -->
<sysproperty key="net.java.sip.communicator.slick.runner.TEST_LIST"
value="${simple.test.names}"/>
-
+
<!-- Tell java.util.logging about our logging preferences -->
<sysproperty key="java.util.logging.config.file"
value="${lib}/logging.properties"/>
@@ -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"
@@ -1052,7 +1070,7 @@ javax.swing.event, javax.swing.border"/>
<zipfileset src="${lib.noinst}/commons-logging.jar" prefix=""/>
</jar>
</target>
-
+
<!-- BUNDLE-SSH -->
<target name="bundle-ssh">
<!-- Creates a bundle containing the SSH impl of the protocol provider.-->
@@ -1407,7 +1425,7 @@ javax.swing.event, javax.swing.border"/>
<zipfileset src="${lib.noinst}/aclibico-2.1.jar" prefix=""/>
</jar>
</target>
-
+
<!-- BUNDLE-RSS-SLICK -->
<!-- Creates a bundle containing the slick for the Gibberish protocol
provider.-->
@@ -1563,7 +1581,7 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/impl/protocol/dict"/>
</jar>
</target>
-
+
<!-- BUNDLE-PLUGIN-DICTACCREGWIZZ -->
<target name="bundle-plugin-dictaccregwizz">
<!-- Creates a bundle for the plugin Dict Account Registration
@@ -1574,7 +1592,7 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/plugin/dictaccregwizz"/>
</jar>
</target>
-
+
<!--BUNDLE-UpdateCheckPlugin-->
<target name="bundle-updatecheckplugin">
<jar compress="false" destfile="${bundles.dest}/updatechecker.jar"
@@ -1583,7 +1601,7 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/plugin/updatechecker" />
</jar>
</target>
-
+
<!--BUNDLE-AutoAwayPlugin-->
<target name="bundle-plugin-autoaway">
<jar compress="false" destfile="${bundles.dest}/autoaway.jar"
@@ -1625,7 +1643,7 @@ javax.swing.event, javax.swing.border"/>
<zipfileset src="${lib.noinst}/KeybindingUtil.jar" prefix=""/>
</jar>
</target>
-
+
<!--BUNDLE-PLUGIN-KeybindingChooser-->
<target name="bundle-plugin-keybindingChooser">
<jar compress="false" destfile="${bundles.dest}/keybindingChooser.jar"
@@ -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>
-</project>
+
+ <!-- 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> \ No newline at end of file
diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties
index 7be7bd7..9be3558 100644
--- a/lib/felix.client.run.properties
+++ b/lib/felix.client.run.properties
@@ -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
@@ -93,8 +95,8 @@ felix.auto.start.50= \
reference:file:sc-bundles/protocol-dict.jar
felix.auto.start.55= \
- reference:file:sc-bundles/meta-cl.jar
-
+ reference:file:sc-bundles/meta-cl.jar
+
felix.auto.start.60= \
reference:file:sc-bundles/history.jar \
@@ -130,14 +132,19 @@ 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 \
reference:file:sc-bundles/dictaccregwizz.jar
felix.auto.start.70= \
- reference:file:sc-bundles/simpleaccreg.jar
+ 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.
diff --git a/lib/logging.properties b/lib/logging.properties
index 7162d2e..902a3a5 100644
--- a/lib/logging.properties
+++ b/lib/logging.properties
@@ -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:
diff --git a/lib/testing.properties b/lib/testing.properties
index 47cab6f..194ee4f 100644
--- a/lib/testing.properties
+++ b/lib/testing.properties
@@ -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 \
diff --git a/src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java b/src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java
new file mode 100644
index 0000000..99ed0e3
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java
@@ -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;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java b/src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java
new file mode 100644
index 0000000..9d58951
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java
@@ -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);
+ }
+ }
+
+
+}
diff --git a/src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf b/src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf
new file mode 100644
index 0000000..ba896e7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf
@@ -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
diff --git a/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java b/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java
index 1ab0438..5218f53 100644
--- a/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java
+++ b/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java
@@ -91,7 +91,7 @@ public class MclStorageManager
* A reference to the file containing the locally stored meta contact list.
*/
private File contactlistFile = null;
-
+
/**
* A reference to the failsafe transaction used with the contactlist file.
*/
@@ -174,13 +174,13 @@ public class MclStorageManager
*/
private static final String META_CONTACT_DISPLAY_NAME_NODE_NAME
= "display-name";
-
+
/**
* The name of the XML node that contains meta contact detail.
*/
private static final String META_CONTACT_DETAIL_NAME_NODE_NAME
= "detail";
-
+
/**
* The name of the XML attribute that contains detail name.
*/
@@ -190,7 +190,7 @@ public class MclStorageManager
* The name of the XML attribute that contains detail value.
*/
private static final String DETAIL_VALUE_ATTR_NAME = "value";
-
+
/**
* The name of the XML node that contains information of a proto contact
*/
@@ -400,7 +400,7 @@ public class MclStorageManager
} catch (IllegalStateException e) {
logger.error("the contactlist file is missing", e);
}
-
+
// really write the modification
OutputStream stream = new FileOutputStream(contactlistFile);
XMLUtils.indentedWriteXML(contactListDocument,
@@ -573,14 +573,14 @@ public class MclStorageManager
{
if(!isStarted())
return;
-
+
//we don't want to receive meta contact events triggerred by ourselves
//so we stop listening. it is possible but very unlikely that other
//events, not triggerred by us are received while we're off the channel
//but that would be a very bizzare case ..... I guess we got to live
//with the risk.
this.mclServiceImpl.removeMetaContactListListener(this);
-
+
try
{
Element root = findMetaContactGroupNode(
@@ -591,12 +591,12 @@ public class MclStorageManager
// If there is no root, there is definitely something wrong
// really broken file will create it again
logger.fatal("The contactlist file is recreated cause its broken");
-
+
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
contactListDocument = builder.newDocument();
-
+
initVirginDocument(mclServiceImpl, contactListDocument);
//write the contact list so that it is there for the parser
@@ -608,11 +608,11 @@ public class MclStorageManager
//parse the group node and extract all its child groups and contacts
processGroupXmlNode(mclServiceImpl, accountID, root
, null, null);
-
+
//now save the contact list in case it has changed
scheduleContactListStorage();
}
-
+
}catch(Throwable exc)
{
// catch everything because we MUST NOT disturb the thread
@@ -779,7 +779,7 @@ public class MclStorageManager
//contain any contacts matching the currently parsed account id.
if (protoContacts.size() < 1)
continue;
-
+
// Extract contact details.
Hashtable details = new Hashtable();
try
@@ -787,7 +787,7 @@ public class MclStorageManager
List detailsNodes = XMLUtils.findChildren(
(Element) currentMetaContactNode
, META_CONTACT_DETAIL_NAME_NODE_NAME);
- for (int j = 0; j < detailsNodes.size(); j++)
+ for (int j = 0; j < detailsNodes.size(); j++)
{
Element e = (Element)detailsNodes.get(j);
String name = e.getAttribute(DETAIL_NAME_ATTR_NAME);
@@ -795,7 +795,7 @@ public class MclStorageManager
Object detailsObj = details.get(name);
if(detailsObj == null)
- {
+ {
ArrayList ds = new ArrayList();
ds.add(value);
details.put(name, ds);
@@ -808,7 +808,7 @@ public class MclStorageManager
{
// catch any exception from loading contacts
// that will prevent loading the contact
- logger.error("Cannot load details for contact node " +
+ logger.error("Cannot load details for contact node " +
currentMetaContactNode, ex);
}
@@ -1203,7 +1203,7 @@ public class MclStorageManager
* @param evt the MetaContactListEvent containing the corresponding contact
*/
public void metaContactGroupAdded(MetaContactGroupEvent evt)
- {
+ {
//if the group was created as an encapsulator of a non persistent proto
//group then we'll ignore it.
if (evt.getSourceProtoGroup() != null
@@ -1449,7 +1449,7 @@ public class MclStorageManager
+ evt.getSourceMetaContact(), ex);
}
}
-
+
/**
* Indicates that a MetaContact has been modified.
* @param evt the MetaContactModifiedEvent containing the corresponding contact
@@ -1457,7 +1457,7 @@ public class MclStorageManager
public void metaContactModified(MetaContactModifiedEvent evt)
{
String name = evt.getModificationName();
-
+
Element metaContactNode = findMetaContactNode(
evt.getSourceMetaContact().getMetaUID());
@@ -1469,26 +1469,26 @@ public class MclStorageManager
+ evt.getSourceMetaContact());
return;
}
-
+
Object oldValue = evt.getOldValue();
Object newValue = evt.getNewValue();
-
+
boolean isChanged = false;
-
+
if(oldValue == null && newValue != null)
{
// indicates add
-
+
if(!(newValue instanceof String))
return;
-
+
Element detailElement = contactListDocument.createElement(
META_CONTACT_DETAIL_NAME_NODE_NAME);
-
+
detailElement.setAttribute(DETAIL_NAME_ATTR_NAME, name);
- detailElement.setAttribute(DETAIL_VALUE_ATTR_NAME,
+ detailElement.setAttribute(DETAIL_VALUE_ATTR_NAME,
(String)newValue);
-
+
metaContactNode.appendChild(detailElement);
isChanged = true;
}
@@ -1506,7 +1506,7 @@ public class MclStorageManager
, name);
ArrayList nodesToRemove = new ArrayList();
- for (int i = 0; i < nodes.size(); i++)
+ for (int i = 0; i < nodes.size(); i++)
{
Element e = (Element)nodes.get(i);
if(valuesToRemove.contains(
@@ -1515,8 +1515,8 @@ public class MclStorageManager
nodesToRemove.add(e);
}
}
-
- for (int i = 0; i < nodesToRemove.size(); i++)
+
+ for (int i = 0; i < nodesToRemove.size(); i++)
{
Element e = (Element)nodesToRemove.get(i);
metaContactNode.removeChild(e);
@@ -1534,7 +1534,7 @@ public class MclStorageManager
, name);
Element elementToRemove = null;
- for (int i = 0; i < nodes.size(); i++)
+ for (int i = 0; i < nodes.size(); i++)
{
Element e = (Element)nodes.get(i);
if(e.getAttribute(DETAIL_VALUE_ATTR_NAME).equals(oldValue))
@@ -1543,12 +1543,12 @@ public class MclStorageManager
break;
}
}
-
+
if(elementToRemove == null)
return;
-
+
metaContactNode.removeChild(elementToRemove);
-
+
isChanged = true;
}
}
@@ -1562,7 +1562,7 @@ public class MclStorageManager
, name);
Element changedElement = null;
- for (int i = 0; i < nodes.size(); i++)
+ for (int i = 0; i < nodes.size(); i++)
{
Element e = (Element)nodes.get(i);
if(e.getAttribute(DETAIL_VALUE_ATTR_NAME).equals(oldValue))
@@ -1571,16 +1571,16 @@ public class MclStorageManager
break;
}
}
-
+
if(changedElement == null)
return;
-
- changedElement.setAttribute(DETAIL_VALUE_ATTR_NAME,
+
+ changedElement.setAttribute(DETAIL_VALUE_ATTR_NAME,
(String)newValue);
-
+
isChanged = true;
}
-
+
if(!isChanged)
return;
diff --git a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
index 689e941..228b8bf 100644
--- a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
@@ -329,7 +329,7 @@ public class UIServiceImpl
/**
* Unregisters the given <tt>ExportedWindow</tt> from the list of windows
* that could be accessed from other bundles.
- *
+ *
* @param window the window to no longer be exported
*/
public void unregisterExportedWindow(ExportedWindow window)
@@ -482,7 +482,7 @@ public class UIServiceImpl
/**
* Returns the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>.
- *
+ *
* @param chatRoom the <tt>ChatRoom</tt> for which the searched chat is
* about.
* @return the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>.
@@ -560,6 +560,25 @@ public class UIServiceImpl
}
/**
+ * 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
*/
@@ -683,7 +702,7 @@ public class UIServiceImpl
/**
* Returns the corresponding <tt>BorderLayout</tt> constraint from the given
* <tt>Container</tt> constraint.
- *
+ *
* @param containerConstraints constraints defined in the <tt>Container</tt>
* @return the corresponding <tt>BorderLayout</tt> constraint from the given
* <tt>Container</tt> constraint.
@@ -710,7 +729,7 @@ public class UIServiceImpl
return layoutConstraint;
}
-
+
private class DefaultPluginComponent implements PluginComponent
{
private Component component;
diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
index 653a8bd..862fa9a 100755
--- a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
+++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
@@ -61,7 +61,7 @@ public class MainFrame
private JPanel contactListPanel = new JPanel(new BorderLayout());
private JPanel mainPanel = new JPanel(new BorderLayout(0, 5));
-
+
private MainMenu menu;
private CallManager callManager;
@@ -171,7 +171,7 @@ public class MainFrame
this.getContentPane().add(northPanel, BorderLayout.NORTH);
this.getContentPane().add(mainPanel, BorderLayout.CENTER);
}
-
+
/**
* Sets frame size and position.
*/
@@ -341,7 +341,7 @@ public class MainFrame
multiUserChat.addInvitationListener(multiUserChatManager);
multiUserChat.addInvitationRejectionListener(multiUserChatManager);
multiUserChat.addPresenceListener(multiUserChatManager);
-
+
this.getChatRoomsListPanel()
.getChatRoomsList()
.addChatServer(protocolProvider, multiUserChat);
@@ -357,7 +357,7 @@ public class MainFrame
{
return ((LinkedHashMap)protocolProviders.clone()).keySet().iterator();
}
-
+
/**
* Returns the protocol provider associated to the account given
* by the account user identifier.
@@ -429,10 +429,10 @@ public class MainFrame
public void addAccount(ProtocolProviderService protocolProvider)
{
if (!getStatusPanel().containsAccount(protocolProvider)) {
-
+
logger.trace("Add the following account to the status bar: "
+ protocolProvider.getAccountID().getAccountAddress());
-
+
this.getStatusPanel().addAccount(protocolProvider);
//request the focus in the contact list panel, which
@@ -440,7 +440,7 @@ public class MainFrame
this.tabbedPane.getContactListPanel().getContactList()
.requestFocus();
}
-
+
if(!callManager.containsCallAccount(protocolProvider)
&& getTelephonyOpSet(protocolProvider) != null) {
callManager.addCallAccount(protocolProvider);
@@ -503,10 +503,10 @@ public class MainFrame
{
OperationSet opSet
= protocolProvider.getOperationSet(OperationSetPresence.class);
-
+
if(opSet != null && opSet instanceof OperationSetPresence)
return (OperationSetPresence) opSet;
-
+
return null;
}
@@ -521,13 +521,13 @@ public class MainFrame
*/
public OperationSetWebContactInfo getWebContactInfoOpSet(
ProtocolProviderService protocolProvider)
- {
+ {
OperationSet opSet
= protocolProvider.getOperationSet(OperationSetWebContactInfo.class);
-
+
if(opSet != null && opSet instanceof OperationSetWebContactInfo)
return (OperationSetWebContactInfo) opSet;
-
+
return null;
}
@@ -544,13 +544,13 @@ public class MainFrame
{
OperationSet opSet
= protocolProvider.getOperationSet(OperationSetBasicTelephony.class);
-
+
if(opSet != null && opSet instanceof OperationSetBasicTelephony)
return (OperationSetBasicTelephony) opSet;
-
+
return null;
}
-
+
/**
* Returns the multi user chat operation set for the given protocol provider.
*
@@ -564,10 +564,10 @@ public class MainFrame
{
OperationSet opSet
= protocolProvider.getOperationSet(OperationSetMultiUserChat.class);
-
+
if(opSet != null && opSet instanceof OperationSetMultiUserChat)
return (OperationSetMultiUserChat) opSet;
-
+
return null;
}
@@ -598,7 +598,7 @@ public class MainFrame
{
/**
* Indicates that a contact has changed its status.
- *
+ *
* @param evt the presence event containing information about the
* contact status change
*/
@@ -716,7 +716,7 @@ public class MainFrame
ConfigurationManager.setApplicationVisible(false);
}
}
-
+
public void windowClosed(WindowEvent e)
{
if(GuiActivator.getUIService().getExitOnMainWindowClose())
@@ -738,7 +738,7 @@ public class MainFrame
//System.exit(0);
}
}
- }
+ }
/**
* Returns the class that manages user login.
@@ -771,7 +771,7 @@ public class MainFrame
{
return this.tabbedPane.getContactListPanel();
}
-
+
/**
* Returns the panel containing the chat rooms list.
* @return the panel containing the chat rooms list
@@ -802,7 +802,7 @@ public class MainFrame
// Should remove all participant panels explicetly, thus removing also
// all related dialogs (like dialpad for example).
callPanel.removeDialogs();
-
+
tabbedPane.remove(callPanel);
Component c = getSelectedTab();
@@ -1180,7 +1180,7 @@ public class MainFrame
};
/**
- *
+ *
* @param protocolProvider
* @param contactHandler
*/
@@ -1194,7 +1194,7 @@ public class MainFrame
/**
* Returns the <tt>ContactEventHandler</tt> registered for this protocol
* provider.
- *
+ *
* @param protocolProvider the <tt>ProtocolProviderService</tt> for which
* we are searching a <tt>ContactEventHandler</tt>.
* @return the <tt>ContactEventHandler</tt> registered for this protocol
@@ -1207,7 +1207,7 @@ public class MainFrame
}
/**
- *
+ *
* @param protocolProvider
* @return
*/
@@ -1434,7 +1434,7 @@ public class MainFrame
/**
* Paints the logo bar.
- *
+ *
* @param g the <tt>Graphics</tt> object used to paint the background
* image of this logo bar.
*/
@@ -1538,7 +1538,7 @@ public class MainFrame
{
this.setExtendedState(JFrame.ICONIFIED);
}
-
+
/**
* Implements <code>isVisible</code> in the UIService interface. Checks if
* the main application window is visible.
@@ -1559,7 +1559,7 @@ public class MainFrame
else
return false;
}
-
+
/**
* Implements <code>setVisible</code> in the UIService interface. Shows or
* hides the main application window depending on the parameter
@@ -1576,15 +1576,15 @@ public class MainFrame
public void run()
{
if(isVisible)
- {
- MainFrame.this.addNativePlugins();
- MainFrame.super.setVisible(isVisible);
- MainFrame.super.toFront();
- }
- else
- {
- MainFrame.super.setVisible(isVisible);
- }
+ {
+ MainFrame.this.addNativePlugins();
+ MainFrame.super.setVisible(isVisible);
+ MainFrame.super.toFront();
+ }
+ else
+ {
+ MainFrame.super.setVisible(isVisible);
+ }
}
});
}
@@ -1592,7 +1592,7 @@ public class MainFrame
/**
* Adds the given component with to the container corresponding to the
* given constraints.
- *
+ *
* @param c the component to add
* @param constraints the constraints determining the container
*/
@@ -1632,7 +1632,7 @@ public class MainFrame
/**
* Removes the given component from the container corresponding to the given
* constraints.
- *
+ *
* @param c the component to remove
* @param constraints the constraints determining the container
*/
diff --git a/src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java b/src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java
new file mode 100644
index 0000000..03fe01e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java
@@ -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;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java b/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java
index 9eafcd4..c67c0c6 100644
--- a/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java
@@ -71,7 +71,7 @@ public class MessageHistoryServiceImpl
private ConfigurationService configService;
private MessageHistoryPropertyChangeListener msgHistoryPropListener;
-
+
private static ResourceManagementService resourcesService;
public HistoryService getHistoryService()
@@ -96,7 +96,7 @@ public class MessageHistoryServiceImpl
Hashtable readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
-
+
Iterator iter = readers.keySet().iterator();
while (iter.hasNext())
{
@@ -141,7 +141,7 @@ public class MessageHistoryServiceImpl
Hashtable readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
-
+
Iterator iter = readers.keySet().iterator();
while (iter.hasNext())
{
@@ -186,7 +186,7 @@ public class MessageHistoryServiceImpl
Hashtable readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
-
+
Iterator iter = readers.keySet().iterator();
while (iter.hasNext())
{
@@ -353,11 +353,11 @@ public class MessageHistoryServiceImpl
}
LinkedList resultAsList = new LinkedList(result);
-
+
int toIndex = count;
if(toIndex > resultAsList.size())
toIndex = resultAsList.size();
-
+
return resultAsList.subList(0, toIndex);
}
@@ -443,10 +443,10 @@ public class MessageHistoryServiceImpl
return retVal;
}
-
+
/**
* Returns the history by specified local contact
- * (if is null the default is used)
+ * (if is null the default is used)
* and by the chat room
*
* @param room The chat room
@@ -456,20 +456,20 @@ public class MessageHistoryServiceImpl
private History getHistoryForMultiChat(
Contact localContact,
ChatRoom room)
- throws IOException
+ throws IOException
{
AccountID account = room.getParentProvider().getAccountID();
return this.getHistoryForMultiChat(
- null,
+ null,
account.getAccountUniqueID(),
account.getService(),
room.getName());
}
-
+
/**
* Returns the history by specified local contact
- * (if is null the default is used)
+ * (if is null the default is used)
* and by accountUniqueID, channel and server
* used by the multichat account.
*
@@ -485,7 +485,7 @@ public class MessageHistoryServiceImpl
String account,
String server,
String channel)
- throws IOException
+ throws IOException
{
History retVal = null;
@@ -494,8 +494,8 @@ public class MessageHistoryServiceImpl
HistoryID historyId = HistoryID.createFromRawID(
new String[] { "messages",
- localId,
- account,
+ localId,
+ account,
channel + "@" + server });
if (this.historyService.isHistoryExisting(historyId))
@@ -550,9 +550,9 @@ public class MessageHistoryServiceImpl
contact,
timestamp);
}
-
+
/**
- * Used to convert HistoryRecord in ChatRoomMessageDeliveredEvent or
+ * Used to convert HistoryRecord in ChatRoomMessageDeliveredEvent or
* ChatRoomMessageReceivedEvent
* which are returned by the finder methods
*
@@ -580,10 +580,10 @@ public class MessageHistoryServiceImpl
}
else
timestamp = hr.getTimestamp();
-
+
// 5 is the index of the subject in the structure
String fromStr = hr.getPropertyValues()[5];
-
+
ChatRoomMember from = new ChatRoomMemberImpl(fromStr, room, null);
if(msg.isOutgoing)
@@ -669,15 +669,15 @@ public class MessageHistoryServiceImpl
configService = (ConfigurationService)
bundleContext.getService(refConfig);
- // Check if the message history is enabled in the configuration
+ // Check if the message history is enabled in the configuration
// service, and if not do not register the service.
String isMessageHistoryEnabledPropertyString =
"net.java.sip.communicator.impl.msghistory.isMessageHistoryEnabled";
String isMessageHistoryEnabledString = configService.getString(
isMessageHistoryEnabledPropertyString);
-
+
if(isMessageHistoryEnabledString == null)
- isMessageHistoryEnabledString =
+ isMessageHistoryEnabledString =
getResources().
getSettingsString(isMessageHistoryEnabledPropertyString);
@@ -737,19 +737,19 @@ public class MessageHistoryServiceImpl
public void messageDeliveryFailed(MessageDeliveryFailedEvent evt)
{
}
-
+
// //////////////////////////////////////////////////////////////////////////
// ChatRoomMessageListener implementation methods
-
+
public void messageReceived(ChatRoomMessageReceivedEvent evt)
{
- try
+ try
{
History history = this.getHistoryForMultiChat(
- null,
+ null,
evt.getSourceChatRoom());
-
- writeMessage(history, "in", evt.getSourceChatRoomMember(),
+
+ writeMessage(history, "in", evt.getSourceChatRoomMember(),
evt.getMessage(), evt.getTimestamp());
} catch (IOException e)
{
@@ -759,13 +759,13 @@ public class MessageHistoryServiceImpl
public void messageDelivered(ChatRoomMessageDeliveredEvent evt)
{
- try
+ try
{
History history = this.
getHistoryForMultiChat(
- null,
+ null,
evt.getSourceChatRoom());
-
+
writeMessage(history, "out", evt.getMessage(), evt.getTimestamp());
} catch (IOException e)
{
@@ -776,11 +776,11 @@ public class MessageHistoryServiceImpl
public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent evt)
{
}
-
+
/**
* Writes message to the history
* @param direction String direction of the message
- * @param source The source Contact
+ * @param source The source Contact
* @param destination The destiantion Contact
* @param message Message message to be written
* @param messageTimestamp Date this is the timestamp when was message received
@@ -791,14 +791,14 @@ public class MessageHistoryServiceImpl
{
try {
History history = this.getHistory(source, destination);
-
+
writeMessage(history, direction, message, messageTimestamp);
} catch (IOException e)
{
logger.error("Could not add message to history", e);
}
}
-
+
/**
* Writes message to the history
* @param history The history to which will write the message
@@ -821,7 +821,7 @@ public class MessageHistoryServiceImpl
logger.error("Could not add message to history", e);
}
}
-
+
/**
* Writes message to the history
* @param history The history to which will write the message
@@ -838,7 +838,7 @@ public class MessageHistoryServiceImpl
historyWriter.addRecord(new String[] { direction,
message.getContent(), message.getContentType(),
message.getEncoding(), message.getMessageUID(),
- from.getContactAddress(),
+ from.getContactAddress(),
String.valueOf(messageTimestamp.getTime()) },
new Date()); // this date is when the history record is written
} catch (IOException e)
@@ -846,7 +846,7 @@ public class MessageHistoryServiceImpl
logger.error("Could not add message to history", e);
}
}
-
+
// //////////////////////////////////////////////////////////////////////////
/**
@@ -942,7 +942,7 @@ public class MessageHistoryServiceImpl
{
logger.trace("Service did not have a im op. set.");
}
-
+
OperationSetMultiUserChat opSetMultiUChat
= (OperationSetMultiUserChat) provider
.getSupportedOperationSets().get(
@@ -950,7 +950,7 @@ public class MessageHistoryServiceImpl
if (opSetMultiUChat != null)
{
- Iterator iter =
+ Iterator iter =
opSetMultiUChat.getCurrentlyJoinedChatRooms().iterator();
while(iter.hasNext())
@@ -958,7 +958,7 @@ public class MessageHistoryServiceImpl
ChatRoom room = (ChatRoom)iter.next();
room.addMessageListener(this);
}
-
+
opSetMultiUChat.addPresenceListener(this);
}
else
@@ -984,7 +984,7 @@ public class MessageHistoryServiceImpl
{
opSetIm.removeMessageListener(this);
}
-
+
OperationSetMultiUserChat opSetMultiUChat
= (OperationSetMultiUserChat) provider
.getSupportedOperationSets().get(
@@ -992,9 +992,9 @@ public class MessageHistoryServiceImpl
if (opSetMultiUChat != null)
{
- Iterator iter =
+ Iterator iter =
opSetMultiUChat.getCurrentlyJoinedChatRooms().iterator();
-
+
while(iter.hasNext())
{
ChatRoom room = (ChatRoom)iter.next();
@@ -1002,7 +1002,7 @@ public class MessageHistoryServiceImpl
}
}
}
-
+
/**
* Called to notify interested parties that a change in our presence in
* a chat room has occured. Changes may include us being kicked, join,
@@ -1012,7 +1012,7 @@ public class MessageHistoryServiceImpl
*/
public void localUserPresenceChanged(LocalUserChatRoomPresenceChangeEvent evt)
{
- if(evt.getEventType() ==
+ if(evt.getEventType() ==
LocalUserChatRoomPresenceChangeEvent.LOCAL_USER_JOINED)
{
if (!evt.getChatRoom().isSystem())
@@ -1119,7 +1119,7 @@ public class MessageHistoryServiceImpl
Hashtable readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
-
+
Iterator iter = readers.keySet().iterator();
while (iter.hasNext())
{
@@ -1167,7 +1167,7 @@ public class MessageHistoryServiceImpl
Hashtable readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
-
+
Iterator iter = readers.keySet().iterator();
while (iter.hasNext())
{
@@ -1215,7 +1215,7 @@ public class MessageHistoryServiceImpl
Hashtable readers = getHistoryReaders(contact);
int recordsCount = countRecords(readers);
-
+
Iterator iter = readers.keySet().iterator();
while (iter.hasNext())
{
@@ -1269,22 +1269,22 @@ public class MessageHistoryServiceImpl
}
return readers;
}
-
+
/**
* Total count of records for supplied history readers will read through
- *
+ *
* @param readers hashtable with pairs contact <-> history reader
* @return the number of searched messages
- * @throws UnsupportedOperationException
+ * @throws UnsupportedOperationException
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public int countRecords(Hashtable readers)
{
int result = 0;
-
+
Enumeration readersEnum = readers.elements();
-
+
while (readersEnum.hasMoreElements())
{
HistoryReader r = (HistoryReader)readersEnum.nextElement();
@@ -1293,9 +1293,9 @@ public class MessageHistoryServiceImpl
return result;
}
-
+
/**
- * Returns all the messages exchanged in the supplied
+ * Returns all the messages exchanged in the supplied
* chat room after the given date
*
* @param room The chat room
@@ -1310,7 +1310,7 @@ public class MessageHistoryServiceImpl
try
{
// get the readers for this room
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
// add the progress listeners
@@ -1328,12 +1328,12 @@ public class MessageHistoryServiceImpl
{
logger.error("Could not read history", e);
}
-
+
return result;
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room before the given date
*
* @param room The chat room
@@ -1348,7 +1348,7 @@ public class MessageHistoryServiceImpl
try
{
// get the readers for this room
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
// add the progress listeners
@@ -1371,7 +1371,7 @@ public class MessageHistoryServiceImpl
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room between the given dates
*
* @param room The chat room
@@ -1387,7 +1387,7 @@ public class MessageHistoryServiceImpl
try
{
// get the readers for this room
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
// add the progress listeners
@@ -1405,12 +1405,12 @@ public class MessageHistoryServiceImpl
{
logger.error("Could not read history", e);
}
-
+
return result;
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room between the given dates and having the given
* keywords
*
@@ -1421,7 +1421,7 @@ public class MessageHistoryServiceImpl
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
- public Collection findByPeriod(ChatRoom room,
+ public Collection findByPeriod(ChatRoom room,
Date startDate, Date endDate, String[] keywords)
throws RuntimeException
{
@@ -1429,7 +1429,7 @@ public class MessageHistoryServiceImpl
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room between the given dates and having the given
* keywords
*
@@ -1449,7 +1449,7 @@ public class MessageHistoryServiceImpl
try
{
// get the readers for this room
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
// add the progress listeners
@@ -1468,12 +1468,12 @@ public class MessageHistoryServiceImpl
{
logger.error("Could not read history", e);
}
-
+
return result;
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied room having the given keyword
*
* @param room The Chat room
@@ -1488,7 +1488,7 @@ public class MessageHistoryServiceImpl
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room having the given keyword
*
* @param room The chat room
@@ -1497,7 +1497,7 @@ public class MessageHistoryServiceImpl
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
- public Collection findByKeyword(ChatRoom room, String keyword,
+ public Collection findByKeyword(ChatRoom room, String keyword,
boolean caseSensitive)
throws RuntimeException
{
@@ -1505,7 +1505,7 @@ public class MessageHistoryServiceImpl
try
{
// get the readers for this room
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
// add the progress listeners
@@ -1529,7 +1529,7 @@ public class MessageHistoryServiceImpl
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room having the given keywords
*
* @param room The chat room
@@ -1544,7 +1544,7 @@ public class MessageHistoryServiceImpl
}
/**
- * Returns all the messages exchanged
+ * Returns all the messages exchanged
* in the supplied chat room having the given keywords
*
* @param room The chat room
@@ -1553,7 +1553,7 @@ public class MessageHistoryServiceImpl
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
- public Collection findByKeywords(ChatRoom room, String[] keywords,
+ public Collection findByKeywords(ChatRoom room, String[] keywords,
boolean caseSensitive)
throws RuntimeException
{
@@ -1561,7 +1561,7 @@ public class MessageHistoryServiceImpl
try
{
// get the readers for this room
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
// add the progress listeners
@@ -1580,12 +1580,12 @@ public class MessageHistoryServiceImpl
{
logger.error("Could not read history", e);
}
-
+
return result;
}
/**
- * Returns the supplied number of recent messages exchanged
+ * Returns the supplied number of recent messages exchanged
* in the supplied chat room
*
* @param room The chat room
@@ -1597,12 +1597,12 @@ public class MessageHistoryServiceImpl
throws RuntimeException
{
TreeSet result = new TreeSet(new ChatRoomMessageEventComparator());
-
+
try
{
// get the readers for this room
- HistoryReader reader =
- this.getHistoryForMultiChat(null, room).getReader();
+ HistoryReader reader =
+ this.getHistoryForMultiChat(null, room).getReader();
Iterator recs = reader.findLast(count);
while (recs.hasNext())
{
@@ -1616,7 +1616,7 @@ public class MessageHistoryServiceImpl
{
logger.error("Could not read history", e);
}
-
+
LinkedList resultAsList = new LinkedList(result);
int startIndex = resultAsList.size() - count;
@@ -1643,7 +1643,7 @@ public class MessageHistoryServiceImpl
try
{
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
Iterator recs = reader.findFirstRecordsAfter(date, count);
while (recs.hasNext())
@@ -1660,11 +1660,11 @@ public class MessageHistoryServiceImpl
}
LinkedList resultAsList = new LinkedList(result);
-
+
int toIndex = count;
if(toIndex > resultAsList.size())
toIndex = resultAsList.size();
-
+
return resultAsList.subList(0, toIndex);
}
@@ -1685,7 +1685,7 @@ public class MessageHistoryServiceImpl
try
{
- HistoryReader reader =
+ HistoryReader reader =
this.getHistoryForMultiChat(null, room).getReader();
Iterator recs = reader.findLastRecordsBefore(date, count);
while (recs.hasNext())
@@ -1709,7 +1709,7 @@ public class MessageHistoryServiceImpl
return resultAsList.subList(startIndex, resultAsList.size());
}
-
+
public static ResourceManagementService getResources()
{
if (resourcesService == null)
@@ -1719,8 +1719,8 @@ public class MessageHistoryServiceImpl
if(serviceReference == null)
return null;
-
- resourcesService = (ResourceManagementService)
+
+ resourcesService = (ResourceManagementService)
MessageHistoryActivator.bundleContext
.getService(serviceReference);
}
@@ -1742,7 +1742,7 @@ public class MessageHistoryServiceImpl
double accumulatedRatio = 0;
double currentProgress = 0;
double lastHistoryProgress = 0;
-
+
// used for more precise calculations with double values
int raiser = 1000;
@@ -1750,12 +1750,12 @@ public class MessageHistoryServiceImpl
{
this.listener = listener;
}
-
+
private void setCurrentValues(HistoryReader currentReader, int allRecords)
{
this.allRecords = allRecords;
this.reader = currentReader;
- currentReaderProgressRatio =
+ currentReaderProgressRatio =
(double)currentReader.countRecords()/allRecords * raiser;
accumulatedRatio += currentReaderProgressRatio;
}
@@ -1779,15 +1779,15 @@ public class MessageHistoryServiceImpl
*/
private int getProgressMapping(ProgressEvent evt)
{
- double tmpHistoryProgress =
+ double tmpHistoryProgress =
currentReaderProgressRatio * evt.getProgress();
-
+
currentProgress += tmpHistoryProgress - lastHistoryProgress;
-
+
if(evt.getProgress() == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
lastHistoryProgress = 0;
-
+
// this is the last one and the last event fire the max
// there will be looses in currentProgress due to the devision
if((int)accumulatedRatio == raiser)
@@ -1796,7 +1796,7 @@ public class MessageHistoryServiceImpl
}
else
lastHistoryProgress = tmpHistoryProgress;
-
+
return (int)currentProgress;
}
@@ -1804,7 +1804,7 @@ public class MessageHistoryServiceImpl
* clear the values
*/
void clear()
- {
+ {
allRecords = 0;
currentProgress = 0;
lastHistoryProgress = 0;
@@ -1866,9 +1866,9 @@ public class MessageHistoryServiceImpl
return date1.compareTo(date2);
}
}
-
+
/**
- * Used to compare ChatRoomMessageDeliveredEvent
+ * Used to compare ChatRoomMessageDeliveredEvent
* or ChatRoomMessageReceivedEvent
* and to be ordered in TreeSet according their timestamp
*/
@@ -1879,7 +1879,7 @@ public class MessageHistoryServiceImpl
{
Date date1 = null;
Date date2 = null;
-
+
if(o1 instanceof ChatRoomMessageDeliveredEvent)
date1 = ((ChatRoomMessageDeliveredEvent)o1).getTimestamp();
else if(o1 instanceof ChatRoomMessageReceivedEvent)
@@ -1897,7 +1897,7 @@ public class MessageHistoryServiceImpl
return date1.compareTo(date2);
}
}
-
+
/**
* Simple ChatRoomMember implementation.
*/
@@ -1908,7 +1908,7 @@ public class MessageHistoryServiceImpl
private String name;
private ChatRoomMemberRole role;
- public ChatRoomMemberImpl(String name, ChatRoom chatRoom,
+ public ChatRoomMemberImpl(String name, ChatRoom chatRoom,
ChatRoomMemberRole role)
{
this.chatRoom = chatRoom;
@@ -1941,7 +1941,7 @@ public class MessageHistoryServiceImpl
return role;
}
}
-
+
/**
* Handles <tt>PropertyChangeEvent</tt> triggered from the modification of
* the isMessageHistoryEnabled property.
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index a04f23b..ec23e10 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -155,6 +155,14 @@ public class OperationSetBasicTelephonySipImpl
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);
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java
index 586eb34..8764a6e 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java
@@ -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 class ProtocolProviderFactorySipImpl
* @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());
}
/**
@@ -153,7 +154,7 @@ public class ProtocolProviderFactorySipImpl
* the modified account.
* @param accountProperties a set of protocol (or implementation) specific
* properties defining the new account.
- *
+ *
* @throws java.lang.NullPointerException if any of the arguments is null.
*/
public void modifyAccount( ProtocolProviderService protocolProvider,
@@ -235,7 +236,7 @@ public class ProtocolProviderFactorySipImpl
+ ex.getMessage());
}
}
-
+
/**
* Initializes and creates an account corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
index 9f5a59b..9562436 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
@@ -312,6 +312,9 @@ public class ProtocolProviderServiceSipImpl
*/
private ProtocolIconSipImpl protocolIcon;
+ /**
+ * The presence status set supported by this provider
+ */
private SipStatusEnum sipStatusEnum;
/**
@@ -2107,7 +2110,7 @@ public class ProtocolProviderServiceSipImpl
* so that it would receives all messages in a transaction initiated by a
* <tt>method</tt> request. If any previous processors exist for the same
* method, they will be replaced by this one.
- *
+ *
* @param method a String representing the SIP method that we're registering
* the processor for (e.g. INVITE, REGISTER, or SUBSCRIBE).
* @param methodProcessor a <tt>MethodProcessor</tt> implementation that
@@ -2143,7 +2146,7 @@ public class ProtocolProviderServiceSipImpl
* Unregisters <tt>methodProcessor</tt> from the <tt>methorProcessors</tt>
* table so that it won't receive further messages in a transaction
* initiated by a <tt>method</tt> request.
- *
+ *
* @param method the name of the method whose processor we'd like to
* unregister.
* @param methodProcessor
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java
index 192536a..832060d 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java
@@ -10,6 +10,7 @@ import java.util.*;
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.*;
@@ -26,15 +27,18 @@ public class SipActivator
private Logger logger = Logger.getLogger(SipActivator.class.getName());
private ServiceRegistration sipPpFactoryServReg = null;
- static BundleContext bundleContext = null;
+ static BundleContext bundleContext = null;
private static ConfigurationService configurationService = null;
private static NetworkAddressManagerService networkAddressManagerService
- = null;
+ = 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 class SipActivator
sipProviderFactory,
hashtable);
+ uriHandler = new UriHandlerSipImpl(sipProviderFactory);
+
logger.debug("SIP Protocol Provider Factory ... [REGISTERED]");
}
@@ -148,7 +154,7 @@ public class SipActivator
}
return mediaService;
}
-
+
/**
* Returns a reference to a VersionService implementation currently registered
* in the bundle context or null if no such implementation was found.
@@ -170,6 +176,26 @@ public class SipActivator
}
/**
+ * 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.
*
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java
new file mode 100644
index 0000000..1f7a28b
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java
@@ -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() + "]";
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java
new file mode 100644
index 0000000..6957842
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java
@@ -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();
+ }
+ }
+}
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 8408124..c0cefad 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
@@ -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,
diff --git a/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java b/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java
index 436d57a..100629d 100644
--- a/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java
+++ b/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java
@@ -19,19 +19,17 @@ import net.java.sip.communicator.util.*;
* 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 class ProviderRegistration
*/
public void run()
{
- try {
- protocolProvider.register(this);
+ try
+ {
+ protocolProvider.register(SystrayActivator.getUIService()
+ .getDefaultSecurityAuthority(protocolProvider));
}
catch (OperationFailedException ex)
{
@@ -86,69 +86,4 @@ public class ProviderRegistration
}
}
}
-
- /**
- * 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;
- }
}
diff --git a/src/net/java/sip/communicator/launcher/SIPCommunicator.java b/src/net/java/sip/communicator/launcher/SIPCommunicator.java
index ca35e99..e3d65fa 100644
--- a/src/net/java/sip/communicator/launcher/SIPCommunicator.java
+++ b/src/net/java/sip/communicator/launcher/SIPCommunicator.java
@@ -9,25 +9,28 @@ package net.java.sip.communicator.launcher;
import java.awt.*;
import java.io.*;
+import net.java.sip.communicator.util.launchutils.*;
+
import org.apache.felix.main.*;
/**
* Starts the SIP Communicator.
- *
+ *
* @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";
/**
* Starts the SIP Communicator.
- *
+ *
* @param args
*/
public static void main(String[] args)
@@ -68,6 +71,51 @@ public class SIPCommunicator
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);
}
@@ -75,13 +123,12 @@ public class SIPCommunicator
* Sets the system properties net.java.sip.communicator.SC_HOME_DIR_LOCATION
* and net.java.sip.communicator.SC_HOME_DIR_NAME (if they aren't already
* set) in accord with the OS conventions specified by the name of the OS.
- *
+ *
* @param osName the name of the OS according to which the SC_HOME_DIR_*
* properties are to be set
*/
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
diff --git a/src/net/java/sip/communicator/service/argdelegation/UriHandler.java b/src/net/java/sip/communicator/service/argdelegation/UriHandler.java
new file mode 100644
index 0000000..d533dc1
--- /dev/null
+++ b/src/net/java/sip/communicator/service/argdelegation/UriHandler.java
@@ -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);
+}
diff --git a/src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf b/src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf
new file mode 100644
index 0000000..1e97f1a
--- /dev/null
+++ b/src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf
@@ -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
diff --git a/src/net/java/sip/communicator/service/gui/UIService.java b/src/net/java/sip/communicator/service/gui/UIService.java
index 40b74cc..b8ceb26 100644
--- a/src/net/java/sip/communicator/service/gui/UIService.java
+++ b/src/net/java/sip/communicator/service/gui/UIService.java
@@ -23,22 +23,22 @@ import net.java.sip.communicator.service.protocol.*;
* The <tt>UIService</tt> provides also methods that would allow to other
* modules to control the visibility, size and position of the main application
* window. Some of these methods are: setVisible, minimize, maximize, resize,
- * move, etc.
+ * move, etc.
* <p>
* A way to show different types of simple windows is provided to allow other
* modules to show different simple messages, like warning or error messages.
- * In order to show a simple warning message, a module should invoke the
+ * In order to show a simple warning message, a module should invoke the
* getPopupDialog method and then one of the showXXX methods, which corresponds
- * best to the required dialog.
+ * best to the required dialog.
* <p>
* Certain components within the GUI, like "AddContact" window for example,
* could be also shown from outside the ui. To make one of
* these component exportable, the <tt>UIService</tt> implementation should attach
* to it an <tt>WindowID</tt>. A window then could be shown, by invoking
- * <code>getExportedWindow(WindowID)</code> and then
+ * <code>getExportedWindow(WindowID)</code> and then
* <code>show</code>. The <tt>WindowID</tt> above should be obtained from
* <code>getSupportedExportedWindows</code>.
- *
+ *
* @author Yana Stamcheva
*/
public interface UIService
@@ -47,52 +47,52 @@ public interface UIService
* Returns TRUE if the application is visible and FALSE otherwise.
* This method is meant to be used by the systray service in order to
* detect the visibility of the application.
- *
+ *
* @return <code>true</code> if the application is visible and
* <code>false</code> otherwise.
- *
+ *
* @see #setVisible(boolean)
*/
public boolean isVisible();
-
+
/**
* Shows or hides the main application window depending on the value of
* parameter <code>visible</code>. Meant to be used by the systray when it
* needs to show or hide the application.
- *
+ *
* @param visible if <code>true</code>, shows the main application window;
* otherwise, hides the main application window.
- *
+ *
* @see #isVisible()
*/
public void setVisible(boolean visible);
-
+
/**
* Minimizes the main application window.
*/
public void minimize();
-
+
/**
* Mawimizes the main application window.
*/
public void maximize();
-
+
/**
* Restores the main application window.
*/
public void restore();
-
+
/**
* Resizes the main application window with the given width and height.
- *
+ *
* @param width The new width.
* @param height The new height.
*/
public void resize(int width, int height);
-
+
/**
* Moves the main application window to the given coordinates.
- *
+ *
* @param x The x coordinate.
* @param y The y coordinate.
*/
@@ -103,29 +103,29 @@ public interface UIService
* application by simply closing the main application window (by clicking
* the X button or pressing Alt-F4). When set to FALSE the main application
* window will be only hidden.
- *
+ *
* @param exitOnClose When TRUE, the user could exit the
* application by simply closing the main application window (by clicking
* the X button or pressing Alt-F4). When set to FALSE the main application
* window will be only hidden.
*/
public void setExitOnMainWindowClose(boolean exitOnClose);
-
+
/**
* Returns TRUE if the application could be exited by closing the main
* application window, otherwise returns FALSE.
- *
+ *
* @return Returns TRUE if the application could be exited by closing the
* main application window, otherwise returns FALSE
*/
public boolean getExitOnMainWindowClose();
-
+
/**
* Returns an exported window given by the <tt>WindowID</tt>.
* This could be for example the "Add contact" window or any other window
* within the application. The <tt>windowID</tt> should be one of the
* WINDOW_XXX obtained by the <tt>getSupportedExportedWindows</tt> method.
- *
+ *
* @param windowID One of the WINDOW_XXX WindowID-s.
* @throws IllegalArgumentException if the specified <tt>windowID</tt>
* is not recognized by the implementation (note that implementations
@@ -135,20 +135,20 @@ public interface UIService
*/
public ExportedWindow getExportedWindow(WindowID windowID)
throws IllegalArgumentException;
-
+
/**
* Returns a configurable popup dialog, that could be used to show either
* a warning message, error message, information message, etc. or to prompt
* user for simple one field input or to question the user.
- *
+ *
* @return a <code>PopupDialog</code>.
* @see PopupDialog
*/
public PopupDialog getPopupDialog();
-
+
/**
* Returns the <tt>Chat</tt> corresponding to the given <tt>Contact</tt>.
- *
+ *
* @param contact the <tt>Contact</tt> for which the searched chat is about.
* @return the <tt>Chat</tt> corresponding to the given <tt>Contact</tt>.
*/
@@ -156,7 +156,7 @@ public interface UIService
/**
* Returns the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>.
- *
+ *
* @param chatRoom the <tt>ChatRoom</tt> for which the searched chat is
* about.
* @return the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>.
@@ -165,18 +165,18 @@ public interface UIService
/**
* Returns the selected <tt>Chat</tt>.
- *
+ *
* @return the selected <tt>Chat</tt>.
*/
public Chat getCurrentChat();
-
+
/**
* Returns an <tt>ExportableComponent</tt> that corresponds to an
* authentication window for the given protocol provider and user
* inromation. Initially this method is meant to be used by the
* <tt>SystrayService</tt> in order to show a login window when user tries
* to connect using the systray menu.
- *
+ *
* @param protocolProvider the <tt>ProtocolProviderService</tt> for which
* the authentication window is about.
* @param realm the realm
@@ -194,6 +194,22 @@ public interface UIService
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
* <tt>WindowID</tt> in the set is one of the constants in the
@@ -201,25 +217,25 @@ public interface UIService
* bundles that would like to have access to some windows in the gui
* - for example the "Add contact" window, the "Settings" window, the
* "Chat window", etc.
- *
- * @return Iterator An iterator to a set containing WindowID-s
+ *
+ * @return Iterator An iterator to a set containing WindowID-s
* representing all exported windows supported by the current UI
* implementation.
*/
public Iterator getSupportedExportedWindows();
-
+
/**
* Chechks if a window with the given <tt>WindowID</tt> is contained in the
* current UI implementation.
- *
+ *
* @param windowID one of the <tt>WindowID</tt>-s, defined in the
- * <tt>ExportedWindow</tt> interface.
+ * <tt>ExportedWindow</tt> interface.
* @return <code>true</code> if the component with the given
* <tt>WindowID</tt> is contained in the current UI implementation,
* <code>false</code> otherwise.
*/
public boolean isExportedWindowSupported(WindowID windowID);
-
+
/**
* Returns the <tt>WizardContainer</tt> for the current
* UIService implementation. The <tt>WizardContainer</tt>
@@ -228,7 +244,7 @@ public interface UIService
* s. Each of these wizards is made for a given protocol and should provide
* a sequence of user interface forms through which the user could
* registrate a new account.
- *
+ *
* @return Returns the <tt>AccountRegistrationWizardContainer</tt> for the
* current UIService implementation.
*/
@@ -238,12 +254,12 @@ public interface UIService
* Returns an iterator over a set containing containerID-s pointing to
* containers supported by the current UI implementation. Each containerID
* in the set is one of the CONTAINER_XXX constants. The method is meant to
- * be used by plugins or bundles that would like to add components to the
+ * be used by plugins or bundles that would like to add components to the
* user interface. Before adding any component they should use this method
* to obtain all possible places, which could contain external components,
* like different menus, toolbars, etc.
- *
- * @return Iterator An iterator to a set containing containerID-s
+ *
+ * @return Iterator An iterator to a set containing containerID-s
* representing all containers supported by the current UI implementation.
*/
public Iterator getSupportedContainers();
@@ -251,9 +267,9 @@ public interface UIService
/**
* Chechks if the container with the given <tt>Container</tt> is supported
* from the current UI implementation.
- *
- * @param containderID One of the CONTAINER_XXX Container-s.
- * @return <code>true</code> if the contaner with the given
+ *
+ * @param containderID One of the CONTAINER_XXX Container-s.
+ * @return <code>true</code> if the contaner with the given
* <tt>Container</tt> is supported from the current UI implementation,
* <code>false</code> otherwise.
*/
diff --git a/src/net/java/sip/communicator/service/protocol/OperationFailedException.java b/src/net/java/sip/communicator/service/protocol/OperationFailedException.java
index 19b1322..d877497 100644
--- a/src/net/java/sip/communicator/service/protocol/OperationFailedException.java
+++ b/src/net/java/sip/communicator/service/protocol/OperationFailedException.java
@@ -122,10 +122,15 @@ 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
*/
private int errorCode = GENERAL_ERROR;
-
+
/**
* Creates an exception with the specified error message and error code.
* @param message A message containing details on the error that caused the
diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
index 97410fb..3501cd5 100644
--- a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
+++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
@@ -133,12 +133,12 @@ public abstract class ProtocolProviderFactory
* resource property.
*/
public static final String RESOURCE = "RESOURCE";
-
+
/**
* The name of the property under which we store resource priority.
*/
public static final String RESOURCE_PRIORITY = "RESOURCE_PRIORITY";
-
+
/**
* The name of the property under which we store the boolean value
* indicating if the user name should be automatically changed if the
@@ -146,44 +146,44 @@ public abstract class ProtocolProviderFactory
* implementations.
*/
public static final String AUTO_CHANGE_USER_NAME = "AUTO_CHANGE_USER_NAME";
-
+
/**
* The name of the property under which we store the boolean value
* indicating if a password is required. Initially this property is meant to
* be used by IRC implementations.
*/
public static final String NO_PASSWORD_REQUIRED = "NO_PASSWORD_REQUIRED";
-
+
/**
* The name of the property under which we store if the presence is enabled.
*/
public static final String IS_PRESENCE_ENABLED = "IS_PRESENCE_ENABLED";
-
+
/**
* The name of the property under which we store if the p2p mode for SIMPLE
* should be forced.
*/
public static final String FORCE_P2P_MODE = "FORCE_P2P_MODE";
-
+
/**
* The name of the property under which we store the offline contact polling
* period for SIMPLE.
*/
- public static final String POLLING_PERIOD = "POLLING_PERIOD";
-
+ public static final String POLLING_PERIOD = "POLLING_PERIOD";
+
/**
* The name of the property under which we store the chosen default
* subscription expiration value for SIMPLE.
*/
public static final String SUBSCRIPTION_EXPIRATION
= "SUBSCRIPTION_EXPIRATION";
-
+
/**
* Indicates if the server address has been validated.
*/
public static final String SERVER_ADDRESS_VALIDATED
= "SERVER_ADDRESS_VALIDATED";
-
+
/**
* Indicates if the proxy address has been validated.
*/
@@ -240,7 +240,7 @@ public abstract class ProtocolProviderFactory
* the modified account.
* @param accountProperties a set of protocol (or implementation) specific
* properties defining the new account.
- *
+ *
* @throws java.lang.NullPointerException if any of the arguments is null.
*/
public abstract void modifyAccount(
@@ -254,7 +254,7 @@ public abstract class ProtocolProviderFactory
* @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
@@ -531,7 +531,7 @@ public abstract class ProtocolProviderFactory
Base64.decode(storedPropertyValue));
}
}
-
+
if(storedPropertyValue != null)
accountProperties.put(propertyName, storedPropertyValue);
}
@@ -558,7 +558,7 @@ public abstract class ProtocolProviderFactory
*
* @param accountProperties a set of protocol (or implementation)
* specific properties defining the new account.
- *
+ *
* @return the AccountID of the newly loaded account
*/
protected abstract AccountID loadAccount( Map accountProperties);
diff --git a/src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java b/src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java
new file mode 100644
index 0000000..fe08c2c
--- /dev/null
+++ b/src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java
@@ -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);
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java b/src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java
new file mode 100644
index 0000000..51198e0
--- /dev/null
+++ b/src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java
@@ -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());
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/util/launchutils/UriArgManager.java b/src/net/java/sip/communicator/util/launchutils/UriArgManager.java
new file mode 100644
index 0000000..6c669fc
--- /dev/null
+++ b/src/net/java/sip/communicator/util/launchutils/UriArgManager.java
@@ -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();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java b/src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java
new file mode 100644
index 0000000..e19bc18
--- /dev/null
+++ b/src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java
@@ -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);
+}