"
+ +"]]>"
+ +" http://mihaibalan.wordpress.com/2007/08/20/tuig-if"
+ +"-i-could-turn-back-time/feed/"
+ +" ";
+
+ /**
+ * String containing some invalid mark-up in the RSS feed.
+ */
+ private static String rssBodyInvalid =
+ "Lorem ipsum";
+
+ /**
+ * String containing the footer of a RSS feed.
+ */
+ private static String rssFooter = "";
+
+ /**
+ * String containing the header of a ATOM feed.
+ */
+ private static String atomHeader =
+ ""
+ +""
+ +""
+ +" tag:blogger.com,1999:blog-34909258791457560582007-"
+ +"08-08T10:24:42.424-07:00miChou's Summer of "
+ +"Code blog"
+ +" "
+ +" "
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" Blogger6125";
+
+ /**
+ * String containing the body of an ATOM feed.
+ */
+ private static String atomBody =
+ "tag:blogger.com,1999:blog-3490925879145756058.post-445569517"
+ +"55418486052007-06-05T02:15:00.000-07:002007-06-05T03:34:35.892-07:00"
+ +" "
+ +""
+ +" Happy hacking!Fi"
+ +"nally the second semester is over, and the summer exams sessions is abou"
+ +"t to begin (that means I have the first exam tomorrow - Digital Computer"
+ +"s 2).And in between all this, I should also get my exams going :)"
+ +" "
+ +" "
+ +" "
+ +" "
+ +""
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" "
+ +" "
+ +" tag:blogger.com,1999:blog-3490925879145756058.post-160551373"
+ +"8611698052007-05-31T13:34:00.000-07:002007-06-01T04:21:00.167-07:00"
+ +" "
+ +" "
+ +" Un debut attarde"
+ +"<span style=\"font-style: italic;font-size:100%;\" ><span style="
+ +"\"font-family:georgia;\">Bien qu'on m'ait dit que toute communication "
+ +"regardant SIP se fera en anglais je prends le risque d'ecrire ce premier "
+ +"post en francais."
+ +" "
+ +" "
+ +" "
+ +" "
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" ";
+
+ /**
+ * String containing the updates to the ATOM feed.
+ */
+ private static String atomBodyUpdate =
+ "tag:blogger.com,1999:blog-3490925879145756058.post-722978588"
+ +"38255321932007-06-25T14:29:00.000-07:002007-06-26T06:12:44.479-07:00"
+ +" "
+ +""
+ +" "
+ +" "
+ +" Google politicsO"
+ +"ne of the problems well known in the current implementation of the RSS s"
+ +"upport in SIP Communicator was the inability to retrieve feeds from news"
+ +".google.com (and you must admit, news.google.com is quite a source of ne"
+ +"ws ;) ).<br /><br /> A brief look at the exception returned"
+ +" by the plugin points out the problem. The server is sending a HTTP/403 "
+ +"(Forbidden) response code instead of the HTTP/200 (OK) response code."
+ +" "
+ +" "
+ +" "
+ +" "
+ +""
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" ";
+
+ /**
+ * String containing a new ATOM feed.
+ */
+ private static String atomBodyNew =
+ "tag:blogger.com,1999:blog-3490925879145756058.post-325829288"
+ +"52251106862007-08-08T09:59:00.000-07:002007-08-08T10:24:42.470-07:00"
+ +" "
+ +" "
+ +""
+ +" "
+ +" check out. again...<div style=\"text-align: justify;\">It seems I do have a strange"
+ +" problem when it comes to using CVS. It's not version control systems in "
+ +"general, but CVS. Or at least, the CVS I'm using here, on Sip Communicat"
+ +"or. Somehow, I never get it right."
+ +" "
+ +" "
+ +" "
+ +" "
+ +""
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" "
+ +" "
+ +" tag:blogger.com,1999:blog-3490925879145756058.post-186104691"
+ +"57144790072007-07-06T13:23:00.000-07:002007-07-06T13:54:46.478-07:00"
+ +" "
+ +" "
+ +" Favicon - check!"
+ +"<div style=\"text-align: justify;\">Finally, favicon retrieval supp"
+ +"ort is here. What is this all about? Almost all IM protocols out there al"
+ +"low you to specify and associate an image to your account (a so called a"
+ +"vatar), either natively (e.g. Y!M), either through extensions (e.g. XMPP"
+ +"/Jabber)."
+ +" "
+ +" "
+ +" "
+ +" "
+ +""
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" "
+ +" "
+ +" tag:blogger.com,1999:blog-3490925879145756058.post-717349866"
+ +"0796698482007-06-29T12:35:00.000-07:002007-06-29T12:39:34.816-07:00"
+ +" "
+ +""
+ +" Interim objectivesI know I've been a little late setting these, but now it's kinda' sett"
+ +"led."
+ +" "
+ +" "
+ +" "
+ +" "
+ +" "
+ +" Mihai Balan"
+ +" "
+ +" ";
+
+ /**
+ * String containing some invalid markup in the ATOM feed.
+ */
+ private static String atomBodyInvalid =
+ "Lorem ipsum";
+
+ /**
+ * String containing the footer of the ATOM feed.
+ */
+ private static String atomFooter = "";
+
+ /**
+ * Returns a String representing an RSS feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getRss()
+ {
+ return rssHeader + rssBody + rssFooter;
+ }
+
+ /**
+ * Returns a String representing the updated RSS feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getRssUpdated()
+ {
+ return rssHeader + rssBodyUpdate + rssBody + rssFooter;
+ }
+
+ /**
+ * Returns a String representing a new version of the RSS feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getRssNew()
+ {
+ return rssHeader + rssBodyNew + rssFooter;
+ }
+
+ /**
+ * Returns a String representing an invalid RSS feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getRssInvalid()
+ {
+ return rssHeader + rssBodyInvalid + rssFooter;
+ }
+
+ /**
+ * Returns a String representing an ATOM feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getAtom()
+ {
+ return atomHeader + atomBody + atomFooter;
+ }
+
+ /**
+ * Returns a String representing the updated ATOM feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getAtomUpdated()
+ {
+ return atomHeader + atomBodyUpdate + atomBody + atomFooter;
+ }
+
+ /**
+ * Returns a String representing a new version of the ATOM
+ * feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getAtomNew()
+ {
+ return atomHeader + atomBodyNew + atomFooter;
+ }
+
+ /**
+ * Returns a String representing an invalid ATOM feed.
+ * @return textual representation of the RSS feed.
+ */
+ public static String getAtomInvalid()
+ {
+ return atomHeader + atomBodyInvalid + atomFooter;
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/RssProtocolProviderServiceLick.java b/test/net/java/sip/communicator/slick/protocol/rss/RssProtocolProviderServiceLick.java
new file mode 100755
index 000000000..92ebcdb5d
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/RssProtocolProviderServiceLick.java
@@ -0,0 +1,67 @@
+/*
+ * 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.slick.protocol.rss;
+
+import java.util.*;
+import junit.framework.*;
+import net.java.sip.communicator.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * RSS specific testing for an RSS Protocol provider Service implementation.
+ * [...]
+ * @author Mihai Balan
+ */
+public class RssProtocolProviderServiceLick
+ extends TestSuite
+ implements BundleActivator
+{
+ /**
+ * Initializes and registers all tests that we'll run as a part of this
+ * SLICK.
+ *
+ * @param bundleContext a currently valid bundle context.
+ */
+ public void start(BundleContext bundleContext)
+ {
+ Logger logger =
+ Logger.getLogger(RssProtocolProviderServiceLick.class);
+ logger.setLevelAll();
+ logger.debug("***Start() called on RSS slick***");
+
+ setName("RssProtocolProviderServiceLick");
+
+ Hashtable properties = new Hashtable();
+ properties.put("service.pid", getName());
+
+ RssSlickFixture.bc = bundleContext;
+
+ //test account installation
+ addTestSuite(TestAccountInstallation.class);
+
+ //test Protocol Provider Service implementation
+ addTestSuite(TestProtocolProviderServiceRssImpl.class);
+
+ //test account uninstallation
+ addTest(TestAccountUninstallation.suite());
+ addTestSuite(TestAccountUninstallationPersistence.class);
+
+ bundleContext.registerService(getClass().getName(), this, properties);
+ }
+
+ /**
+ * Prepares the slick for shutdown.
+ *
+ * @param context a currently valid bundle context.
+ */
+ public void stop(BundleContext context)
+ {
+
+ }
+
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/RssSlickFixture.java b/test/net/java/sip/communicator/slick/protocol/rss/RssSlickFixture.java
new file mode 100755
index 000000000..8d1018d68
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/RssSlickFixture.java
@@ -0,0 +1,155 @@
+/*
+ * 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.slick.protocol.rss;
+
+import java.util.*;
+import junit.framework.*;
+import org.osgi.framework.*;
+
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Contains constants and methods used by almost all of our tests.
+ *
+ * @author Mihai Balan
+ */
+public class RssSlickFixture
+ extends TestCase
+{
+ /**
+ * To be set by the slick itself upon activation.
+ */
+ public static BundleContext bc = null;
+
+ /**
+ * And OSGi service reference for the protocol provider corresponding to our
+ * testing account.
+ */
+ public ServiceReference providerServiceReference = null;
+
+ /**
+ * Protocol provider corresponding to the testing account.
+ */
+ public ProtocolProviderService provider = null;
+
+ /**
+ * The user ID associated with the testing account.
+ */
+ //public String userId = null;
+
+ public ProtocolProviderFactory providerFactory = null;
+
+ /**
+ * A reference to the bundle containing the tested ProtocolProvider
+ * implementation. This reference is set during the account installation
+ * testing and used during the account installation persistence testing.
+ */
+ public static Bundle providerBundle = null;
+
+ /**
+ * A HashTable containing group names mapped against array
+ * lists of buddy screen names.
+ */
+ public static Hashtable preInstalledBuddyList = null;
+ //XXX: Do I really need that? :-\
+
+ /**
+ * Initializes protocol provider references and whatever else there is to
+ * initialize.
+ *
+ * @throws java.lang.Exception in case we meet problems while retriving
+ * protocol providers through OSGI
+ */
+ public void setUp()
+ throws Exception
+ {
+ //get a reference to the provider factory
+ ServiceReference[] serRefs = null;
+ String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL
+ + "=" + ProtocolNames.RSS + ")";
+
+ try {
+ serRefs = bc.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), osgiFilter);
+ } catch (InvalidSyntaxException ise)
+ {
+ //shouldn't happen as the filter is static (typos maybe? :D)
+ fail(osgiFilter + "is not a valid OSGi filter");
+ }
+
+ assertTrue("Failed to find a provider factory service for protocol RSS",
+ serRefs != null && serRefs.length > 0);
+
+ providerFactory = (ProtocolProviderFactory)bc.getService(serRefs[0]);
+
+ //find the protocol providers exported for the two accounts
+ ServiceReference[] rssProviderRefs = bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&("+ProtocolProviderFactory.PROTOCOL+"="+ProtocolNames.RSS+"))");
+
+ //make sure we found a service
+ assertNotNull("No protocol provider was found for RSS account ",
+ rssProviderRefs);
+ assertTrue("No protocol provider was found for RSS account",
+ rssProviderRefs.length > 0);
+
+ //save the service for other tests to use.
+ providerServiceReference = rssProviderRefs[0];
+ provider = (ProtocolProviderService)bc.getService(
+ providerServiceReference);
+ }
+
+ /**
+ * Un get service references used in here.
+ */
+ public void tearDown()
+ {
+ bc.ungetService(providerServiceReference);
+ }
+
+ /**
+ * Returns the bundle that has registered the protocol provider service
+ * implementation that we're currently testing. The method would go through
+ * all bundles currently installed in the framework and return the first
+ * one that exports the same protocol provider instance as the one we test
+ * in this slick.
+ *
+ * @param provider the provider whose bundle we're looking for.
+ * @return the Bundle that has registered the protocol provider service
+ * we're testing in the slick.
+ */
+ public static Bundle findProtocolProviderBundle(
+ ProtocolProviderService provider)
+ {
+ Bundle[] bundles = bc.getBundles();
+
+ for (int i = 0; i < bundles.length; i++)
+ {
+ ServiceReference[] registeredServices
+ = bundles[i].getRegisteredServices();
+
+ if (registeredServices == null)
+ continue;
+
+ for (int j = 0; j < registeredServices.length; j++)
+ {
+ Object service
+ = bc.getService(registeredServices[j]);
+ if (service == provider)
+ return bundles[i];
+ }
+ }
+
+ return null;
+ }
+
+ public void clearProvidersList()
+ throws Exception
+ {
+ //XXX: FTM, do_nothing()
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/TestAccountInstallation.java b/test/net/java/sip/communicator/slick/protocol/rss/TestAccountInstallation.java
new file mode 100755
index 000000000..f361d4f01
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/TestAccountInstallation.java
@@ -0,0 +1,133 @@
+/*
+ * 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.slick.protocol.rss;
+
+import org.osgi.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+import junit.framework.*;
+import java.util.*;
+
+/**
+ * Installs a test account and verifies it is available after installation.
+ *
+ * @author Mihai Balan
+ */
+public class TestAccountInstallation
+ extends TestCase
+{
+ private static final Logger logger =
+ Logger.getLogger(TestAccountInstallation.class);
+
+ /**
+ * Creates a test with the specified method name.
+ * @param name the name of the method tu execute.
+ */
+ public TestAccountInstallation(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * JUnit setup method.
+ */
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * JUnit cleanup method.
+ */
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ /**
+ * Installs and account and verifies whether the installation succedded.
+ */
+ public void testInstallAccount()
+ {
+ Hashtable accountProperties = new Hashtable();
+
+ ServiceReference[] serRefs = null;
+ String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL + "="
+ + ProtocolNames.RSS + ")";
+
+ accountProperties.put(ProtocolProviderFactory.USER_ID, "RSS");
+
+ try {
+ serRefs = RssSlickFixture.bc.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), osgiFilter);
+ } catch (InvalidSyntaxException ise)
+ {
+ //shouldn't happen as the filter is static
+ fail(osgiFilter + "is not a valid filter");
+ }
+
+ //couldn't find a provider factory service.
+ assertTrue("Failed to find a provider factory service or protocol RSS",
+ serRefs != null && serRefs.length > 0);
+
+ ProtocolProviderFactory rssProviderFactory = (ProtocolProviderFactory)
+ RssSlickFixture.bc.getService(serRefs[0]);
+
+ //there shouldn't be any account installed
+ assertTrue("There was an account already registered with the account "
+ + "manager",
+ rssProviderFactory.getRegisteredAccounts().size() == 0);
+
+ try {
+ rssProviderFactory.installAccount(null, accountProperties);
+ fail("Installing an account with a null account id must result in a"
+ + " NullPointerException!");
+ } catch(NullPointerException npe)
+ {
+ //that's ought to happen
+ }
+
+ rssProviderFactory.installAccount("RSS", accountProperties);
+
+ //try to install the same account twice and check for exceptions
+ try {
+ rssProviderFactory.installAccount("RSS", accountProperties);
+ fail("An IllegalStateException must be thrown when trying to "
+ + "install a duplicate account.");
+ } catch(IllegalStateException ise)
+ {
+ //that's ought to happen
+ }
+
+ assertTrue("Newly installed account is not in the account manager's "
+ + "registered accounts!",
+ rssProviderFactory.getRegisteredAccounts().size() == 1);
+
+ osgiFilter = "(&(" + ProtocolProviderFactory.PROTOCOL + "="
+ + ProtocolNames.RSS + ")"
+ + "(" + ProtocolProviderFactory.USER_ID + "=RSS))";
+
+ try {
+ serRefs = RssSlickFixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(), osgiFilter);
+ } catch(InvalidSyntaxException ise)
+ {
+ fail(osgiFilter + " is not a valid filter");
+ }
+
+ assertTrue("A protocol provider was apparently not installed as"
+ + " requested",
+ serRefs != null && serRefs.length > 0);
+
+ Object rssProtocolProvider =
+ RssSlickFixture.bc.getService(serRefs[0]);
+
+ assertTrue("The installed protocol provider does not implement the"
+ + "protocol provider service.",
+ rssProtocolProvider instanceof ProtocolProviderService);
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/TestAccountUninstallation.java b/test/net/java/sip/communicator/slick/protocol/rss/TestAccountUninstallation.java
new file mode 100755
index 000000000..658adce6a
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/TestAccountUninstallation.java
@@ -0,0 +1,211 @@
+/*
+ * 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.slick.protocol.rss;
+
+import junit.framework.*;
+import org.osgi.framework.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Tests whether accounts are uninstalled properly. It is important that
+ * tests from this class be called last since they will install the accounts
+ * that have been used to test the implementations. Apart from uninstallation
+ * tests the class also contains tests that remove and reinstall the protocol
+ * provider bundle in order to verify that accounts are persistent.
+ *
+ * @author Mihai Balan
+ */
+public class TestAccountUninstallation
+ extends TestCase
+{
+ private Logger logger = Logger.getLogger(TestAccountUninstallation.class);
+
+ private RssSlickFixture fixture = new RssSlickFixture();
+
+ /**
+ * Constructs a test instance
+ * @param name The name of the test.
+ */
+ public TestAccountUninstallation(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * JUnit setup method.
+ * @throws Exception in case anything goes wrong.
+ */
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+ }
+
+ /**
+ * JUnit teardown method.
+ * @throws Exception in case anything goes wrong.
+ */
+ public void tearDown() throws Exception
+ {
+ fixture.tearDown();
+ super.tearDown();
+ }
+
+ /**
+ * Returns a suite containing tests in this class in the order that we'd
+ * like them executed.
+ * @return a Test suite containing tests in this class in the order that
+ * we'd like them executed.
+ */
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite();
+
+ suite.addTest(
+ new TestAccountUninstallation("testInstallationPersistence"));
+ suite.addTest(
+ new TestAccountUninstallation("testUninstallAccount"));
+
+ return suite;
+ }
+
+ /**
+ * Stops and removes the tested bundle, verifies that it has unregistered
+ * its provider, then reloads and restarts the bundle and verifies that
+ * the protocol provider is reRegistered in the bundle context.
+ *
+ * @throws java.lang.Exception if an exception occurs during testing.
+ */
+ public void testInstallationPersistence() throws Exception
+ {
+ Bundle providerBundle =
+ fixture.findProtocolProviderBundle(fixture.provider);
+
+ //set the global providerBundle reference that we will be using
+ //in the last series of tests (Account uninstallation persistency)
+ RssSlickFixture.providerBundle = providerBundle;
+
+ assertNotNull("Couldn't find a bundle for the protocol provider",
+ providerBundle);
+
+ providerBundle.stop();
+ assertTrue("Couldn't stop protocol provider bundle. State was "
+ + providerBundle.getSymbolicName(),
+ providerBundle.getState() != Bundle.ACTIVE
+ && providerBundle.getState() != Bundle.STOPPING);
+
+ providerBundle.uninstall();
+ assertEquals("Couldn't uninstall protocol provider bundle.",
+ providerBundle.getState(), Bundle.UNINSTALLED);
+
+ //verify that the provider is no longer available
+ ServiceReference providerRefs[] = null;
+ try {
+ providerRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&(" + ProtocolProviderFactory.PROTOCOL + "="
+ + ProtocolNames.RSS + "))");
+ } catch (InvalidSyntaxException ise)
+ {
+ fail("Invalid OSGi filter. Exception was" + ise.getMessage());
+ }
+
+ //verify the provider really uninstalled
+ assertTrue("Protocol provider still present after being explicitly" +
+ " uninstalled",
+ providerRefs == null || providerRefs.length == 0);
+ assertTrue("The RSS protocol provider factory didn't completely "
+ + "uninstalled the provider service",
+ fixture.providerFactory.getRegisteredAccounts().size() == 0 &&
+ fixture.providerFactory.getProviderForAccount(
+ fixture.provider.getAccountID()) == null);
+
+ //reinstall provider
+ providerBundle = fixture.bc.installBundle(providerBundle.getLocation());
+ RssSlickFixture.providerBundle = providerBundle;
+
+ assertTrue("Couldn't reinstall provider bundle",
+ providerBundle.getState() == Bundle.INSTALLED);
+
+ providerBundle.start();
+ assertTrue("Couldn't start provider",
+ providerBundle.getState() == Bundle.ACTIVE);
+
+ providerRefs = null;
+ try {
+ providerRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(&(" + ProtocolProviderFactory.PROTOCOL + "="
+ + ProtocolNames.RSS + "))");
+ } catch (InvalidSyntaxException ise)
+ {
+ fail("Invalid OSGi filter. Exception was" + ise.getMessage());
+ }
+ assertTrue("The protocol provider hasn't been restored after being "
+ + "reinstalled",
+ providerRefs != null && providerRefs.length > 0);
+
+ ServiceReference factoryRefs[] = null;
+ try {
+ factoryRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderFactory.class.getName(),
+ "(" + ProtocolProviderFactory.PROTOCOL + "=RSS)");
+ } catch (InvalidSyntaxException ise)
+ {
+ fail("Invalid OSGi filter. Exception was " + ise.getMessage());
+ }
+
+ fixture.providerFactory =
+ (ProtocolProviderFactory) fixture.bc.getService(factoryRefs[0]);
+ fixture.provider =
+ (ProtocolProviderService) fixture.bc.getService(providerRefs[0]);
+
+ assertFalse("RSS provider did not restore its own reference to the"
+ + " provider that we just reinstalled.",
+ fixture.providerFactory.getRegisteredAccounts().isEmpty()
+ && fixture.providerFactory.getProviderForAccount(
+ fixture.provider.getAccountID()) == null);
+ }
+
+ /**
+ * Unistalls our test account and makes sure it really has been removed.
+ */
+ public void testUninstallAccount() throws Exception
+ {
+ assertTrue("No accounts found.",
+ ! fixture.providerFactory.getRegisteredAccounts().isEmpty());
+
+ assertNotNull("Found no provider corresponding to RSS",
+ fixture.providerFactory.getProviderForAccount(
+ fixture.provider.getAccountID()));
+
+ assertTrue("Failed to remove provider",
+ fixture.providerFactory.uninstallAccount(
+ fixture.provider.getAccountID()));
+
+ ServiceReference[] providerRefs = null;
+ try {
+ providerRefs = fixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(" + ProtocolProviderFactory.PROTOCOL + "=" + ProtocolNames.RSS
+ + ")");
+ } catch (InvalidSyntaxException ise)
+ {
+ fail("Invalid OSGi filter.Exception was: " + ise.getMessage());
+ }
+
+ assertTrue("Protocol provider service still registered as OSGi service",
+ providerRefs == null || providerRefs.length == 0);
+
+ assertTrue("Provider factory didn't properly uninstalled the provider"
+ + "service we just uninstalled",
+ fixture.providerFactory.getRegisteredAccounts().isEmpty()
+ && fixture.providerFactory.getProviderForAccount(
+ fixture.provider.getAccountID()) == null);
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/TestAccountUninstallationPersistence.java b/test/net/java/sip/communicator/slick/protocol/rss/TestAccountUninstallationPersistence.java
new file mode 100755
index 000000000..d0da2fb8b
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/TestAccountUninstallationPersistence.java
@@ -0,0 +1,92 @@
+/*
+ * 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.slick.protocol.rss;
+
+import junit.framework.*;
+import org.osgi.framework.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Contains tests verifying persistence of account uninstallation. In other
+ * words we try to make sure that once uninstalled an account remains
+ * uninstalled.
+ *
+ * @author Mihai Balan
+ */
+public class TestAccountUninstallationPersistence
+ extends TestCase
+{
+ /**
+ * Creates a new test instance wrapper around the test with the specified
+ * name.
+ *
+ * @param testName the name of the test that we will be executing.
+ */
+ public TestAccountUninstallationPersistence(String testName)
+ {
+ super(testName);
+ }
+
+ /**
+ * Retrieves a reference to the RSS bundle, stops and uninstalls it and then
+ * reinstalls it in order to make sure that accounts are not reloaded once
+ * removed.
+ *
+ * @throws Exception if anything goes wrong while manipulating the bundles.
+ */
+ public void testAccountUninstallationPersistence()
+ throws Exception
+ {
+ Bundle providerBundle = RssSlickFixture.providerBundle;
+ providerBundle.stop();
+
+ assertTrue("Couldn't stop the protocol provider bundle. State was"
+ + providerBundle.getState(),
+ providerBundle.getState() != Bundle.ACTIVE
+ && providerBundle.getState() != Bundle.STOPPING);
+
+ providerBundle.uninstall();
+ assertEquals("Couldn't stop the protocol provider bundle.",
+ providerBundle.getState(), Bundle.UNINSTALLED);
+
+ //now reinstall the bundle and restart the provider
+ providerBundle = RssSlickFixture.bc.installBundle(
+ providerBundle.getLocation());
+ assertEquals("Couldn't reinstall protocol provider bundle.",
+ providerBundle.getState(), Bundle.INSTALLED);
+
+ providerBundle.start();
+ assertEquals("Couldn't restart protocol provider bundle.",
+ providerBundle.getState(), Bundle.ACTIVE);
+
+ //verify the provider is not reinstalled
+ ServiceReference[] providerRefs = null;
+ try {
+ providerRefs = RssSlickFixture.bc.getServiceReferences(
+ ProtocolProviderService.class.getName(),
+ "(" + ProtocolProviderFactory.PROTOCOL + "=" + ProtocolNames.RSS
+ + ")");
+ } catch (InvalidSyntaxException ise)
+ {
+ fail("OSGi filter is wrong. Error was: " + ise.getMessage());
+ }
+
+ //make sure we didn't retrieve a service
+ assertTrue("An RSS protocol provider service was still registered as an"
+ + " OSGi service even after being explicitly uninstalled",
+ providerRefs == null || providerRefs.length == 0);
+
+ //now delete configuration file for a fresh start for the next test
+ ServiceReference confRef = RssSlickFixture.bc.getServiceReference(
+ ConfigurationService.class.getName());
+ ConfigurationService confServ =
+ (ConfigurationService) RssSlickFixture.bc.getService(confRef);
+
+ confServ.purgeStoredConfiguration();
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/TestProtocolProviderServiceRssImpl.java b/test/net/java/sip/communicator/slick/protocol/rss/TestProtocolProviderServiceRssImpl.java
new file mode 100755
index 000000000..a1ce66ef0
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/TestProtocolProviderServiceRssImpl.java
@@ -0,0 +1,235 @@
+/*
+ * 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.slick.protocol.rss;
+
+import junit.framework.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+import java.util.*;
+
+/**
+ * This class performs testing on protocol provider methods. It verifies that
+ * the protocol provider service implements the declared operation sets and also
+ * tests the process of registering an account.
+ *
+ * @author Mihai Balan
+ */
+public class TestProtocolProviderServiceRssImpl
+ extends TestCase
+{
+ /**
+ * Time in miliseconds to wait for registration to complete. Is subject to
+ * further fine-tuning.
+ */
+ public static final int WAIT_DELAY = 10000;
+
+ public static final Logger logger =
+ Logger.getLogger(TestProtocolProviderServiceRssImpl.class);
+
+ /**
+ * Test's fixture.
+ */
+ private RssSlickFixture fixture = new RssSlickFixture();
+
+ public RegistrationEventCollector eventCollector =
+ new RegistrationEventCollector();
+
+ /**
+ * Creates a test case that runs the specified test.
+ *
+ * @param testName the name of the method the test should run.
+ */
+ public TestProtocolProviderServiceRssImpl(String testName)
+ {
+ super(testName);
+ }
+
+ /**
+ * Initializes the fixture.
+ *
+ * @throws Exception if super.setUp() throws one.
+ */
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ fixture.setUp();
+ }
+
+ /**
+ * Cleans up after the test.
+ *
+ * @throws Exception if fixture.tearDown() fails.
+ */
+ public void tearDown() throws Exception
+ {
+ fixture.tearDown();
+ super.tearDown();
+ }
+
+ /**
+ * This methods makes sure that the instance of the RSS protocol provider
+ * that we are going to use is properly initialized and registered. It also
+ * verifies that a registration event is fired upon successful registration
+ * and collected by our event collector.
+ *
+ * @throws OperationFailedException if provider.register()
+ * fails.
+ */
+ public void testRegister() throws OperationFailedException
+ {
+ //registering as a listener & starting registration with a
+ //null SecurityAuthority
+ fixture.provider.addRegistrationStateChangeListener(
+ this.eventCollector);
+ fixture.provider.register(new NullSecurityAuthority());
+
+ //give it a little time to do his magic ;)
+ logger.debug("Waiting for registration to complete...");
+ eventCollector.waitForEvent(WAIT_DELAY);
+
+ //make sure we received events...
+ assertTrue("No events were dispatched during the registration process.",
+ eventCollector.collectedStates.size() > 0);
+ //...and they were the right ones
+ assertTrue("No registration event notifying of registration was"
+ + " dispatched."
+ + " All events were:" + eventCollector.collectedStates,
+ eventCollector.collectedStates.contains(
+ RegistrationState.REGISTERED));
+
+ //if everything is ok, we leave it alone
+ fixture.provider.removeRegistrationStateChangeListener(eventCollector);
+ }
+
+ /**
+ * This method verifies that all operation sets have the type they are
+ * declared to have.
+ *
+ * @throws ClassNotFoundException if a class name found in the keys for the
+ * supported operation sets doesn't correspond to a class that the
+ * class loader can find.
+ */
+ public void testOperationsSets() throws ClassNotFoundException
+ {
+ Map supportedOperationSets =
+ fixture.provider.getSupportedOperationSets();
+
+ //get the keys for the supported operation set. The keys are strings
+ //corresponding to class names.
+ Iterator setNames = supportedOperationSets.keySet().iterator();
+ while (setNames.hasNext())
+ {
+ String key = (String) setNames.next();
+ Object opSet = supportedOperationSets.get(key);
+
+ assertTrue(opSet + " was not an instance of " + key +"as declared",
+ Class.forName(key).isInstance(opSet));
+ }
+ }
+
+ /**
+ * This class acts as a very simple registration listener for the protocol
+ * provider and simply records all events it receives and notifies
+ * (notifyAll()) all objects waiting for an instance of this
+ * class upon receiving an event that signals a completed registration.
+ *
+ * TODO: This class is just the same as the one in
+ * TestProtocolProviderServiceGibberishImpl.java . Is there any reason for
+ * duplicating code this way?
+ *
+ * @author Emil Ivov
+ * @author Mihai Balan
+ */
+ public class RegistrationEventCollector
+ implements RegistrationStateChangeListener
+ {
+ /**
+ * We store all the received events in this list. It's made public to
+ * ease later inspection by unit tests.
+ */
+ public List collectedStates = new LinkedList();
+
+ /**
+ * This method simply records all received events in a List
+ * that can be easily inspected by unit tests. In case we receive a
+ * registration event notifying us of a completed registration, the
+ * method calls notifyAll().
+ *
+ * @param e a RegistrationStateChangeEvent decribing the
+ * status change.
+ */
+ public void registrationStateChanged(RegistrationStateChangeEvent e)
+ {
+ logger.debug("Received a RegistrationChangeEvent: " + e.toString());
+
+ collectedStates.add(e.getNewState());
+
+ if (e.getNewState().equals(RegistrationState.REGISTERED))
+ {
+ logger.debug("We're registered. Notifying waiting threads");
+
+ synchronized(this)
+ {
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Blocks until an event notifying us of the awaited status change is
+ * received or until waitFor miliseconds pass, whichever
+ * comes first.
+ *
+ * @param waitFor the number of seconds to wait for an event. If no
+ * event is received, we simply return.
+ */
+ public void waitForEvent(long waitFor)
+ {
+ logger.trace("Waiting for a RegistrationChangeEvent");
+
+ synchronized(this)
+ {
+ if (collectedStates.contains(RegistrationState.REGISTERED))
+ {
+ logger.trace("Event already received" + collectedStates);
+ return;
+ }
+
+ try {
+ wait(waitFor);
+
+ if (collectedStates.size() > 0)
+ logger.trace("Received a RegistrationStateChangeEvent.");
+ else
+ logger.trace("No registrationStateChangeEvent received"
+ + " for" + waitFor + "ms.");
+ } catch(InterruptedException ie)
+ {
+ logger.debug("Interrupted while waiting for "
+ + "a RegistrationStateChangeEvent", ie);
+ }
+ }
+ }
+ }
+
+ /**
+ * Dummy implementation of a SecurityAuthority. It simply
+ * returns null credentials as the RSS "authentication" protocol requires
+ * none.
+ *
+ * @author Mihai Balan
+ */
+ public class NullSecurityAuthority
+ implements SecurityAuthority
+ {
+ public UserCredentials obtainCredentials(String realm,
+ UserCredentials defaultValues) {
+ return null;
+ }
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/TestingServer.java b/test/net/java/sip/communicator/slick/protocol/rss/TestingServer.java
new file mode 100755
index 000000000..2bafc972b
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/TestingServer.java
@@ -0,0 +1,188 @@
+/*
+ * 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.slick.protocol.rss;
+
+import java.net.*;
+import java.io.*;
+
+/**
+ * This classed is used in the automatic testing of the RSS protocol. It acts as
+ * a very simple HTTP server that can serve RSS files. It has the ability
+ * to simulate an update to the file, or to send an invalid file.
+ *
+ * The usual flow when using a TestingServer is as follows:
+ *
+ *
Create TestingServer object.
+ *
Set server behaviour through calls to setAtomUsage() and
+ * setServerBehaviour().
+ *
Start the server with start().
+ *
Retrieve contents served by the server.
+ *
Stop the server with stop().
+ *
If necessary rewind from 2.
+ *
+ *
+ * @see setAtomUsage, setServerBehaviour, start, stop
+ * @author Mihai Balan
+ */
+public class TestingServer
+{
+ /* Java 1.5 specific - could simplfy things a lot when the switch is decided
+ * enum ServerResponse
+ * {
+ * VALID, VALID_UPDATE, VALID_NEW, INVALID
+ * }
+ */
+ /**
+ * Numeric constant specifying an invalid file to be served.
+ */
+ public static final int INVALID = 0;
+
+ /**
+ * Numeric constant specifying a valid file to be served.
+ */
+ public static final int VALID = 1;
+
+ /**
+ * Numeric constant specifying a valid, updated file to be served.
+ */
+ public static final int VALID_UPDATE = 2;
+
+ /**
+ * Numeric constant specifying a valid, new file to be served.
+ */
+ public static final int VALID_NEW = 3;
+
+ /**
+ * true if ATOM-like files are used, false
+ * otherwise.
+ */
+ private boolean usesAtom;
+
+ /**
+ * Flag specifying the current type of file used.
+ */
+ private int currentFile = VALID;
+
+ /**
+ * ServerSocket used to listen for incoming connections.
+ */
+ private ServerSocket server = null;
+
+ /**
+ * Thread for responding to client requests.
+ */
+ private TestingServerThread runner = null;
+
+ /**
+ * true if the server was successfully launched (through a call
+ * to start(), false otherwise.
+ */
+ private boolean serverActive;
+
+ /**
+ * Public constructor. Creates the server and binds it to port 8080 of the
+ * loop-back address. It uses port 8080 instead of the more
+ * standard port 80, because on Linux machines binding on ports smaller than
+ * 1023 requires root privileges.
+ * @throws IOException in case the server cannot bind to the specified
+ * address/port, it throws an IOException detailing the problem.
+ */
+ public TestingServer()
+ throws IOException
+ {
+ server = new ServerSocket(8080, 20,
+ InetAddress.getByName(null));
+ usesAtom = false;
+ serverActive = false;
+ currentFile = INVALID;
+ }
+
+ /**
+ * Sets whether or not to use ATOM-like files or not
+ * @param usesAtom true to serve ATOM-like files,
+ * false otherwise.
+ */
+ public void setAtomUsage(boolean usesAtom)
+ {
+ this.usesAtom = usesAtom;
+ }
+
+ /**
+ * Accessor for the field variable usesAtom
+ * @return true if ATOM-like files are used, false
+ * otherwise.
+ */
+ public boolean usesAtom()
+ {
+ return this.usesAtom;
+ }
+
+ /**
+ * Returns the current state of the server.
+ * @return true if a successful call to start()
+ * has been issued and the server hasn't yet processed any request,
+ * false otherwise.
+ */
+ public boolean isActive()
+ {
+ return serverActive;
+ }
+
+ /**
+ * Sets the type of file the server serves.
+ * @param type int enum specifying the type of file.
+ */
+ public void setServerBehaviour(int type)
+ {
+ if (type == INVALID || type == VALID || type == VALID_UPDATE
+ || type == VALID_NEW )
+ currentFile = type;
+ else
+ currentFile = INVALID;
+ }
+
+ /**
+ * Return the type of file currently server by the server.
+ * @return file type.
+ */
+ public int getServerBehaviour()
+ {
+ return currentFile;
+ }
+
+ /**
+ * Starts the listening process for incoming connections in a separate
+ * thread.
+ */
+ public void start()
+ {
+ //only allow one "instance" of the server to be running at a given time
+ if (serverActive)
+ return;
+
+ //create new thread and launch
+ runner = new TestingServerThread(this, server);
+ serverActive = true;
+ runner.start();
+ }
+
+ /**
+ * Takes the server into an inactive state. If the listening thread is still
+ * running it waits for it to end.
+ * @throws InterruptedException
+ */
+ public void stop() throws InterruptedException
+ {
+ //wait for listening thread to end
+ if (runner.isAlive())
+ runner.join();
+
+ //restore server state
+ serverActive = false;
+ runner = null;
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/TestingServerThread.java b/test/net/java/sip/communicator/slick/protocol/rss/TestingServerThread.java
new file mode 100755
index 000000000..0dea8faf4
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/TestingServerThread.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.slick.protocol.rss;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+/**
+ * This class represents the separate thread that a TestingServer
+ * uses to listen for incoming connections.
+ * A reference to the object that launched the thread is also kept too in order
+ * to have easier access to the server settings.
+ *
+ * @author Mihai Balan
+ */
+public class TestingServerThread
+ extends Thread
+{
+ /**
+ * The server that launched us.
+ */
+ private TestingServer launcher = null;
+
+ /**
+ * The server's ServerSocket. We keep a reference to it to as
+ * the call to ServerSocket.accept() is made in the thread (us)
+ * and not in the server to avoid deadlocks.
+ */
+ private ServerSocket serverSocket;
+ /**
+ * Socket used for network I/O.
+ */
+ private Socket socket;
+
+ /**
+ * Creates a new thread for listening for connections.
+ * @param launcher the TestingServer object that created us.
+ * @param serverSock the ServerSocket used to listen for
+ * connections on.
+ */
+ public TestingServerThread(TestingServer launcher, ServerSocket serverSock)
+ {
+ /* would a check for null (mainly for the launcher) and an eventual
+ * NullPointerException make sense here?*/
+ this.launcher = launcher;
+ this.serverSocket = serverSock;
+ }
+
+ /**
+ * Effectively launches the server thread. It starts listening for
+ * connections on serverSocket, and upon a successful request
+ * serves the file according to the launcher's settings.
+ */
+ public void run()
+ {
+ if (launcher.isActive())
+ {
+ try {
+ socket = serverSocket.accept();
+
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(socket.getInputStream()));
+ processRequest(in);
+
+ launcher.stop();
+
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ socket = null;
+ } catch (InterruptedException ie)
+ {
+ ie.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Processes the HTTP request and initiates the sending of a response
+ * according to the server settings.
+ *
+ * @param in network end-point from which we read.
+ *
+ * @throws IOException
+ */
+ public void processRequest(BufferedReader in)
+ throws IOException
+ {
+ String crtLine;
+ String httpVersion = null;
+ boolean gotRequest = false;
+
+ //XXX: Debug
+ //System.out.println("Processing request. Dump follows:");
+
+ //look for the get string and totally and blindly ignore all other
+ //HTTP headers
+ while (! "".equals(crtLine = in.readLine().trim()) && crtLine != null)
+ {
+ //XXX: Debug
+ //System.out.println("> " + crtLine);
+ if (crtLine.startsWith("GET /"))
+ {
+ gotRequest = true;
+ int httpPos = -1;
+ httpPos = crtLine.lastIndexOf(" HTTP/");
+ if (httpPos != -1)
+ httpVersion = crtLine.substring(httpPos + 1);
+ else
+ httpVersion = "HTTP/1.0";
+ }
+ }
+
+ //if no file was requested, just abort
+ if (! gotRequest)
+ return;
+
+ BufferedWriter out = new BufferedWriter(
+ new OutputStreamWriter(socket.getOutputStream()));
+
+ sendResponse(out, httpVersion);
+ }
+
+ /**
+ * Builds and sends the HTTP response according to the server settings.
+ *
+ * @param out the network end-point to which we write to.
+ * @param httpVersion textual representation of the HTTP version used.
+ * Should either be "HTTP/1.0" or "HTTP/1.1".
+ *
+ * @throws IOException
+ */
+ public void sendResponse(BufferedWriter out, String httpVersion)
+ throws IOException
+ {
+ String content = "";
+ SimpleDateFormat dateFormatter =
+ new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
+
+ //getting output
+ switch (launcher.getServerBehaviour())
+ {
+ case TestingServer.INVALID:
+ content = launcher.usesAtom() ? FeedFactory.getAtomInvalid()
+ : FeedFactory.getRssInvalid();
+ break;
+ case TestingServer.VALID:
+ content = launcher.usesAtom() ? FeedFactory.getAtom()
+ : FeedFactory.getRss();
+ break;
+ case TestingServer.VALID_UPDATE:
+ content = launcher.usesAtom() ? FeedFactory.getAtomUpdated()
+ : FeedFactory.getRssUpdated();
+ break;
+ case TestingServer.VALID_NEW:
+ content = launcher.usesAtom() ? FeedFactory.getAtomNew()
+ : FeedFactory.getRssNew();
+ break;
+ }
+
+ //XXX: Debug
+ //System.out.println("Sending response headers...");
+
+ //building response HTTP headers
+ out.write(httpVersion + "200 OK\r\n");
+ out.write("Server: RssTestingServer/0.0.1\r\n");
+ out.write("Date: " + dateFormatter.format(new Date()) + "\r\n");
+ out.write("Content-Type: text/html; charset=utf-8\r\n");
+ out.write("Content-length: " + content.length() + "\r\n");
+ out.write("\r\n");
+
+ //outputting the file
+ out.write(content);
+
+ out.close();
+ socket.close();
+ socket = null;
+ }
+}
diff --git a/test/net/java/sip/communicator/slick/protocol/rss/rss.provider.slick.manifest.mf b/test/net/java/sip/communicator/slick/protocol/rss/rss.provider.slick.manifest.mf
new file mode 100755
index 000000000..81b8c7369
--- /dev/null
+++ b/test/net/java/sip/communicator/slick/protocol/rss/rss.provider.slick.manifest.mf
@@ -0,0 +1,12 @@
+Bundle-Activator: net.java.sip.communicator.slick.protocol.rss.RssProtocolProviderServiceLick
+Bundle-Name: RSS Protocol Provider Service Leveraging Implementation Compatibility Kit
+Bundle-Description: A Service Leveraging Implementation Compatibility Kit for the RSS implementation of the ProtocolProvider Service
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+Import-Package: net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.configuration.event,
+ junit.framework,
+ org.osgi.framework,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.service.protocol.event
\ No newline at end of file