Merge my changes from alpha2 branch (mostly improvements to SIMPLE, usage of transactions for contactlist.xml and fix the wrong location of jain-sip logs).

cusax-fix
Benoit Pradelle 18 years ago
parent caa5551282
commit 3b82d91027

@ -51,6 +51,8 @@ felix.auto.start.20= \
reference:file:sc-bundles/util.jar \
reference:file:lib/bundle/log4j.jar
felix.auto.start.30= \
reference:file:sc-bundles/fileaccess.jar
felix.auto.start.40= \
reference:file:sc-bundles/configuration.jar \
@ -59,7 +61,6 @@ felix.auto.start.40= \
reference:file:sc-bundles/splashscreen.jar \
felix.auto.start.50= \
reference:file:sc-bundles/fileaccess.jar \
reference:file:sc-bundles/browserlauncher.jar \
reference:file:sc-bundles/protocol.jar \
reference:file:sc-bundles/contactlist.jar \

@ -49,17 +49,18 @@ felix.auto.start.2= \
reference:file:sc-bundles/util.jar \
reference:file:lib/bundle/log4j.jar
felix.auto.start.3= \
file:sc-bundles/fileaccess.jar
felix.auto.start.4= \
file:sc-bundles/configuration.jar \
reference:file:sc-bundles/version.jar \
reference:file:sc-bundles/version-impl.jar \
file:sc-bundles/netaddr.jar
felix.auto.start.4= \
felix.auto.start.5= \
file:sc-bundles/protocol.jar \
file:sc-bundles/contactlist.jar \
file:sc-bundles/fileaccess.jar \
file:sc-bundles/history.jar \
file:sc-bundles/protocol-icq.jar \
file:sc-bundles/smacklib.jar \
@ -74,7 +75,7 @@ felix.auto.start.4= \
file:sc-bundles/msghistory.jar \
file:sc-bundles/callhistory.jar
felix.auto.start.5= \
felix.auto.start.6= \
file:sc-bundles/slickless.jar \
file:sc-bundles/configuration-slick.jar \
file:sc-bundles/protocol.jar \

@ -17,6 +17,11 @@
public class ConfigurationActivator
implements BundleActivator
{
/**
* The current bundle context
*/
public static BundleContext bundleContext;
private Logger logger = Logger.getLogger(ConfigurationServiceImpl.class);
private ConfigurationServiceImpl impl = new ConfigurationServiceImpl();
@ -30,7 +35,8 @@ public class ConfigurationActivator
public void start(BundleContext bundleContext) throws Exception
{
logger.debug("Service Impl: " + getClass().getName() + " [ STARTED ]");
this.bundleContext = bundleContext;
impl.start();
bundleContext.registerService(ConfigurationService.class.getName(),

@ -10,11 +10,13 @@
import java.util.*;
import javax.xml.parsers.*;
import org.osgi.framework.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import net.java.sip.communicator.impl.configuration.xml.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.configuration.event.*;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.xml.*;
@ -88,6 +90,11 @@ public class ConfigurationServiceImpl
* Indicates whether the service is started or stopped.
*/
private boolean started = false;
/**
* a reference to the FileAccessService
*/
private FileAccessService faService = null;
/**
* Sets the property with the specified name to the specified value. Calling
@ -374,6 +381,12 @@ void start()
{
this.started = true;
// retrieve a reference to the FileAccessService
BundleContext bc = ConfigurationActivator.bundleContext;
ServiceReference faServiceReference = bc.getServiceReference(
FileAccessService.class.getName());
this.faService = (FileAccessService) bc.getService(faServiceReference);
try
{
debugPrintSystemProperties();
@ -414,6 +427,16 @@ public void reloadConfiguration()
Map loadConfiguration(File file)
throws IOException, XMLException
{
// restore the file if needed
FailSafeTransaction trans = this.faService
.createFailSafeTransaction(file);
try {
trans.restoreFile();
} catch (Exception e) {
logger.error("can't restore the configuration file before loading" +
" it", e);
}
try
{
DocumentBuilderFactory factory =
@ -536,10 +559,20 @@ private void storeConfiguration(File file)
newlyAddedProperties);
//write the file.
OutputStream stream = new FileOutputStream(getConfigurationFile());
XMLUtils.indentedWriteXML(
propertiesDocument, stream);
stream.close();
File config = getConfigurationFile();
FailSafeTransaction trans = this.faService
.createFailSafeTransaction(config);
try {
trans.beginTransaction();
OutputStream stream = new FileOutputStream(config);
XMLUtils.indentedWriteXML(
propertiesDocument, stream);
stream.close();
trans.commit();
} catch (Exception e) {
logger.error("can't write data in the configuration file", e);
trans.rollback();
}
}
/**

@ -12,6 +12,7 @@ Import-Package: org.osgi.framework,
javax.xml.transform.stream,
javax.xml.parsers,
net.java.sip.communicator.util,
net.java.sip.communicator.util.xml
net.java.sip.communicator.util.xml,
net.java.sip.communicator.service.fileaccess
Export-Package: net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.configuration.event

@ -87,8 +87,6 @@ public void restoreFile()
* When the transaction begins, the file is restored to a coherent state if
* needed.
*
* @return The fail-safe file in which every modification must be done.
*
* @throws IllegalStateException if the file doesn't exists anymore
* @throws IOException if an IOException occurs during the transaction
* creation

@ -35,11 +35,6 @@ public class FileAccessServiceImpl implements FileAccessService {
*/
public static final String TEMP_FILE_SUFFIX = "TEMP";
/**
* List of available configuration services.
*/
private ConfigurationService configurationService = null;
/**
* An synchronization object.
*
@ -47,41 +42,6 @@ public class FileAccessServiceImpl implements FileAccessService {
*/
private Object syncRoot = new Object();
/**
* Set the configuration service.
*
* @param configurationService a currently active instance of the
* configuration service
*/
public void setConfigurationService(
ConfigurationService configurationService)
{
synchronized (this.syncRoot)
{
this.configurationService = configurationService;
logger.debug("New configuration service registered.");
}
}
/**
* Remove a configuration service.
*
* @param configurationService a currently active instance of the
* configuration service
*/
public void unsetConfigurationService(
ConfigurationService configurationService)
{
synchronized (this.syncRoot)
{
if (this.configurationService == configurationService)
{
this.configurationService = null;
logger.debug("Configuration service unregistered.");
}
}
}
/**
* This method returns a created temporary file. After you close this file
* it is not guaranteed that you will be able to open it again nor that it
@ -253,9 +213,9 @@ public File getPrivatePersistentDirectory(String[] dirNames)
*/
private String getFullPath(String fileName)
{
String userhome = this.configurationService.getScHomeDirLocation();
String sipSubdir = this.configurationService.getScHomeDirName();
// bypass the configurationService here to remove the dependancy
String userhome = getScHomeDirLocation();
String sipSubdir = getScHomeDirName();
if (!userhome.endsWith(File.separator))
{
@ -268,7 +228,77 @@ private String getFullPath(String fileName)
return userhome + sipSubdir;
}
/**
* Returns the name of the directory where SIP Communicator is to store user
* specific data such as configuration files, message and call history
* as well as is bundle repository.
*
* @return the name of the directory where SIP Communicator is to store
* user specific data such as configuration files, message and call history
* as well as is bundle repository.
*/
public String getScHomeDirName()
{
//check whether user has specified a custom name in the
//system properties
String scHomeDirName
= getSystemProperty(ConfigurationService.PNAME_SC_HOME_DIR_NAME);
if (scHomeDirName == null)
{
scHomeDirName = ".sip-communicator";
}
return scHomeDirName;
}
/**
* Returns the location of the directory where SIP Communicator is to store
* user specific data such as configuration files, message and call history
* as well as is bundle repository.
*
* @return the location of the directory where SIP Communicator is to store
* user specific data such as configuration files, message and call history
* as well as is bundle repository.
*/
public String getScHomeDirLocation()
{
//check whether user has specified a custom name in the
//system properties
String scHomeDirLocation
= getSystemProperty(ConfigurationService
.PNAME_SC_HOME_DIR_LOCATION);
if (scHomeDirLocation == null)
{
scHomeDirLocation = getSystemProperty("user.home");
}
return scHomeDirLocation;
}
/**
* Returns the value of the specified java system property. In case the
* value was a zero length String or one that only contained whitespaces,
* null is returned. This method is for internal use only. Users of the
* configuration service are to use the getProperty() or getString() methods
* which would automatically determine whether a property is system or not.
* @param propertyName the name of the property whose value we need.
* @return the value of the property with name propertyName or null if
* the value had length 0 or only contained spaces tabs or new lines.
*/
private static String getSystemProperty(String propertyName)
{
String retval = System.getProperty(propertyName);
if (retval == null){
return retval;
}
if (retval.trim().length() == 0){
return null;
}
return retval;
}
/**
* Checks if a file exists and if it is writable or readable. If not -
* checks if the user has a write privileges to the containing directory.

@ -13,9 +13,7 @@ Import-Package: org.osgi.framework,
javax.xml.parsers,
net.java.sip.communicator.util,
net.java.sip.communicator.util.xml,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.impl.fileaccess
Export-Package: net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.impl.fileaccess
Metadata-Location: /net/java/sip/communicator/impl/fileaccess/fileaccess.metadata.xml

@ -3,16 +3,5 @@
<bundle>
<component class="net.java.sip.communicator.impl.fileaccess.FileAccessServiceImpl">
<provides service="net.java.sip.communicator.service.fileaccess.FileAccessService"/>
<!--
One and only one reference to a configuration service is needed.
If this service is stopped, the file access service is also uninstalled
-->
<requires service="net.java.sip.communicator.service.configuration.ConfigurationService"
filter=""
policy="static"
cardinality="1..1"
bind-method="setConfigurationService"
unbind-method="unsetConfigurationService" />
</component>
</bundle>

@ -1,5 +1,5 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
u * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
@ -205,6 +205,7 @@ public class OperationSetPresenceSipImpl
private static final String BASIC_ELEMENT = "basic";
private static final String CONTACT_ELEMENT = "contact";
private static final String NOTE_ELEMENT = "note";
private static final String PRIORITY_ATTRIBUTE = "priority";
// rpid elements and attributes
private static final String RPID_NS_ELEMENT = "xmlns:rpid";
@ -592,15 +593,19 @@ public void publishPresenceStatus(PresenceStatus status,
// now inform our distant presence agent if we have one
if (this.useDistantPA) {
Request req = createPublish(this.subscriptionDuration, true);
Request req = null;
if (status.equals(SipStatusEnum.OFFLINE)) {
// unpublish our state
req = createPublish(0, false);
// remember the callid to be sure that the publish arrived
// before unregister
synchronized (this.waitedCallIds) {
this.waitedCallIds.add(((CallIdHeader)
req.getHeader(CallIdHeader.NAME)).getCallId());
}
} else {
req = createPublish(this.subscriptionDuration, true);
}
ClientTransaction transac = null;
@ -1963,6 +1968,14 @@ public void processResponse(ResponseEvent responseEvent)
logger.error("no Expires header in the response");
return;
}
// if it's a response to an unpublish request (Expires: 0),
// invalidate the etag and don't schedule a republish
if (expires.getExpires() == 0)
{
this.distantPAET = null;
return;
}
// just to be sure to not have two refreshing task
if (this.republishTask != null) {
@ -2020,7 +2033,40 @@ public void processResponse(ResponseEvent responseEvent)
ClientTransaction transac = null;
try {
transac = this.parentProvider
.getDefaultJainSipProvider().getNewClientTransaction(req);
.getDefaultJainSipProvider()
.getNewClientTransaction(req);
} catch (TransactionUnavailableException e) {
logger.error("can't create the client transaction", e);
return;
}
try {
transac.sendRequest();
} catch (SipException e) {
logger.error("can't send the PUBLISH request", e);
return;
}
// CONDITIONAL REQUEST FAILED (412)
} else if (response.getStatusCode() == Response
.CONDITIONAL_REQUEST_FAILED)
{
// as recommanded in rfc3903#5, we start a totally new
// publication
this.distantPAET = null;
Request req = null;
try {
req = createPublish(this.subscriptionDuration, true);
} catch (OperationFailedException e) {
logger.error("can't create the new publish request", e);
return;
}
ClientTransaction transac = null;
try {
transac = this.parentProvider
.getDefaultJainSipProvider()
.getNewClientTransaction(req);
} catch (TransactionUnavailableException e) {
logger.error("can't create the client transaction", e);
return;
@ -3595,9 +3641,14 @@ public void setPidfPresenceStatus(String presenceDoc)
// <presence>
NodeList presList = doc.getElementsByTagNameNS(NS_VALUE,
PRESENCE_ELEMENT);
if (presList.getLength() == 0) {
logger.error("no presence element in this document");
return;
presList = doc.getElementsByTagNameNS(ANY_NS, PRESENCE_ELEMENT);
if (presList.getLength() == 0) {
logger.error("no presence element in this document");
return;
}
}
if (presList.getLength() > 1) {
logger.warn("more than one presence element in this document");
@ -3672,10 +3723,14 @@ else if (statusname.equals(OTP_ELEMENT)) {
}
}
}
// Vector containing the list of status to set for each contact in
// the presence document ordered by priority (highest first).
// <SipContact, Float (priority), SipStatusEnum>
Vector newPresenceStates = new Vector(3, 2);
// <tuple>
NodeList tupleList = presence.getElementsByTagNameNS(NS_VALUE,
TUPLE_ELEMENT);
NodeList tupleList = getPidfChilds(presence, TUPLE_ELEMENT);
for (int i = 0; i < tupleList.getLength(); i++) {
Node tupleNode = tupleList.item(i);
@ -3686,9 +3741,8 @@ else if (statusname.equals(OTP_ELEMENT)) {
Element tuple = (Element) tupleNode;
// <contact>
NodeList contactList = tuple.getElementsByTagNameNS(NS_VALUE,
CONTACT_ELEMENT);
NodeList contactList = getPidfChilds(tuple, CONTACT_ELEMENT);
// we use a vector here and not an unique contact to handle an
// error case where many contacts are associated with a status
// Vector<ContactSipImpl>
@ -3700,11 +3754,14 @@ else if (statusname.equals(OTP_ELEMENT)) {
contactID = XMLUtils.getAttribute(
presNode, ENTITY_ATTRIBUTE);
// also accept entity URIs starting with pres: instead of sip:
if (contactID.startsWith("pres:"))
if (contactID.startsWith("pres:")) {
contactID = contactID.substring("pres:".length());
}
Contact tmpContact = resolveContactID(contactID);
if (tmpContact != null) {
sipcontact.add(tmpContact);
Object tab[] = {tmpContact, new Float(0f)};
sipcontact.add(tab);
}
}
else
@ -3720,9 +3777,74 @@ else if (statusname.equals(OTP_ELEMENT)) {
Element contact = (Element) contactNode;
contactID = getTextContent(contact);
// also accept entity URIs starting with pres: instead
// of sip:
if (contactID.startsWith("pres:")) {
contactID = contactID.substring("pres:".length());
}
Contact tmpContact = resolveContactID(contactID);
if (tmpContact != null) {
sipcontact.add(tmpContact);
if (tmpContact == null) {
continue;
}
// defines an array containing the contact and its
// priority
Object tab[] = new Object[2];
// search if the contact has a priority
String prioStr = contact.getAttribute(PRIORITY_ATTRIBUTE);
Float prio = null;
try {
if (prioStr == null || prioStr.length() == 0) {
prio = new Float(0f);
} else {
prio = Float.valueOf(prioStr);
}
} catch (NumberFormatException e) {
logger.debug("contact priority is not a valid float",
e);
prio = new Float(0f);
}
// 0 <= priority <= 1 according to rfc
if (prio.floatValue() < 0) {
prio = new Float(0f);
}
if (prio.floatValue() > 1) {
prio = new Float(1f);
}
tab[0] = tmpContact;
tab[1] = prio;
// search if the contact hasn't already been added
Iterator iter = sipcontact.iterator();
boolean contactAlreadyListed = false;
int k = 0;
while (iter.hasNext()) {
Object tmp[];
tmp = ((Object[]) iter.next());
if (((Contact) tmp[0]).equals(tmpContact)) {
contactAlreadyListed = true;
// take the highest priority
if (((Float) tmp[1]).floatValue() <
prio.floatValue())
{
sipcontact.remove(k);
sipcontact.add(tab);
}
break;
}
k++;
}
// add the contact and its priority to the list
if (!contactAlreadyListed) {
sipcontact.add(tab);
}
}
}
@ -3734,18 +3856,15 @@ else if (statusname.equals(OTP_ELEMENT)) {
// if we use RPID, simply ignore the standard PIDF status
if (personStatus != null) {
for (int j = 0; j < sipcontact.size(); j++) {
changePresenceStatusForContact(
(ContactSipImpl) sipcontact.get(j),
personStatus);
}
newPresenceStates = setStatusForContacts(personStatus,
sipcontact,
newPresenceStates);
continue;
}
// <status>
NodeList statusList = tuple.getElementsByTagNameNS(NS_VALUE,
STATUS_ELEMENT);
NodeList statusList = getPidfChilds(tuple, STATUS_ELEMENT);
// in case of many status, just consider the last one
// this is normally not permitted by RFC3863
int index = statusList.getLength() - 1;
@ -3767,9 +3886,8 @@ else if (statusname.equals(OTP_ELEMENT)) {
Element status = (Element) statusNode;
// <basic>
NodeList basicList = status.getElementsByTagNameNS(NS_VALUE,
BASIC_ELEMENT);
NodeList basicList = getPidfChilds(status, BASIC_ELEMENT);
// in case of many basic, just consider the last one
// this is normally not permitted by RFC3863
index = basicList.getLength() - 1;
@ -3793,8 +3911,7 @@ else if (statusname.equals(OTP_ELEMENT)) {
// search for a <note> that can define a more precise
// status this is not recommended by RFC3863 but some im
// clients use this.
NodeList noteList = tuple.getElementsByTagNameNS(NS_VALUE,
NOTE_ELEMENT);
NodeList noteList = getPidfChilds(tuple, NOTE_ELEMENT);
boolean changed = false;
for (int k = 0; k < noteList.getLength() && !changed; k++)
@ -3815,11 +3932,9 @@ else if (statusname.equals(OTP_ELEMENT)) {
if (current.getStatusName().equalsIgnoreCase(state)) {
changed = true;
for (int j = 0; j < sipcontact.size(); j++) {
changePresenceStatusForContact(
(ContactSipImpl) sipcontact.get(j),
current);
}
newPresenceStates = setStatusForContacts(current,
sipcontact,
newPresenceStates);
break;
}
}
@ -3828,19 +3943,17 @@ else if (statusname.equals(OTP_ELEMENT)) {
if (changed == false && basic != null) {
if (getTextContent(basic).equalsIgnoreCase(ONLINE_STATUS))
{
for (int j = 0; j < sipcontact.size(); j++) {
changePresenceStatusForContact(
(ContactSipImpl) sipcontact.get(j),
SipStatusEnum.ONLINE);
}
newPresenceStates = setStatusForContacts(
SipStatusEnum.ONLINE,
sipcontact,
newPresenceStates);
} else if (getTextContent(basic).equalsIgnoreCase(
OFFLINE_STATUS))
{
for (int j = 0; j < sipcontact.size(); j++) {
changePresenceStatusForContact(
(ContactSipImpl) sipcontact.get(j),
SipStatusEnum.OFFLINE);
}
newPresenceStates = setStatusForContacts(
SipStatusEnum.OFFLINE,
sipcontact,
newPresenceStates);
}
} else {
if (changed == false) {
@ -3850,6 +3963,16 @@ else if (statusname.equals(OTP_ELEMENT)) {
}
} // for each <tuple>
// Now really set the new presence status for the listed contacts
// newPresenceStates is ordered so priority order is respected
Iterator iter = newPresenceStates.iterator();
while (iter.hasNext()) {
Object tab[] = (Object[]) iter.next();
ContactSipImpl contact = (ContactSipImpl) tab[0];
SipStatusEnum status = (SipStatusEnum) tab[2];
changePresenceStatusForContact(contact, status);
}
}
/**
@ -3870,6 +3993,112 @@ private String getTextContent(Element node) {
return res;
}
/**
* Gets the list of the descendant of an element in the pidf namespace.
* If the list is empty, we try to get this list in any namespace.
* This method is usefull for being able to read pidf document without any
* namespace or with a wrong namespace.
*
* @param element the base element concerned.
* @paran childName the name of the descendants to match on.
*
* @return The list of all the descendant node.
*/
private NodeList getPidfChilds(Element element, String childName) {
NodeList res;
res = element.getElementsByTagNameNS(NS_VALUE, childName);
if (res.getLength() == 0) {
res = element.getElementsByTagNameNS(ANY_NS, childName);
}
return res;
}
/**
* Associate the provided presence state to the contacts considering the
* current presence states and priorities.
*
* @param presenceState The presence state to associate to the contacts
* @param contacts A list of <contact, priority> concerned by the
* presence status.
* @param curStatus The list of the current presence status ordered by
* priority (highest priority first).
*
* @return a Vector containing a list of <contact, priority, status>
* ordered by priority (highest first). Null if a parameter is null.
*/
private Vector setStatusForContacts(SipStatusEnum presenceState,
Vector contacts, Vector curStatus)
{
// test parameters
if (presenceState == null || contacts == null || curStatus == null) {
return null;
}
// for each contact in the list
Iterator iter = contacts.iterator();
while (iter.hasNext()) {
Object tab[] = (Object[]) iter.next();
Contact contact = (Contact) tab[0];
float priority = ((Float) tab[1]).floatValue();
// for each existing contact
int pos = 0, i = 0;
boolean skip = false;
Iterator iter2 = curStatus.iterator();
while (iter2.hasNext()) {
Object tab2[] = (Object[]) iter2.next();
Contact curContact = (Contact) tab2[0];
float curPriority = ((Float) tab2[1]).floatValue();
// save the place where to add this contact in the list
if (pos == 0 && curPriority <= priority) {
pos = i;
}
if (curContact.equals(contact)) {
// same contact but with an higher priority
// simply ignore this new status affectation
if (curPriority > priority) {
skip = true;
break;
// same contact but with a lower priority
// replace the old status with this one
} else if (curPriority < priority) {
curStatus.remove(i);
// same contact and same priority
// consider the reachability of the status
} else {
SipStatusEnum curPresence = (SipStatusEnum) tab2[2];
if (curPresence.getStatus() >=
presenceState.getStatus())
{
skip = true;
break;
}
curStatus.remove(i);
}
}
i++;
}
if (skip) {
continue;
}
// insert the new entry
Object tabRes[] = {contact, new Float(priority), presenceState};
curStatus.insertElementAt(tabRes, pos);
}
return curStatus;
}
/**
* Forces the poll of a contact to update its current state.

@ -556,9 +556,15 @@ protected void initialize(String sipAddress,
+ SipActivator.getConfigurationService().getScHomeDirName()
+ System.getProperty("file.separator");
NSPVALUE_DEBUG_LOG = logDir + NSPVALUE_DEBUG_LOG;
NSPVALUE_SERVER_LOG = logDir + NSPVALUE_SERVER_LOG;
// don't do it more than one time if many provider are initialised
if (!NSPVALUE_DEBUG_LOG.startsWith(logDir)) {
NSPVALUE_DEBUG_LOG = logDir + NSPVALUE_DEBUG_LOG;
}
if (!NSPVALUE_SERVER_LOG.startsWith(logDir)) {
NSPVALUE_SERVER_LOG = logDir + NSPVALUE_SERVER_LOG;
}
this.accountID = accountID;
sipFactory = SipFactory.getInstance();

@ -32,8 +32,6 @@ public void restoreFile()
* When the transaction begins, the file is restored to a coherent state if
* needed.
*
* @return The fail-safe file in which every modification must be done.
*
* @throws IllegalStateException if the file doesn't exists anymore
* @throws IOException if an IOException occurs during the transaction
* creation

Loading…
Cancel
Save