aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--resources/languages/resources.properties14
-rw-r--r--src/net/java/sip/communicator/impl/protocol/AccountManagerImpl.java838
-rw-r--r--src/net/java/sip/communicator/service/protocol/AccountManager.java784
-rw-r--r--src/net/java/sip/communicator/service/protocol/CallPeerState.java104
-rw-r--r--src/net/java/sip/communicator/service/protocol/ProtocolProviderActivator.java (renamed from src/net/java/sip/communicator/impl/protocol/ProtocolProviderActivator.java)32
-rw-r--r--src/net/java/sip/communicator/service/protocol/SingleCallInProgressPolicy.java (renamed from src/net/java/sip/communicator/impl/protocol/SingleCallInProgressPolicy.java)3
-rw-r--r--src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf5
7 files changed, 864 insertions, 916 deletions
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index ac0e409..dbdc417 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -74,6 +74,7 @@ service.gui.BAN_FAILED_NOT_ENOUGH_PERMISSIONS=Failed to ban {0}. You don''t have
service.gui.BRB_MESSAGE=I'm gone right now, but I'll be back.
service.gui.BROWSE=Browse
service.gui.BUSY_MESSAGE=Sorry, I'm busy right now.
+service.gui.BUSY_STATUS=Busy
service.gui.CALL=Call
service.gui.CALL_CONTACT=Call contact
service.gui.CALL_FAILED=Call failed
@@ -114,7 +115,10 @@ service.gui.CLOSE_CHAT_ACTIVE_FILE_TRANSFER=You have active file transfers. Are
service.gui.COMPARE_WITH_PARTNER=Compare with partner and click the padlock to confirm.
# keep the following string short
service.gui.COMPARE_WITH_PARTNER_SHORT=Compare with partner: {0}
+service.gui.CONNECTED_STATUS=Connected
service.gui.CONNECTING=Connecting...
+service.gui.CONNECTING_STATUS=Connecting
+service.gui.CONNECTING_EARLY_MEDIA_STATUS=Connecting*
service.gui.CONNECTION=Connection
service.gui.CONNECTION_FAILED_MSG=Connection failed for the following account: User name: {0}, Server name: {1}. Please check your network connection or contact your network administrator for more information.
service.gui.CONNECTION_EXPIRED_MSG=You are currently disconnected from the {0} server.
@@ -143,6 +147,7 @@ service.gui.DATE=Date
service.gui.DELETE=Delete
service.gui.DESKTOP_SHARING_WARNING=<b>Are you sure you want to start screen sharing?</b> <br> Clicking OK will let people on this call see your screen.
service.gui.DIALPAD=Dialpad
+service.gui.DISCONNECTED_STATUS=Disconnected
service.gui.DND_STATUS=Do not disturb
service.gui.DO_NOT_ASK_AGAIN=Don't ask again
service.gui.DO_NOT_SHOW_AGAIN=Don't show this message again
@@ -162,6 +167,7 @@ service.gui.EXTENDED_CRITERIA=Extended criteria
service.gui.GENERAL=General
service.gui.GENERAL_ERROR=General error
service.gui.GROUP_NAME=Group name
+service.gui.FAILED_STATUS=Call failed
service.gui.FAILED_TO_JOIN_CHAT_ROOM=Failed to join chat room with name: {0}.
service.gui.FFC_STATUS=Free for chat
service.gui.FILE=&File
@@ -218,6 +224,8 @@ service.gui.IDENTIFIER=Identifier
service.gui.IGNORE=&Ignore
service.gui.INSERT_SMILEY=Insert smiley
service.gui.INCOMING_CALL=Incoming call received from: {0}
+service.gui.INCOMING_CALL_STATUS=Incoming call
+service.gui.INITIATING_CALL_STATUS=Initiating call
service.gui.INVITATION=Invitation text
service.gui.INVITATION_RECEIVED=Invitation received
service.gui.INVITATION_RECEIVED_MSG={0} has invited you to join {1} chat room. You could accept, reject or ignore this invitation.
@@ -244,6 +252,7 @@ service.gui.LAST=Last
service.gui.LEAVE=&Leave
service.gui.LIMIT_REACHED_FOR_IP=You have too many existing registrations from the local IP address and the {0} server doesn''t allow to open any more of them.
service.gui.LOADING_ROOMS=Loading rooms...
+service.gui.LOCALLY_ON_HOLD_STATUS=Locally on hold
service.gui.LOGIN=&Login
service.gui.LOGIN_NETWORK_ERROR=Unable to log in with account: User name: {0}, Server name: {1}, due to a network failure. Please check your network connection.
service.gui.LOGIN_GENERAL_ERROR=An error occurred while logging in with account: User name: {0}, Server name: {1}:{2}.
@@ -278,6 +287,7 @@ service.gui.MULTIPLE_LOGINS=You have logged in more than once with the same acco
service.gui.MY_CHAT_ROOMS=Go to chat room...
service.gui.MY_CHAT_ROOMS_TITLE=Go to chat room
service.gui.MUTE=Mute
+service.gui.MUTUALLY_ON_HOLD_STATUS=Mutually On Hold
service.gui.NETWORK=Network
service.gui.NETWORK_FAILURE=Network failure
service.gui.NEXT=&Next
@@ -325,6 +335,7 @@ service.gui.READY=Ready
service.gui.REASON=Reason
service.gui.RECEIVED=received
service.gui.RECONNECTION_LIMIT_EXCEEDED=You have have been disconnecting and reconnecting to the server too fast. The following account: User name: {0}, Server name: {1} is temporarily banned and would have to wait a bit before trying to login again.
+service.gui.REFERRED_STATUS=Referred
service.gui.REJECT=&Reject
service.gui.REMEMBER_PASSWORD=Remember password
service.gui.REMOVE=&Remove
@@ -333,11 +344,13 @@ service.gui.REMOVE_ACCOUNT_MESSAGE=Are you sure you want to remove this account?
service.gui.REMOVE_CONTACT=&Remove contact
service.gui.REMOVE_CONTACT_TEXT=<DIV>Are you sure you want to remove <B> {0} </B><BR>from your contact list?</DIV>
service.gui.REMOVE_GROUP=&Remove group
+service.gui.REMOTELY_ON_HOLD_STATUS=Remotely on hold
service.gui.RENAME=&Rename
service.gui.RENAME_CONTACT=Re&name contact
service.gui.RENAME_CONTACT_WIZARD=In the field below you can specify the name you would like to use for the given contact.
service.gui.RENAME_GROUP=Re&name group
service.gui.RENAME_GROUP_INFO=In the field below you can specify the name you would like to use for the given group.
+service.gui.RINGING_STATUS=Ringing
service.gui.REQUEST=&Request
service.gui.REQUEST_AUTHORIZATION=&Request authorization
service.gui.REQUEST_AUTHORIZATION_MSG=Can''t add {0} to your Contact List. {0} must authorize your request to add him/her. Please enter your request below.
@@ -407,6 +420,7 @@ service.gui.USER_EXISTS_ERROR=This user already exists on the selected network.
service.gui.USERNAME_NULL=Please fill the name of your account.
service.gui.ACCOUNT_CREATION_FAILED=We failed to create your account due to the following error: {0}
service.gui.UNKNOWN=Unknown user
+service.gui.UNKNOWN_STATUS=Unknown state
service.gui.UNREGISTERED_MESSAGE=Unable to connect the following account: User name: {0}, Server name: {1}. You are currently offline.
service.gui.VIDEO_CALL=&Video call
service.gui.VIEW=&View
diff --git a/src/net/java/sip/communicator/impl/protocol/AccountManagerImpl.java b/src/net/java/sip/communicator/impl/protocol/AccountManagerImpl.java
deleted file mode 100644
index 5bbbf93..0000000
--- a/src/net/java/sip/communicator/impl/protocol/AccountManagerImpl.java
+++ /dev/null
@@ -1,838 +0,0 @@
-/*
- * 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;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.configuration.*;
-import net.java.sip.communicator.service.credentialsstorage.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-
-import org.osgi.framework.*;
-
-/**
- * Represents an implementation of <tt>AccountManager</tt> which loads the
- * accounts in a separate thread.
- *
- * @author Lubomir Marinov
- * @author Yana Stamcheva
- */
-public class AccountManagerImpl
- implements AccountManager
-{
-
- /**
- * The delay in milliseconds the background <tt>Thread</tt> loading the
- * stored accounts should wait before dying so that it doesn't get recreated
- * for each <tt>ProtocolProviderFactory</tt> registration.
- */
- private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000;
-
- /**
- * The <tt>BundleContext</tt> this service is registered in.
- */
- private final BundleContext bundleContext;
-
- /**
- * The <tt>AccountManagerListener</tt>s currently interested in the
- * events fired by this manager.
- */
- private final List<AccountManagerListener> listeners =
- new LinkedList<AccountManagerListener>();
-
- /**
- * The queue of <tt>ProtocolProviderFactory</tt> services awaiting their
- * stored accounts to be loaded.
- */
- private final Queue<ProtocolProviderFactory> loadStoredAccountsQueue =
- new LinkedList<ProtocolProviderFactory>();
-
- /**
- * The <tt>Thread</tt> loading the stored accounts of the
- * <tt>ProtocolProviderFactory</tt> services waiting in
- * {@link #loadStoredAccountsQueue}.
- */
- private Thread loadStoredAccountsThread;
-
- /**
- * The <tt>Logger</tt> used by this <tt>AccountManagerImpl</tt> instance for
- * logging output.
- */
- private final Logger logger = Logger.getLogger(AccountManagerImpl.class);
-
- /**
- * The list of <tt>AccountID</tt>s, corresponding to all stored accounts.
- */
- private Vector<AccountID> storedAccounts = new Vector<AccountID>();
-
- /**
- * Initializes a new <tt>AccountManagerImpl</tt> instance loaded in a
- * specific <tt>BundleContext</tt> (in which the caller will usually
- * later register it).
- *
- * @param bundleContext the <tt>BundleContext</tt> in which the new
- * instance is loaded (and in which the caller will usually later
- * register it as a service)
- */
- public AccountManagerImpl(BundleContext bundleContext)
- {
- this.bundleContext = bundleContext;
-
- this.bundleContext.addServiceListener(new ServiceListener()
- {
- public void serviceChanged(ServiceEvent serviceEvent)
- {
- AccountManagerImpl.this.serviceChanged(serviceEvent);
- }
- });
- }
-
- /**
- * Implements AccountManager#addListener(AccountManagerListener).
- * @param listener the <tt>AccountManagerListener</tt> to add
- */
- public void addListener(AccountManagerListener listener)
- {
- synchronized (listeners)
- {
- if (!listeners.contains(listener))
- listeners.add(listener);
- }
- }
-
- /**
- * Loads the accounts stored for a specific
- * <tt>ProtocolProviderFactory</tt>.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> to load the
- * stored accounts of
- */
- private void doLoadStoredAccounts(ProtocolProviderFactory factory)
- {
- ConfigurationService configService
- = ProtocolProviderActivator.getConfigurationService();
- String factoryPackage = getFactoryImplPackageName(factory);
- List<String> accounts
- = configService.getPropertyNamesByPrefix(factoryPackage, true);
-
- if (logger.isDebugEnabled())
- logger.debug("Discovered " + accounts.size() + " stored "
- + factoryPackage + " accounts");
-
- for (Iterator<String> storedAccountIter = accounts.iterator();
- storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
-
- if (logger.isDebugEnabled())
- logger.debug("Loading account " + storedAccount);
-
- List<String> storedAccountProperties =
- configService.getPropertyNamesByPrefix(storedAccount, true);
- Map<String, String> accountProperties =
- new Hashtable<String, String>();
- boolean disabled = false;
- CredentialsStorageService credentialsStorage
- = ServiceUtils.getService(
- bundleContext,
- CredentialsStorageService.class);
-
- for (Iterator<String> storedAccountPropertyIter
- = storedAccountProperties.iterator();
- storedAccountPropertyIter.hasNext();)
- {
- String property = storedAccountPropertyIter.next();
- String value = configService.getString(property);
-
- property = stripPackagePrefix(property);
-
- if (ProtocolProviderFactory.IS_ACCOUNT_DISABLED.equals(property))
- disabled = Boolean.parseBoolean(value);
- // Decode passwords.
- else if (ProtocolProviderFactory.PASSWORD.equals(property)
- && !credentialsStorage.isStoredEncrypted(storedAccount))
- {
- if ((value != null) && value.length() != 0)
- {
-
- /*
- * TODO Converting byte[] to String using the platform's
- * default charset may result in an invalid password.
- */
- value = new String(Base64.decode(value));
- }
- }
-
- if (value != null)
- accountProperties.put(property, value);
- }
-
- try
- {
- AccountID accountID = factory.createAccount(accountProperties);
-
- // If for some reason the account id is not created we move to
- // the next account.
- if (accountID == null)
- continue;
-
- synchronized (storedAccounts)
- {
- storedAccounts.add(accountID);
- }
- if (!disabled)
- factory.loadAccount(accountID);
- }
- catch (Exception ex)
- {
-
- /*
- * Swallow the exception in order to prevent a single account
- * from halting the loading of subsequent accounts.
- */
- logger.error("Failed to load account " + accountProperties, ex);
- }
- }
- }
-
- /**
- * Notifies the registered {@link #listeners} that the stored accounts of a
- * specific <tt>ProtocolProviderFactory</tt> have just been loaded.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which had its
- * stored accounts just loaded
- */
- private void fireStoredAccountsLoaded(ProtocolProviderFactory factory)
- {
- AccountManagerListener[] listeners;
- synchronized (this.listeners)
- {
- listeners =
- this.listeners
- .toArray(new AccountManagerListener[this.listeners.size()]);
- }
-
- int listenerCount = listeners.length;
- if (listenerCount > 0)
- {
- AccountManagerEvent event =
- new AccountManagerEvent(this,
- AccountManagerEvent.STORED_ACCOUNTS_LOADED, factory);
-
- for (int listenerIndex = 0;
- listenerIndex < listenerCount; listenerIndex++)
- {
- listeners[listenerIndex].handleAccountManagerEvent(event);
- }
- }
- }
-
- private String getFactoryImplPackageName(ProtocolProviderFactory factory)
- {
- String className = factory.getClass().getName();
-
- return className.substring(0, className.lastIndexOf('.'));
- }
-
- public boolean hasStoredAccounts(String protocolName, boolean includeHidden)
- {
- ServiceReference[] factoryRefs = null;
- boolean hasStoredAccounts = false;
-
- try
- {
- factoryRefs
- = bundleContext.getServiceReferences(
- ProtocolProviderFactory.class.getName(), null);
- }
- catch (InvalidSyntaxException ex)
- {
- logger.error(
- "Failed to retrieve the registered ProtocolProviderFactories",
- ex);
- }
-
- if ((factoryRefs != null) && (factoryRefs.length > 0))
- {
- ConfigurationService configService
- = ProtocolProviderActivator.getConfigurationService();
-
- for (ServiceReference factoryRef : factoryRefs)
- {
- ProtocolProviderFactory factory
- = (ProtocolProviderFactory)
- bundleContext.getService(factoryRef);
-
- if ((protocolName != null)
- && !protocolName.equals(factory.getProtocolName()))
- {
- continue;
- }
-
- String factoryPackage = getFactoryImplPackageName(factory);
- List<String> storedAccounts
- = configService
- .getPropertyNamesByPrefix(factoryPackage, true);
-
- /* Ignore the hidden accounts. */
- for (Iterator<String> storedAccountIter =
- storedAccounts.iterator(); storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
- List<String> storedAccountProperties =
- configService.getPropertyNamesByPrefix(storedAccount,
- true);
- boolean hidden = false;
-
- if (!includeHidden)
- {
- for (Iterator<String> storedAccountPropertyIter =
- storedAccountProperties.iterator();
- storedAccountPropertyIter.hasNext();)
- {
- String property = storedAccountPropertyIter.next();
- String value = configService.getString(property);
-
- property = stripPackagePrefix(property);
-
- if (ProtocolProviderFactory.IS_PROTOCOL_HIDDEN
- .equals(property))
- {
- hidden = (value != null);
- break;
- }
- }
- }
-
- if (!hidden)
- {
- hasStoredAccounts = true;
- break;
- }
- }
-
- if (hasStoredAccounts || (protocolName != null))
- {
- break;
- }
- }
- }
- return hasStoredAccounts;
- }
-
- /**
- * Loads the accounts stored for a specific
- * <tt>ProtocolProviderFactory</tt> and notifies the registered
- * {@link #listeners} that the stored accounts of the specified
- * <tt>factory</tt> have just been loaded
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> to load the
- * stored accounts of
- */
- private void loadStoredAccounts(ProtocolProviderFactory factory)
- {
- doLoadStoredAccounts(factory);
-
- fireStoredAccountsLoaded(factory);
- }
-
- /**
- * Notifies this manager that a specific
- * <tt>ProtocolProviderFactory</tt> has been registered as a service.
- * The current implementation queues the specified <tt>factory</tt> to
- * have its stored accounts as soon as possible.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which has been
- * registered as a service.
- */
- private void protocolProviderFactoryRegistered(
- ProtocolProviderFactory factory)
- {
- queueLoadStoredAccounts(factory);
- }
-
- /**
- * Queues a specific <tt>ProtocolProviderFactory</tt> to have its stored
- * accounts loaded as soon as possible.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> to be queued for
- * loading its stored accounts as soon as possible
- */
- private void queueLoadStoredAccounts(ProtocolProviderFactory factory)
- {
- synchronized (loadStoredAccountsQueue)
- {
- loadStoredAccountsQueue.add(factory);
- loadStoredAccountsQueue.notify();
-
- if (loadStoredAccountsThread == null)
- {
- loadStoredAccountsThread = new Thread()
- {
- public void run()
- {
- runInLoadStoredAccountsThread();
- }
- };
- loadStoredAccountsThread.setDaemon(true);
- loadStoredAccountsThread
- .setName("AccountManager.loadStoredAccounts");
- loadStoredAccountsThread.start();
- }
- }
- }
-
- /**
- * Implements AccountManager#removeListener(AccountManagerListener).
- * @param listener the <tt>AccountManagerListener</tt> to remove
- */
- public void removeListener(AccountManagerListener listener)
- {
- synchronized (listeners)
- {
- listeners.remove(listener);
- }
- }
-
- /**
- * Running in {@link #loadStoredAccountsThread}, loads the stored accounts
- * of the <tt>ProtocolProviderFactory</tt> services waiting in
- * {@link #loadStoredAccountsQueue}
- */
- private void runInLoadStoredAccountsThread()
- {
- boolean interrupted = false;
- while (!interrupted)
- {
- try
- {
- ProtocolProviderFactory factory;
-
- synchronized (loadStoredAccountsQueue)
- {
- factory = loadStoredAccountsQueue.poll();
- if (factory == null)
- {
- /*
- * Technically, we should be handing spurious wakeups.
- * However, we cannot check the condition in a queue.
- * Anyway, we just want to keep this Thread alive long
- * enough to allow it to not be re-created multiple
- * times and not handing a spurious wakeup will just
- * cause such an inconvenience.
- */
- try
- {
- loadStoredAccountsQueue
- .wait(LOAD_STORED_ACCOUNTS_TIMEOUT);
- }
- catch (InterruptedException ex)
- {
- logger
- .warn(
- "The loading of the stored accounts has"
- + " been interrupted",
- ex);
- interrupted = true;
- break;
- }
- factory = loadStoredAccountsQueue.poll();
- }
- }
-
- if (factory != null)
- {
- try
- {
- loadStoredAccounts(factory);
- }
- catch (Exception ex)
- {
-
- /*
- * Swallow the exception in order to prevent a single
- * factory from halting the loading of subsequent
- * factories.
- */
- logger.error("Failed to load accounts for " + factory,
- ex);
- }
- }
- }
- finally
- {
- synchronized (loadStoredAccountsQueue)
- {
- if (!interrupted && (loadStoredAccountsQueue.size() <= 0))
- {
- if (loadStoredAccountsThread == Thread.currentThread())
- {
- loadStoredAccountsThread = null;
- }
- break;
- }
- }
- }
- }
- }
-
- /**
- * Notifies this manager that an OSGi service has changed. The current
- * implementation tracks the registrations of
- * <tt>ProtocolProviderFactory</tt> services in order to queue them for
- * loading their stored accounts.
- *
- * @param serviceEvent the <tt>ServiceEvent</tt> containing the event
- * data
- */
- private void serviceChanged(ServiceEvent serviceEvent)
- {
- switch (serviceEvent.getType())
- {
- case ServiceEvent.REGISTERED:
- Object service
- = bundleContext.getService(serviceEvent.getServiceReference());
-
- if (service instanceof ProtocolProviderFactory)
- {
- protocolProviderFactoryRegistered(
- (ProtocolProviderFactory) service);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Stores an account represented in the form of an <tt>AccountID</tt>
- * created by a specific <tt>ProtocolProviderFactory</tt>.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which created the
- * account to be stored
- * @param accountID the account in the form of <tt>AccountID</tt> to be
- * stored
- * @throws OperationFailedException if anything goes wrong while storing the
- * account
- */
- public void storeAccount(
- ProtocolProviderFactory factory,
- AccountID accountID)
- throws OperationFailedException
- {
- synchronized (storedAccounts)
- {
- if (!storedAccounts.contains(accountID))
- storedAccounts.add(accountID);
- }
-
- ConfigurationService configurationService
- = ProtocolProviderActivator.getConfigurationService();
- String factoryPackage = getFactoryImplPackageName(factory);
-
- // First check if such accountID already exists in the configuration.
- List<String> storedAccounts =
- configurationService.getPropertyNamesByPrefix(factoryPackage, true);
- String accountUID = accountID.getAccountUniqueID();
- String accountNodeName = null;
-
- for (Iterator<String> storedAccountIter = storedAccounts.iterator();
- storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
- String storedAccountUID =
- configurationService.getString(storedAccount + "."
- + ProtocolProviderFactory.ACCOUNT_UID);
-
- if (storedAccountUID.equals(accountUID))
- accountNodeName = configurationService.getString(storedAccount);
- }
-
- Map<String, Object> configurationProperties
- = new HashMap<String, Object>();
-
- // Create a unique node name of the properties node that will contain
- // this account's properties.
- if (accountNodeName == null)
- {
- accountNodeName = "acc" + Long.toString(System.currentTimeMillis());
-
- // set a value for the persistent node so that we could later
- // retrieve it as a property
- configurationProperties.put(
- factoryPackage /* prefix */ + "." + accountNodeName,
- accountNodeName);
-
- // register the account in the configuration service.
- // we register all the properties in the following hierarchy
- //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
- configurationProperties.put(factoryPackage// prefix
- + "." + accountNodeName // node name for the account id
- + "." + ProtocolProviderFactory.ACCOUNT_UID, // propname
- accountID.getAccountUniqueID()); // value
- }
-
- // store the rest of the properties
- Map<String, String> accountProperties = accountID.getAccountProperties();
-
- for (Map.Entry<String, String> entry : accountProperties.entrySet())
- {
- String property = entry.getKey();
- String value = entry.getValue();
-
- // If the property is a password, store it securely.
- if (property.equals(ProtocolProviderFactory.PASSWORD))
- {
- CredentialsStorageService credentialsStorage
- = ServiceUtils.getService(
- bundleContext,
- CredentialsStorageService.class);
- String accountPrefix = factoryPackage + "." + accountNodeName;
-
- // encrypt and store
- if ((value != null)
- && (value.length() != 0)
- && !credentialsStorage.storePassword(
- accountPrefix,
- value))
- {
- throw
- new OperationFailedException(
- "CredentialsStorageService failed to"
- + " storePassword",
- OperationFailedException.GENERAL_ERROR);
- }
- }
- else
- {
- configurationProperties.put(
- factoryPackage // prefix
- + "." + accountNodeName // a unique node name for the account id
- + "." + property, // propname
- value); // value
- }
- }
-
- if (configurationProperties.size() > 0)
- configurationService.setProperties(configurationProperties);
-
- if (logger.isDebugEnabled())
- logger.debug("Stored account for id " + accountID.getAccountUniqueID()
- + " for package " + factoryPackage);
- }
-
- /**
- * Removes the account with <tt>accountID</tt> from the set of accounts
- * that are persistently stored inside the configuration service.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which created the
- * account to be stored
- * @param accountID the AccountID of the account to remove.
- * @return true if an account has been removed and false otherwise.
- */
- public boolean removeStoredAccount(ProtocolProviderFactory factory,
- AccountID accountID)
- {
- synchronized (storedAccounts)
- {
- if (storedAccounts.contains(accountID))
- storedAccounts.remove(accountID);
- }
-
- String factoryPackage = getFactoryImplPackageName(factory);
-
- // remove the stored password explicitly using credentials service
- CredentialsStorageService credentialsStorage
- = ServiceUtils.getService(
- bundleContext,
- CredentialsStorageService.class);
- String accountPrefix =
- ProtocolProviderFactory.findAccountPrefix(bundleContext, accountID,
- factoryPackage);
-
- credentialsStorage.removePassword(accountPrefix);
-
- ConfigurationService configurationService
- = ServiceUtils.getService(
- bundleContext,
- ConfigurationService.class);
- //first retrieve all accounts that we've registered
- List<String> storedAccounts
- = configurationService.getPropertyNamesByPrefix(
- factoryPackage, true);
-
- //find an account with the corresponding id.
- for (String accountRootPropertyName : storedAccounts)
- {
- //unregister the account in the configuration service.
- //all the properties must have been registered in the following
- //hierarchy:
- //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
- String accountUID = configurationService.getString(
- accountRootPropertyName //node id
- + "." + ProtocolProviderFactory.ACCOUNT_UID); // propname
-
- if (accountUID.equals(accountID.getAccountUniqueID()))
- {
- //retrieve the names of all properties registered for the
- //current account.
- List<String> accountPropertyNames
- = configurationService.getPropertyNamesByPrefix(
- accountRootPropertyName, false);
-
- //set all account properties to null in order to remove them.
- for (String propName : accountPropertyNames)
- configurationService.setProperty(propName, null);
-
- //and now remove the parent too.
- configurationService.setProperty(accountRootPropertyName, null);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns an <tt>Iterator</tt> over a list of all stored
- * <tt>AccountID</tt>s. The list of stored accounts include all registered
- * accounts and all disabled accounts. In other words in this list we could
- * find accounts that aren't loaded.
- * <p>
- * In order to check if an account is already loaded please use the
- * #isAccountLoaded(AccountID accountID) method. To load an account use the
- * #loadAccount(AccountID accountID) method.
- *
- * @return an <tt>Iterator</tt> over a list of all stored
- * <tt>AccountID</tt>s
- */
- public Collection<AccountID> getStoredAccounts()
- {
- synchronized (storedAccounts)
- {
- return new Vector<AccountID>(storedAccounts);
- }
- }
-
- /**
- * Loads the account corresponding to the given <tt>AccountID</tt>. An
- * account is loaded when its <tt>ProtocolProviderService</tt> is registered
- * in the bundle context. This method is meant to load the account through
- * the corresponding <tt>ProtocolProviderFactory</tt>.
- *
- * @param accountID the identifier of the account to load
- * @throws OperationFailedException if anything goes wrong while loading the
- * account corresponding to the specified <tt>accountID</tt>
- */
- public void loadAccount(AccountID accountID)
- throws OperationFailedException
- {
- // If the account with the given id is already loaded we have nothing
- // to do here.
- if (isAccountLoaded(accountID))
- return;
-
- ProtocolProviderFactory providerFactory
- = ProtocolProviderActivator.getProtocolProviderFactory(
- accountID.getProtocolName());
-
- if(providerFactory.loadAccount(accountID))
- {
- accountID.putAccountProperty(
- ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
- String.valueOf(false));
- // Finally store the modified properties.
- storeAccount(providerFactory, accountID);
- }
- }
-
- /**
- * Unloads the account corresponding to the given <tt>AccountID</tt>. An
- * account is unloaded when its <tt>ProtocolProviderService</tt> is
- * unregistered in the bundle context. This method is meant to unload the
- * account through the corresponding <tt>ProtocolProviderFactory</tt>.
- *
- * @param accountID the identifier of the account to load
- * @throws OperationFailedException if anything goes wrong while unloading
- * the account corresponding to the specified <tt>accountID</tt>
- */
- public void unloadAccount(AccountID accountID)
- throws OperationFailedException
- {
- // If the account with the given id is already unloaded we have nothing
- // to do here.
- if (!isAccountLoaded(accountID))
- return;
-
- ProtocolProviderFactory providerFactory
- = ProtocolProviderActivator.getProtocolProviderFactory(
- accountID.getProtocolName());
-
- // Obtain the protocol provider.
- ServiceReference serRef
- = providerFactory.getProviderForAccount(accountID);
-
- // If there's no such provider we have nothing to do here.
- if (serRef == null)
- return;
-
- ProtocolProviderService protocolProvider =
- (ProtocolProviderService) bundleContext.getService(serRef);
-
- // Set the account icon path for unloaded accounts.
- String iconPathProperty = accountID.getAccountPropertyString(
- ProtocolProviderFactory.ACCOUNT_ICON_PATH);
-
- if (iconPathProperty == null)
- {
- accountID.putAccountProperty(
- ProtocolProviderFactory.ACCOUNT_ICON_PATH,
- protocolProvider.getProtocolIcon()
- .getIconPath(ProtocolIcon.ICON_SIZE_32x32));
- }
-
- accountID.putAccountProperty(
- ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
- String.valueOf(true));
-
- if (!providerFactory.unloadAccount(accountID))
- {
- accountID.putAccountProperty(
- ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
- String.valueOf(false));
- }
- // Finally store the modified properties.
- storeAccount(providerFactory, accountID);
- }
-
- /**
- * Checks if the account corresponding to the given <tt>accountID</tt> is
- * loaded. An account is loaded if its <tt>ProtocolProviderService</tt> is
- * registered in the bundle context. By default all accounts are loaded.
- * However the user could manually unload an account, which would be
- * unregistered from the bundle context, but would remain in the
- * configuration file.
- *
- * @param accountID the identifier of the account to load
- * @return <tt>true</tt> to indicate that the account with the given
- * <tt>accountID</tt> is loaded, <tt>false</tt> - otherwise
- */
- public boolean isAccountLoaded(AccountID accountID)
- {
- return storedAccounts.contains(accountID) && accountID.isEnabled();
- }
-
- private String stripPackagePrefix(String property)
- {
- int packageEndIndex = property.lastIndexOf('.');
-
- if (packageEndIndex != -1)
- property = property.substring(packageEndIndex + 1);
- return property;
- }
-}
diff --git a/src/net/java/sip/communicator/service/protocol/AccountManager.java b/src/net/java/sip/communicator/service/protocol/AccountManager.java
index 043941b..7a17c05 100644
--- a/src/net/java/sip/communicator/service/protocol/AccountManager.java
+++ b/src/net/java/sip/communicator/service/protocol/AccountManager.java
@@ -8,82 +8,692 @@ package net.java.sip.communicator.service.protocol;
import java.util.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.credentialsstorage.*;
import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+import org.osgi.framework.*;
/**
- * Represents a manager of accounts which contains the details about the format
- * in which the accounts in question are stored (i.e. knows how to store and
- * load them) and takes care of loading them on start-up.
+ * Represents an implementation of <tt>AccountManager</tt> which loads the
+ * accounts in a separate thread.
*
* @author Lubomir Marinov
* @author Yana Stamcheva
*/
-public interface AccountManager
+public class AccountManager
{
/**
- * Registers a specific listener to be notified about events fired by this
- * <tt>AccountManager</tt>. If the <tt>listener</tt> is already
- * registered, it will not be registered again.
- *
- * @param listener
- * the listener to be registered for notification events fired by
- * this <tt>AccountManager</tt>
+ * The delay in milliseconds the background <tt>Thread</tt> loading the
+ * stored accounts should wait before dying so that it doesn't get recreated
+ * for each <tt>ProtocolProviderFactory</tt> registration.
*/
- void addListener(AccountManagerListener listener);
+ private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000;
/**
- * Determines whether the account store represented by this manager contains
- * stored accounts.
- *
- * @param protocolName
- * the name of the protocol for which the stored accounts are to
- * be checked or <tt>null</tt> for all protocols
- * @param includeHidden
- * <tt>true</tt> to take into account both non-hidden and hidden
- * stored accounts; <tt>false</tt> for non-hidden only
- * @return <tt>true</tt> if the account store represented by this manager
- * contains stored accounts; <tt>false</tt>, otherwise
+ * The <tt>BundleContext</tt> this service is registered in.
+ */
+ private final BundleContext bundleContext;
+
+ /**
+ * The <tt>AccountManagerListener</tt>s currently interested in the
+ * events fired by this manager.
+ */
+ private final List<AccountManagerListener> listeners =
+ new LinkedList<AccountManagerListener>();
+
+ /**
+ * The queue of <tt>ProtocolProviderFactory</tt> services awaiting their
+ * stored accounts to be loaded.
+ */
+ private final Queue<ProtocolProviderFactory> loadStoredAccountsQueue =
+ new LinkedList<ProtocolProviderFactory>();
+
+ /**
+ * The <tt>Thread</tt> loading the stored accounts of the
+ * <tt>ProtocolProviderFactory</tt> services waiting in
+ * {@link #loadStoredAccountsQueue}.
+ */
+ private Thread loadStoredAccountsThread;
+
+ /**
+ * The <tt>Logger</tt> used by this <tt>AccountManagerImpl</tt> instance for
+ * logging output.
+ */
+ private final Logger logger = Logger.getLogger(AccountManager.class);
+
+ /**
+ * The list of <tt>AccountID</tt>s, corresponding to all stored accounts.
+ */
+ private Vector<AccountID> storedAccounts = new Vector<AccountID>();
+
+ /**
+ * Initializes a new <tt>AccountManagerImpl</tt> instance loaded in a
+ * specific <tt>BundleContext</tt> (in which the caller will usually
+ * later register it).
+ *
+ * @param bundleContext the <tt>BundleContext</tt> in which the new
+ * instance is loaded (and in which the caller will usually later
+ * register it as a service)
+ */
+ public AccountManager(BundleContext bundleContext)
+ {
+ this.bundleContext = bundleContext;
+
+ this.bundleContext.addServiceListener(new ServiceListener()
+ {
+ public void serviceChanged(ServiceEvent serviceEvent)
+ {
+ AccountManager.this.serviceChanged(serviceEvent);
+ }
+ });
+ }
+
+ /**
+ * Implements AccountManager#addListener(AccountManagerListener).
+ * @param listener the <tt>AccountManagerListener</tt> to add
+ */
+ public void addListener(AccountManagerListener listener)
+ {
+ synchronized (listeners)
+ {
+ if (!listeners.contains(listener))
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Loads the accounts stored for a specific
+ * <tt>ProtocolProviderFactory</tt>.
+ *
+ * @param factory the <tt>ProtocolProviderFactory</tt> to load the
+ * stored accounts of
+ */
+ private void doLoadStoredAccounts(ProtocolProviderFactory factory)
+ {
+ ConfigurationService configService
+ = ProtocolProviderActivator.getConfigurationService();
+ String factoryPackage = getFactoryImplPackageName(factory);
+ List<String> accounts
+ = configService.getPropertyNamesByPrefix(factoryPackage, true);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Discovered " + accounts.size() + " stored "
+ + factoryPackage + " accounts");
+
+ for (Iterator<String> storedAccountIter = accounts.iterator();
+ storedAccountIter.hasNext();)
+ {
+ String storedAccount = storedAccountIter.next();
+
+ if (logger.isDebugEnabled())
+ logger.debug("Loading account " + storedAccount);
+
+ List<String> storedAccountProperties =
+ configService.getPropertyNamesByPrefix(storedAccount, true);
+ Map<String, String> accountProperties =
+ new Hashtable<String, String>();
+ boolean disabled = false;
+ CredentialsStorageService credentialsStorage
+ = ServiceUtils.getService(
+ bundleContext,
+ CredentialsStorageService.class);
+
+ for (Iterator<String> storedAccountPropertyIter
+ = storedAccountProperties.iterator();
+ storedAccountPropertyIter.hasNext();)
+ {
+ String property = storedAccountPropertyIter.next();
+ String value = configService.getString(property);
+
+ property = stripPackagePrefix(property);
+
+ if (ProtocolProviderFactory.IS_ACCOUNT_DISABLED.equals(property))
+ disabled = Boolean.parseBoolean(value);
+ // Decode passwords.
+ else if (ProtocolProviderFactory.PASSWORD.equals(property)
+ && !credentialsStorage.isStoredEncrypted(storedAccount))
+ {
+ if ((value != null) && value.length() != 0)
+ {
+
+ /*
+ * TODO Converting byte[] to String using the platform's
+ * default charset may result in an invalid password.
+ */
+ value = new String(Base64.decode(value));
+ }
+ }
+
+ if (value != null)
+ accountProperties.put(property, value);
+ }
+
+ try
+ {
+ AccountID accountID = factory.createAccount(accountProperties);
+
+ // If for some reason the account id is not created we move to
+ // the next account.
+ if (accountID == null)
+ continue;
+
+ synchronized (storedAccounts)
+ {
+ storedAccounts.add(accountID);
+ }
+ if (!disabled)
+ factory.loadAccount(accountID);
+ }
+ catch (Exception ex)
+ {
+
+ /*
+ * Swallow the exception in order to prevent a single account
+ * from halting the loading of subsequent accounts.
+ */
+ logger.error("Failed to load account " + accountProperties, ex);
+ }
+ }
+ }
+
+ /**
+ * Notifies the registered {@link #listeners} that the stored accounts of a
+ * specific <tt>ProtocolProviderFactory</tt> have just been loaded.
+ *
+ * @param factory the <tt>ProtocolProviderFactory</tt> which had its
+ * stored accounts just loaded
+ */
+ private void fireStoredAccountsLoaded(ProtocolProviderFactory factory)
+ {
+ AccountManagerListener[] listeners;
+ synchronized (this.listeners)
+ {
+ listeners =
+ this.listeners
+ .toArray(new AccountManagerListener[this.listeners.size()]);
+ }
+
+ int listenerCount = listeners.length;
+ if (listenerCount > 0)
+ {
+ AccountManagerEvent event =
+ new AccountManagerEvent(this,
+ AccountManagerEvent.STORED_ACCOUNTS_LOADED, factory);
+
+ for (int listenerIndex = 0;
+ listenerIndex < listenerCount; listenerIndex++)
+ {
+ listeners[listenerIndex].handleAccountManagerEvent(event);
+ }
+ }
+ }
+
+ private String getFactoryImplPackageName(ProtocolProviderFactory factory)
+ {
+ String className = factory.getClass().getName();
+
+ return className.substring(0, className.lastIndexOf('.'));
+ }
+
+ public boolean hasStoredAccounts(String protocolName, boolean includeHidden)
+ {
+ ServiceReference[] factoryRefs = null;
+ boolean hasStoredAccounts = false;
+
+ try
+ {
+ factoryRefs
+ = bundleContext.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), null);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ logger.error(
+ "Failed to retrieve the registered ProtocolProviderFactories",
+ ex);
+ }
+
+ if ((factoryRefs != null) && (factoryRefs.length > 0))
+ {
+ ConfigurationService configService
+ = ProtocolProviderActivator.getConfigurationService();
+
+ for (ServiceReference factoryRef : factoryRefs)
+ {
+ ProtocolProviderFactory factory
+ = (ProtocolProviderFactory)
+ bundleContext.getService(factoryRef);
+
+ if ((protocolName != null)
+ && !protocolName.equals(factory.getProtocolName()))
+ {
+ continue;
+ }
+
+ String factoryPackage = getFactoryImplPackageName(factory);
+ List<String> storedAccounts
+ = configService
+ .getPropertyNamesByPrefix(factoryPackage, true);
+
+ /* Ignore the hidden accounts. */
+ for (Iterator<String> storedAccountIter =
+ storedAccounts.iterator(); storedAccountIter.hasNext();)
+ {
+ String storedAccount = storedAccountIter.next();
+ List<String> storedAccountProperties =
+ configService.getPropertyNamesByPrefix(storedAccount,
+ true);
+ boolean hidden = false;
+
+ if (!includeHidden)
+ {
+ for (Iterator<String> storedAccountPropertyIter =
+ storedAccountProperties.iterator();
+ storedAccountPropertyIter.hasNext();)
+ {
+ String property = storedAccountPropertyIter.next();
+ String value = configService.getString(property);
+
+ property = stripPackagePrefix(property);
+
+ if (ProtocolProviderFactory.IS_PROTOCOL_HIDDEN
+ .equals(property))
+ {
+ hidden = (value != null);
+ break;
+ }
+ }
+ }
+
+ if (!hidden)
+ {
+ hasStoredAccounts = true;
+ break;
+ }
+ }
+
+ if (hasStoredAccounts || (protocolName != null))
+ {
+ break;
+ }
+ }
+ }
+ return hasStoredAccounts;
+ }
+
+ /**
+ * Loads the accounts stored for a specific
+ * <tt>ProtocolProviderFactory</tt> and notifies the registered
+ * {@link #listeners} that the stored accounts of the specified
+ * <tt>factory</tt> have just been loaded
+ *
+ * @param factory the <tt>ProtocolProviderFactory</tt> to load the
+ * stored accounts of
+ */
+ private void loadStoredAccounts(ProtocolProviderFactory factory)
+ {
+ doLoadStoredAccounts(factory);
+
+ fireStoredAccountsLoaded(factory);
+ }
+
+ /**
+ * Notifies this manager that a specific
+ * <tt>ProtocolProviderFactory</tt> has been registered as a service.
+ * The current implementation queues the specified <tt>factory</tt> to
+ * have its stored accounts as soon as possible.
+ *
+ * @param factory the <tt>ProtocolProviderFactory</tt> which has been
+ * registered as a service.
+ */
+ private void protocolProviderFactoryRegistered(
+ ProtocolProviderFactory factory)
+ {
+ queueLoadStoredAccounts(factory);
+ }
+
+ /**
+ * Queues a specific <tt>ProtocolProviderFactory</tt> to have its stored
+ * accounts loaded as soon as possible.
+ *
+ * @param factory the <tt>ProtocolProviderFactory</tt> to be queued for
+ * loading its stored accounts as soon as possible
+ */
+ private void queueLoadStoredAccounts(ProtocolProviderFactory factory)
+ {
+ synchronized (loadStoredAccountsQueue)
+ {
+ loadStoredAccountsQueue.add(factory);
+ loadStoredAccountsQueue.notify();
+
+ if (loadStoredAccountsThread == null)
+ {
+ loadStoredAccountsThread = new Thread()
+ {
+ public void run()
+ {
+ runInLoadStoredAccountsThread();
+ }
+ };
+ loadStoredAccountsThread.setDaemon(true);
+ loadStoredAccountsThread
+ .setName("AccountManager.loadStoredAccounts");
+ loadStoredAccountsThread.start();
+ }
+ }
+ }
+
+ /**
+ * Implements AccountManager#removeListener(AccountManagerListener).
+ * @param listener the <tt>AccountManagerListener</tt> to remove
+ */
+ public void removeListener(AccountManagerListener listener)
+ {
+ synchronized (listeners)
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Running in {@link #loadStoredAccountsThread}, loads the stored accounts
+ * of the <tt>ProtocolProviderFactory</tt> services waiting in
+ * {@link #loadStoredAccountsQueue}
*/
- boolean hasStoredAccounts(String protocolName, boolean includeHidden);
+ private void runInLoadStoredAccountsThread()
+ {
+ boolean interrupted = false;
+ while (!interrupted)
+ {
+ try
+ {
+ ProtocolProviderFactory factory;
+
+ synchronized (loadStoredAccountsQueue)
+ {
+ factory = loadStoredAccountsQueue.poll();
+ if (factory == null)
+ {
+ /*
+ * Technically, we should be handing spurious wakeups.
+ * However, we cannot check the condition in a queue.
+ * Anyway, we just want to keep this Thread alive long
+ * enough to allow it to not be re-created multiple
+ * times and not handing a spurious wakeup will just
+ * cause such an inconvenience.
+ */
+ try
+ {
+ loadStoredAccountsQueue
+ .wait(LOAD_STORED_ACCOUNTS_TIMEOUT);
+ }
+ catch (InterruptedException ex)
+ {
+ logger
+ .warn(
+ "The loading of the stored accounts has"
+ + " been interrupted",
+ ex);
+ interrupted = true;
+ break;
+ }
+ factory = loadStoredAccountsQueue.poll();
+ }
+ }
+
+ if (factory != null)
+ {
+ try
+ {
+ loadStoredAccounts(factory);
+ }
+ catch (Exception ex)
+ {
+
+ /*
+ * Swallow the exception in order to prevent a single
+ * factory from halting the loading of subsequent
+ * factories.
+ */
+ logger.error("Failed to load accounts for " + factory,
+ ex);
+ }
+ }
+ }
+ finally
+ {
+ synchronized (loadStoredAccountsQueue)
+ {
+ if (!interrupted && (loadStoredAccountsQueue.size() <= 0))
+ {
+ if (loadStoredAccountsThread == Thread.currentThread())
+ {
+ loadStoredAccountsThread = null;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
/**
- * Unregisters a specific listener from this <tt>AccountManager</tt> so
- * that it no longer received notifications about events fired by this
- * manager.
+ * Notifies this manager that an OSGi service has changed. The current
+ * implementation tracks the registrations of
+ * <tt>ProtocolProviderFactory</tt> services in order to queue them for
+ * loading their stored accounts.
*
- * @param listener
- * the listener to be unregistered from this
- * <tt>AccountManager</tt> so that it no longer receives
- * notifications about events fired by this manager
+ * @param serviceEvent the <tt>ServiceEvent</tt> containing the event
+ * data
*/
- void removeListener(AccountManagerListener listener);
+ private void serviceChanged(ServiceEvent serviceEvent)
+ {
+ switch (serviceEvent.getType())
+ {
+ case ServiceEvent.REGISTERED:
+ Object service
+ = bundleContext.getService(serviceEvent.getServiceReference());
+
+ if (service instanceof ProtocolProviderFactory)
+ {
+ protocolProviderFactoryRegistered(
+ (ProtocolProviderFactory) service);
+ }
+ break;
+ default:
+ break;
+ }
+ }
/**
* Stores an account represented in the form of an <tt>AccountID</tt>
* created by a specific <tt>ProtocolProviderFactory</tt>.
- *
+ *
* @param factory the <tt>ProtocolProviderFactory</tt> which created the
* account to be stored
* @param accountID the account in the form of <tt>AccountID</tt> to be
* stored
* @throws OperationFailedException if anything goes wrong while storing the
- * specified account
+ * account
*/
- void storeAccount(ProtocolProviderFactory factory, AccountID accountID)
- throws OperationFailedException;
+ public void storeAccount(
+ ProtocolProviderFactory factory,
+ AccountID accountID)
+ throws OperationFailedException
+ {
+ synchronized (storedAccounts)
+ {
+ if (!storedAccounts.contains(accountID))
+ storedAccounts.add(accountID);
+ }
+
+ ConfigurationService configurationService
+ = ProtocolProviderActivator.getConfigurationService();
+ String factoryPackage = getFactoryImplPackageName(factory);
+
+ // First check if such accountID already exists in the configuration.
+ List<String> storedAccounts =
+ configurationService.getPropertyNamesByPrefix(factoryPackage, true);
+ String accountUID = accountID.getAccountUniqueID();
+ String accountNodeName = null;
+
+ for (Iterator<String> storedAccountIter = storedAccounts.iterator();
+ storedAccountIter.hasNext();)
+ {
+ String storedAccount = storedAccountIter.next();
+ String storedAccountUID =
+ configurationService.getString(storedAccount + "."
+ + ProtocolProviderFactory.ACCOUNT_UID);
+
+ if (storedAccountUID.equals(accountUID))
+ accountNodeName = configurationService.getString(storedAccount);
+ }
+
+ Map<String, Object> configurationProperties
+ = new HashMap<String, Object>();
+
+ // Create a unique node name of the properties node that will contain
+ // this account's properties.
+ if (accountNodeName == null)
+ {
+ accountNodeName = "acc" + Long.toString(System.currentTimeMillis());
+
+ // set a value for the persistent node so that we could later
+ // retrieve it as a property
+ configurationProperties.put(
+ factoryPackage /* prefix */ + "." + accountNodeName,
+ accountNodeName);
+
+ // register the account in the configuration service.
+ // we register all the properties in the following hierarchy
+ //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
+ configurationProperties.put(factoryPackage// prefix
+ + "." + accountNodeName // node name for the account id
+ + "." + ProtocolProviderFactory.ACCOUNT_UID, // propname
+ accountID.getAccountUniqueID()); // value
+ }
+
+ // store the rest of the properties
+ Map<String, String> accountProperties = accountID.getAccountProperties();
+
+ for (Map.Entry<String, String> entry : accountProperties.entrySet())
+ {
+ String property = entry.getKey();
+ String value = entry.getValue();
+
+ // If the property is a password, store it securely.
+ if (property.equals(ProtocolProviderFactory.PASSWORD))
+ {
+ CredentialsStorageService credentialsStorage
+ = ServiceUtils.getService(
+ bundleContext,
+ CredentialsStorageService.class);
+ String accountPrefix = factoryPackage + "." + accountNodeName;
+
+ // encrypt and store
+ if ((value != null)
+ && (value.length() != 0)
+ && !credentialsStorage.storePassword(
+ accountPrefix,
+ value))
+ {
+ throw
+ new OperationFailedException(
+ "CredentialsStorageService failed to"
+ + " storePassword",
+ OperationFailedException.GENERAL_ERROR);
+ }
+ }
+ else
+ {
+ configurationProperties.put(
+ factoryPackage // prefix
+ + "." + accountNodeName // a unique node name for the account id
+ + "." + property, // propname
+ value); // value
+ }
+ }
+
+ if (configurationProperties.size() > 0)
+ configurationService.setProperties(configurationProperties);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Stored account for id " + accountID.getAccountUniqueID()
+ + " for package " + factoryPackage);
+ }
/**
* Removes the account with <tt>accountID</tt> from the set of accounts
* that are persistently stored inside the configuration service.
- * <p>
+ *
* @param factory the <tt>ProtocolProviderFactory</tt> which created the
* account to be stored
* @param accountID the AccountID of the account to remove.
- * <p>
* @return true if an account has been removed and false otherwise.
*/
- boolean removeStoredAccount(ProtocolProviderFactory factory,
- AccountID accountID);
+ public boolean removeStoredAccount(ProtocolProviderFactory factory,
+ AccountID accountID)
+ {
+ synchronized (storedAccounts)
+ {
+ if (storedAccounts.contains(accountID))
+ storedAccounts.remove(accountID);
+ }
+
+ String factoryPackage = getFactoryImplPackageName(factory);
+
+ // remove the stored password explicitly using credentials service
+ CredentialsStorageService credentialsStorage
+ = ServiceUtils.getService(
+ bundleContext,
+ CredentialsStorageService.class);
+ String accountPrefix =
+ ProtocolProviderFactory.findAccountPrefix(bundleContext, accountID,
+ factoryPackage);
+
+ credentialsStorage.removePassword(accountPrefix);
+
+ ConfigurationService configurationService
+ = ServiceUtils.getService(
+ bundleContext,
+ ConfigurationService.class);
+ //first retrieve all accounts that we've registered
+ List<String> storedAccounts
+ = configurationService.getPropertyNamesByPrefix(
+ factoryPackage, true);
+
+ //find an account with the corresponding id.
+ for (String accountRootPropertyName : storedAccounts)
+ {
+ //unregister the account in the configuration service.
+ //all the properties must have been registered in the following
+ //hierarchy:
+ //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
+ String accountUID = configurationService.getString(
+ accountRootPropertyName //node id
+ + "." + ProtocolProviderFactory.ACCOUNT_UID); // propname
+
+ if (accountUID.equals(accountID.getAccountUniqueID()))
+ {
+ //retrieve the names of all properties registered for the
+ //current account.
+ List<String> accountPropertyNames
+ = configurationService.getPropertyNamesByPrefix(
+ accountRootPropertyName, false);
+
+ //set all account properties to null in order to remove them.
+ for (String propName : accountPropertyNames)
+ configurationService.setProperty(propName, null);
+
+ //and now remove the parent too.
+ configurationService.setProperty(accountRootPropertyName, null);
+ return true;
+ }
+ }
+ return false;
+ }
/**
* Returns an <tt>Iterator</tt> over a list of all stored
@@ -95,9 +705,16 @@ public interface AccountManager
* #isAccountLoaded(AccountID accountID) method. To load an account use the
* #loadAccount(AccountID accountID) method.
*
- * @return a <tt>Collection</tt> of all stored <tt>AccountID</tt>s
+ * @return an <tt>Iterator</tt> over a list of all stored
+ * <tt>AccountID</tt>s
*/
- public Collection<AccountID> getStoredAccounts();
+ public Collection<AccountID> getStoredAccounts()
+ {
+ synchronized (storedAccounts)
+ {
+ return new Vector<AccountID>(storedAccounts);
+ }
+ }
/**
* Loads the account corresponding to the given <tt>AccountID</tt>. An
@@ -110,7 +727,26 @@ public interface AccountManager
* account corresponding to the specified <tt>accountID</tt>
*/
public void loadAccount(AccountID accountID)
- throws OperationFailedException;
+ throws OperationFailedException
+ {
+ // If the account with the given id is already loaded we have nothing
+ // to do here.
+ if (isAccountLoaded(accountID))
+ return;
+
+ ProtocolProviderFactory providerFactory
+ = ProtocolProviderActivator.getProtocolProviderFactory(
+ accountID.getProtocolName());
+
+ if(providerFactory.loadAccount(accountID))
+ {
+ accountID.putAccountProperty(
+ ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
+ String.valueOf(false));
+ // Finally store the modified properties.
+ storeAccount(providerFactory, accountID);
+ }
+ }
/**
* Unloads the account corresponding to the given <tt>AccountID</tt>. An
@@ -123,7 +759,53 @@ public interface AccountManager
* the account corresponding to the specified <tt>accountID</tt>
*/
public void unloadAccount(AccountID accountID)
- throws OperationFailedException;
+ throws OperationFailedException
+ {
+ // If the account with the given id is already unloaded we have nothing
+ // to do here.
+ if (!isAccountLoaded(accountID))
+ return;
+
+ ProtocolProviderFactory providerFactory
+ = ProtocolProviderActivator.getProtocolProviderFactory(
+ accountID.getProtocolName());
+
+ // Obtain the protocol provider.
+ ServiceReference serRef
+ = providerFactory.getProviderForAccount(accountID);
+
+ // If there's no such provider we have nothing to do here.
+ if (serRef == null)
+ return;
+
+ ProtocolProviderService protocolProvider =
+ (ProtocolProviderService) bundleContext.getService(serRef);
+
+ // Set the account icon path for unloaded accounts.
+ String iconPathProperty = accountID.getAccountPropertyString(
+ ProtocolProviderFactory.ACCOUNT_ICON_PATH);
+
+ if (iconPathProperty == null)
+ {
+ accountID.putAccountProperty(
+ ProtocolProviderFactory.ACCOUNT_ICON_PATH,
+ protocolProvider.getProtocolIcon()
+ .getIconPath(ProtocolIcon.ICON_SIZE_32x32));
+ }
+
+ accountID.putAccountProperty(
+ ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
+ String.valueOf(true));
+
+ if (!providerFactory.unloadAccount(accountID))
+ {
+ accountID.putAccountProperty(
+ ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
+ String.valueOf(false));
+ }
+ // Finally store the modified properties.
+ storeAccount(providerFactory, accountID);
+ }
/**
* Checks if the account corresponding to the given <tt>accountID</tt> is
@@ -137,5 +819,17 @@ public interface AccountManager
* @return <tt>true</tt> to indicate that the account with the given
* <tt>accountID</tt> is loaded, <tt>false</tt> - otherwise
*/
- public boolean isAccountLoaded(AccountID accountID);
+ public boolean isAccountLoaded(AccountID accountID)
+ {
+ return storedAccounts.contains(accountID) && accountID.isEnabled();
+ }
+
+ private String stripPackagePrefix(String property)
+ {
+ int packageEndIndex = property.lastIndexOf('.');
+
+ if (packageEndIndex != -1)
+ property = property.substring(packageEndIndex + 1);
+ return property;
+ }
}
diff --git a/src/net/java/sip/communicator/service/protocol/CallPeerState.java b/src/net/java/sip/communicator/service/protocol/CallPeerState.java
index 79d83d1..5775e3d 100644
--- a/src/net/java/sip/communicator/service/protocol/CallPeerState.java
+++ b/src/net/java/sip/communicator/service/protocol/CallPeerState.java
@@ -34,6 +34,7 @@ package net.java.sip.communicator.service.protocol;
*
* @author Emil Ivov
* @author Lubomir Marinov
+ * @author Yana Stamcheva
*/
public class CallPeerState
{
@@ -51,8 +52,10 @@ public class CallPeerState
* newly created call peer that don't yet have an attributed call
* state.
*/
- public static final CallPeerState UNKNOWN =
- new CallPeerState(_UNKNOWN);
+ public static final CallPeerState UNKNOWN
+ = new CallPeerState(_UNKNOWN,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.UNKNOWN_STATUS"));
/**
* This constant value indicates a String representation of the
@@ -68,8 +71,10 @@ public class CallPeerState
* this state the moment we receive a "100 Trying" request from a proxy or
* the remote side.
*/
- public static final CallPeerState INITIATING_CALL =
- new CallPeerState(_INITIATING_CALL);
+ public static final CallPeerState INITIATING_CALL
+ = new CallPeerState(_INITIATING_CALL,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.INITIATING_CALL_STATUS"));
/**
* This constant value indicates a String representation of the CONNECTING
@@ -83,8 +88,10 @@ public class CallPeerState
* CONNECTING - which means that a network connection to that peer
* is currently being established.
*/
- public static final CallPeerState CONNECTING =
- new CallPeerState(_CONNECTING);
+ public static final CallPeerState CONNECTING
+ = new CallPeerState(_CONNECTING,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.CONNECTING_STATUS"));
/**
* This constant value indicates a String representation of the CONNECTING
@@ -98,16 +105,20 @@ public class CallPeerState
* CONNECTING - which means that a network connection to that peer
* is currently being established.
*/
- public static final CallPeerState CONNECTING_WITH_EARLY_MEDIA =
- new CallPeerState( _CONNECTING_WITH_EARLY_MEDIA );
+ public static final CallPeerState CONNECTING_WITH_EARLY_MEDIA
+ = new CallPeerState( _CONNECTING_WITH_EARLY_MEDIA,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.CONNECTING_EARLY_MEDIA_STATUS"));
/**
* This constant value indicates that the state of the incoming call peer
* is CONNECTING - which means that a network connection to that peer
* is currently being established.
*/
- public static final CallPeerState CONNECTING_INCOMING_CALL =
- new CallPeerState( _CONNECTING_WITH_EARLY_MEDIA );
+ public static final CallPeerState CONNECTING_INCOMING_CALL
+ = new CallPeerState( _CONNECTING_WITH_EARLY_MEDIA,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.CONNECTING_STATUS"));
/**
* This constant value indicates that the state of the incoming call peer
@@ -116,8 +127,10 @@ public class CallPeerState
* the other peer we can still can hear media coming from the
* server for example.
*/
- public static final CallPeerState CONNECTING_INCOMING_CALL_WITH_MEDIA =
- new CallPeerState( _CONNECTING_WITH_EARLY_MEDIA );
+ public static final CallPeerState CONNECTING_INCOMING_CALL_WITH_MEDIA
+ = new CallPeerState( _CONNECTING_WITH_EARLY_MEDIA,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.CONNECTING_EARLY_MEDIA_STATUS"));
/**
* This constant value indicates a String representation of the
@@ -133,8 +146,10 @@ public class CallPeerState
* peer has been established and peer's phone is currently alerting the
* remote user of the current call.
*/
- public static final CallPeerState ALERTING_REMOTE_SIDE =
- new CallPeerState(_ALERTING_REMOTE_SIDE);
+ public static final CallPeerState ALERTING_REMOTE_SIDE
+ = new CallPeerState(_ALERTING_REMOTE_SIDE,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.RINGING_STATUS"));
/**
* This constant value indicates a String representation of the
@@ -150,7 +165,9 @@ public class CallPeerState
* graphical alert (the phone is ringing).
*/
public static final CallPeerState INCOMING_CALL
- = new CallPeerState(_INCOMING_CALL);
+ = new CallPeerState(_INCOMING_CALL,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.INCOMING_CALL_STATUS"));
/**
* This constant value indicates a String representation of the CONNECTED
@@ -165,7 +182,9 @@ public class CallPeerState
* peer.
*/
public static final CallPeerState CONNECTED
- = new CallPeerState(_CONNECTED);
+ = new CallPeerState(_CONNECTED,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.CONNECTED_STATUS"));
/**
* This constant value indicates a String representation of the DISCONNECTED
@@ -179,8 +198,10 @@ public class CallPeerState
* is DISCONNECTED - which means that this peer is not participating :)
* in the call any more.
*/
- public static final CallPeerState DISCONNECTED =
- new CallPeerState(_DISCONNECTED);
+ public static final CallPeerState DISCONNECTED
+ = new CallPeerState(_DISCONNECTED,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.DISCONNECTED_STATUS"));
/**
* This constant value indicates a String representation of the REFERRED
@@ -194,7 +215,10 @@ public class CallPeerState
* is REFERRED - which means that this peer has transfered us to another
* peer.
*/
- public static final CallPeerState REFERRED = new CallPeerState(_REFERRED);
+ public static final CallPeerState REFERRED
+ = new CallPeerState(_REFERRED,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.REFERRED_STATUS"));
/**
* This constant value indicates a String representation of the BUSY
@@ -210,7 +234,9 @@ public class CallPeerState
* because they were already in a call).
*/
public static final CallPeerState BUSY
- = new CallPeerState(_BUSY);
+ = new CallPeerState(_BUSY,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.BUSY_STATUS"));
/**
* This constant value indicates a String representation of the FAILED
@@ -224,7 +250,9 @@ public class CallPeerState
* peer has failed for an unexpected reason.
*/
public static final CallPeerState FAILED
- = new CallPeerState(_FAILED);
+ = new CallPeerState(_FAILED,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.FAILED_STATUS"));
/**
* The constant value being a String representation of the ON_HOLD_LOCALLY
@@ -239,7 +267,9 @@ public class CallPeerState
* locally put on hold.
*/
public static final CallPeerState ON_HOLD_LOCALLY
- = new CallPeerState(_ON_HOLD_LOCALLY);
+ = new CallPeerState(_ON_HOLD_LOCALLY,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.LOCALLY_ON_HOLD_STATUS"));
/**
* The constant value being a String representation of the ON_HOLD_MUTUALLY
@@ -254,7 +284,9 @@ public class CallPeerState
* mutually - locally and remotely - put on hold.
*/
public static final CallPeerState ON_HOLD_MUTUALLY
- = new CallPeerState(_ON_HOLD_MUTUALLY);
+ = new CallPeerState(_ON_HOLD_MUTUALLY,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.MUTUALLY_ON_HOLD_STATUS"));
/**
* The constant value being a String representation of the ON_HOLD_REMOTELY
@@ -270,7 +302,9 @@ public class CallPeerState
* remotely put on hold.
*/
public static final CallPeerState ON_HOLD_REMOTELY
- = new CallPeerState(_ON_HOLD_REMOTELY);
+ = new CallPeerState(_ON_HOLD_REMOTELY,
+ ProtocolProviderActivator.getResourceService().getI18NString(
+ "service.gui.REMOTELY_ON_HOLD_STATUS"));
/**
* Determines whether a specific <tt>CallPeerState</tt> value
@@ -297,17 +331,25 @@ public class CallPeerState
private String callStateStr;
/**
+ * A localized string representation of this peer's Call State.
+ */
+ private String callStateLocalizedStr;
+
+ /**
* Create a peer call state object with a value corresponding to the
* specified string.
* @param callPeerState a string representation of the state.
+ * @param callStateLocalizedStr the localized string representing this state
*/
- private CallPeerState(String callPeerState)
+ private CallPeerState( String callPeerState,
+ String callStateLocalizedStr)
{
this.callStateStr = callPeerState;
+ this.callStateLocalizedStr = callStateLocalizedStr;
}
/**
- * Returns a String representation of tha CallPeerState.
+ * Returns a String representation of the CallPeerState.
*
* @return A string value (one of the _BUSY, _CALLING, _CONNECTED,
* _CONNECTING, _DISCONNECTED, _FAILED, _RINGING constants) representing
@@ -319,6 +361,16 @@ public class CallPeerState
}
/**
+ * Returns a localized String representation of the CallPeerState.
+ *
+ * @return a localized String representation of the CallPeerState
+ */
+ public String getLocalizedStateString()
+ {
+ return callStateLocalizedStr;
+ }
+
+ /**
* Returns a string representation of this call state. Strings returned
* by this method have the following format:
* CallPeerState:<STATE_STRING>
diff --git a/src/net/java/sip/communicator/impl/protocol/ProtocolProviderActivator.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderActivator.java
index 00ceb2f..1cd9d25 100644
--- a/src/net/java/sip/communicator/impl/protocol/ProtocolProviderActivator.java
+++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderActivator.java
@@ -4,12 +4,12 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.impl.protocol;
+package net.java.sip.communicator.service.protocol;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
-import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
/**
@@ -51,6 +51,11 @@ public class ProtocolProviderActivator
private static ConfigurationService configurationService;
/**
+ * The resource service through which we obtain localized strings.
+ */
+ private static ResourceManagementService resourceService;
+
+ /**
* The <code>SingleCallInProgressPolicy</code> making sure that the
* <code>Call</code>s accessible in the <code>BundleContext</code> of this
* activator will obey to the rule that a new <code>Call</code> should put
@@ -80,6 +85,27 @@ public class ProtocolProviderActivator
}
/**
+ * Gets the <code>ResourceManagementService</code> to be used by the classes
+ * in the bundle represented by <code>ProtocolProviderActivator</code>.
+ *
+ * @return the <code>ResourceManagementService</code> to be used by the
+ * classes in the bundle represented by
+ * <code>ProtocolProviderActivator</code>
+ */
+ public static ResourceManagementService getResourceService()
+ {
+ if (resourceService == null)
+ {
+ resourceService
+ = (ResourceManagementService)
+ bundleContext.getService(
+ bundleContext.getServiceReference(
+ ResourceManagementService.class.getName()));
+ }
+ return resourceService;
+ }
+
+ /**
* Returns a <tt>ProtocolProviderFactory</tt> for a given protocol
* provider.
* @param protocolName the name of the protocol, which factory we're
@@ -130,7 +156,7 @@ public class ProtocolProviderActivator
accountManagerServiceRegistration =
bundleContext.registerService(AccountManager.class.getName(),
- new AccountManagerImpl(bundleContext), null);
+ new AccountManager(bundleContext), null);
singleCallInProgressPolicy =
new SingleCallInProgressPolicy(bundleContext);
diff --git a/src/net/java/sip/communicator/impl/protocol/SingleCallInProgressPolicy.java b/src/net/java/sip/communicator/service/protocol/SingleCallInProgressPolicy.java
index fbed031..adb31d8 100644
--- a/src/net/java/sip/communicator/impl/protocol/SingleCallInProgressPolicy.java
+++ b/src/net/java/sip/communicator/service/protocol/SingleCallInProgressPolicy.java
@@ -4,13 +4,12 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.impl.protocol;
+package net.java.sip.communicator.service.protocol;
import java.util.*;
import org.osgi.framework.*;
-import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
diff --git a/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf b/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
index 4f5abb7..06867f8 100644
--- a/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
+++ b/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
@@ -1,4 +1,4 @@
-Bundle-Activator: net.java.sip.communicator.impl.protocol.ProtocolProviderActivator
+Bundle-Activator: net.java.sip.communicator.service.protocol.ProtocolProviderActivator
Bundle-Name: Protocol Provider Service
Bundle-Description: Protocol Provider Service.
Bundle-Vendor: sip-communicator.org
@@ -7,7 +7,8 @@ System-Bundle: yes
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.credentialsstorage,
- net.java.sip.communicator.util
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.service.resources
Export-Package: net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.aimconstants,
net.java.sip.communicator.service.protocol.event,