MSN PP work in progress

cusax-fix
Damian Minkov 20 years ago
parent 0f058b55cc
commit 15736655e0

@ -574,9 +574,10 @@
bundle-fileaccess-slick,bundle-media,bundle-media-slick,
bundle-protocol,bundle-icq,bundle-icq-slick,bundle-mock,
bundle-jabber,bundle-jabber-slick,bundle-swing-ui,
bundle-msn,bundle-msn-slick,
meta-contactlist,meta-contactlist-slick,
bundle-plugin-icqaccregwizz,bundle-plugin-jabberaccregwizz,
bundle-plugin-sipaccregwizz"/>
bundle-plugin-msnaccregwizz,bundle-plugin-sipaccregwizz"/>
<!--BUNDLE-HISTORY-->
<target name="bundle-history">
@ -893,6 +894,32 @@ javax.swing.event, javax.swing.border"/>
</jar>
</target>
<!-- BUNDLE-MSN -->
<target name="bundle-msn">
<!-- Creates a bundle containing the msn impl of the protocol provider."-->
<jar compress="false" destfile="${bundles.dest}/protocol-msn.jar"
manifest="src/net/java/sip/communicator/impl/protocol/msn/msn.provider.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/impl/protocol/msn"
prefix="net/java/sip/communicator/impl/protocol/msn"/>
<zipfileset src="${lib}/jml-1.0a3.jar" prefix=""/>
<zipfileset src="${lib}/cindy.jar" prefix=""/>
<zipfileset src="${lib}/commons-logging.jar" prefix=""/>
</jar>
</target>
<!-- BUNDLE-MSN-SLICK -->
<!-- Creates a bundle containing the slick for the Msn protocol provider."-->
<target name="bundle-msn-slick">
<jar compress="false" destfile="${bundles.dest}/protocol-msn-slick.jar"
manifest="test/net/java/sip/communicator/slick/protocol/msn/msn.provider.slick.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/slick/protocol/msn"
prefix="net/java/sip/communicator/slick/protocol/msn"/>
<zipfileset src="${lib}/jml-1.0a3.jar" prefix=""/>
<zipfileset src="${lib}/cindy.jar" prefix=""/>
<zipfileset src="${lib}/commons-logging.jar" prefix=""/>
</jar>
</target>
<!-- BUNDLE-SWING-UI -->
@ -949,6 +976,16 @@ javax.swing.event, javax.swing.border"/>
</jar>
</target>
<!-- BUNDLE-PLUGIN-MSNACCREGWIZZ -->
<target name="bundle-plugin-msnaccregwizz">
<!-- Creates a bundle for the plugin Msn Account Registration Wizard."-->
<jar compress="false" destfile="${bundles.dest}/msnaccregwizz.jar"
manifest="src/net/java/sip/communicator/plugin/msnaccregwizz/msnaccregwizz.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/plugin/msnaccregwizz"
prefix="net/java/sip/communicator/plugin/msnaccregwizz"/>
</jar>
</target>
<!-- BUNDLE-PLUGIN-SIPACCREGWIZZ -->
<target name="bundle-plugin-sipaccregwizz">
<!-- Creates a bundle for the plugin SIP Account Registration Wizard."-->

@ -20,5 +20,8 @@
<classpathentry kind="lib" path="lib/oscar-aim.jar"/>
<classpathentry kind="lib" path="lib/retroweaver.jar"/>
<classpathentry kind="lib" path="lib/retroweaver-rt.jar"/>
<classpathentry kind="lib" path="lib/jml-1.0a3.jar"/>
<classpathentry kind="lib" path="lib/commons-logging.jar"/>
<classpathentry kind="lib" path="lib/cindy.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -115,7 +115,7 @@
<package-root>test</package-root>
<unit-tests/>
<classpath mode="compile">
:lib/Stun4J.jar:lib/jaxen-1.1-beta-8.jar:lib/oscar.jar:lib/sip-sdp.jar:lib/osgi.jar:lib/junit.jar:lib/moduleloader.jar:lib/servicebinder.jar:lib/jmf-all/jmf.jar:lib/jmf-lin/jmf.jar:lib/jmf-sol/jmf.jar:lib/jmf-win/jmf.jar:lib/bcprov-jdk14-130.jar:lib/joscar-0.9.4-cvs-bin.jar:lib/oscar-aim.jar:lib/retroweaver.jar:lib/retroweaver-rt.jar
:lib/Stun4J.jar:lib/jaxen-1.1-beta-8.jar:lib/oscar.jar:lib/sip-sdp.jar:lib/osgi.jar:lib/junit.jar:lib/moduleloader.jar:lib/servicebinder.jar:lib/jmf-all/jmf.jar:lib/jmf-lin/jmf.jar:lib/jmf-sol/jmf.jar:lib/jmf-win/jmf.jar:lib/bcprov-jdk14-130.jar:lib/joscar-0.9.4-cvs-bin.jar:lib/oscar-aim.jar:lib/retroweaver.jar:lib/retroweaver-rt.jar:lib/jml-1.0a3.jar:lib/commons-logging.jar:lib/cindy.jar
</classpath>
<source-level>1.4</source-level>
</compilation-unit>

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -24,6 +24,7 @@ org.osgi.framework.system.packages= org.osgi.framework; \
javax.xml.transform; \
javax.xml.transform.dom; \
javax.xml.transform.stream; \
sun.security.action; \
net.java.stun4j; \
net.java.stun4j.client; \
net.java.stun4j.StunAddress; \
@ -54,6 +55,7 @@ oscar.auto.start.3= \
file:sc-bundles/protocol-icq.jar \
file:sc-bundles/protocol-sip.jar \
file:sc-bundles/protocol-jabber.jar \
file:sc-bundles/protocol-msn.jar \
file:sc-bundles/netaddr.jar \
file:sc-bundles/meta-cl.jar
@ -69,7 +71,8 @@ oscar.auto.start.4= \
oscar.auto.start.67= \
file:sc-bundles/icqaccregwizz.jar \
file:sc-bundles/sipaccregwizz.jar \
file:sc-bundles/jabberaccregwizz.jar
file:sc-bundles/jabberaccregwizz.jar \
file:sc-bundles/msnaccregwizz.jar
# Uncomment the following lines if you want to run the architect viewer
# bundle.

@ -9,6 +9,7 @@ org.osgi.framework.system.packages= org.osgi.framework; \
javax.swing.table; \
org.w3c.dom; \
org.xml.sax; \
sun.security.action; \
javax.xml.parsers;\
org.apache.xml.serializer;\
javax.xml.transform;\
@ -50,6 +51,7 @@ oscar.auto.start.4= \
file:sc-bundles/history.jar \
file:sc-bundles/protocol-icq.jar \
file:sc-bundles/protocol-jabber.jar \
file:sc-bundles/protocol-msn.jar \
file:sc-bundles/protocol-sip.jar \
file:sc-bundles/media.jar \
file:sc-bundles/meta-cl.jar \
@ -69,6 +71,7 @@ oscar.auto.start.5= \
file:sc-bundles/protocol-icq-slick.jar \
file:sc-bundles/protocol-sip-slick.jar \
file:sc-bundles/protocol-jabber-slick.jar \
file:sc-bundles/protocol-msn-slick.jar \
file:sc-bundles/msghistory-slick.jar \
file:sc-bundles/callhistory-slick.jar

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -169,21 +169,21 @@ public class Constants {
*/
public static final Color HISTORY_DATE_COLOR
= new Color(255, 201, 102);
/**
* The color used to paint the background of an incoming call history
* record.
*/
public static final Color HISTORY_IN_CALL_COLOR
= new Color(249, 255, 197);
/**
* The color used to paint the background of an outgoing call history
* record.
*/
public static final Color HISTORY_OUT_CALL_COLOR
= new Color(243, 244, 247);
/**
* The start color used to paint a gradient selected background of some
* components.
@ -297,7 +297,7 @@ public class Constants {
* The SIP protocol.
*/
public static final String SIP = "SIP";
/*
* ======================================================================
* ------------------------ OTHER CONSTANTS ------------------------------
@ -439,7 +439,7 @@ else if (protocolName.equals(Constants.ICQ)){
}
else if (protocolName.equals(Constants.MSN)) {
return ImageLoader.getAnimatedImage(
ImageLoader.ICQ_CONNECTING);
ImageLoader.MSN_CONNECTING);
}
else if (protocolName.equals(Constants.AIM)) {
return ImageLoader.getAnimatedImage(
@ -471,7 +471,7 @@ else if (protocolName.equals(Constants.SKYPE)) {
public static BufferedImage getStatusIcon(PresenceStatus status) {
if(status != null) {
int connectivity = status.getStatus();
if(connectivity < 20) {
return ImageLoader
.getImage(ImageLoader.USER_OFFLINE_ICON);
@ -532,10 +532,10 @@ public static void loadSimpleStyle(StyleSheet style) {
* @return the default sound used when user receives a message.
*/
public static AudioClip getDefaultMessageAudio()
{
{
return SoundLoader.getSound(SoundLoader.INCOMING_MESSAGE);
}
/**
* Returns the default sound used when user makes a call.
* @return the default sound used when user makes a call.
@ -544,13 +544,13 @@ public static AudioClip getDefaultOutgoingCallAudio()
{
return SoundLoader.getSound(SoundLoader.OUTGOING_CALL);
}
/**
* Returns the default sound used when user receives a call.
* @return the default sound used when user receives a call.
*/
public static AudioClip getDefaultIncomingCallAudio()
{
{
return SoundLoader.getSound(SoundLoader.INCOMING_CALL);
}
}

@ -201,19 +201,19 @@ public class ImageLoader {
*/
public static final ImageID HANGUP_ROLLOVER_BUTTON_BG
= new ImageID("HANGUP_ROLLOVER_BUTTON_BG");
/**
* The hangup button pressed image.
*/
public static final ImageID CALL_BUTTON_PRESSED_BG
= new ImageID("CALL_BUTTON_PRESSED_BG");
/**
* The hangup button pressed image.
*/
public static final ImageID HANGUP_BUTTON_PRESSED_BG
= new ImageID("HANGUP_BUTTON_PRESSED_BG");
/**
* The background image for the <tt>StatusSelectorBox</tt>.
*/
@ -361,13 +361,13 @@ public class ImageLoader {
*/
public static final ImageID ADD_CONTACT_WIZARD_ICON
= new ImageID("ADD_CONTACT_WIZARD_ICON");
/**
* The image used for decoration of the "Add group" window.
*/
public static final ImageID ADD_GROUP_ICON
= new ImageID("ADD_GROUP_ICON");
/**
* The image used for decoration of the "Rename contact" window.
*/
@ -380,32 +380,32 @@ public class ImageLoader {
*/
public static final ImageID BROWSER_ICON
= new ImageID("BROWSER_ICON");
/**
* The image used for decoration of all windows concerning the process of
* authorization.
*/
public static final ImageID AUTHORIZATION_ICON
= new ImageID("AUTHORIZATION_ICON");
/**
* The image used for decoration of incoming calls in the call list panel.
*/
public static final ImageID INCOMING_CALL_ICON
= new ImageID("INCOMING_CALL");
/**
* The image used for decoration of outgoing calls in the call list panel.
*/
public static final ImageID OUTGOING_CALL_ICON
= new ImageID("OUTGOING_CALL");
/**
* The image used in the right button menu for the move contact item.
*/
public static final ImageID MOVE_CONTACT_ICON
= new ImageID("MOVE_CONTACT");
// ///////////////////// Edit Text Toolbar icons //////////////////////////
@ -656,19 +656,19 @@ public class ImageLoader {
*/
public static final ImageID ADD_CONTACT_16x16_ICON
= new ImageID("ADD_CONTACT_16x16_ICON");
/**
* Add contact 16x16 image.
*/
public static final ImageID ADD_GROUP_16x16_ICON
= new ImageID("ADD_GROUP_16x16_ICON");
/**
* Rename 16x16 image.
*/
public static final ImageID RENAME_16x16_ICON
= new ImageID("RENAME_16x16_ICON");
/**
* Remove 16x16 image.
*/
@ -705,7 +705,7 @@ public class ImageLoader {
* The icon used to indicate a search.
*/
public static final ImageID SEARCH_ICON = new ImageID("SEARCH_ICON");
/*
* =========================================================================
* --------------------- PROTOCOLS STATUS ICONS ---------------------------
@ -716,7 +716,7 @@ public class ImageLoader {
*/
public static final ImageID SIP_COMMUNICATOR_LOGO
= new ImageID("SIP_COMMUNICATOR_LOGO");
/**
* The ICQ logo 32x32 icon.
*/
@ -781,6 +781,11 @@ public class ImageLoader {
*/
public static final ImageID MSN_LOGO = new ImageID("MSN_LOGO");
/**
* The MSN "connecting" 16x16 animated icon.
*/
public static final ImageID MSN_CONNECTING = new ImageID("MSN_CONNECTING");
/**
* The AIM logo 32x32 icon.
*/
@ -837,13 +842,13 @@ public class ImageLoader {
* The SIP "connecting" 16x16 animated icon.
*/
public static final ImageID SIP_CONNECTING = new ImageID("SIP_CONNECTING");
/**
* The SIP online 16x16 icon.
*/
public static final ImageID SIP_ONLINE_ICON
= new ImageID("SIP_ONLINE_ICON");
/**
* The SIP offline 16x16 icon.
*/

@ -109,6 +109,7 @@ ICQ_OFFLINE_ICON=net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16
ICQ_INVISIBLE_ICON=net/java/sip/communicator/impl/gui/resources/protocols/icq/cr16-action-icq_invisible.png
MSN_32x32=net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn.png
MSN_LOGO=net/java/sip/communicator/impl/gui/resources/protocols/msn/Msn16.png
MSN_CONNECTING=net/java/sip/communicator/impl/gui/resources/protocols/msn/msn16x16-connecting.gif
AIM_32x32=net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim.png
AIM_LOGO=net/java/sip/communicator/impl/gui/resources/protocols/aim/Aim16.png
YAHOO_32x32=net/java/sip/communicator/impl/gui/resources/protocols/yahoo/Yahoo.png
@ -181,4 +182,4 @@ BROWSER_ICON=net/java/sip/communicator/impl/gui/resources/common/browser16x16.pn
AUTHORIZATION_ICON=net/java/sip/communicator/impl/gui/resources/common/padlock.gif
INCOMING_CALL=net/java/sip/communicator/impl/gui/resources/common/incomingCall.png
OUTGOING_CALL=net/java/sip/communicator/impl/gui/resources/common/outgoingCall.png
OUTGOING_CALL=net/java/sip/communicator/impl/gui/resources/common/outgoingCall.png

@ -0,0 +1,480 @@
/*
* 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.jabber;
import java.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.jivesoftware.smack.util.*;
import org.jivesoftware.smackx.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.Message;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.protocol.jabberconstants.*;
/**
* A straightforward implementation of the basic instant messaging operation
* set.
*
* @author Damian Minkov
*/
public class OperationSetBasicInstantMessagingJabberImpl
implements OperationSetBasicInstantMessaging
{
private static final Logger logger =
Logger.getLogger(OperationSetBasicInstantMessagingJabberImpl.class);
/**
* KeepAlive interval for sending packets
*/
private static long KEEPALIVE_INTERVAL = 180000l; // 3 minutes
/**
* The interval after which a packet is considered to be lost
*/
private static long KEEPALIVE_WAIT = 20000l;
/**
* The task sending packets
*/
private KeepAliveSendTask keepAliveSendTask = null;
/**
* The timer executing tasks on specified intervals
*/
private Timer keepAliveTimer = new Timer();
/**
* The queue holding the received packets
*/
private LinkedList receivedKeepAlivePackets = new LinkedList();
/**
* A list of listeneres registered for message events.
*/
private Vector messageListeners = new Vector();
/**
* The provider that created us.
*/
private ProtocolProviderServiceJabberImpl jabberProvider = null;
/**
* A reference to the persistent presence operation set that we use
* to match incoming messages to <tt>Contact</tt>s and vice versa.
*/
private OperationSetPersistentPresenceJabberImpl opSetPersPresence = null;
/**
* Creates an instance of this operation set.
* @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
* that created us and that we'll use for retrieving the underlying aim
* connection.
*/
OperationSetBasicInstantMessagingJabberImpl(
ProtocolProviderServiceJabberImpl provider)
{
this.jabberProvider = provider;
provider.addRegistrationStateChangeListener(new RegistrationStateListener());
// register the KeepAlive Extension in the smack library
ProviderManager.addExtensionProvider(KeepAliveEventProvider.ELEMENT_NAME,
KeepAliveEventProvider.NAMESPACE,
new KeepAliveEventProvider());
}
/**
* Registeres a MessageListener with this operation set so that it gets
* notifications of successful message delivery, failure or reception of
* incoming messages..
*
* @param listener the <tt>MessageListener</tt> to register.
*/
public void addMessageListener(MessageListener listener)
{
synchronized(messageListeners)
{
if(!messageListeners.contains(listener))
{
this.messageListeners.add(listener);
}
}
}
/**
* Unregisteres <tt>listener</tt> so that it won't receive any further
* notifications upon successful message delivery, failure or reception of
* incoming messages..
*
* @param listener the <tt>MessageListener</tt> to unregister.
*/
public void removeMessageListener(MessageListener listener)
{
synchronized(messageListeners)
{
this.messageListeners.remove(listener);
}
}
/**
* Create a Message instance for sending arbitrary MIME-encoding content.
*
* @param content content value
* @param contentType the MIME-type for <tt>content</tt>
* @param contentEncoding encoding used for <tt>content</tt>
* @param subject a <tt>String</tt> subject or <tt>null</tt> for now subject.
* @return the newly created message.
*/
public Message createMessage(byte[] content, String contentType,
String contentEncoding, String subject)
{
return new MessageJabberImpl(new String(content), contentType
, contentEncoding, subject);
}
/**
* Create a Message instance for sending a simple text messages with
* default (text/plain) content type and encoding.
*
* @param messageText the string content of the message.
* @return Message the newly created message
*/
public Message createMessage(String messageText)
{
return new MessageJabberImpl(messageText, DEFAULT_MIME_TYPE
, DEFAULT_MIME_ENCODING, null);
}
/**
* Sends the <tt>message</tt> to the destination indicated by the
* <tt>to</tt> contact.
*
* @param to the <tt>Contact</tt> to send <tt>message</tt> to
* @param message the <tt>Message</tt> to send.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
* @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an
* instance of ContactImpl.
*/
public void sendInstantMessage(Contact to, Message message)
throws IllegalStateException, IllegalArgumentException
{
try
{
assertConnected();
Chat chat =
jabberProvider.getConnection().
createChat(to.getAddress());
org.jivesoftware.smack.packet.Message msg = chat.createMessage();
msg.setBody(message.getContent());
MessageEventManager.
addNotificationsRequests(msg, true, false, false, true);
chat.sendMessage(msg);
MessageDeliveredEvent msgDeliveredEvt
= new MessageDeliveredEvent(
message, to, new Date());
fireMessageEvent(msgDeliveredEvt);
}
catch (XMPPException ex)
{
logger.error("message not send", ex);
}
}
/**
* Utility method throwing an exception if the stack is not properly
* initialized.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
*/
private void assertConnected() throws IllegalStateException
{
if (jabberProvider == null)
throw new IllegalStateException(
"The provider must be non-null and signed on the "
+"service before being able to communicate.");
if (!jabberProvider.isRegistered())
throw new IllegalStateException(
"The provider must be signed on the service before "
+"being able to communicate.");
}
/**
* Our listener that will tell us when we're registered to
*/
private class RegistrationStateListener
implements RegistrationStateChangeListener
{
/**
* The method is called by a ProtocolProvider implementation whenver
* a change in the registration state of the corresponding provider had
* occurred.
* @param evt ProviderStatusChangeEvent the event describing the status
* change.
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
logger.debug("The provider changed state from: "
+ evt.getOldState()
+ " to: " + evt.getNewState());
if (evt.getNewState() == RegistrationState.REGISTERED)
{
opSetPersPresence = (OperationSetPersistentPresenceJabberImpl)
jabberProvider.getSupportedOperationSets()
.get(OperationSetPersistentPresence.class.getName());
jabberProvider.getConnection().addPacketListener(
new SmackMessageListener(),
new PacketTypeFilter(
org.jivesoftware.smack.packet.Message.class));
// run keepalive thread
if(keepAliveSendTask == null)
{
keepAliveSendTask = new KeepAliveSendTask();
keepAliveTimer.scheduleAtFixedRate(
keepAliveSendTask, KEEPALIVE_INTERVAL, KEEPALIVE_INTERVAL);
}
}
}
}
/**
* Delivers the specified event to all registered message listeners.
* @param evt the <tt>EventObject</tt> that we'd like delivered to all
* registered message listerners.
*/
private void fireMessageEvent(EventObject evt)
{
Iterator listeners = null;
synchronized (messageListeners)
{
listeners = new ArrayList(messageListeners).iterator();
}
while (listeners.hasNext())
{
MessageListener listener
= (MessageListener) listeners.next();
if (evt instanceof MessageDeliveredEvent)
{
listener.messageDelivered( (MessageDeliveredEvent) evt);
}
else if (evt instanceof MessageReceivedEvent)
{
listener.messageReceived( (MessageReceivedEvent) evt);
}
else if (evt instanceof MessageDeliveryFailedEvent)
{
listener.messageDeliveryFailed(
(MessageDeliveryFailedEvent) evt);
}
}
}
private class SmackMessageListener
implements PacketListener
{
public void processPacket(Packet packet)
{
if(!(packet instanceof org.jivesoftware.smack.packet.Message))
return;
org.jivesoftware.smack.packet.Message msg =
(org.jivesoftware.smack.packet.Message)packet;
if(msg.getBody() == null)
return;
String fromUserID = StringUtils.parseBareAddress(msg.getFrom());
if(logger.isDebugEnabled())
{
logger.debug("Received from "
+ fromUserID
+ " the message "
+ msg.getBody());
}
KeepAliveEvent keepAliveEvent =
(KeepAliveEvent)packet.getExtension(
KeepAliveEventProvider.ELEMENT_NAME,
KeepAliveEventProvider.NAMESPACE);
if(keepAliveEvent != null)
{
logger.info("received keepalive");
keepAliveEvent.setFromUserID(fromUserID);
receivedKeepAlivePackets.addLast(keepAliveEvent);
return;
}
Message newMessage = createMessage(msg.getBody());
Contact sourceContact =
opSetPersPresence.findContactByID(fromUserID);
if(msg.getType() == org.jivesoftware.smack.packet.Message.Type.ERROR)
{
logger.info("Message error received from " + fromUserID);
int errorCode = packet.getError().getCode();
int errorResultCode = MessageDeliveryFailedEvent.UNKNOWN_ERROR;
if(errorCode == 503)
{
org.jivesoftware.smackx.packet.MessageEvent msgEvent =
(org.jivesoftware.smackx.packet.MessageEvent)
packet.getExtension("x", "jabber:x:event");
if(msgEvent != null && msgEvent.isOffline())
{
errorResultCode =
MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED;
}
}
MessageDeliveryFailedEvent ev =
new MessageDeliveryFailedEvent(newMessage,
sourceContact,
errorResultCode,
new Date());
fireMessageEvent(ev);
return;
}
if(sourceContact == null)
{
logger.debug("received a message from an unknown contact: "
+ fromUserID);
//create the volatile contact
sourceContact = opSetPersPresence
.createVolatileContact(fromUserID);
}
MessageReceivedEvent msgReceivedEvt
= new MessageReceivedEvent(
newMessage, sourceContact , new Date() );
fireMessageEvent(msgReceivedEvt);
}
}
/**
* Task sending packets on intervals.
* The task is runned on specified intervals by the keepAliveTimer
*/
private class KeepAliveSendTask
extends TimerTask
{
public void run()
{
try
{
// if we are not registerd do nothing
if(!jabberProvider.isRegistered())
return;
Chat chat =
jabberProvider.getConnection().
createChat(jabberProvider.getAccountID().getUserID());
org.jivesoftware.smack.packet.Message msg = chat.createMessage();
msg.setBody("SYSTEM MESSAGE!");
KeepAliveEvent keepAliveEvent = new KeepAliveEvent();
keepAliveEvent.setSrcOpSetHash(
OperationSetBasicInstantMessagingJabberImpl.this.hashCode());
keepAliveEvent.setSrcProviderHash(jabberProvider.hashCode());
// add keepalive data
msg.addExtension(keepAliveEvent);
// schedule the check task
keepAliveTimer.schedule(
new KeepAliveCheckTask(), KEEPALIVE_WAIT);
logger.trace("send keepalive");
chat.sendMessage(msg);
}
catch (XMPPException ex)
{
logger.error("", ex);
}
}
}
/**
* Check if the first received packet in the queue
* is ok and if its not or the queue has no received packets
* the this means there is some network problem, so fire event
*/
private class KeepAliveCheckTask
extends TimerTask
{
public void run()
{
try
{
// check till we find a correct message
// or if NoSuchElementException is thrown
// there is no message
while(!checkFirstPacket());
}
catch (NoSuchElementException ex)
{
fireUnregisterd();
}
}
/**
* Checks whether first packet in queue is ok
* @return boolean
* @throws NoSuchElementException
*/
boolean checkFirstPacket()
throws NoSuchElementException
{
KeepAliveEvent receivedEvent =
(KeepAliveEvent)receivedKeepAlivePackets.removeLast();
if(jabberProvider.hashCode() != receivedEvent.getSrcProviderHash() ||
OperationSetBasicInstantMessagingJabberImpl.this.hashCode() !=
receivedEvent.getSrcOpSetHash() ||
!jabberProvider.getAccountID().getUserID().
equals(receivedEvent.getFromUserID()) )
return false;
else
return true;
}
/**
* Fire Unregistered event
*/
void fireUnregisterd()
{
jabberProvider.fireRegistrationStateChanged(
jabberProvider.getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
RegistrationStateChangeEvent.REASON_INTERNAL_ERROR, null);
opSetPersPresence.fireProviderPresenceStatusChangeEvent(
opSetPersPresence.getPresenceStatus(),
JabberStatusEnum.OFFLINE);
}
}
}

@ -0,0 +1,486 @@
/*
* 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.jabber;
import java.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.jivesoftware.smack.util.*;
import org.jivesoftware.smackx.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.Message;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.protocol.jabberconstants.*;
/**
* A straightforward implementation of the basic instant messaging operation
* set.
*
* @author Damian Minkov
*/
public class OperationSetBasicInstantMessagingJabberImpl
implements OperationSetBasicInstantMessaging
{
private static final Logger logger =
Logger.getLogger(OperationSetBasicInstantMessagingJabberImpl.class);
/**
* KeepAlive interval for sending packets
*/
private static long KEEPALIVE_INTERVAL = 180000l; // 3 minutes
/**
* The interval after which a packet is considered to be lost
*/
private static long KEEPALIVE_WAIT = 20000l;
/**
* The task sending packets
*/
private KeepAliveSendTask keepAliveSendTask = null;
/**
* The timer executing tasks on specified intervals
*/
private Timer keepAliveTimer = new Timer();
/**
* The queue holding the received packets
*/
private LinkedList receivedKeepAlivePackets = new LinkedList();
/**
* A list of listeneres registered for message events.
*/
private Vector messageListeners = new Vector();
/**
* The provider that created us.
*/
private ProtocolProviderServiceJabberImpl jabberProvider = null;
/**
* A reference to the persistent presence operation set that we use
* to match incoming messages to <tt>Contact</tt>s and vice versa.
*/
private OperationSetPersistentPresenceJabberImpl opSetPersPresence = null;
/**
* Creates an instance of this operation set.
* @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
* that created us and that we'll use for retrieving the underlying aim
* connection.
*/
OperationSetBasicInstantMessagingJabberImpl(
ProtocolProviderServiceJabberImpl provider)
{
this.jabberProvider = provider;
provider.addRegistrationStateChangeListener(new RegistrationStateListener());
// register the KeepAlive Extension in the smack library
ProviderManager.addExtensionProvider(KeepAliveEventProvider.ELEMENT_NAME,
KeepAliveEventProvider.NAMESPACE,
new KeepAliveEventProvider());
}
/**
* Registeres a MessageListener with this operation set so that it gets
* notifications of successful message delivery, failure or reception of
* incoming messages..
*
* @param listener the <tt>MessageListener</tt> to register.
*/
public void addMessageListener(MessageListener listener)
{
synchronized(messageListeners)
{
if(!messageListeners.contains(listener))
{
this.messageListeners.add(listener);
}
}
}
/**
* Unregisteres <tt>listener</tt> so that it won't receive any further
* notifications upon successful message delivery, failure or reception of
* incoming messages..
*
* @param listener the <tt>MessageListener</tt> to unregister.
*/
public void removeMessageListener(MessageListener listener)
{
synchronized(messageListeners)
{
this.messageListeners.remove(listener);
}
}
/**
* Create a Message instance for sending arbitrary MIME-encoding content.
*
* @param content content value
* @param contentType the MIME-type for <tt>content</tt>
* @param contentEncoding encoding used for <tt>content</tt>
* @param subject a <tt>String</tt> subject or <tt>null</tt> for now subject.
* @return the newly created message.
*/
public Message createMessage(byte[] content, String contentType,
String contentEncoding, String subject)
{
return new MessageJabberImpl(new String(content), contentType
, contentEncoding, subject);
}
/**
* Create a Message instance for sending a simple text messages with
* default (text/plain) content type and encoding.
*
* @param messageText the string content of the message.
* @return Message the newly created message
*/
public Message createMessage(String messageText)
{
return new MessageJabberImpl(messageText, DEFAULT_MIME_TYPE
, DEFAULT_MIME_ENCODING, null);
}
/**
* Sends the <tt>message</tt> to the destination indicated by the
* <tt>to</tt> contact.
*
* @param to the <tt>Contact</tt> to send <tt>message</tt> to
* @param message the <tt>Message</tt> to send.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
* @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an
* instance of ContactImpl.
*/
public void sendInstantMessage(Contact to, Message message)
throws IllegalStateException, IllegalArgumentException
{
try
{
assertConnected();
Chat chat =
jabberProvider.getConnection().
createChat(to.getAddress());
org.jivesoftware.smack.packet.Message msg = chat.createMessage();
msg.setBody(message.getContent());
MessageEventManager.
addNotificationsRequests(msg, true, false, false, true);
chat.sendMessage(msg);
MessageDeliveredEvent msgDeliveredEvt
= new MessageDeliveredEvent(
message, to, new Date());
fireMessageEvent(msgDeliveredEvt);
}
catch (XMPPException ex)
{
logger.error("message not send", ex);
}
}
/**
* Utility method throwing an exception if the stack is not properly
* initialized.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
*/
private void assertConnected() throws IllegalStateException
{
if (jabberProvider == null)
throw new IllegalStateException(
"The provider must be non-null and signed on the "
+"service before being able to communicate.");
if (!jabberProvider.isRegistered())
throw new IllegalStateException(
"The provider must be signed on the service before "
+"being able to communicate.");
}
/**
* Our listener that will tell us when we're registered to
*/
private class RegistrationStateListener
implements RegistrationStateChangeListener
{
/**
* The method is called by a ProtocolProvider implementation whenver
* a change in the registration state of the corresponding provider had
* occurred.
* @param evt ProviderStatusChangeEvent the event describing the status
* change.
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
logger.debug("The provider changed state from: "
+ evt.getOldState()
+ " to: " + evt.getNewState());
if (evt.getNewState() == RegistrationState.REGISTERED)
{
opSetPersPresence = (OperationSetPersistentPresenceJabberImpl)
jabberProvider.getSupportedOperationSets()
.get(OperationSetPersistentPresence.class.getName());
jabberProvider.getConnection().addPacketListener(
new SmackMessageListener(),
new PacketTypeFilter(
org.jivesoftware.smack.packet.Message.class));
// run keepalive thread
if(keepAliveSendTask == null)
{
keepAliveSendTask = new KeepAliveSendTask();
keepAliveTimer.scheduleAtFixedRate(
keepAliveSendTask, KEEPALIVE_INTERVAL, KEEPALIVE_INTERVAL);
}
}
}
}
/**
* Delivers the specified event to all registered message listeners.
* @param evt the <tt>EventObject</tt> that we'd like delivered to all
* registered message listerners.
*/
private void fireMessageEvent(EventObject evt)
{
Iterator listeners = null;
synchronized (messageListeners)
{
listeners = new ArrayList(messageListeners).iterator();
}
while (listeners.hasNext())
{
MessageListener listener
= (MessageListener) listeners.next();
if (evt instanceof MessageDeliveredEvent)
{
listener.messageDelivered( (MessageDeliveredEvent) evt);
}
else if (evt instanceof MessageReceivedEvent)
{
listener.messageReceived( (MessageReceivedEvent) evt);
}
else if (evt instanceof MessageDeliveryFailedEvent)
{
listener.messageDeliveryFailed(
(MessageDeliveryFailedEvent) evt);
}
}
}
private class SmackMessageListener
implements PacketListener
{
public void processPacket(Packet packet)
{
if(!(packet instanceof org.jivesoftware.smack.packet.Message))
return;
org.jivesoftware.smack.packet.Message msg =
(org.jivesoftware.smack.packet.Message)packet;
if(msg.getBody() == null)
return;
String fromUserID = StringUtils.parseBareAddress(msg.getFrom());
if(logger.isDebugEnabled())
{
logger.debug("Received from "
+ fromUserID
+ " the message "
+ msg.getBody());
}
KeepAliveEvent keepAliveEvent =
(KeepAliveEvent)packet.getExtension(
KeepAliveEventProvider.ELEMENT_NAME,
KeepAliveEventProvider.NAMESPACE);
if(keepAliveEvent != null)
{
logger.info("received keepalive");
keepAliveEvent.setFromUserID(fromUserID);
receivedKeepAlivePackets.addLast(keepAliveEvent);
return;
}
Message newMessage = createMessage(msg.getBody());
Contact sourceContact =
opSetPersPresence.findContactByID(fromUserID);
if(msg.getType() == org.jivesoftware.smack.packet.Message.Type.ERROR)
{
logger.info("Message error received from " + fromUserID);
int errorCode = packet.getError().getCode();
int errorResultCode = MessageDeliveryFailedEvent.UNKNOWN_ERROR;
if(errorCode == 503)
{
org.jivesoftware.smackx.packet.MessageEvent msgEvent =
(org.jivesoftware.smackx.packet.MessageEvent)
packet.getExtension("x", "jabber:x:event");
if(msgEvent != null && msgEvent.isOffline())
{
errorResultCode =
MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED;
}
}
MessageDeliveryFailedEvent ev =
new MessageDeliveryFailedEvent(newMessage,
sourceContact,
errorResultCode,
new Date());
fireMessageEvent(ev);
return;
}
if(sourceContact == null)
{
logger.debug("received a message from an unknown contact: "
+ fromUserID);
//create the volatile contact
sourceContact = opSetPersPresence
.createVolatileContact(fromUserID);
}
MessageReceivedEvent msgReceivedEvt
= new MessageReceivedEvent(
newMessage, sourceContact , new Date() );
fireMessageEvent(msgReceivedEvt);
}
}
/**
* Task sending packets on intervals.
* The task is runned on specified intervals by the keepAliveTimer
*/
private class KeepAliveSendTask
extends TimerTask
{
public void run()
{
try
{
// if we are not registerd do nothing
if(!jabberProvider.isRegistered())
{
logger.trace("provider not registered. "
+"won't send keep alive. acc.id="
+ jabberProvider.getAccountID()
.getAccountUniqueID());
return;
}
Chat chat =
jabberProvider.getConnection().
createChat(jabberProvider.getAccountID().getUserID());
org.jivesoftware.smack.packet.Message msg = chat.createMessage();
msg.setBody("SYSTEM MESSAGE!");
KeepAliveEvent keepAliveEvent = new KeepAliveEvent();
keepAliveEvent.setSrcOpSetHash(
OperationSetBasicInstantMessagingJabberImpl.this.hashCode());
keepAliveEvent.setSrcProviderHash(jabberProvider.hashCode());
// add keepalive data
msg.addExtension(keepAliveEvent);
// schedule the check task
keepAliveTimer.schedule(
new KeepAliveCheckTask(), KEEPALIVE_WAIT);
logger.trace("send keepalive");
chat.sendMessage(msg);
}
catch (XMPPException ex)
{
logger.error("", ex);
}
}
}
/**
* Check if the first received packet in the queue
* is ok and if its not or the queue has no received packets
* the this means there is some network problem, so fire event
*/
private class KeepAliveCheckTask
extends TimerTask
{
public void run()
{
try
{
// check till we find a correct message
// or if NoSuchElementException is thrown
// there is no message
while(!checkFirstPacket());
}
catch (NoSuchElementException ex)
{
fireUnregisterd();
}
}
/**
* Checks whether first packet in queue is ok
* @return boolean
* @throws NoSuchElementException
*/
boolean checkFirstPacket()
throws NoSuchElementException
{
KeepAliveEvent receivedEvent =
(KeepAliveEvent)receivedKeepAlivePackets.removeLast();
if(jabberProvider.hashCode() != receivedEvent.getSrcProviderHash() ||
OperationSetBasicInstantMessagingJabberImpl.this.hashCode() !=
receivedEvent.getSrcOpSetHash() ||
!jabberProvider.getAccountID().getUserID().
equals(receivedEvent.getFromUserID()) )
return false;
else
return true;
}
/**
* Fire Unregistered event
*/
void fireUnregisterd()
{
jabberProvider.fireRegistrationStateChanged(
jabberProvider.getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
RegistrationStateChangeEvent.REASON_INTERNAL_ERROR, null);
opSetPersPresence.fireProviderPresenceStatusChangeEvent(
opSetPersPresence.getPresenceStatus(),
JabberStatusEnum.OFFLINE);
}
}
}

@ -0,0 +1,29 @@
/*
* 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.msn;
import net.java.sip.communicator.service.protocol.*;
/**
* The Msn implementation of the service.protocol.ContactGroup interface. There
* are two types of groups possible here. <tt>RootContactGroupMsnImpl</tt>
* which is the root node of the ContactList itself and
* <tt>ContactGroupMsnImpl</tt> which represents standard groups. The
* reason for having those 2 is that generally, Msn groups may not contain
* subgroups. A contact list on the other hand may not directly contain buddies.
*
*
* The reason for having an abstract class is only - being able to esily
* recognize our own (Msn) contacts.
* @author Damian Minkov
*/
public abstract class AbstractContactGroupMsnImpl
implements ContactGroup
{
}

@ -0,0 +1,456 @@
/*
* 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.msn;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import net.sf.jml.MsnGroup;
import net.sf.jml.MsnContact;
/**
* The Msn implementation of the ContactGroup interface. Intances of this class
* (contrary to <tt>RootContactGroupMsnImpl</tt>) may only contain buddies
* and cannot have sub groups. Note that instances of this class only use the
* corresponding smack source group for reading their names and only
* initially fill their <tt>buddies</tt> <tt>java.util.List</tt> with
* the ContactMsnImpl objects corresponding to those contained in the source
* group at the moment it is being created. They would, however, never try to
* sync or update their contents ulteriorly. This would have to be done through
* the addContact()/removeContact() methods.
* The content of buddies is created on creating of the group and when the smack
* source group is changed.
*
* @author Damian Minkov
*/
public class ContactGroupMsnImpl
extends AbstractContactGroupMsnImpl
{
private static final Logger logger =
Logger.getLogger(ContactGroupMsnImpl.class);
private List buddies = new LinkedList();
private boolean isResolved = false;
/**
* The Msn Group corresponding to this contact group.
*/
private MsnGroup msnGroup = null;
/**
* a list that would always remain empty. We only use it so that we're able
* to extract empty iterators
*/
private List dummyGroupsList = new LinkedList();
private ServerStoredContactListMsnImpl ssclCallback = null;
/**
* Creates an Msn group using the specified <tt>RosterGroup</tt> as
* a source. The newly created group will always return the name of the
* underlying RosterGroup and would thus automatically adapt to changes.
* It would, however, not receive or try to poll for modifications of the
* buddies it contains and would therefore have to be updated manually by
* ServerStoredContactListImpl update will only be done if source group
* is changed.
* @param rosterGroup the Msn Group correspoinding to the group
* @param groupMembers the group members that we should add to the group.
* @param ssclCallback a callback to the server stored contact list
* we're creating.
* @param isResolved a boolean indicating whether or not the group has been
* resolved against the server.
*/
ContactGroupMsnImpl(
MsnGroup msnGroup,
MsnContact[] groupMembers,
ServerStoredContactListMsnImpl ssclCallback,
boolean isResolved)
{
this.msnGroup = msnGroup;
this.isResolved = isResolved;
this.ssclCallback = ssclCallback;
for (int i = 0; i < groupMembers.length; i++)
{
addContact(
new ContactMsnImpl(groupMembers[i], ssclCallback, true, true) );
}
}
/**
* Returns the number of <tt>Contact</tt> members of this
* <tt>ContactGroup</tt>
*
* @return an int indicating the number of <tt>Contact</tt>s,
* members of this <tt>ContactGroup</tt>.
*/
public int countContacts()
{
return buddies.size();
}
/**
* Returns a reference to the root group which in Msn is the parent of
* any other group since the protocol does not support subgroups.
* @return a reference to the root group.
*/
public ContactGroup getParentContactGroup()
{
return ssclCallback.getRootGroup();
}
/**
* Adds the specified contact at the specified position.
* @param contact the new contact to add to this group
* @param index the position where the new contact should be added.
*/
void addContact(int index, ContactMsnImpl contact)
{
buddies.add(index, contact);
}
/**
* Adds the specified contact to the end of this group.
* @param contact the new contact to add to this group
*/
void addContact(ContactMsnImpl contact)
{
addContact(countContacts(), contact);
}
/**
* Removes the specified contact from this contact group
* @param contact the contact to remove.
*/
void removeContact(ContactMsnImpl contact)
{
removeContact(buddies.indexOf(contact));
}
/**
* Removes the contact with the specified index.
* @param index the index of the cntact to remove
*/
void removeContact(int index)
{
buddies.remove(index);
}
/**
* Removes all buddies in this group and reinsterts them as specified
* by the <tt>newOrder</tt> param. Contacts not contained in the
* newOrder list are left at the end of this group.
*
* @param newOrder a list containing all contacts in the order that is
* to be applied.
*
*/
void reorderContacts(List newOrder)
{
buddies.removeAll(newOrder);
buddies.addAll(0, newOrder);
}
/**
* Returns an Iterator over all contacts, member of this
* <tt>ContactGroup</tt>.
*
* @return a java.util.Iterator over all contacts inside this
* <tt>ContactGroup</tt>. In case the group doesn't contain any
* memebers it will return an empty iterator.
*/
public Iterator contacts()
{
return buddies.iterator();
}
/**
* Returns the <tt>Contact</tt> with the specified index.
*
* @param index the index of the <tt>Contact</tt> to return.
* @return the <tt>Contact</tt> with the specified index.
*/
public Contact getContact(int index)
{
return (ContactMsnImpl) buddies.get(index);
}
/**
* Returns the <tt>Contact</tt> with the specified address or
* identifier.
* @param id the addres or identifier of the <tt>Contact</tt> we are
* looking for.
* @return the <tt>Contact</tt> with the specified id or address.
*/
public Contact getContact(String id)
{
return this.findContact(id);
}
/**
* Returns the name of this group.
* @return a String containing the name of this group.
*/
public String getGroupName()
{
return msnGroup.getGroupName();
}
/**
* Determines whether the group may contain subgroups or not.
*
* @return always false since only the root group may contain subgroups.
*/
public boolean canContainSubgroups()
{
return false;
}
/**
* Returns the subgroup with the specified index (i.e. always null since
* this group may not contain subgroups).
*
* @param index the index of the <tt>ContactGroup</tt> to retrieve.
* @return always null
*/
public ContactGroup getGroup(int index)
{
return null;
}
/**
* Returns the subgroup with the specified name.
* @param groupName the name of the <tt>ContactGroup</tt> to retrieve.
* @return the <tt>ContactGroup</tt> with the specified index.
*/
public ContactGroup getGroup(String groupName)
{
return null;
}
/**
* Returns an empty iterator. Subgroups may only be present in the root
* group.
*
* @return an empty iterator
*/
public Iterator subgroups()
{
return dummyGroupsList.iterator();
}
/**
* Returns the number of subgroups contained by this group, which is
* always 0 since sub groups in the protocol may only be contained
* by the root group - <tt>RootContactGroupImpl</tt>.
* @return a 0 int.
*/
public int countSubgroups()
{
return 0;
}
/**
* Returns a hash code value for the object, which is actually the hashcode
* value of the groupname.
*
* @return a hash code value for this ContactGroup.
*/
public int hashCode()
{
return getGroupName().hashCode();
}
/**
* Indicates whether some other object is "equal to" this group.
*
* @param obj the reference object with which to compare.
* @return <tt>true</tt> if this object is the same as the obj
* argument; <tt>false</tt> otherwise.
*/
public boolean equals(Object obj)
{
if( obj == this )
return true;
if (obj == null
|| !(obj instanceof ContactGroupMsnImpl) )
return false;
if(!((ContactGroup)obj).getGroupName().equals(getGroupName()))
return false;
//since Msn does not support having two groups with the same name
// at this point we could bravely state that the groups are the same
// and not bother to compare buddies. (gotta check that though)
return true;
}
/**
* Returns the protocol provider that this group belongs to.
* @return a regerence to the ProtocolProviderService instance that this
* ContactGroup belongs to.
*/
public ProtocolProviderService getProtocolProvider()
{
return this.ssclCallback.getParentProvider();
}
/**
* Returns a string representation of this group, in the form
* MsnGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}.
* @return a String representation of the object.
*/
public String toString()
{
StringBuffer buff = new StringBuffer("MSnGroup.");
buff.append(getGroupName());
buff.append(", childContacts="+countContacts()+":[");
Iterator contacts = contacts();
while (contacts.hasNext())
{
ContactMsnImpl contact = (ContactMsnImpl) contacts.next();
buff.append(contact.toString());
if(contacts.hasNext())
buff.append(", ");
}
return buff.append("]").toString();
}
/**
* Returns the contact encapsulating with the spcieified name or
* null if no such contact was found.
*
* @param id the id for the contact we're looking for.
* @return the <tt>ContactMsnImpl</tt> corresponding to the specified
* screnname or null if no such contact existed.
*/
ContactMsnImpl findContact(String id)
{
Iterator contacts = contacts();
while (contacts.hasNext())
{
ContactMsnImpl item = (ContactMsnImpl) contacts.next();
if(item.getAddress().equals(id))
return item;
}
return null;
}
/**
* Determines whether or not this contact group is being stored by the
* server. Non persistent contact groups exist for the sole purpose of
* containing non persistent contacts.
* @return true if the contact group is persistent and false otherwise.
*/
public boolean isPersistent()
{
return !(msnGroup instanceof VolatileGroup);
}
/**
* Returns null as no persistent data is required and the contact address is
* sufficient for restoring the contact.
* <p>
* @return null as no such data is needed.
*/
public String getPersistentData()
{
return null;
}
/**
* Determines whether or not this contact group has been resolved against
* the server. Unresolved group are used when initially loading a contact
* list that has been stored in a local file until the presence operation
* set has managed to retrieve all the contact list from the server and has
* properly mapped contacts and groups to their corresponding on-line
* buddies.
* @return true if the contact has been resolved (mapped against a buddy)
* and false otherwise.
*/
public boolean isResolved()
{
return isResolved;
}
/**
* Resolve this contact group against the specified group
* @param source the server stored group
*/
void setResolved(MsnGroup msnGroup)
{
if(isResolved)
return;
this.isResolved = true;
this.msnGroup = msnGroup;
MsnContact[] contacts = msnGroup.getContacts();
for (int i = 0; i < contacts.length; i++)
{
MsnContact item = contacts[i];
ContactMsnImpl contact =
ssclCallback.findContactById(item.getEmail().getEmailAddress());
if(contact != null)
{
contact.setResolved(item);
ssclCallback.fireContactResolved(this, contact);
}
else
{
ContactMsnImpl newContact =
new ContactMsnImpl(item, ssclCallback, true, true);
addContact(newContact);
ssclCallback.fireContactAdded(this, newContact);
}
}
}
/**
* Returns a <tt>String</tt> that uniquely represnets the group. In this we
* use the name of the group as an identifier. This may cause problems
* though, in clase the name is changed by some other application between
* consecutive runs of the sip-communicator.
*
* @return a String representing this group in a unique and persistent
* way.
*/
public String getUID()
{
return getGroupName();
}
/**
* The source group we are encapsulating
* @return MsnGroup
*/
MsnGroup getSourceGroup()
{
return msnGroup;
}
/**
* Change the source group
* change the buddies
*
* @param newGroup MsnGroup
*/
void setSourceGroup(MsnGroup newGroup)
{
this.msnGroup = newGroup;
}
}

@ -0,0 +1,265 @@
/*
* 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.msn;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.msnconstants.*;
import net.sf.jml.MsnContact;
/**
* The Msn implementation of the service.protocol.Contact interface.
* @author Damian Minkov
*/
public class ContactMsnImpl
implements Contact
{
private MsnContact contact = null;
private boolean isLocal = false;
private byte[] image = null;
private PresenceStatus status = MsnStatusEnum.OFFLINE;
private ServerStoredContactListMsnImpl ssclCallback = null;
private boolean isPersistent = false;
private boolean isResolved = false;
/**
* Creates an MsnContactImpl
* @param rosterEntry the RosterEntry object that we will be encapsulating.
* @param ssclCallback a reference to the ServerStoredContactListImpl
* instance that created us.
* @param isPersistent determines whether this contact is persistent or not.
* @param isResolved specifies whether the contact has been resolved against
* the server contact list
*/
ContactMsnImpl(MsnContact contact,
ServerStoredContactListMsnImpl ssclCallback,
boolean isPersistent,
boolean isResolved)
{
this.contact = contact;
this.isLocal = isLocal;
this.ssclCallback = ssclCallback;
this.isPersistent = isPersistent;
this.isResolved = isResolved;
}
/**
* Returns the Msn Userid of this contact
* @return the Msn Userid of this contact
*/
public String getAddress()
{
if(isResolved)
return contact.getEmail().getEmailAddress();
else
return contact.getId();
}
/**
* Determines whether or not this Contact instance represents the user used
* by this protocol provider to connect to the service.
*
* @return true if this Contact represents us (the local user) and false
* otherwise.
*/
public boolean isLocal()
{
return isLocal;
}
public byte[] getImage()
{
return image;
}
/**
* Returns a hashCode for this contact. The returned hashcode is actually
* that of the Contact's Address
* @return the hashcode of this Contact
*/
public int hashCode()
{
return getAddress().hashCode();
}
/**
* Indicates whether some other object is "equal to" this one.
* <p>
*
* @param obj the reference object with which to compare.
* @return <tt>true</tt> if this object is the same as the obj
* argument; <tt>false</tt> otherwise.
*/
public boolean equals(Object obj)
{
if (obj == null
|| !(obj instanceof ContactMsnImpl)
|| !(((ContactMsnImpl)obj).getAddress().equals(getAddress())
&& ((ContactMsnImpl)obj).getProtocolProvider()
== getProtocolProvider()))
return false;
else
return true;
}
/**
* Returns a string representation of this contact, containing most of its
* representative details.
*
* @return a string representation of this contact.
*/
public String toString()
{
StringBuffer buff = new StringBuffer("MsnContact[ id=");
buff.append(getAddress()).append("]");
return buff.toString();
}
/**
* Sets the status that this contact is currently in. The method is to
* only be called as a result of a status update received from the server.
*
* @param status the MsnStatusEnum that this contact is currently in.
*/
void updatePresenceStatus(PresenceStatus status)
{
this.status = status;
}
/**
* Returns the status of the contact as per the last status update we've
* received for it. Note that this method is not to perform any network
* operations and will simply return the status received in the last
* status update message. If you want a reliable way of retrieving someone's
* status, you should use the <tt>queryContactStatus()</tt> method in
* <tt>OperationSetPresence</tt>.
* @return the PresenceStatus that we've received in the last status update
* pertaining to this contact.
*/
public PresenceStatus getPresenceStatus()
{
return status;
}
/**
* Returns a String that could be used by any user interacting modules for
* referring to this contact. An alias is not necessarily unique but is
* often more human readable than an address (or id).
* @return a String that can be used for referring to this contact when
* interacting with the user.
*/
public String getDisplayName()
{
String name = contact.getDisplayName();
if (name == null)
name = getAddress();
return name;
}
/**
* Returns a reference to the contact group that this contact is currently
* a child of or null if the underlying protocol does not suppord persistent
* presence.
* @return a reference to the contact group that this contact is currently
* a child of or null if the underlying protocol does not suppord persistent
* presence.
*/
public ContactGroup getParentContactGroup()
{
return ssclCallback.findContactGroup(this);
}
/**
* Returns a reference to the protocol provider that created the contact.
* @return a refererence to an instance of the ProtocolProviderService
*/
public ProtocolProviderService getProtocolProvider()
{
return ssclCallback.getParentProvider();
}
/**
* Determines whether or not this contact is being stored by the server.
* Non persistent contacts are common in the case of simple, non-persistent
* presence operation sets. They could however also be seen in persistent
* presence operation sets when for example we have received an event
* from someone not on our contact list. Non persistent contacts are
* volatile even when coming from a persistent presence op. set. They would
* only exist until the application is closed and will not be there next
* time it is loaded.
* @return true if the contact is persistent and false otherwise.
*/
public boolean isPersistent()
{
return isPersistent;
}
/**
* Specifies whether this contact is to be considered persistent or not. The
* method is to be used _only_ when a non-persistent contact has been added
* to the contact list and its encapsulated VolatileBuddy has been repalced
* with a standard buddy.
* @param persistent true if the buddy is to be considered persistent and
* false for volatile.
*/
void setPersistent(boolean persistent)
{
this.isPersistent = persistent;
}
/**
* Resolve this contact against the given entry
* @param entry the server stored entry
*/
void setResolved(MsnContact entry)
{
if(isResolved)
return;
this.isResolved = true;
contact = entry;
}
/**
* Returns the persistent data
* @return the persistent data
*/
public String getPersistentData()
{
return null;
}
/**
* Determines whether or not this contact has been resolved against the
* server. Unresolved contacts are used when initially loading a contact
* list that has been stored in a local file until the presence operation
* set has managed to retrieve all the contact list from the server and has
* properly mapped contacts to their on-line buddies.
* @return true if the contact has been resolved (mapped against a buddy)
* and false otherwise.
*/
public boolean isResolved()
{
return isResolved;
}
public void setPersistentData(String persistentData)
{
}
/**
* Get source contact
* @return MsnContact
*/
MsnContact getSourceContact()
{
return contact;
}
}

@ -0,0 +1,67 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.msn;
import net.sf.jml.*;
/**
* Contactlist modification listener receives events
* for successful chngings
*
* @author Damian Minkov
*/
public class EventAdapter
implements EventListener
{
/**
* Message is successfully delivered
* @param transactionID int the transaction that send the message
*/
public void messageDelivered(int transactionID){}
/**
* Message is not delivered
* @param transactionID int the transaction that send the message
*/
public void messageDeliveredFailed(int transactionID){}
/**
* Indicates that a contact is successfully added
* @param contact MsnContact the contact
*/
public void contactAdded(MsnContact contact){}
/**
* Indicates that a contact is successfully added to the group
* @param contact MsnContact the contact
* @param group MsnGroup the group
*/
public void contactAddedInGroup(MsnContact contact, MsnGroup group){}
/**
* Indicates successful removing of a contact
* @param contact MsnContact the removed contact
*/
public void contactRemoved(MsnContact contact){}
/**
* Indicates successful removing of a contact from a group
* @param contact MsnContact the contact removed
* @param group MsnGroup the group
*/
public void contactRemovedFromGroup(MsnContact contact, MsnGroup group){}
/**
* Indicates that a group is successfully added
* @param group MsnGroup the added group
*/
public void groupAdded(MsnGroup group){}
/**
* Indicates that a group is successfully renamed
* @param group MsnGroup the renmaed group with the new name
*/
public void groupRenamed(MsnGroup group){}
/**
* Indicates successful removing of a group
* @param id String the id of the removed group
*/
public void groupRemoved(String id){}
}

@ -0,0 +1,66 @@
/*
* 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.msn;
import net.sf.jml.*;
/**
* Contactlist modification listener receives events
* for successful chngings
*
* @author Damian Minkov
*/
public interface EventListener
{
/**
* Message is successfully delivered
* @param transactionID int the transaction that send the message
*/
public void messageDelivered(int transactionID);
/**
* Message is not delivered
* @param transactionID int the transaction that send the message
*/
public void messageDeliveredFailed(int transactionID);
/**
* Indicates that a contact is successfully added
* @param contact MsnContact the contact
*/
public void contactAdded(MsnContact contact);
/**
* Indicates that a contact is successfully added to the group
* @param contact MsnContact the contact
* @param group MsnGroup the group
*/
public void contactAddedInGroup(MsnContact contact, MsnGroup group);
/**
* Indicates successful removing of a contact
* @param contact MsnContact the removed contact
*/
public void contactRemoved(MsnContact contact);
/**
* Indicates successful removing of a contact from a group
* @param contact MsnContact the contact removed
* @param group MsnGroup the group
*/
public void contactRemovedFromGroup(MsnContact contact, MsnGroup group);
/**
* Indicates that a group is successfully added
* @param group MsnGroup the added group
*/
public void groupAdded(MsnGroup group);
/**
* Indicates that a group is successfully renamed
* @param group MsnGroup the renmaed group with the new name
*/
public void groupRenamed(MsnGroup group);
/**
* Indicates successful removing of a group
* @param id String the id of the removed group
*/
public void groupRemoved(String id);
}

@ -0,0 +1,337 @@
/*
* 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.msn;
import java.util.*;
import net.sf.cindy.*;
import net.sf.jml.*;
import net.sf.jml.impl.*;
import net.sf.jml.protocol.*;
import net.sf.jml.protocol.incoming.*;
import net.java.sip.communicator.util.Logger;
/**
* Manager which listens for changing of the contact list
* and fires some events
*
* @author Damian Minkov
*/
public class EventManager
extends SessionAdapter
{
private static final Logger logger = Logger.getLogger(EventManager.class);
private BasicMessenger msnMessenger = null;
private Vector listeners = new Vector();
/**
* Creates the manager
* @param msnMessenger BasicMessenger the messanger
*/
public EventManager(BasicMessenger msnMessenger)
{
this.msnMessenger = msnMessenger;
msnMessenger.addSessionListener(this);
}
/**
* Adds listener of the modification fired events
* @param listener EventListener
*/
public void addModificationListener(EventListener listener)
{
synchronized(listeners)
{
listeners.add(listener);
}
}
/**
* Removes listener of the modification fired events
* @param listener EventListener
*/
public void removeModificationListener(EventListener listener)
{
synchronized(listeners)
{
listeners.remove(listener);
}
}
/**
* Called from the underling lib when message is sent to the server
* @param session Session
* @param message Message
* @throws Exception
*/
public void messageSent(Session session, Message message) throws Exception
{
logger.trace(msnMessenger.getOwner().getEmail().getEmailAddress() +
" outgoing " + message);
}
/**
* Called from the underling lib when message is received from the server
* @param session Session
* @param message Message
* @throws Exception
*/
public void messageReceived(Session session, Message message)
throws Exception
{
MsnIncomingMessage incoming = (MsnIncomingMessage)((WrapperMessage)message)
.getMessage();
logger.trace(msnMessenger.getOwner().getEmail().getEmailAddress() +
" incoming : " + incoming);
// These are the incoming messages that are NOT handled
//IncomingADC,IncomingANS,IncomingBLP,IncomingBPR,IncomingBYE
//IncomingCAL,IncomingCHL,IncomingCHG,IncomingCVR,IncomingFLN
//IncomingGTC,IncomingILN,IncomingIRO,IncomingJOI,IncomingLSG
//IncomingLST,IncomingMSG,IncomingNLN
//IncomingOUT - The notice message that logout. Maybe because of MSN
// server maintenance or someone login in other place.
//IncomingPRP,IncomingQNG,IncomingQRY,IncomingREA,IncomingRNG
//IncomingSYN,IncomingUBX,IncomingURL,IncomingUSR,IncomingUUX
//IncomingUnknown,IncomingVER,IncomingXFR
if(incoming instanceof IncomingACK)
{
//indicate the message has successed send to remote user.
fireMessageDelivered(((IncomingACK)incoming).getTransactionId());
}
else if(incoming instanceof IncomingADC)
{
// add user to contact list
IncomingADC incomingADC = (IncomingADC) incoming;
if (incomingADC.getId() != null &&
incomingADC.getList().equals(MsnList.FL))
{
MsnContact contact = msnMessenger.getContactList().
getContactById(incomingADC.getId());
if (incomingADC.getGroupId() != null)
{
MsnGroup group = msnMessenger.getContactList().
getGroup(incomingADC.getGroupId());
fireContactAddedInGroup(contact, group);
}
else
fireContactAdded(contact);
}
}
else if(incoming instanceof IncomingADG)
{
//indicate add a group success
IncomingADG incomingADG = (IncomingADG)incoming;
MsnGroupImpl group =
(MsnGroupImpl)msnMessenger.getContactList().getGroup(incomingADG.getGroupId());
fireGroupAdded(group);
}
else if(incoming instanceof IncomingNAK)
{
//indicate the message has not successed send to remote user.
fireMessageDeliveredFailed(((IncomingNAK)incoming).getTransactionId());
}
else if(incoming instanceof IncomingREG)
{
//indicate the group name has changed successfully.
IncomingREG incomingREG = (IncomingREG)incoming;
MsnGroupImpl group = (MsnGroupImpl)msnMessenger.getContactList().
getGroup(incomingREG.getGroupId());
fireGroupRenamed(group);
}
else if(incoming instanceof IncomingRMG)
{
// indicate delete the group successfully.
IncomingRMG incomingRMG = (IncomingRMG)incoming;
fireGroupRemoved(incomingRMG.getGroupId());
}
else if(incoming instanceof IncomingREM)
{
// indicate delete the contact successfully.
IncomingREM incomingREM = (IncomingREM)incoming;
if(incomingREM.getList().equals(MsnList.FL))
{
if(incomingREM.getGroupId() == null)
{
// just contact removed
MsnContactImpl contact = (MsnContactImpl)
msnMessenger.getContactList().getContactById(incomingREM.getId());
if(contact != null)
{
fireContactRemoved(contact);
}
}
else
{
// contact removed from group
MsnContact contact =
msnMessenger.getContactList().
getContactById(incomingREM.getId());
MsnGroup group =
msnMessenger.getContactList().
getGroup(incomingREM.getGroupId());
fireContactRemovedFromGroup(contact, group);
}
}
}
}
/**
* Fired when a message is delivered successfully
* @param transactionID int
*/
private void fireMessageDelivered(int transactionID)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).
messageDelivered(transactionID);
}
}
}
/**
* Fired when a message is not delivered successfully
* @param transactionID int
*/
private void fireMessageDeliveredFailed(int transactionID)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).
messageDeliveredFailed(transactionID);
}
}
}
/**
* Fired when a contact is added successfully
* @param contact MsnContact
*/
private void fireContactAdded(MsnContact contact)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).contactAdded(contact);
}
}
}
/**
* Fired when a contact is added in a group successfully
* @param contact MsnContact
* @param group MsnGroup
*/
private void fireContactAddedInGroup(MsnContact contact, MsnGroup group)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).
contactAddedInGroup(contact, group);
}
}
}
/**
* Fired when a contact is removed successfully
* @param contact MsnContact
*/
private void fireContactRemoved(MsnContact contact)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).contactRemoved(contact);
}
}
}
/**
* Fired when a contact is removed from group successfully
* @param contact MsnContact
* @param group MsnGroup
*/
private void fireContactRemovedFromGroup(MsnContact contact, MsnGroup group)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).
contactRemovedFromGroup(contact, group);
}
}
}
/**
* Fired when a group is added successfully
* @param group MsnGroup
*/
private void fireGroupAdded(MsnGroup group)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).groupAdded(group);
}
}
}
/**
* Fired when a group is renamed successfully
* @param group MsnGroup
*/
private void fireGroupRenamed(MsnGroup group)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).groupRenamed(group);
}
}
}
/**
* Fired when a group is removed successfully
* @param id String
*/
private void fireGroupRemoved(String id)
{
synchronized(listeners){
Iterator iter = listeners.iterator();
while (iter.hasNext())
{
((EventListener)iter.next()).groupRemoved(id);
}
}
}
}

@ -0,0 +1,147 @@
/*
* 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.msn;
import net.java.sip.communicator.service.protocol.*;
/**
* A simple implementation of the <tt>Message</tt> interface. Right now the
* message only supports test contents and no binary data.
*
* @author Damian Minkov
*/
public class MessageMsnImpl
implements Message
{
/**
* The content of this message.
*/
private String textContent = null;
/**
* The content type of text. Right now only text content types (such as
* text/plain or text/html) are supported.
*/
private String contentType = null;
/**
* The encoding under which the contennt of this message is encoded.
*/
private String contentEncoding = null;
/**
* An String uniquely identifying this Message.
*/
private String messageUID = null;
/**
* The subject of the message if any (may remain null).
*/
private String subject = null;
/**
* Creates an instance of this Message with the specified parameters.
*
* @param content the text content of the message.
* @param contentType a MIME string indicating the content type of the
* <tt>content</tt> String.
* @param contentEncoding a MIME String indicating the content encoding of
* the <tt>content</tt> String.
* @param subject the subject of the message or null for empty.
*/
public MessageMsnImpl(String content,
String contentType,
String contentEncoding,
String subject)
{
this.textContent = content;
this.contentType = contentType;
this.contentEncoding = contentEncoding;
this.subject = subject;
//generate the uid
this.messageUID = String.valueOf( System.currentTimeMillis())
+ String.valueOf(hashCode());
}
/**
* Returns the content of this message if representable in text form or
* null if this message does not contain text data.
*
* @return a String containing the content of this message or null if
* the message does not contain data representable in text form.
*/
public String getContent()
{
return textContent;
}
/**
* Returns the MIME type for the message content.
*
* @return a String containing the mime type of the message contant.
*/
public String getContentType()
{
return contentType;
}
/**
* Returns the MIME content encoding of this message.
*
* @return a String indicating the MIME encoding of this message.
*/
public String getEncoding()
{
return contentEncoding;
}
/**
* Returns a unique identifier of this message.
*
* @return a String that uniquely represents this message in the scope
* of this protocol.
*/
public String getMessageUID()
{
return messageUID;
}
/**
* Get the raw/binary content of an instant message.
*
* @return a byte[] array containing message bytes.
*/
public byte[] getRawData()
{
return getContent().getBytes();
}
/**
* Returns the size of the content stored in this message.
*
* @return an int indicating the number of bytes that this message
* contains.
*/
public int getSize()
{
return getContent().length();
}
/**
* Returns the subject of this message or null if the message contains no
* subject.
*
* @return the subject of this message or null if the message contains
* no subject.
*/
public String getSubject()
{
return subject;
}
}

@ -0,0 +1,24 @@
package net.java.sip.communicator.impl.protocol.msn;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The Msn implementation of a sip-communicator AccountID
*
* @author Damian Minkov
*/
public class MsnAccountID
extends AccountID
{
/**
* Creates an account id from the specified id and account properties.
* @param id the id identifying this account
* @param accountProperties any other properties necessary for the account.
*/
MsnAccountID(String id, Map accountProperties )
{
super(id, accountProperties, ProtocolNames.MSN, "msn.com");
}
}

@ -0,0 +1,115 @@
/*
* 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.msn;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Loads the MSN provider factory and registers it with service in the OSGI
* bundle context.
*
* @author Damian Minkov
*/
public class MsnActivator
implements BundleActivator
{
private ServiceRegistration msnPpFactoryServReg = null;
private static BundleContext bundleContext = null;
private static ConfigurationService configurationService = null;
private static ProtocolProviderFactoryMsnImpl msnProviderFactory = null;
/**
* Called when this bundle is started so the Framework can perform the
* bundle-specific activities necessary to start this bundle.
*
* @param context The execution context of the bundle being started.
* @throws Exception If this method throws an exception, this bundle is
* marked as stopped and the Framework will remove this bundle's
* listeners, unregister all services registered by this bundle, and
* release all services used by this bundle.
*/
public void start(BundleContext context) throws Exception
{
this.bundleContext = context;
Hashtable hashtable = new Hashtable();
hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.MSN);
msnProviderFactory = new ProtocolProviderFactoryMsnImpl();
//load all msn providers
msnProviderFactory.loadStoredAccounts();
//reg the msn account man.
msnPpFactoryServReg = context.registerService(
ProtocolProviderFactory.class.getName(),
msnProviderFactory,
hashtable);
}
/**
* Returns a reference to a ConfigurationService implementation currently
* registered in the bundle context or null if no such implementation was
* found.
*
* @return ConfigurationService a currently valid implementation of the
* configuration service.
*/
public static ConfigurationService getConfigurationService()
{
if(configurationService == null)
{
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
configurationService
= (ConfigurationService) bundleContext.getService(confReference);
}
return configurationService;
}
/**
* Returns a reference to the bundle context that we were started with.
* @return a reference to the BundleContext instance that we were started
* witn.
*/
public static BundleContext getBundleContext()
{
return bundleContext;
}
/**
* Retrurns a reference to the protocol provider factory that we have
* registered.
* @return a reference to the <tt>ProtocolProviderFactoryMsnImpl</tt>
* instance that we have registered from this package.
*/
static ProtocolProviderFactoryMsnImpl getProtocolProviderFactory()
{
return msnProviderFactory;
}
/**
* Called when this bundle is stopped so the Framework can perform the
* bundle-specific activities necessary to stop the bundle.
*
* @param context The execution context of the bundle being stopped.
* @throws Exception If this method throws an exception, the bundle is
* still marked as stopped, and the Framework will remove the bundle's
* listeners, unregister all services registered by the bundle, and
* release all services used by the bundle.
*/
public void stop(BundleContext context) throws Exception
{
msnProviderFactory.stop();
msnPpFactoryServReg.unregister();
}
}

@ -0,0 +1,272 @@
/*
* 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.msn;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.msnconstants.*;
import net.java.sip.communicator.util.*;
import net.sf.jml.*;
import net.sf.jml.event.*;
import net.sf.jml.message.*;
/**
* A straightforward implementation of the basic instant messaging operation
* set.
*
* @author Damian Minkov
*/
public class OperationSetBasicInstantMessagingMsnImpl
implements OperationSetBasicInstantMessaging
{
private static final Logger logger =
Logger.getLogger(OperationSetBasicInstantMessagingMsnImpl.class);
/**
* A list of listeneres registered for message events.
*/
private Vector messageListeners = new Vector();
/**
* The provider that created us.
*/
private ProtocolProviderServiceMsnImpl msnProvider = null;
/**
* A reference to the persistent presence operation set that we use
* to match incoming messages to <tt>Contact</tt>s and vice versa.
*/
private OperationSetPersistentPresenceMsnImpl opSetPersPresence = null;
/**
* Creates an instance of this operation set.
* @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
* that created us and that we'll use for retrieving the underlying aim
* connection.
*/
OperationSetBasicInstantMessagingMsnImpl(
ProtocolProviderServiceMsnImpl provider)
{
this.msnProvider = provider;
provider.addRegistrationStateChangeListener(new RegistrationStateListener());
}
/**
* Registeres a MessageListener with this operation set so that it gets
* notifications of successful message delivery, failure or reception of
* incoming messages..
*
* @param listener the <tt>MessageListener</tt> to register.
*/
public void addMessageListener(MessageListener listener)
{
synchronized(messageListeners)
{
if(!messageListeners.contains(listener))
{
this.messageListeners.add(listener);
}
}
}
/**
* Unregisteres <tt>listener</tt> so that it won't receive any further
* notifications upon successful message delivery, failure or reception of
* incoming messages..
*
* @param listener the <tt>MessageListener</tt> to unregister.
*/
public void removeMessageListener(MessageListener listener)
{
synchronized(messageListeners)
{
this.messageListeners.remove(listener);
}
}
/**
* Create a Message instance for sending arbitrary MIME-encoding content.
*
* @param content content value
* @param contentType the MIME-type for <tt>content</tt>
* @param contentEncoding encoding used for <tt>content</tt>
* @param subject a <tt>String</tt> subject or <tt>null</tt> for now subject.
* @return the newly created message.
*/
public Message createMessage(byte[] content, String contentType,
String contentEncoding, String subject)
{
return new MessageMsnImpl(new String(content), contentType
, contentEncoding, subject);
}
/**
* Create a Message instance for sending a simple text messages with
* default (text/plain) content type and encoding.
*
* @param messageText the string content of the message.
* @return Message the newly created message
*/
public Message createMessage(String messageText)
{
return new MessageMsnImpl(messageText, DEFAULT_MIME_TYPE
, DEFAULT_MIME_ENCODING, null);
}
/**
* Sends the <tt>message</tt> to the destination indicated by the
* <tt>to</tt> contact.
*
* @param to the <tt>Contact</tt> to send <tt>message</tt> to
* @param message the <tt>Message</tt> to send.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
* @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an
* instance of ContactImpl.
*/
public void sendInstantMessage(Contact to, Message message)
throws IllegalStateException, IllegalArgumentException
{
assertConnected();
if(to.getPresenceStatus().equals(MsnStatusEnum.OFFLINE))
{
MessageDeliveryFailedEvent evt =
new MessageDeliveryFailedEvent(
message,
to,
MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED,
new Date());
fireMessageEvent(evt);
return;
}
msnProvider.getMessenger().
sendText(
((ContactMsnImpl)to).getSourceContact().getEmail(),
message.getContent()
);
MessageDeliveredEvent msgDeliveredEvt
= new MessageDeliveredEvent(
message, to, new Date());
fireMessageEvent(msgDeliveredEvt);
}
/**
* Utility method throwing an exception if the stack is not properly
* initialized.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
*/
private void assertConnected() throws IllegalStateException
{
if (msnProvider == null)
throw new IllegalStateException(
"The provider must be non-null and signed on the "
+"service before being able to communicate.");
if (!msnProvider.isRegistered())
throw new IllegalStateException(
"The provider must be signed on the service before "
+"being able to communicate.");
}
/**
* Our listener that will tell us when we're registered to
*/
private class RegistrationStateListener
implements RegistrationStateChangeListener
{
/**
* The method is called by a ProtocolProvider implementation whenver
* a change in the registration state of the corresponding provider had
* occurred.
* @param evt ProviderStatusChangeEvent the event describing the status
* change.
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
logger.debug("The provider changed state from: "
+ evt.getOldState()
+ " to: " + evt.getNewState());
if (evt.getNewState() == RegistrationState.REGISTERED)
{
opSetPersPresence = (OperationSetPersistentPresenceMsnImpl)
msnProvider.getSupportedOperationSets()
.get(OperationSetPersistentPresence.class.getName());
msnProvider.getMessenger().
addMessageListener(new MsnMessageListener());
}
}
}
/**
* Delivers the specified event to all registered message listeners.
* @param evt the <tt>EventObject</tt> that we'd like delivered to all
* registered message listerners.
*/
private void fireMessageEvent(EventObject evt)
{
Iterator listeners = null;
synchronized (messageListeners)
{
listeners = new ArrayList(messageListeners).iterator();
}
while (listeners.hasNext())
{
MessageListener listener
= (MessageListener) listeners.next();
if (evt instanceof MessageDeliveredEvent)
{
listener.messageDelivered( (MessageDeliveredEvent) evt);
}
else if (evt instanceof MessageReceivedEvent)
{
listener.messageReceived( (MessageReceivedEvent) evt);
}
else if (evt instanceof MessageDeliveryFailedEvent)
{
listener.messageDeliveryFailed(
(MessageDeliveryFailedEvent) evt);
}
}
}
private class MsnMessageListener
extends MsnMessageAdapter
{
public void instantMessageReceived(MsnSwitchboard switchboard,
MsnInstantMessage message,
MsnContact contact)
{
Message newMessage = createMessage(message.getContent());
Contact sourceContact = opSetPersPresence.
findContactByID(contact.getEmail().getEmailAddress());
if(sourceContact == null)
{
logger.debug("received a message from an unknown contact: "
+ contact);
//create the volatile contact
sourceContact = opSetPersPresence
.createVolatileContact(contact.getEmail().getEmailAddress());
}
MessageReceivedEvent msgReceivedEvt
= new MessageReceivedEvent(
newMessage, sourceContact , new Date() );
fireMessageEvent(msgReceivedEvt);
}
}
}

@ -0,0 +1,151 @@
/*
* 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.msn;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Maps SIP Communicator typing notifications to those going and coming from
* smack lib.
*
* @author Damian Minkov
*/
public class OperationSetTypingNotificationsMsnImpl
implements OperationSetTypingNotifications
{
private static final Logger logger =
Logger.getLogger(OperationSetTypingNotificationsMsnImpl.class);
/**
* All currently registered TN listeners.
*/
private List typingNotificationsListeners = new ArrayList();
/**
* The provider that created us.
*/
private ProtocolProviderServiceMsnImpl msnProvider = null;
/**
* An active instance of the opSetPersPresence operation set. We're using
* it to map incoming events to contacts in our contact list.
*/
private OperationSetPersistentPresenceMsnImpl opSetPersPresence = null;
/**
* @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
* that created us and that we'll use for retrieving the underlying aim
* connection.
*/
OperationSetTypingNotificationsMsnImpl(
ProtocolProviderServiceMsnImpl provider)
{
this.msnProvider = provider;
}
/**
* Adds <tt>l</tt> to the list of listeners registered for receiving
* <tt>TypingNotificationEvent</tt>s
*
* @param l the <tt>TypingNotificationsListener</tt> listener that we'd
* like to add
* method
*/
public void addTypingNotificationsListener(TypingNotificationsListener l)
{
synchronized(typingNotificationsListeners)
{
typingNotificationsListeners.add(l);
}
}
/**
* Removes <tt>l</tt> from the list of listeners registered for receiving
* <tt>TypingNotificationEvent</tt>s
*
* @param l the <tt>TypingNotificationsListener</tt> listener that we'd
* like to remove
*/
public void removeTypingNotificationsListener(TypingNotificationsListener l)
{
synchronized(typingNotificationsListeners)
{
typingNotificationsListeners.remove(l);
}
}
/**
* Delivers a <tt>TypingNotificationEvent</tt> to all registered listeners.
* @param sourceContact the contact who has sent the notification.
* @param evtCode the code of the event to deliver.
*/
private void fireTypingNotificationsEvent(Contact sourceContact
,int evtCode)
{
logger.debug("Dispatching a TypingNotif. event to "
+ typingNotificationsListeners.size()+" listeners. Contact "
+ sourceContact.getAddress() + " has now a typing status of "
+ evtCode);
TypingNotificationEvent evt = new TypingNotificationEvent(
sourceContact, evtCode);
Iterator listeners = null;
synchronized (typingNotificationsListeners)
{
listeners = new ArrayList(typingNotificationsListeners).iterator();
}
while (listeners.hasNext())
{
TypingNotificationsListener listener
= (TypingNotificationsListener) listeners.next();
listener.typingNotificationReceifed(evt);
}
}
/**
* Sends a notification to <tt>notifiedContatct</tt> that we have entered
* <tt>typingState</tt>.
*
* @param notifiedContact the <tt>Contact</tt> to notify
* @param typingState the typing state that we have entered.
*
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
* @throws java.lang.IllegalArgumentException if <tt>notifiedContact</tt> is
* not an instance belonging to the underlying implementation.
*/
public void sendTypingNotification(Contact notifiedContact, int typingState)
throws IllegalStateException, IllegalArgumentException
{
assertConnected();
}
/**
* Utility method throwing an exception if the stack is not properly
* initialized.
* @throws java.lang.IllegalStateException if the underlying stack is
* not registered and initialized.
*/
private void assertConnected() throws IllegalStateException
{
if (msnProvider == null)
throw new IllegalStateException(
"The msn provider must be non-null and signed on the "
+"service before being able to communicate.");
if (!msnProvider.isRegistered())
throw new IllegalStateException(
"The msn provider must be signed on the service before "
+"being able to communicate.");
}
}

@ -0,0 +1,264 @@
/*
* 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.msn;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The Msn implementation of the ProtocolProviderFactory.
* @author Damian Minkov
*/
public class ProtocolProviderFactoryMsnImpl
extends ProtocolProviderFactory
{
/**
* The table that we store our accounts in.
*/
private Hashtable registeredAccounts = new Hashtable();
/**
* Creates an instance of the ProtocolProviderFactoryMsnImpl.
*/
protected ProtocolProviderFactoryMsnImpl()
{
}
/**
* Returns a copy of the list containing all accounts currently
* registered in this protocol provider.
*
* @return a copy of the llist containing all accounts currently installed
* in the protocol provider.
*/
public ArrayList getRegisteredAccounts()
{
return new ArrayList(registeredAccounts.keySet());
}
/**
* Returns the ServiceReference for the protocol provider corresponding to
* the specified accountID or null if the accountID is unknown.
* @param accountID the accountID of the protocol provider we'd like to get
* @return a ServiceReference object to the protocol provider with the
* specified account id and null if the account id is unknwon to the
* provider factory.
*/
public ServiceReference getProviderForAccount(AccountID accountID)
{
ServiceRegistration registration
= (ServiceRegistration)registeredAccounts.get(accountID);
return (registration == null )
? null
: registration.getReference();
}
/**
* Initializes and creates an account corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
* <tt>context</tt> BundleContext parameter. This method has a persistent
* effect. Once created the resulting account will remain installed until
* removed through the uninstall account method.
*
* @param userIDStr the user identifier for the new account
* @param accountProperties a set of protocol (or implementation)
* specific properties defining the new account.
* @return the AccountID of the newly created account
*/
public AccountID installAccount( String userIDStr,
Map accountProperties)
{
BundleContext context
= MsnActivator.getBundleContext();
if (context == null)
throw new NullPointerException("The specified BundleContext was null");
if (userIDStr == null)
throw new NullPointerException("The specified AccountID was null");
if (accountProperties == null)
throw new NullPointerException("The specified property map was null");
accountProperties.put(USER_ID, userIDStr);
AccountID accountID = new MsnAccountID(userIDStr, accountProperties);
//make sure we haven't seen this account id before.
if( registeredAccounts.containsKey(accountID) )
throw new IllegalStateException(
"An account for id " + userIDStr + " was already installed!");
//first store the account and only then load it as the load generates
//an osgi event, the osgi event triggers (trhgough the UI) a call to
//the register() method and it needs to acces the configuration service
//and check for a password.
this.storeAccount(
MsnActivator.getBundleContext()
, accountID);
accountID = loadAccount(accountProperties);
return accountID;
}
/**
* Initializes and creates an account corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
* <tt>context</tt> BundleContext parameter.
*
* @param accountProperties a set of protocol (or implementation)
* specific properties defining the new account.
* @return the AccountID of the newly created account
*/
public AccountID loadAccount( Map accountProperties)
{
BundleContext context
= MsnActivator.getBundleContext();
if(context == null)
throw new NullPointerException("The specified BundleContext was null");
String userIDStr = (String)accountProperties.get(USER_ID);
AccountID accountID = new MsnAccountID(userIDStr, accountProperties);
//get a reference to the configuration service and register whatever
//properties we have in it.
Hashtable properties = new Hashtable();
properties.put(PROTOCOL, ProtocolNames.MSN);
properties.put(USER_ID, userIDStr);
ProtocolProviderServiceMsnImpl msnProtocolProvider
= new ProtocolProviderServiceMsnImpl();
msnProtocolProvider.initialize(userIDStr, accountID);
ServiceRegistration registration
= context.registerService( ProtocolProviderService.class.getName(),
msnProtocolProvider,
properties);
registeredAccounts.put(accountID, registration);
return accountID;
}
/**
* Removes the specified account from the list of accounts that this
* provider factory is handling. If the specified accountID is unknown to
* the ProtocolProviderFactory, the call has no effect and false is returned.
* This method is persistent in nature and once called the account
* corresponding to the specified ID will not be loaded during future runs
* of the project.
*
* @param accountID the ID of the account to remove.
* @return true if an account with the specified ID existed and was removed
* and false otherwise.
*/
public boolean uninstallAccount(AccountID accountID)
{
ServiceRegistration registration
= (ServiceRegistration)registeredAccounts.remove(accountID);
if(registration == null)
return false;
//kill the service
registration.unregister();
return removeStoredAccount(
MsnActivator.getBundleContext()
, accountID);
}
/**
* Loads (and hence installs) all accounts previously stored in the
* configuration service.
*/
public void loadStoredAccounts()
{
super.loadStoredAccounts( MsnActivator.getBundleContext());
}
/**
* Prepares the factory for bundle shutdown.
*/
public void stop()
{
Enumeration registrations = this.registeredAccounts.elements();
while(registrations.hasMoreElements())
{
ServiceRegistration reg
= ((ServiceRegistration)registrations.nextElement());
reg.unregister();
}
Enumeration idEnum = registeredAccounts.keys();
while(idEnum.hasMoreElements())
{
registeredAccounts.remove(idEnum.nextElement());
}
}
/**
* Returns the configuraiton service property name prefix that we use to
* store properties concerning the account with the specified id.
* @param accountID the AccountID whose property name prefix we're looking
* for.
* @return the prefix of the configuration service property name that
* we're using when storing properties for the specified account.
*/
public String findAccountPrefix(AccountID accountID)
{
return super.findAccountPrefix(MsnActivator.getBundleContext()
, accountID);
}
/**
* Saves the password for the specified account after scrambling it a bit
* so that it is not visible from first sight (Method remains highly
* insecure).
*
* @param accountID the AccountID for the account whose password we're
* storing.
* @param passwd the password itself.
*
* @throws java.lang.IllegalArgumentException if no account corresponding
* to <tt>accountID</tt> has been previously stored.
*/
public void storePassword(AccountID accountID, String passwd)
throws IllegalArgumentException
{
super.storePassword(MsnActivator.getBundleContext()
, accountID
, passwd);
}
/**
* Returns the password last saved for the specified account.
*
* @param accountID the AccountID for the account whose password we're
* looking for..
*
* @return a String containing the password for the specified accountID.
*
* @throws java.lang.IllegalArgumentException if no account corresponding
* to <tt>accountID</tt> has been previously stored.
*/
public String loadPassword(AccountID accountID)
throws IllegalArgumentException
{
return super.loadPassword(MsnActivator.getBundleContext()
, accountID );
}
}

@ -0,0 +1,441 @@
/*
* 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.msn;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.sf.jml.*;
import net.sf.jml.event.*;
import net.sf.jml.impl.*;
/**
* An implementation of the protocol provider service over the Msn protocol
*
* @author Damian Minkov
*/
public class ProtocolProviderServiceMsnImpl
implements ProtocolProviderService
{
private static final Logger logger =
Logger.getLogger(ProtocolProviderServiceMsnImpl.class);
/**
* The hashtable with the operation sets that we support locally.
*/
private Hashtable supportedOperationSets = new Hashtable();
private MsnMessenger messenger = null;
/**
* indicates whether or not the provider is initialized and ready for use.
*/
private boolean isInitialized = false;
/**
* We use this to lock access to initialization.
*/
private Object initializationLock = new Object();
/**
* A list of all listeners registered for
* <tt>RegistrationStateChangeEvent</tt>s.
*/
private List registrationListeners = new ArrayList();
/**
* The identifier of the account that this provider represents.
*/
private AccountID accountID = null;
/**
* Used when we need to re-register
*/
private SecurityAuthority authority = null;
private OperationSetPersistentPresenceMsnImpl persistentPresence = null;
/**
* Returns the state of the registration of this protocol provider
* @return the <tt>RegistrationState</tt> that this provider is
* currently in or null in case it is in a unknown state.
*/
public RegistrationState getRegistrationState()
{
if(messenger.getConnection() == null)
return RegistrationState.UNREGISTERED;
else
return RegistrationState.REGISTERED;
}
/**
* Starts the registration process. Connection details such as
* registration server, user name/number are provided through the
* configuration service through implementation specific properties.
*
* @param authority the security authority that will be used for resolving
* any security challenges that may be returned during the
* registration or at any moment while wer're registered.
* @throws OperationFailedException with the corresponding code it the
* registration fails for some reason (e.g. a networking error or an
* implementation problem).
*/
public void register(final SecurityAuthority authority)
throws OperationFailedException
{
if(authority == null)
throw new IllegalArgumentException(
"The register method needs a valid non-null authority impl "
+ " in order to be able and retrieve passwords.");
this.authority = authority;
connectAndLogin(authority);
}
/**
* Connects and logins to the server
* @param authority SecurityAuthority
* @throws XMPPException if we cannot connect to the server - network problem
* @throws OperationFailedException if login parameters
* as server port are not correct
*/
private void connectAndLogin(SecurityAuthority authority)
throws OperationFailedException
{
synchronized(initializationLock)
{
//verify whether a password has already been stored for this account
String password = MsnActivator.
getProtocolProviderFactory().loadPassword(getAccountID());
//decode
if (password == null)
{
//create a default credentials object
UserCredentials credentials = new UserCredentials();
credentials.setUserName(getAccountID().getUserID());
//request a password from the user
credentials = authority.obtainCredentials(ProtocolNames.MSN
, credentials);
//extract the password the user passed us.
char[] pass = credentials.getPassword();
// the user didn't provide us a password (canceled the operation)
if(pass == null)
{
fireRegistrationStateChanged(
getRegistrationState(),
RegistrationState.UNREGISTERED,
RegistrationStateChangeEvent.REASON_USER_REQUEST, "");
return;
}
password = new String(pass);
if (credentials.isPasswordPersistent())
{
MsnActivator.getProtocolProviderFactory()
.storePassword(getAccountID(), password);
}
}
messenger = MsnMessengerFactory.createMsnMessenger(
getAccountID().getUserID(),
password);
messenger.addMessengerListener(new MsnConnectionListener());
persistentPresence.setMessenger(messenger);
messenger.login();
}
}
/**
* Ends the registration of this protocol provider with the service.
*/
public void unregister()
{
unregister(true);
}
/**
* Unregister and fire the event if requested
* @param fireEvent boolean
*/
private void unregister(boolean fireEvent)
{
RegistrationState currRegState = getRegistrationState();
messenger.logout();
if(fireEvent)
{
fireRegistrationStateChanged(
currRegState,
RegistrationState.UNREGISTERED,
RegistrationStateChangeEvent.REASON_USER_REQUEST, null);
}
}
/**
* Indicates whether or not this provider is signed on the service
* @return true if the provider is currently signed on (and hence online)
* and false otherwise.
*/
public boolean isRegistered()
{
return getRegistrationState().equals(RegistrationState.REGISTERED);
}
/**
* Returns the short name of the protocol that the implementation of this
* provider is based upon (like SIP, Msn, ICQ/AIM, or others for
* example).
*
* @return a String containing the short name of the protocol this
* service is taking care of.
*/
public String getProtocolName()
{
return ProtocolNames.MSN;
}
/**
* Returns an array containing all operation sets supported by the
* current implementation.
*
* @return an array of OperationSet-s supported by this protocol
* provider implementation.
*/
public Map getSupportedOperationSets()
{
return supportedOperationSets;
}
/**
* Returns the operation set corresponding to the specified class or null
* if this operation set is not supported by the provider implementation.
*
* @param opsetClass the <tt>Class</tt> of the operation set that we're
* looking for.
* @return returns an OperationSet of the specified <tt>Class</tt> if the
* undelying implementation supports it or null otherwise.
*/
public OperationSet getOperationSet(Class opsetClass)
{
return (OperationSet)getSupportedOperationSets()
.get(opsetClass.getName());
}
/**
* Initialized the service implementation, and puts it in a sate where it
* could interoperate with other services. It is strongly recomended that
* properties in this Map be mapped to property names as specified by
* <tt>AccountProperties</tt>.
*
* @param screenname the account id/uin/screenname of the account that
* we're about to create
* @param accountID the identifier of the account that this protocol
* provider represents.
*
* @see net.java.sip.communicator.service.protocol.AccountID
*/
protected void initialize(String screenname,
AccountID accountID)
{
synchronized(initializationLock)
{
this.accountID = accountID;
//initialize the presence operationset
persistentPresence = new OperationSetPersistentPresenceMsnImpl(this);
supportedOperationSets.put(
OperationSetPersistentPresence.class.getName(),
persistentPresence);
//register it once again for those that simply need presence
supportedOperationSets.put( OperationSetPresence.class.getName(),
persistentPresence);
//initialize the IM operation set
OperationSetBasicInstantMessagingMsnImpl basicInstantMessaging =
new OperationSetBasicInstantMessagingMsnImpl(this);
supportedOperationSets.put(
OperationSetBasicInstantMessaging.class.getName(),
basicInstantMessaging);
//initialize the typing notifications operation set
OperationSetTypingNotifications typingNotifications =
new OperationSetTypingNotificationsMsnImpl(this);
supportedOperationSets.put(
OperationSetTypingNotifications.class.getName(),
typingNotifications);
isInitialized = true;
}
}
/**
* Makes the service implementation close all open sockets and release
* any resources that it might have taken and prepare for
* shutdown/garbage collection.
*/
public void shutdown()
{
synchronized(initializationLock){
messenger.logout();
messenger = null;
isInitialized = false;
}
}
/**
* Returns true if the provider service implementation is initialized and
* ready for use by other services, and false otherwise.
*
* @return true if the provider is initialized and ready for use and false
* otherwise
*/
public boolean isInitialized()
{
return isInitialized;
}
/**
* Removes the specified registration state change listener so that it does
* not receive any further notifications upon changes of the
* RegistrationState of this provider.
*
* @param listener the listener to register for
* <tt>RegistrationStateChangeEvent</tt>s.
*/
public void removeRegistrationStateChangeListener(
RegistrationStateChangeListener listener)
{
synchronized(registrationListeners)
{
registrationListeners.remove(listener);
}
}
/**
* Registers the specified listener with this provider so that it would
* receive notifications on changes of its state or other properties such
* as its local address and display name.
*
* @param listener the listener to register.
*/
public void addRegistrationStateChangeListener(
RegistrationStateChangeListener listener)
{
synchronized(registrationListeners)
{
if (!registrationListeners.contains(listener))
registrationListeners.add(listener);
}
}
/**
* Returns the AccountID that uniquely identifies the account represented
* by this instance of the ProtocolProviderService.
* @return the id of the account represented by this provider.
*/
public AccountID getAccountID()
{
return accountID;
}
/**
* Returns the <tt>XMPPConnection</tt>opened by this provider
* @return a reference to the <tt>XMPPConnection</tt> last opened by this
* provider.
*/
MsnMessenger getMessenger()
{
return messenger;
}
/**
* Creates a RegistrationStateChange event corresponding to the specified
* old and new states and notifies all currently registered listeners.
*
* @param oldState the state that the provider had before the change
* occurred
* @param newState the state that the provider is currently in.
* @param reasonCode a value corresponding to one of the REASON_XXX fields
* of the RegistrationStateChangeEvent class, indicating the reason for
* this state transition.
* @param reason a String further explaining the reason code or null if
* no such explanation is necessary.
*/
void fireRegistrationStateChanged( RegistrationState oldState,
RegistrationState newState,
int reasonCode,
String reason)
{
RegistrationStateChangeEvent event =
new RegistrationStateChangeEvent(
this, oldState, newState, reasonCode, reason);
logger.debug("Dispatching " + event + " to "
+ registrationListeners.size()+ " listeners.");
Iterator listeners = null;
synchronized (registrationListeners)
{
listeners = new ArrayList(registrationListeners).iterator();
}
while (listeners.hasNext())
{
RegistrationStateChangeListener listener
= (RegistrationStateChangeListener) listeners.next();
listener.registrationStateChanged(event);
}
logger.trace("Done.");
}
/**
* Listens when we are logged in or out from the server
* or incoming exception in the lib impl.
*/
private class MsnConnectionListener
implements MsnMessengerListener
{
public void loginCompleted(MsnMessenger msnMessenger)
{
logger.trace("loginCompleted " + msnMessenger.getActualMsnProtocol());
fireRegistrationStateChanged(
getRegistrationState(),
RegistrationState.REGISTERED,
RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
}
public void logout(MsnMessenger msnMessenger)
{
logger.trace("logout");
}
public void exceptionCaught(MsnMessenger msnMessenger, Throwable throwable)
{
logger.error("Error in Msn lib ", throwable);
}
}
}

@ -0,0 +1,316 @@
/*
* 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.msn;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* A dummy ContactGroup implementation representing the ContactList root for
* Msn contact lists.
* @author Damian Minkov
*/
public class RootContactGroupMsnImpl
extends AbstractContactGroupMsnImpl
{
private String ROOT_CONTACT_GROUP_NAME = "ContactListRoot";
private List subGroups = new LinkedList();
private boolean isResolved = false;
private List contacts = new LinkedList();
private ProtocolProviderServiceMsnImpl ownerProvider = null;
/**
* Creates a ContactGroup instance.
*/
RootContactGroupMsnImpl(){}
/**
* Sets the currently valid provider
* @param ownerProvider ProtocolProviderServiceImpl
*/
void setOwnerProvider(ProtocolProviderServiceMsnImpl ownerProvider)
{
this.ownerProvider = ownerProvider;
}
/**
* The ContactListRoot is the only group that can contain subgroups.
*
* @return true (always)
*/
public boolean canContainSubgroups()
{
return true;
}
/**
* Returns the name of this group which is always
* <tt>ROOT_CONTACT_GROUP_NAME</tt>.
*
* @return a String containing the name of this group.
*/
public String getGroupName()
{
return ROOT_CONTACT_GROUP_NAME;
}
/**
* Removes the specified contact from this contact group
* @param contact the contact to remove.
*/
void removeContact(ContactMsnImpl contact)
{
contacts.remove(contact);
}
/**
* Adds the specified contact to the end of this group.
* @param contact the new contact to add to this group
*/
void addContact(ContactMsnImpl contact)
{
contacts.add(contact);
}
/**
* Adds the specified group to the end of the list of sub groups.
* @param group the group to add.
*/
void addSubGroup(ContactGroupMsnImpl group)
{
subGroups.add(group);
}
/**
* Removes the specified from the list of sub groups
* @param group the group to remove.
*/
void removeSubGroup(ContactGroupMsnImpl group)
{
removeSubGroup(subGroups.indexOf(group));
}
/**
* Removes the sub group with the specified index.
* @param index the index of the group to remove
*/
void removeSubGroup(int index)
{
subGroups.remove(index);
}
/**
* Removes all contact sub groups and reinsterts them as specified
* by the <tt>newOrder</tt> param. Contact groups not contained in the
* newOrder list are left at the end of this group.
*
* @param newOrder a list containing all contact groups in the order that is
* to be applied.
*
*/
void reorderSubGroups(List newOrder)
{
subGroups.removeAll(newOrder);
subGroups.addAll(0, newOrder);
}
/**
* Returns the number of subgroups contained by this
* <tt>RootContactGroupImpl</tt>.
*
* @return an int indicating the number of subgroups that this
* ContactGroup contains.
*/
public int countSubgroups()
{
return subGroups.size();
}
/**
* Returns null as this is the root contact group.
* @return null as this is the root contact group.
*/
public ContactGroup getParentContactGroup()
{
return null;
}
/**
* Returns the subgroup with the specified index.
*
* @param index the index of the <tt>ContactGroup</tt> to retrieve.
* @return the <tt>ContactGroup</tt> with the specified index.
*/
public ContactGroup getGroup(int index)
{
return (ContactGroupMsnImpl) subGroups.get(index);
}
/**
* Returns the subgroup with the specified name.
* @param groupName the name of the <tt>ContactGroup</tt> to retrieve.
* @return the <tt>ContactGroup</tt> with the specified index.
*/
public ContactGroup getGroup(String groupName)
{
Iterator subgroups = subgroups();
while (subgroups.hasNext())
{
ContactGroupMsnImpl grp = (ContactGroupMsnImpl) subgroups.next();
if (grp.getGroupName().equals(groupName))
return grp;
}
return null;
}
/**
* Returns an iterator over the sub groups that this
* <tt>ContactGroup</tt> contains.
*
* @return a java.util.Iterator over the <tt>ContactGroup</tt>
* children of this group (i.e. subgroups).
*/
public Iterator subgroups()
{
return subGroups.iterator();
}
/**
* Returns the number, which is always 0, of <tt>Contact</tt> members
* of this <tt>ContactGroup</tt>
* @return an int indicating the number of <tt>Contact</tt>s, members
* of this <tt>ContactGroup</tt>.
*/
public int countContacts()
{
return contacts.size();
}
/**
* Returns an Iterator over all contacts, member of this
* <tt>ContactGroup</tt>.
* @return a java.util.Iterator over all contacts inside this
* <tt>ContactGroup</tt>
*/
public Iterator contacts()
{
return contacts.iterator();
}
/**
* A dummy impl of the corresponding interface method - always returns null.
*
* @param index the index of the <tt>Contact</tt> to return.
* @return the <tt>Contact</tt> with the specified index, i.e. always
* null.
*/
public Contact getContact(int index)
{
return null;
}
/**
* Returns the <tt>Contact</tt> with the specified address or
* identifier.
* @param id the addres or identifier of the <tt>Contact</tt> we are
* looking for.
* @return the <tt>Contact</tt> with the specified id or address.
*/
public Contact getContact(String id)
{
//no contacts in the root group for this msn impl.
return null;
}
/**
* Returns a string representation of the root contact group that contains
* all subgroups and subcontacts of this group.
*
* @return a string representation of this root contact group.
*/
public String toString()
{
StringBuffer buff = new StringBuffer(getGroupName());
buff.append(".subGroups=" + countSubgroups() + ":\n");
Iterator subGroups = subgroups();
while (subGroups.hasNext())
{
ContactGroup group = (ContactGroup) subGroups.next();
buff.append(group.toString());
if (subGroups.hasNext())
buff.append("\n");
}
return buff.toString();
}
/**
* Returns the protocol provider that this group belongs to.
* @return a regerence to the ProtocolProviderService instance that this
* ContactGroup belongs to.
*/
public ProtocolProviderService getProtocolProvider()
{
return this.ownerProvider;
}
/**
* Determines whether or not this contact group is being stored by the
* server. Non persistent contact groups exist for the sole purpose of
* containing non persistent contacts.
* @return true if the contact group is persistent and false otherwise.
*/
public boolean isPersistent()
{
return true;
}
/**
* Returns null as no persistent data is required and the group name is
* sufficient for restoring the contact.
* <p>
* @return null as no such data is needed.
*/
public String getPersistentData()
{
return null;
}
/**
* Determines whether or not this group has been resolved against the
* server. Unresolved groups are used when initially loading a contact
* list that has been stored in a local file until the presence operation
* set has managed to retrieve all the contact list from the server and has
* properly mapped groups to their on-line buddies.
* @return true if the group has been resolved (mapped against a buddy)
* and false otherwise.
*/
public boolean isResolved()
{
return isResolved;
}
/**
* Returns a <tt>String</tt> that uniquely represnets the group. In this we
* use the name of the group as an identifier. This may cause problems
* though, in clase the name is changed by some other application between
* consecutive runs of the sip-communicator.
*
* @return a String representing this group in a unique and persistent
* way.
*/
public String getUID()
{
return getGroupName();
}
}

@ -0,0 +1,12 @@
[PropertyInfo]
contactListModManager,net.java.sip.communicator.impl.protocol.msn.EventManager,false,false, , ,false,<default>
messenger,net.sf.jml.MsnMessenger,false,false, , ,false,<default>
msnProvider,net.java.sip.communicator.impl.protocol.msn.ProtocolProviderServiceMsnImpl,false,false, , ,false,<default>
parentOperationSet,net.java.sip.communicator.impl.protocol.msn.OperationSetPersistentPresenceMsnImpl,false,false, , ,false,<default>
rootGroup,net.java.sip.communicator.service.protocol.ContactGroup,false,false, , ,true,<default>
serverStoredGroupListeners,java.util.Vector,false,false, , ,false,<default>
[IconNames]

@ -0,0 +1,56 @@
/*
* 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.msn;
import net.sf.jml.*;
/**
* The Msn implementation for Volatile Contact
* @author Damian Minkov
*/
public class VolatileContact
implements MsnContact
{
private String contactId = null;
VolatileContact(String id)
{
this.contactId = id;
}
public MsnContactList getContactList(){return null;}
public String getId()
{
return contactId;
}
public String getFriendlyName()
{
return contactId;
}
public boolean isInList(MsnList msnList){return false;}
public MsnGroup[] getBelongGroups(){return null;}
public boolean belongGroup(MsnGroup msnGroup){return false;}
public Email getEmail(){return null;}
public String getDisplayName(){return "";}
public MsnUserStatus getStatus(){return null;}
public MsnClientId getClientId(){return null;}
public MsnUserProperties getProperties(){return null;}
public String getOldDisplayName(){return "";}
public MsnUserStatus getOldStatus(){return null;}
}

@ -0,0 +1,38 @@
/*
* 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.msn;
import net.sf.jml.*;
/**
* The Msn implementation of the Volatile ContactGroup interface.
*
* @author Damian Minkov
*/
public class VolatileGroup
implements MsnGroup
{
private String groupName = new String("NotInContactList");
public MsnContactList getContactList(){return null;}
public String getGroupId()
{
return groupName;
}
public String getGroupName()
{
return getGroupId();
}
public MsnContact[] getContacts(){return null;}
public boolean containContact(MsnContact contact){return false;}
public boolean isDefaultGroup(){return false;}
}

@ -0,0 +1,19 @@
Bundle-Activator: net.java.sip.communicator.impl.protocol.msn.MsnActivator
Bundle-Name: Msn Protocol Provider Implementation
Bundle-Description: An Msn implementation of the Protocol Provider Service.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: org.osgi.framework,
javax.net.ssl,
javax.swing,
javax.xml.parsers,
javax.naming,
javax.naming.directory,
org.xml.sax,
sun.security.action,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.util,
net.java.sip.communicator.service.configuration.event,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.msnconstants,
net.java.sip.communicator.service.protocol.event

@ -0,0 +1,211 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.plugin.msnaccregwizz;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>FirstWizardPage</tt> is the page, where user could enter the uin
* and the password of the account.
*
* @author Yana Stamcheva
* @author Damian Minkov
*/
public class FirstWizardPage extends JPanel
implements WizardPage, DocumentListener {
public static final String FIRST_PAGE_IDENTIFIER = "FirstPageIdentifier";
private JPanel uinPassPanel = new JPanel(new BorderLayout(10, 10));
private JPanel labelsPanel = new JPanel(new GridLayout(0, 1, 10, 10));
private JPanel valuesPanel = new JPanel(new GridLayout(0, 1, 10, 10));
private JLabel uinLabel = new JLabel(Resources.getString("uin"));
private JLabel passLabel = new JLabel(Resources.getString("password"));
private JTextField uinField = new JTextField();
private JPasswordField passField = new JPasswordField();
private JCheckBox rememberPassBox = new JCheckBox(
Resources.getString("rememberPassword"));
private JPanel mainPanel = new JPanel();
private MsnAccountRegistration registration;
private WizardContainer wizardContainer;
/**
* Creates an instance of <tt>FirstWizardPage</tt>.
* @param registration the <tt>MsnAccountRegistration</tt>, where
* all data through the wizard are stored
* @param wizardContainer the wizardContainer, where this page will
* be added
*/
public FirstWizardPage(MsnAccountRegistration registration,
WizardContainer wizardContainer) {
super(new BorderLayout());
this.wizardContainer = wizardContainer;
this.registration = registration;
this.setPreferredSize(new Dimension(300, 150));
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
this.init();
this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
}
/**
* Initializes all panels, buttons, etc.
*/
private void init() {
this.uinField.getDocument().addDocumentListener(this);
this.rememberPassBox.setSelected(true);
labelsPanel.add(uinLabel);
labelsPanel.add(passLabel);
valuesPanel.add(uinField);
valuesPanel.add(passField);
uinPassPanel.add(labelsPanel, BorderLayout.WEST);
uinPassPanel.add(valuesPanel, BorderLayout.CENTER);
uinPassPanel.add(rememberPassBox, BorderLayout.SOUTH);
uinPassPanel.setBorder(BorderFactory
.createTitledBorder(Resources.getString("uinAndPassword")));
mainPanel.add(uinPassPanel);
this.add(mainPanel, BorderLayout.NORTH);
}
/**
* Implements the <code>WizardPage.getIdentifier</code> to return
* this page identifier.
*/
public Object getIdentifier() {
return FIRST_PAGE_IDENTIFIER;
}
/**
* Implements the <code>WizardPage.getNextPageIdentifier</code> to return
* the next page identifier - the summary page.
*/
public Object getNextPageIdentifier() {
return WizardPage.SUMMARY_PAGE_IDENTIFIER;
}
/**
* Implements the <code>WizardPage.getBackPageIdentifier</code> to return
* the next back identifier - the default page.
*/
public Object getBackPageIdentifier() {
return WizardPage.DEFAULT_PAGE_IDENTIFIER;
}
/**
* Implements the <code>WizardPage.getWizardForm</code> to return
* this panel.
*/
public Object getWizardForm() {
return this;
}
/**
* Before this page is displayed enables or disables the "Next" wizard
* button according to whether the UIN field is empty.
*/
public void pageShowing() {
this.setNextButtonAccordingToUIN();
}
/**
* Saves the user input when the "Next" wizard buttons is clicked.
*/
public void pageNext() {
registration.setUin(uinField.getText());
registration.setPassword(new String(passField.getPassword()));
registration.setRememberPassword(rememberPassBox.isSelected());
}
/**
* Enables or disables the "Next" wizard button according to whether the
* UIN field is empty.
*/
private void setNextButtonAccordingToUIN() {
if (uinField.getText() == null || uinField.getText().equals("")) {
wizardContainer.setNextFinishButtonEnabled(false);
}
else {
wizardContainer.setNextFinishButtonEnabled(true);
}
}
/**
* Handles the <tt>DocumentEvent</tt> triggered when user types in the
* UIN field. Enables or disables the "Next" wizard button according to
* whether the UIN field is empty.
*/
public void insertUpdate(DocumentEvent e) {
this.setNextButtonAccordingToUIN();
}
/**
* Handles the <tt>DocumentEvent</tt> triggered when user deletes letters
* from the UIN field. Enables or disables the "Next" wizard button
* according to whether the UIN field is empty.
*/
public void removeUpdate(DocumentEvent e) {
this.setNextButtonAccordingToUIN();
}
public void changedUpdate(DocumentEvent e) {
}
public void pageHiding() {
}
public void pageShown() {
}
public void pageBack() {
}
/**
* Fills the UIN and Password fields in this panel with the data comming
* from the given protocolProvider.
* @param protocolProvider The <tt>ProtocolProviderService</tt> to load the
* data from.
*/
public void loadAccount(ProtocolProviderService protocolProvider) {
AccountID accountID = protocolProvider.getAccountID();
String password = (String)accountID.getAccountProperties()
.get(ProtocolProviderFactory.PASSWORD);
this.uinField.setText(accountID.getUserID());
if(password != null) {
this.passField.setText(password);
this.rememberPassBox.setSelected(true);
}
}
}

@ -0,0 +1,78 @@
/*
* 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.plugin.msnaccregwizz;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* Registers the <tt>MsnAccountRegistrationWizard</tt> in the UI Service.
*
* @author Yana Stamcheva
*/
public class MsnAccRegWizzActivator implements BundleActivator {
public static BundleContext bundleContext;
private static Logger logger = Logger.getLogger(
MsnAccRegWizzActivator.class.getName());
private static ConfigurationService configService;
/**
* Starts this bundle.
* @param bc BundleContext
* @throws Exception
*/
public void start(BundleContext bc) throws Exception {
bundleContext = bc;
ServiceReference uiServiceRef = bundleContext
.getServiceReference(UIService.class.getName());
UIService uiService
= (UIService) bundleContext.getService(uiServiceRef);
AccountRegistrationWizardContainer wizardContainer
= uiService.getAccountRegWizardContainer();
MsnAccountRegistrationWizard msnWizard
= new MsnAccountRegistrationWizard(wizardContainer);
wizardContainer.addAccountRegistrationWizard(msnWizard);
}
public void stop(BundleContext bundleContext) throws Exception {
}
/**
* Returns the <tt>ProtocolProviderFactory</tt> for the Msn protocol.
* @return the <tt>ProtocolProviderFactory</tt> for the Msn protocol
*/
public static ProtocolProviderFactory getMsnProtocolProviderFactory() {
ServiceReference[] serRefs = null;
String osgiFilter = "("
+ ProtocolProviderFactory.PROTOCOL
+ "="+ProtocolNames.MSN+")";
try {
serRefs = bundleContext.getServiceReferences(
ProtocolProviderFactory.class.getName(), osgiFilter);
}
catch (InvalidSyntaxException ex){
logger.error("MsnAccRegWizzActivator : " + ex);
}
return (ProtocolProviderFactory) bundleContext.getService(serRefs[0]);
}
}

@ -0,0 +1,72 @@
/*
* 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.plugin.msnaccregwizz;
/**
* The <tt>MsnAccountRegistration</tt> is used to store all user input data
* through the <tt>MsnAccountRegistrationWizard</tt>.
*
* @author Yana Stamcheva
*/
public class MsnAccountRegistration {
private String uin;
private String password;
private boolean rememberPassword;
/**
* Returns the password of the msn registration account.
* @return the password of the msn registration account.
*/
public String getPassword() {
return password;
}
/**
* Sets the password of the msn registration account.
* @param password the password of the msn registration account.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Returns TRUE if password has to remembered, FALSE otherwise.
* @return TRUE if password has to remembered, FALSE otherwise
*/
public boolean isRememberPassword() {
return rememberPassword;
}
/**
* Sets the rememberPassword value of this msn account registration.
* @param rememberPassword TRUE if password has to remembered, FALSE
* otherwise
*/
public void setRememberPassword(boolean rememberPassword) {
this.rememberPassword = rememberPassword;
}
/**
* Returns the UIN of the msn registration account.
* @return the UIN of the msn registration account.
*/
public String getUin() {
return uin;
}
/**
* Sets the UIN of the msn registration account.
* @param uin the UIN of the msn registration account.
*/
public void setUin(String uin) {
this.uin = uin;
}
}

@ -0,0 +1,161 @@
/*
* 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.plugin.msnaccregwizz;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>MsnAccountRegistrationWizard</tt> is an implementation of the
* <tt>AccountRegistrationWizard</tt> for the Msn protocol. It should allow
* the user to create and configure a new Msn account.
*
* @author Yana Stamcheva
*/
public class MsnAccountRegistrationWizard implements AccountRegistrationWizard {
private FirstWizardPage firstWizardPage;
private MsnAccountRegistration registration
= new MsnAccountRegistration();
private WizardContainer wizardContainer;
private ProtocolProviderService protocolProvider;
private String propertiesPackage = "net.java.sip.communicator.plugin.msnaccregwizz";
/**
* Creates an instance of <tt>MsnAccountRegistrationWizard</tt>.
* @param wizardContainer the wizard container, where this wizard
* is added
*/
public MsnAccountRegistrationWizard(WizardContainer wizardContainer) {
this.wizardContainer = wizardContainer;
}
/**
* Implements the <code>AccountRegistrationWizard.getIcon</code> method.
* Returns the icon to be used for this wizard.
* @return byte[]
*/
public byte[] getIcon() {
return Resources.getImage(Resources.MSN_LOGO);
}
/**
* Implements the <code>AccountRegistrationWizard.getProtocolName</code>
* method. Returns the protocol name for this wizard.
* @return String
*/
public String getProtocolName() {
return Resources.getString("protocolName");
}
/**
* Implements the <code>AccountRegistrationWizard.getProtocolDescription
* </code> method. Returns the description of the protocol for this wizard.
* @return String
*/
public String getProtocolDescription() {
return Resources.getString("protocolDescription");
}
/**
* Returns the set of pages contained in this wizard.
* @return Iterator
*/
public Iterator getPages() {
ArrayList pages = new ArrayList();
firstWizardPage = new FirstWizardPage(registration, wizardContainer);
pages.add(firstWizardPage);
return pages.iterator();
}
/**
* Returns the set of data that user has entered through this wizard.
* @return Iterator
*/
public Iterator getSummary() {
Hashtable summaryTable = new Hashtable();
summaryTable.put("UIN", registration.getUin());
summaryTable.put("Remember password",
new Boolean(registration.isRememberPassword()));
return summaryTable.entrySet().iterator();
}
/**
* Installs the account created through this wizard.
* @return ProtocolProviderService
*/
public ProtocolProviderService finish() {
firstWizardPage = null;
ProtocolProviderFactory factory
= MsnAccRegWizzActivator.getMsnProtocolProviderFactory();
return this.installAccount(factory,
registration.getUin(), registration.getPassword());
}
/**
* Creates an account for the given user and password.
* @param providerFactory the ProtocolProviderFactory which will create
* the account
* @param user the user identifier
* @param passwd the password
* @return the <tt>ProtocolProviderService</tt> for the new account.
*/
public ProtocolProviderService installAccount(
ProtocolProviderFactory providerFactory,
String user,
String passwd) {
Hashtable accountProperties = new Hashtable();
if(registration.isRememberPassword()) {
accountProperties.put(ProtocolProviderFactory.PASSWORD, passwd);
}
if(protocolProvider != null) {
providerFactory.uninstallAccount(protocolProvider.getAccountID());
this.protocolProvider = null;
}
AccountID accountID = providerFactory.installAccount(
user, accountProperties);
ServiceReference serRef = providerFactory
.getProviderForAccount(accountID);
ProtocolProviderService protocolProvider
= (ProtocolProviderService) MsnAccRegWizzActivator.bundleContext
.getService(serRef);
return protocolProvider;
}
/**
* Fills the UIN and Password fields in this panel with the data comming
* from the given protocolProvider.
* @param protocolProvider The <tt>ProtocolProviderService</tt> to load the
* data from.
*/
public void loadAccount(ProtocolProviderService protocolProvider) {
this.protocolProvider = protocolProvider;
this.firstWizardPage.loadAccount(protocolProvider);
}
}

@ -0,0 +1,81 @@
/*
* 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.plugin.msnaccregwizz;
import java.io.*;
import java.util.*;
import net.java.sip.communicator.util.*;
/**
* The Messages class manages the access to the internationalization
* properties files.
* @author Yana Stamcheva
*/
public class Resources {
private static Logger log = Logger.getLogger(Resources.class);
private static final String BUNDLE_NAME
= "net.java.sip.communicator.plugin.msnaccregwizz.resources";
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
.getBundle(BUNDLE_NAME);
public static ImageID MSN_LOGO = new ImageID("protocolIcon");
/**
* Returns an internationalized string corresponding to the given key.
* @param key The key of the string.
* @return An internationalized string corresponding to the given key.
*/
public static String getString(String key) {
try {
return RESOURCE_BUNDLE.getString(key);
} catch (MissingResourceException e) {
return '!' + key + '!';
}
}
/**
* Loads an image from a given image identifier.
* @param imageID The identifier of the image.
* @return The image for the given identifier.
*/
public static byte[] getImage(ImageID imageID) {
byte[] image = new byte[100000];
String path = Resources.getString(imageID.getId());
try {
Resources.class.getClassLoader()
.getResourceAsStream(path).read(image);
} catch (IOException e) {
log.error("Failed to load image:" + path, e);
}
return image;
}
/**
* Represents the Image Identifier.
*/
public static class ImageID {
private String id;
private ImageID(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
}

@ -0,0 +1,48 @@
/*
* 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.plugin.msnaccregwizz;
import javax.swing.*;
import net.java.sip.communicator.service.gui.*;
public class SecondWizardPage extends JPanel
implements WizardPage {
public static final String SECOND_PAGE_IDENTIFIER = "SecondPageIdentifier";
public Object getIdentifier() {
return SECOND_PAGE_IDENTIFIER;
}
public Object getNextPageIdentifier() {
return FINISH_PAGE_IDENTIFIER;
}
public Object getBackPageIdentifier() {
return FirstWizardPage.FIRST_PAGE_IDENTIFIER;
}
public Object getWizardForm() {
return this;
}
public void pageHiding() {
}
public void pageShown() {
}
public void pageShowing() {
}
public void pageNext() {
}
public void pageBack() {
}
}

@ -0,0 +1,31 @@
Bundle-Activator: net.java.sip.communicator.plugin.msnaccregwizz.MsnAccRegWizzActivator
Bundle-Name: Msn account registration wizard
Bundle-Description: Msn account registration wizard.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: org.osgi.framework,
net.java.sip.communicator.util,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.configuration.event,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.contactlist.event,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.gui.event,
javax.swing,
javax.swing.event,
javax.swing.table,
javax.swing.text,
javax.swing.text.html,
javax.accessibility,
javax.swing.plaf,
javax.swing.plaf.metal,
javax.swing.plaf.basic,
javax.imageio,
javax.swing.filechooser,
javax.swing.tree,
javax.swing.undo,
javax.swing.event,
javax.swing.border

@ -0,0 +1,31 @@
Bundle-Activator: net.java.sip.communicator.plugin.msnaccregwizz.MsnAccRegWizzActivator
Bundle-Name: Msn account registration wizard
Bundle-Description: Msn account registration wizard.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: org.osgi.framework,
net.java.sip.communicator.util,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.configuration.event,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.contactlist.event,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.gui.event,
javax.swing,
javax.swing.event,
javax.swing.table,
javax.swing.text,
javax.swing.text.html,
javax.accessibility,
javax.swing.plaf,
javax.swing.plaf.metal,
javax.swing.plaf.basic,
javax.imageio,
javax.swing.filechooser,
javax.swing.tree,
javax.swing.undo,
javax.swing.event,
javax.swing.border

@ -0,0 +1,8 @@
protocolName=MSN
protocolDescription=The Msn service protocol
uin=Email:
password=Password:
rememberPassword=Remember password
uinAndPassword=ID and Password
protocolIcon=net/java/sip/communicator/plugin/msnaccregwizz/resources/msn.gif

@ -0,0 +1,8 @@
protocolName=Msn
protocolDescription=The Msn service protocol
uin=Email:
password=Password:
rememberPassword=Remember password
uinAndPassword=ID and Password
protocolIcon=net/java/sip/communicator/plugin/msnaccregwizz/resources/msn.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -1,10 +1,15 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol.jabberconstants;
import java.io.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.icqconstants.*;
import net.java.sip.communicator.util.*;
/**
@ -21,7 +26,7 @@
public class JabberStatusEnum
extends PresenceStatus
{
private static Logger logger = Logger.getLogger(IcqStatusEnum.class);
private static Logger logger = Logger.getLogger(JabberStatusEnum.class);
/**
* The Free For Chat status. Indicates that the user is eager to

@ -0,0 +1,148 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol.msnconstants;
import java.io.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* An enumeration containing all status instances that MUST be supported by
* an implementation of the msn protocol. Implementations may
* support other forms of PresenceStatus but they MUST ALL support those
* enumerated here.
* <p>
* For testing purposes, this class also provides a <tt>List</tt> containing
* all of the status fields.
*
* @author Damian Minkov
*/
public class MsnStatusEnum
extends PresenceStatus
{
private static Logger logger = Logger.getLogger(MsnStatusEnum.class);
/**
* The Online status. Indicate that the user is able and willing to
* communicate.
*/
public static final MsnStatusEnum ONLINE
= new MsnStatusEnum(65, "Online",
loadIcon("resources/images/msn/msn16x16-online.png"));
/**
* The Idle status. Indicates that the user is not using the messanger.
*/
public static final MsnStatusEnum IDLE
= new MsnStatusEnum(55, "Idle",
loadIcon("resources/images/msn/msn16x16-na.png"));
/**
* The Invisible status. Indicates that the user has connectivity even
* though it may appear otherwise to others, to whom she would appear to be
* offline.
*/
public static final MsnStatusEnum HIDE
= new MsnStatusEnum(45, "Hide",
loadIcon("resources/images/msn/msn16x16-invisible.png"));
/**
* The Away status. Indicates that the user has connectivity but might
* not be able to immediately act upon initiation of communication.
*/
public static final MsnStatusEnum AWAY
= new MsnStatusEnum(40, "Away",
loadIcon("resources/images/msn/msn16x16-away.png"));
/**
* The Out to lunch status. Indicates that the user is eating.
*/
public static final MsnStatusEnum OUT_TO_LUNCH
= new MsnStatusEnum(39, "Out to lunch",
loadIcon("resources/images/msn/msn16x16-lunch.png"));
/**
* The On the phone status. Indicates that the user is talking to the phone.
*/
public static final MsnStatusEnum ON_THE_PHONE
= new MsnStatusEnum(37, "On the phone",
loadIcon("resources/images/msn/msn16x16-phone.png"));
/**
* The Not Available status. Indicates that the user has connectivity
* but might not be able to immediately act (i.e. even less immediately than
* when in an Away status ;-P ) upon initiation of communication.
*
*/
public static final MsnStatusEnum BE_RIGHT_BACK
= new MsnStatusEnum(35, "Be Right Back",
loadIcon("resources/images/msn/msn16x16-brb.png"));
/**
* The DND status. Indicates that the user has connectivity but prefers
* not to be contacted.
*/
public static final MsnStatusEnum BUSY
= new MsnStatusEnum(30, "Busy",
loadIcon("resources/images/msn/msn16x16-busy.png"));
/**
* The Offline status. Indicates the user does not seem to be connected
* to the network or at least does not want us to know she is
*/
public static final MsnStatusEnum OFFLINE
= new MsnStatusEnum(0, "Offline",
loadIcon("resources/images/msn/msn16x16-offline.png"));
/**
* The minimal set of states that any implementation must support.
*/
public static final ArrayList msnStatusSet =new ArrayList();
static{
msnStatusSet.add(OUT_TO_LUNCH);
msnStatusSet.add(ON_THE_PHONE);
msnStatusSet.add(ONLINE);
msnStatusSet.add(OFFLINE);
msnStatusSet.add(IDLE);
msnStatusSet.add(HIDE);
msnStatusSet.add(BUSY);
msnStatusSet.add(BE_RIGHT_BACK);
msnStatusSet.add(AWAY);
}
/**
* Creates a status with the specified connectivity coeff, name and icon.
* @param status the connectivity coefficient for the specified status
* @param statusName String
* @param statusIcon the icon associated with this status
*/
protected MsnStatusEnum(int status, String statusName, byte[] statusIcon)
{
super(status, statusName, statusIcon);
}
/**
* Loads an image from a given image path.
* @param imagePath The identifier of the image.
* @return The image for the given identifier.
*/
public static byte[] loadIcon(String imagePath) {
InputStream is = MsnStatusEnum.class.getClassLoader()
.getResourceAsStream(imagePath);
logger.info("loadIcon " + imagePath);
byte[] icon = null;
try {
icon = new byte[is.available()];
is.read(icon);
} catch (IOException exc) {
logger.error("Failed to load icon: " + imagePath, exc);
}
return icon;
}
}

@ -9,4 +9,5 @@ Import-Package: org.osgi.framework,
Export-Package: net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.jabberconstants,
net.java.sip.communicator.service.protocol.msnconstants,
net.java.sip.communicator.service.protocol.event

@ -0,0 +1,106 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.util.*;
import org.osgi.framework.*;
import junit.framework.*;
/**
* Msn specific testing for a Msn Protocol Provider Service implementation.
* The test suite registers two accounts for
*
* @author Damian Minkov
*/
public class MsnProtocolProviderServiceLick
extends TestSuite
implements BundleActivator
{
/**
* The prefix used for property names containing settings for our first
* testing account.
*/
public static final String ACCOUNT_1_PREFIX
= "accounts.msn.account1.";
/**
* The prefix used for property names containing settings for our second
* testing account.
*/
public static final String ACCOUNT_2_PREFIX
= "accounts.msn.account2.";
/**
* The name of the property that indicates whether the user would like to
* only run the offline tests.
*/
public static final String DISABLE_ONLINE_TESTS_PROPERTY_NAME
= "accounts.msn.DISABLE_ONLINE_TESTING";
/**
* The name of the property the value of which is a formatted string that
* contains the contact list that.
*/
public static final String CONTACT_LIST_PROPERTY_NAME
= "accounts.msn.CONTACT_LIST";
/**
* Initializes and registers all tests that we'll run as a part of this
* slick.
*
* @param context a currently valid bundle context.
*/
public void start(BundleContext context)
{
setName("MsnProtocolProviderSlick");
Hashtable properties = new Hashtable();
properties.put("service.pid", getName());
MsnSlickFixture.bc = context;
// verify whether the user wants to avoid online testing
String offlineMode = System.getProperty(
DISABLE_ONLINE_TESTS_PROPERTY_NAME, null);
if (offlineMode != null && offlineMode.equalsIgnoreCase("true"))
MsnSlickFixture.onlineTestingDisabled = true;
addTestSuite(TestAccountInstallation.class);
addTestSuite(TestProtocolProviderServiceMsnImpl.class);
addTest(TestOperationSetPresence.suite());
//the following should only be run when we want online testing.
if(!MsnSlickFixture.onlineTestingDisabled)
{
// addTest(TestOperationSetPersistentPresence.suite());
//
// addTest(TestOperationSetBasicInstantMessaging.suite());
//
// addTest(TestOperationSetTypingNotifications.suite());
}
//
//
addTest(TestAccountUninstallation.suite());
addTestSuite(TestAccountUninstallationPersistence.class);
context.registerService(getClass().getName(), this, properties);
}
/**
* Prepares the slick for shutdown.
*
* @param context a currently valid bundle context.
*/
public void stop(BundleContext context)
{
}
}

@ -0,0 +1,320 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import org.osgi.framework.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import java.util.Map;
import java.util.*;
/**
* Contains fields and methods used by most or all tests in the msn slick.
*
* @author Damian Minkov
*/
public class MsnSlickFixture
extends TestCase
{
/**
* To be set by the slick itself upon activation.
*/
public static BundleContext bc = null;
/**
* An osgi service reference for the protocol provider corresponding to our
* first testing account.
*/
public ServiceReference provider1ServiceRef = null;
/**
* The protocol provider corresponding to our first testing account.
*/
public ProtocolProviderService provider1 = null;
/**
* The user ID associated with testing account 1.
*/
public String userID1 = null;
/**
* An osgi service reference for the protocol provider corresponding to our
* second testing account.
*/
public ServiceReference provider2ServiceRef = null;
/**
* The protocol provider corresponding to our first testing account.
*/
public ProtocolProviderService provider2 = null;
/**
* The user ID associated with testing account 2.
*/
public String userID2 = null;
/**
* The tested protocol provider factory.
*/
public ProtocolProviderFactory providerFactory = null;
/**
* A reference to the bundle containing the tested pp implementation. This
* reference is set during the accoung uninstallation testing and used during
* the account uninstallation persistence testing.
*/
public static Bundle providerBundle = null;
/**
* Indicates whether the user has requested for onlline tests not to be run.
* (e.g. due to lack of network connectivity or ... time constraints ;)).
*/
public static boolean onlineTestingDisabled = false;
/**
* A Hashtable containing group names mapped against array lists of buddy
* screen names. This is a snapshot of the server stored buddy list for
* the account that is going to be used by the tested implementation.
* It is filled in by the tester agent who'd login with that account
* and initialise the ss contact list before the tested implementation has
* actually done so.
*/
public static Hashtable preInstalledBuddyList = null;
/**
* Initializes protocol provider references and whatever else there is to
* initialize.
*
* @throws java.lang.Exception in case we meet problems while retriving
* protocol providers through OSGI
*/
public void setUp()
throws Exception
{
// first obtain a reference to the provider factory
ServiceReference[] serRefs = null;
String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL
+ "="+ProtocolNames.MSN+")";
try{
serRefs = bc.getServiceReferences(
ProtocolProviderFactory.class.getName(), osgiFilter);
}
catch (InvalidSyntaxException ex){
//this really shouldhn't occur as the filter expression is static.
fail(osgiFilter + " is not a valid osgi filter");
}
assertTrue(
"Failed to find a provider factory service for protocol msn",
serRefs != null || serRefs.length > 0);
//Keep the reference for later usage.
providerFactory = (ProtocolProviderFactory)bc.getService(serRefs[0]);
userID1 =
System.getProperty(
MsnProtocolProviderServiceLick.ACCOUNT_1_PREFIX
+ ProtocolProviderFactory.USER_ID);
userID2 =
System.getProperty(
MsnProtocolProviderServiceLick.ACCOUNT_2_PREFIX
+ ProtocolProviderFactory.USER_ID);
//find the protocol providers exported for the two accounts
ServiceReference[] msnProvider1Refs
= bc.getServiceReferences(
ProtocolProviderService.class.getName(),
"(&"
+"("+ProtocolProviderFactory.PROTOCOL+"="+ProtocolNames.MSN+")"
+"("+ProtocolProviderFactory.USER_ID+"="
+ userID1 +")"
+")");
//make sure we found a service
assertNotNull("No Protocol Provider was found for msn account1:"
+ userID1
, msnProvider1Refs);
assertTrue("No Protocol Provider was found for msn account1:"+ userID1,
msnProvider1Refs.length > 0);
ServiceReference[] msnProvider2Refs
= bc.getServiceReferences(
ProtocolProviderService.class.getName(),
"(&"
+"("+ProtocolProviderFactory.PROTOCOL+"="+ProtocolNames.MSN+")"
+"("+ProtocolProviderFactory.USER_ID+"="
+ userID2 +")"
+")");
//again make sure we found a service.
assertNotNull("No Protocol Provider was found for msn account2:"
+ userID2
, msnProvider2Refs);
assertTrue("No Protocol Provider was found for msn account2:"+ userID2,
msnProvider2Refs.length > 0);
//save the service for other tests to use.
provider1ServiceRef = msnProvider1Refs[0];
provider1 = (ProtocolProviderService)bc.getService(provider1ServiceRef);
provider2ServiceRef = msnProvider2Refs[0];
provider2 = (ProtocolProviderService)bc.getService(provider2ServiceRef);
}
/**
* Un get service references used in here.
*/
public void tearDown()
{
bc.ungetService(provider1ServiceRef);
bc.ungetService(provider2ServiceRef);
}
/**
* Returns the bundle that has registered the protocol provider service
* implementation that we're currently testing. The method would go through
* all bundles currently installed in the framework and return the first
* one that exports the same protocol provider instance as the one we test
* in this slick.
* @param provider the provider whose bundle we're looking for.
* @return the Bundle that has registered the protocol provider service
* we're testing in the slick.
*/
public static Bundle findProtocolProviderBundle(
ProtocolProviderService provider)
{
Bundle[] bundles = bc.getBundles();
for (int i = 0; i < bundles.length; i++)
{
ServiceReference[] registeredServices
= bundles[i].getRegisteredServices();
if (registeredServices == null)
continue;
for (int j = 0; j < registeredServices.length; j++)
{
Object service
= bc.getService(registeredServices[j]);
if (service == provider)
return bundles[i];
}
}
return null;
}
public void clearProvidersLists()
throws Exception
{
Map supportedOperationSets1 = provider1.getSupportedOperationSets();
if ( supportedOperationSets1 == null
|| supportedOperationSets1.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this msn implementation. ");
//get the operation set presence here.
OperationSetPersistentPresence opSetPersPresence1 =
(OperationSetPersistentPresence)supportedOperationSets1.get(
OperationSetPersistentPresence.class.getName());
//if still null then the implementation doesn't offer a presence
//operation set which is unacceptable for msn.
if (opSetPersPresence1 == null)
throw new NullPointerException(
"An implementation of the msn service must provide an "
+ "implementation of at least the one of the Presence "
+ "Operation Sets");
// lets do it once again for the second provider
Map supportedOperationSets2 = provider2.getSupportedOperationSets();
if (supportedOperationSets2 == null
|| supportedOperationSets2.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+ "this msn implementation. ");
//get the operation set presence here.
OperationSetPersistentPresence opSetPersPresence2 =
(OperationSetPersistentPresence) supportedOperationSets2.get(
OperationSetPersistentPresence.class.getName());
//if still null then the implementation doesn't offer a presence
//operation set which is unacceptable for msn.
if (opSetPersPresence2 == null)
throw new NullPointerException(
"An implementation of the msn service must provide an "
+ "implementation of at least the one of the Presence "
+ "Operation Sets");
ContactGroup rootGroup1 = opSetPersPresence1.getServerStoredContactListRoot();
// first delete the groups
Vector groupsToRemove = new Vector();
Iterator iter = rootGroup1.subgroups();
while (iter.hasNext())
{
groupsToRemove.add(iter.next());
}
iter = groupsToRemove.iterator();
while (iter.hasNext())
{
ContactGroup item = (ContactGroup) iter.next();
opSetPersPresence1.removeServerStoredContactGroup(item);
}
//then delete contacts if any in root list
Vector contactsToRemove = new Vector();
iter = rootGroup1.contacts();
while (iter.hasNext())
{
contactsToRemove.add(iter.next());
}
iter = contactsToRemove.iterator();
while (iter.hasNext())
{
opSetPersPresence1.unsubscribe((Contact)iter.next());
}
ContactGroup rootGroup2 = opSetPersPresence2.getServerStoredContactListRoot();
// delete groups
groupsToRemove = new Vector();
iter = rootGroup2.subgroups();
while (iter.hasNext())
{
groupsToRemove.add(iter.next());
}
iter = groupsToRemove.iterator();
while (iter.hasNext())
{
ContactGroup item = (ContactGroup) iter.next();
opSetPersPresence2.removeServerStoredContactGroup(item);
}
//then delete contacts if any in root list
contactsToRemove = new Vector();
iter = rootGroup2.contacts();
while (iter.hasNext())
{
contactsToRemove.add(iter.next());
}
iter = contactsToRemove.iterator();
while (iter.hasNext())
{
opSetPersPresence2.unsubscribe( (Contact) iter.next());
}
}
}

@ -0,0 +1,217 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.util.*;
import org.osgi.framework.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
public class TestAccountInstallation
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestAccountInstallation.class);
/**
* Creates the test with the specified method name.
* @param name the name of the method to execute.
*/
public TestAccountInstallation(String name)
{
super(name);
}
/**
* JUnit setup method.
* @throws Exception in case anything goes wrong.
*/
protected void setUp() throws Exception
{
super.setUp();
}
/**
* JUnit teardown method.
* @throws Exception in case anything goes wrong.
*/
protected void tearDown() throws Exception
{
super.tearDown();
}
/**
* Installs an account and verifies whether the installation has gone well.
*/
public void testInstallAccount()
{
// first obtain a reference to the provider factory
ServiceReference[] serRefs = null;
String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL
+ "="+ProtocolNames.MSN+")";
try{
serRefs = MsnSlickFixture.bc.getServiceReferences(
ProtocolProviderFactory.class.getName(), osgiFilter);
}
catch (InvalidSyntaxException ex)
{
//this really shouldhn't occur as the filter expression is static.
fail(osgiFilter + " is not a valid osgi filter");
}
assertTrue(
"Failed to find a provider factory service for protocol Msn",
serRefs != null && serRefs.length > 0);
//Keep the reference for later usage.
ProtocolProviderFactory msnProviderFactory = (ProtocolProviderFactory)
MsnSlickFixture.bc.getService(serRefs[0]);
//make sure the account is empty
assertTrue("There was an account registered with the account mananger "
+"before we've installed any",
msnProviderFactory.getRegisteredAccounts().size() == 0);
//Prepare the properties of the first msn account.
Hashtable msnAccount1Properties = getAccountProperties(
MsnProtocolProviderServiceLick.ACCOUNT_1_PREFIX);
Hashtable msnAccount2Properties = getAccountProperties(
MsnProtocolProviderServiceLick.ACCOUNT_2_PREFIX);
//try to install an account with a null account id
try{
msnProviderFactory.installAccount(
null, msnAccount1Properties);
fail("installing an account with a null account id must result "
+"in a NullPointerException");
}catch(NullPointerException exc)
{
//that's what had to happen
}
//now really install the accounts
msnProviderFactory.installAccount(
(String)msnAccount1Properties.get(ProtocolProviderFactory.USER_ID)
, msnAccount1Properties);
msnProviderFactory.installAccount(
(String)msnAccount2Properties.get(ProtocolProviderFactory.USER_ID)
, msnAccount2Properties);
//try to install one of the accounts one more time and verify that an
//excepion is thrown.
try{
msnProviderFactory.installAccount(
(String)msnAccount1Properties.get(ProtocolProviderFactory.USER_ID)
, msnAccount1Properties);
fail("An IllegalStateException must be thrown when trying to "+
"install a duplicate account");
}catch(IllegalStateException exc)
{
//that's what supposed to happen.
}
//Verify that the provider factory is aware of our installation
assertTrue(
"The newly installed account was not in the acc man's "
+"registered accounts!",
msnProviderFactory.getRegisteredAccounts().size() == 2);
//Verify protocol providers corresponding to the new account have
//been properly registered with the osgi framework.
osgiFilter =
"(&("+ProtocolProviderFactory.PROTOCOL +"="+ProtocolNames.MSN+")"
+"(" + ProtocolProviderFactory.USER_ID
+ "=" + (String)msnAccount1Properties.get(
ProtocolProviderFactory.USER_ID)
+ "))";
try
{
serRefs = MsnSlickFixture.bc.getServiceReferences(
ProtocolProviderService.class.getName(),
osgiFilter);
}
catch (InvalidSyntaxException ex)
{
//this really shouldhn't occur as the filter expression is static.
fail(osgiFilter + "is not a valid osgi filter");
}
assertTrue("An protocol provider was apparently not installed as "
+ "requested."
, serRefs != null && serRefs.length > 0);
Object msnProtocolProvider
= MsnSlickFixture.bc.getService(serRefs[0]);
assertTrue("The installed protocol provider does not implement "
+ "the protocol provider service."
,msnProtocolProvider instanceof ProtocolProviderService);
}
/**
* Returns all properties necessary for the intialization of the account
* with <tt>accountPrefix</tt>.
* @param accountPrefix the prefix contained by all property names for the
* the account we'd like to initialized
* @return a Hashtable that can be used when creating the account in a
* protocol provider factory.
*/
private Hashtable getAccountProperties(String accountPrefix)
{
Hashtable table = new Hashtable();
String userID = System.getProperty(
accountPrefix + ProtocolProviderFactory.USER_ID, null);
assertNotNull(
"The system property named "
+ accountPrefix + ProtocolProviderFactory.USER_ID
+" has to tontain a valid msn address that could be used during "
+"SIP Communicator's tests."
, userID);
table.put(ProtocolProviderFactory.USER_ID, userID);
String passwd = System.getProperty(
accountPrefix + ProtocolProviderFactory.PASSWORD, null );
assertNotNull(
"The system property named "
+ accountPrefix + ProtocolProviderFactory.PASSWORD
+" has to contain the password corresponding to the account "
+ "specified in "
+ accountPrefix + ProtocolProviderFactory.USER_ID
, passwd);
table.put(ProtocolProviderFactory.PASSWORD, passwd);
String serverAddress = System.getProperty(
accountPrefix + ProtocolProviderFactory.SERVER_ADDRESS, null);
// optional
if(serverAddress != null)
table.put(ProtocolProviderFactory.SERVER_ADDRESS, serverAddress);
String serverPort = System.getProperty(
accountPrefix + ProtocolProviderFactory.SERVER_PORT, null);
// optional
if(serverPort != null)
table.put(ProtocolProviderFactory.SERVER_PORT, serverPort);
return table;
}
}

@ -0,0 +1,270 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import org.osgi.framework.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* Tests whether accaounts are uninstalled properly. It is important that
* tests from this class be called last since they will install the accounts
* that have been used to test the implementations. Apart from uninstallation
* tests the class also contains tests that remove and reinstall the protocol
* provider bundle in order to verify that accounts are persistent.
*
* @author Emil Ivov
*/
public class TestAccountUninstallation
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestAccountUninstallation.class);
private MsnSlickFixture fixture = new MsnSlickFixture();
/**
* Constructs a test instance
* @param name The name of the test.
*/
public TestAccountUninstallation(String name)
{
super(name);
}
/**
* JUnit setup method.
* @throws Exception in case anything goes wrong.
*/
protected void setUp() throws Exception
{
super.setUp();
fixture.setUp();
}
/**
* JUnit teardown method.
* @throws Exception in case anything goes wrong.
*/
protected void tearDown() throws Exception
{
fixture.tearDown();
super.tearDown();
}
/**
* Returns a suite containing tests in this class in the order that we'd
* like them executed.
* @return a Test suite containing tests in this class in the order that
* we'd like them executed.
*/
public static Test suite()
{
TestSuite suite = new TestSuite();
suite.addTest(
new TestAccountUninstallation("testInstallationPersistency"));
suite.addTest(
new TestAccountUninstallation("testUninstallAccount"));
return suite;
}
/**
* Stops and removes the tested bundle, verifies that it has unregistered
* its provider, then reloads and restarts the bundle and verifies that
* the protocol provider is reRegistered in the bundle context.
*
* @throws java.lang.Exception if an exception occurs during testing.
*/
public void testInstallationPersistency() throws Exception
{
Bundle providerBundle
= fixture.findProtocolProviderBundle(fixture.provider1);
//set the global providerBundle reference that we will be using
//in the last series of tests (Account uninstallation persistency)
MsnSlickFixture.providerBundle = providerBundle;
assertNotNull("Couldn't find a bundle for the tested provider"
, providerBundle);
providerBundle.stop();
assertTrue("Couldn't stop the protocol provider bundle. State was "
+ providerBundle.getState()
, Bundle.ACTIVE != providerBundle.getState()
&& Bundle.STOPPING != providerBundle.getState());
providerBundle.uninstall();
assertEquals("Couldn't stop the protocol provider bundle."
, Bundle.UNINSTALLED, providerBundle.getState());
//verify that the provider is no longer available
ServiceReference[] msnProviderRefs = null;
try
{
msnProviderRefs = fixture.bc.getServiceReferences(
ProtocolProviderService.class.getName(),
"(&"
+ "(" + ProtocolProviderFactory.PROTOCOL
+ "=" +ProtocolNames.MSN + ")"
+ "(" + ProtocolProviderFactory.USER_ID
+ "="+ fixture.userID1 + ")"
+ ")");
}
catch (InvalidSyntaxException ex)
{
fail("We apparently got our filter wrong: " + ex.getMessage());
}
//make sure we didn't see a service
assertTrue("A Protocol Provider Service was still regged as an osgi service "
+"for msn URI:" + fixture.userID1
+ "After it was explicitly uninstalled"
,msnProviderRefs == null || msnProviderRefs.length == 0);
//verify that the provider factory knows that we have uninstalled the
//provider.
assertTrue(
"The msn provider factory kept a reference to the provider we just "
+"uninstalled (uri="+fixture.userID1+")",
fixture.providerFactory.getRegisteredAccounts().isEmpty()
&& fixture.providerFactory.getProviderForAccount(
fixture.provider1.getAccountID())
== null);
//Now reinstall the bundle
providerBundle = fixture.bc.installBundle(providerBundle.getLocation());
//set the global providerBundle reference that we will be using
//in the last series of tests (Account uninstallation persistency)
MsnSlickFixture.providerBundle = providerBundle;
assertEquals("Couldn't re-install protocol provider bundle."
, Bundle.INSTALLED, providerBundle.getState());
providerBundle.start();
assertEquals("Couldn't re-start protocol provider bundle."
, Bundle.ACTIVE, providerBundle.getState());
//Make sure that the provider is there again.
//verify that the provider is no longer available
try
{
msnProviderRefs = fixture.bc.getServiceReferences(
ProtocolProviderService.class.getName(),
"(&"
+ "(" + ProtocolProviderFactory.PROTOCOL
+ "=" +ProtocolNames.MSN + ")"
+ "(" + ProtocolProviderFactory.USER_ID
+ "="+ fixture.userID1 + ")"
+ ")");
}
catch (InvalidSyntaxException ex)
{
fail("We apparently got our filter wrong " + ex.getMessage());
}
//make sure we didn't see a service
assertTrue("A Protocol Provider Service was not restored after being"
+"reinstalled. msn URI:" + fixture.userID1
,msnProviderRefs != null && msnProviderRefs.length > 0);
ServiceReference[] msnFactoryRefs = null;
try
{
msnFactoryRefs = fixture.bc.getServiceReferences(
ProtocolProviderFactory.class.getName(),
"(" + ProtocolProviderFactory.PROTOCOL
+ "=" +ProtocolNames.MSN + ")");
}
catch (InvalidSyntaxException ex)
{
fail("We apparently got our filter wrong " + ex.getMessage());
}
//we're the ones who've reinstalled the factory so it's our
//responsibility to update the fixture.
fixture.providerFactory
= (ProtocolProviderFactory)fixture.bc.getService(msnFactoryRefs[0]);
fixture.provider1
= (ProtocolProviderService)fixture.bc.getService(msnProviderRefs[0]);
//verify that the provider is also restored in the provider factory
//itself
assertTrue(
"The msn provider did not restore its own reference to the provider "
+"that we just reinstalled (URI="+fixture.userID1+")",
!fixture.providerFactory.getRegisteredAccounts().isEmpty()
&& fixture.providerFactory.getProviderForAccount(
fixture.provider1.getAccountID())
!= null);
}
/**
* Uinstalls our test account and makes sure it really has been removed.
*
*/
public void testUninstallAccount()
{
assertFalse("No installed accounts found",
fixture.providerFactory.getRegisteredAccounts().isEmpty());
assertNotNull(
"Found no provider corresponding to URI " + fixture.userID1
,fixture.providerFactory.getProviderForAccount(
fixture.provider1.getAccountID()));
assertTrue(
"Failed to remove a provider corresponding to URI "
+ fixture.userID1
,fixture.providerFactory.uninstallAccount(
fixture.provider1.getAccountID()));
assertTrue(
"Failed to remove a provider corresponding to URI "
+ fixture.userID1
,fixture.providerFactory.uninstallAccount(
fixture.provider2.getAccountID()));
//make sure no providers have remained installed.
ServiceReference[] msnProviderRefs = null;
try
{
msnProviderRefs = fixture.bc.getServiceReferences(
ProtocolProviderService.class.getName(),
"(" + ProtocolProviderFactory.PROTOCOL
+ "=" +ProtocolNames.MSN + ")");
}
catch (InvalidSyntaxException ex)
{
fail("We apparently got our filter wrong " + ex.getMessage());
}
//make sure we didn't see a service
assertTrue("A Protocol Provider Service was still regged as an osgi "
+ "service for msn URI:" + fixture.userID1
+ "After it was explicitly uninstalled"
,msnProviderRefs == null || msnProviderRefs.length == 0);
//verify that the provider factory knows that we have uninstalled the
//provider.
assertTrue(
"The msn provider factory kept a reference to the provider we just "
+"uninstalled (uri="+fixture.userID1+")",
fixture.providerFactory.getRegisteredAccounts().isEmpty()
&& fixture.providerFactory.getProviderForAccount(
fixture.provider1.getAccountID())
== null);
}
}

@ -0,0 +1,100 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import org.osgi.framework.*;
import junit.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Contains tests verifying persistence of account uninstallation. In other
* words we try to make sure that once uninstalled an account remains
* uninstalled.
*
* @author Emil Ivov
*/
public class TestAccountUninstallationPersistence
extends TestCase
{
/**
* Creates a new test instance wrapper around the test with the specified
* name.
* @param testName the name of the test that we will be executing.
*/
public TestAccountUninstallationPersistence(String testName)
{
super(testName);
}
/**
* Retrieves a reference to the msn bundle, stops it and uninstalls it and
* then reinstalls it in order to make sure that accounts are not reloaded
* once removed.
*
* @throws java.lang.Exception if something goes wrong while manipulating
* the bundles.
*/
public void testAccountUninstallationPersistence()
throws Exception
{
Bundle providerBundle = MsnSlickFixture.providerBundle;
providerBundle.stop();
assertTrue("Couldn't stop the protocol provider bundle. State was "
+ providerBundle.getState()
, Bundle.ACTIVE != providerBundle.getState()
&& Bundle.STOPPING != providerBundle.getState());
providerBundle.uninstall();
assertEquals("Couldn't stop the protocol provider bundle."
, Bundle.UNINSTALLED, providerBundle.getState());
//Now reinstall the bundle and restart the provider
providerBundle
= MsnSlickFixture.bc.installBundle(providerBundle.getLocation());
assertEquals("Couldn't re-install protocol provider bundle."
, Bundle.INSTALLED, providerBundle.getState());
providerBundle.start();
assertEquals("Couldn't re-start protocol provider bundle."
, Bundle.ACTIVE, providerBundle.getState());
//verify that the provider is not reinstalled
ServiceReference[] msnProviderRefs = null;
try
{
msnProviderRefs = MsnSlickFixture.bc.getServiceReferences(
ProtocolProviderService.class.getName(),
"(" + ProtocolProviderFactory.PROTOCOL
+ "=" +ProtocolNames.MSN + ")");
}
catch (InvalidSyntaxException ex)
{
fail("We apparently got our filter wrong " + ex.getMessage());
}
//make sure we didn't retrieve a service
assertTrue("A msn Protocol Provider Service was still regged as an "
+"osgi service after it was explicitly uninstalled"
,msnProviderRefs == null || msnProviderRefs.length == 0);
//and a nasty hack at the end - delete the configuration file so that
//we get a fresh start on next run.
ServiceReference confReference
= MsnSlickFixture.bc.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) MsnSlickFixture.bc.getService(confReference);
configurationService.purgeStoredConfiguration();
}
}

@ -0,0 +1,508 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.net.*;
import java.util.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.Message;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Performs testing of the basic instant messaging operation set. Tests include
* going over basic functionality such as sending a message from the tested
* implementation and asserting reception by the tester agent and vice versa.
* @author Emil Ivov
*/
public class TestOperationSetBasicInstantMessaging
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestOperationSetBasicInstantMessaging.class);
private MsnSlickFixture fixture = new MsnSlickFixture();
private OperationSetBasicInstantMessaging opSetBasicIM1 = null;
private OperationSetBasicInstantMessaging opSetBasicIM2 = null;
private OperationSetPresence opSetPresence1 = null;
private OperationSetPresence opSetPresence2 = null;
public TestOperationSetBasicInstantMessaging(String name)
{
super(name);
}
/**
* Get a reference to the basic IM operation set.
* @throws Exception if this is not a good day.
*/
protected void setUp() throws Exception
{
super.setUp();
fixture.setUp();
Map supportedOperationSets1 =
fixture.provider1.getSupportedOperationSets();
if ( supportedOperationSets1 == null
|| supportedOperationSets1.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this implementation. ");
//get the operation set presence here.
opSetBasicIM1 =
(OperationSetBasicInstantMessaging)supportedOperationSets1.get(
OperationSetBasicInstantMessaging.class.getName());
if (opSetBasicIM1 == null)
{
throw new NullPointerException(
"No implementation for basic IM was found");
}
//we also need the presence op set in order to retrieve contacts.
opSetPresence1 =
(OperationSetPresence)supportedOperationSets1.get(
OperationSetPresence.class.getName());
//if the op set is null show that we're not happy.
if (opSetPresence1 == null)
{
throw new NullPointerException(
"An implementation of the service must provide an "
+ "implementation of at least one of the PresenceOperationSets");
}
Map supportedOperationSets2 =
fixture.provider2.getSupportedOperationSets();
if ( supportedOperationSets2 == null
|| supportedOperationSets2.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this implementation. ");
//get the operation set presence here.
opSetBasicIM2 =
(OperationSetBasicInstantMessaging)supportedOperationSets2.get(
OperationSetBasicInstantMessaging.class.getName());
if (opSetBasicIM2 == null)
{
throw new NullPointerException(
"No implementation for basic IM was found");
}
opSetPresence2 =
(OperationSetPresence) supportedOperationSets2.get(
OperationSetPresence.class.getName());
//if the op set is null show that we're not happy.
if (opSetPresence2 == null)
{
throw new NullPointerException(
"An implementation of the service must provide an "
+ "implementation of at least one of the PresenceOperationSets");
}
}
protected void tearDown() throws Exception
{
super.tearDown();
fixture.tearDown();
}
/**
* Creates a test suite containing tests of this class in a specific order.
* We'll first execute tests beginning with the "test" prefix and then go to
* ordered tests.We first execture tests for receiving messagese, so that
* a volatile contact is created for the sender. we'll then be able to
* retrieve this volatile contact and send them a message on our turn.
* We need to do things this way as the contact corresponding to the tester
* agent has been removed in the previous test and we no longer have it
* in our contact list.
*
* @return Test a testsuite containing all tests to execute.
*/
public static Test suite()
{
TestSuite suite = new TestSuite();
suite.addTest(new TestOperationSetBasicInstantMessaging(
"prepareContactList"));
suite.addTestSuite(TestOperationSetBasicInstantMessaging.class);
//the following 2 need to be run in the specified order.
suite.addTest(new TestOperationSetBasicInstantMessaging(
"firstTestReceiveMessage"));
suite.addTest(new TestOperationSetBasicInstantMessaging(
"thenTestSendMessage"));
return suite;
}
/**
* Create the list to be sure that contacts exchanging messages
* exists in each other lists
* @throws Exception
*/
public void prepareContactList()
throws Exception
{
fixture.clearProvidersLists();
Object o = new Object();
synchronized(o)
{
o.wait(2000);
}
try
{
opSetPresence1.subscribe(fixture.userID2);
}
catch (OperationFailedException ex)
{
// the contact already exist its OK
}
try
{
opSetPresence2.subscribe(fixture.userID1);
}
catch (OperationFailedException ex1)
{
// the contact already exist its OK
}
synchronized(o)
{
o.wait(2000);
}
}
/**
* Send an instant message from the tested operation set and assert
* reception by the tester agent.
*/
public void firstTestReceiveMessage()
{
String body = "This is an IM coming from the tester agent"
+ " on " + new Date().toString();
ImEventCollector evtCollector = new ImEventCollector();
//add a msg listener and register to the op set and send an instant
//msg from the tester agent.
opSetBasicIM1.addMessageListener(evtCollector);
Contact testerAgentContact
= opSetPresence2.findContactByID(fixture.userID1);
logger.debug("Will send message " + body + " to: " + testerAgentContact);
opSetBasicIM2.sendInstantMessage(testerAgentContact,
opSetBasicIM2.createMessage(body));
evtCollector.waitForEvent(10000);
opSetBasicIM1.removeMessageListener(evtCollector);
//assert reception of a message event
assertTrue( "No events delivered upon a received message"
, evtCollector.collectedEvents.size() > 0);
//assert event instance of Message Received Evt
assertTrue( "Received evt was not an instance of "
+ MessageReceivedEvent.class.getName()
, evtCollector.collectedEvents.get(0)
instanceof MessageReceivedEvent);
//assert source contact == testAgent.uin
MessageReceivedEvent evt
= (MessageReceivedEvent)evtCollector.collectedEvents.get(0);
assertEquals("message sender "
, evt.getSourceContact().getAddress()
, fixture.userID2);
//assert messageBody == body
assertEquals("message body", body, evt.getSourceMessage().getContent());
}
/**
* Send an instant message from the tester agent and assert reception by
* the tested implementation
*/
public void thenTestSendMessage()
{
String body = "This is an IM coming from the tested implementation"
+ " on " + new Date().toString();
//create the message
net.java.sip.communicator.service.protocol.Message msg
= opSetBasicIM1.createMessage(body);
//register a listener in the op set
ImEventCollector imEvtCollector1 = new ImEventCollector();
opSetBasicIM1.addMessageListener(imEvtCollector1);
//register a listener in the tester agent
ImEventCollector imEvtCollector2 = new ImEventCollector();
opSetBasicIM2.addMessageListener(imEvtCollector2);
Contact testerAgentContact
= opSetPresence1.findContactByID(fixture.userID2);
opSetBasicIM1.sendInstantMessage(testerAgentContact, msg);
imEvtCollector1.waitForEvent(10000);
imEvtCollector2.waitForEvent(10000);
opSetBasicIM1.removeMessageListener(imEvtCollector1);
opSetBasicIM2.removeMessageListener(imEvtCollector2);
//verify that the message delivered event was dispatched
assertTrue( "No events delivered when sending a message"
, imEvtCollector1.collectedEvents.size() > 0);
assertTrue( "Received evt was not an instance of "
+ MessageDeliveredEvent.class.getName()
, imEvtCollector1.collectedEvents.get(0)
instanceof MessageDeliveredEvent);
MessageDeliveredEvent evt
= (MessageDeliveredEvent)imEvtCollector1.collectedEvents.get(0);
assertEquals("message destination "
, evt.getDestinationContact().getAddress()
, fixture.userID2);
assertSame("source message", msg, evt.getSourceMessage());
//verify that the message has successfully arived at the destination
assertTrue( "No messages received by the tester agent"
, imEvtCollector2.collectedEvents.size() > 0);
String receivedBody =
((MessageReceivedEvent)imEvtCollector2.collectedEvents
.get(0)).getSourceMessage().getContent();
assertEquals("received message body", msg.getContent(), receivedBody);
}
/**
* Creates an Message through the simple createMessage() method and inspects
* its parameters.
*/
public void testCreateMessage1()
{
String body = "This is an IM coming from the tested implementation"
+ " on " + new Date().toString();
net.java.sip.communicator.service.protocol.Message msg
= opSetBasicIM1.createMessage(body);
assertEquals("message body", body, msg.getContent());
assertTrue("message body bytes"
, Arrays.equals(body.getBytes(), msg.getRawData()));
assertEquals("message length", body.length(), msg.getSize());
assertEquals("message content type"
, OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE
, msg.getContentType());
assertEquals("message encoding"
, OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING
, msg.getEncoding());
assertNotNull("message uid", msg.getMessageUID());
//a further test on message uid.
net.java.sip.communicator.service.protocol.Message msg2
= opSetBasicIM1.createMessage(body);
assertFalse("message uid", msg.getMessageUID().equals(
msg2.getMessageUID()));
}
/**
* Creates an Message through the advance createMessage() method and
* inspects its parameters.
*/
public void testCreateMessage2()
{
String body = "This is an IM coming from the tested implementation"
+ " on " + new Date().toString();
String contentType = "text/html";
String encoding = "UTF-16";
String subject = "test message";
net.java.sip.communicator.service.protocol.Message msg
= opSetBasicIM1.createMessage(
body.getBytes(), contentType, encoding, subject);
assertEquals("message body", body, msg.getContent());
assertTrue("message body bytes"
, Arrays.equals(body.getBytes(), msg.getRawData()));
assertEquals("message length", body.length(), msg.getSize());
assertEquals("message content type", contentType, msg.getContentType());
assertEquals("message encoding", encoding, msg.getEncoding());
assertNotNull("message uid", msg.getMessageUID());
//a further test on message uid.
net.java.sip.communicator.service.protocol.Message msg2
= opSetBasicIM1.createMessage(body);
assertFalse("message uid", msg.getMessageUID().equals(
msg2.getMessageUID()));
}
/**
* Collects instant messaging events.
*/
private class ImEventCollector implements MessageListener
{
private List collectedEvents = new LinkedList();
/**
* Called when a new incoming <tt>Message</tt> has been received.
* @param evt the <tt>MessageReceivedEvent</tt> containing the newly
* received message, its sender and other details.
*/
public void messageReceived(MessageReceivedEvent evt)
{
logger.debug("Received a MessageReceivedEvent: " + evt);
synchronized(this)
{
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Called to indicated that delivery of a message sent earlier has failed.
* Reason code and phrase are contained by the <tt>MessageFailedEvent</tt>
* @param evt the <tt>MessageFailedEvent</tt> containing the ID of the
* message whose delivery has failed.
*/
public void messageDeliveryFailed(MessageDeliveryFailedEvent evt)
{
logger.debug("Received a MessageDeliveryFailedEvent: " + evt);
synchronized(this)
{
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Called when the underlying implementation has received an indication
* that a message, sent earlier has been successfully received by the
* destination.
* @param evt the MessageDeliveredEvent containing the id of the message
* that has caused the event.
*/
public void messageDelivered(MessageDeliveredEvent evt)
{
logger.debug("Received a MessageDeliveredEvent: " + evt);
synchronized(this)
{
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whichever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
synchronized(this)
{
if(collectedEvents.size() > 0)
return;
try{
wait(waitFor);
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a message evt", ex);
}
}
}
}
/**
* A method that would simply send messages to a group of people so that
* they would get notified that tests are being run.
*/
public void testSendFunMessages()
{
String hostname = "";
try{
hostname = java.net.InetAddress.getLocalHost().getHostName() + ": ";
}catch (UnknownHostException ex){}
String message = hostname
+ "Hello this is the SIP Communicator build on: "
+ new Date().toString()
+ ". Have a very nice day!";
String list = System.getProperty("accounts.reporting.MSN_REPORT_LIST");
logger.debug("Will send message " + message + " to: " + list);
//if no property is specified - return
if(list == null || list.trim().length() == 0)
return;
StringTokenizer tokenizer = new StringTokenizer(list, " ");
while(tokenizer.hasMoreTokens())
{
String contactID = tokenizer.nextToken();
Contact contact
= opSetPresence2.findContactByID(contactID);
if(contact == null)
{
try
{
opSetPresence2.subscribe(contactID);
Object o = new Object();
synchronized (o)
{
o.wait(2000);
}
}
catch (Exception ex1)
{
continue;
}
}
contact
= opSetPresence2.findContactByID(contactID);
opSetBasicIM2.sendInstantMessage(contact,
opSetBasicIM2.createMessage(message));
}
}
}

@ -0,0 +1,682 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.util.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* @author Damian Minkov
*/
public class TestOperationSetPersistentPresence
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestOperationSetPersistentPresence.class);
private MsnSlickFixture fixture = new MsnSlickFixture();
private OperationSetPersistentPresence opSetPersPresence1 = null;
private OperationSetPersistentPresence opSetPersPresence2 = null;
private static final String testGroupName = "NewGroup";
private static final String testGroupName2 = "Renamed";
public TestOperationSetPersistentPresence(String name)
{
super(name);
}
/**
* Creates a test suite containing all tests of this class followed by
* test methods that we want executed in a specified order.
* @return the Test suite to run
*/
public static Test suite()
{
TestSuite suite =
new TestSuite();
//the following 2 need to be run in the specified order.
//(postTestRemoveGroup() needs the group created from
//postTestCreateGroup() )
suite.addTest(
new TestOperationSetPersistentPresence("postTestCreateGroup"));
//rename
// suite.addTest( new TestOperationSetPersistentPresence(
// "postTestRenameGroup"));
suite.addTest(
new TestOperationSetPersistentPresence("postTestRemoveGroup"));
// create the contact list
suite.addTest(
new TestOperationSetPersistentPresence("prepareContactList"));
suite.addTestSuite(TestOperationSetPersistentPresence.class);
return suite;
}
protected void setUp() throws Exception
{
super.setUp();
fixture.setUp();
Map supportedOperationSets1 =
fixture.provider1.getSupportedOperationSets();
if ( supportedOperationSets1 == null
|| supportedOperationSets1.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this Msn implementation. ");
//get the operation set presence here.
opSetPersPresence1 =
(OperationSetPersistentPresence)supportedOperationSets1.get(
OperationSetPersistentPresence.class.getName());
//if still null then the implementation doesn't offer a presence
//operation set which is unacceptable for msn.
if (opSetPersPresence1 == null)
throw new NullPointerException(
"An implementation of the Msn service must provide an "
+ "implementation of at least the one of the Presence "
+ "Operation Sets");
// lets do it once again for the second provider
Map supportedOperationSets2 =
fixture.provider2.getSupportedOperationSets();
if (supportedOperationSets2 == null
|| supportedOperationSets2.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+ "this Msn implementation. ");
//get the operation set presence here.
opSetPersPresence2 =
(OperationSetPersistentPresence) supportedOperationSets2.get(
OperationSetPersistentPresence.class.getName());
//if still null then the implementation doesn't offer a presence
//operation set which is unacceptable for msn.
if (opSetPersPresence2 == null)
throw new NullPointerException(
"An implementation of the msn service must provide an "
+ "implementation of at least the one of the Presence "
+ "Operation Sets");
}
protected void tearDown() throws Exception
{
fixture.tearDown();
super.tearDown();
}
/**
* Retrieves a server stored contact list and checks whether it contains
* all contacts that have been added there during the initialization
* phase by the testerAgent.
*/
public void testRetrievingServerStoredContactList()
{
ContactGroup rootGroup
= opSetPersPresence1.getServerStoredContactListRoot();
logger.debug("=========== Server Stored Contact List =================");
logger.debug("rootGroup="+rootGroup.getGroupName()
+" rootGroup.childContacts="+rootGroup.countContacts()
+ "rootGroup.childGroups="+rootGroup.countSubgroups()
+ "Printing rootGroupContents=\n"+rootGroup.toString());
Hashtable expectedContactList = fixture.preInstalledBuddyList;
logger.debug("============== Expected Contact List ===================");
logger.debug(expectedContactList);
//Go through the contact list retrieved by the persistence presence set
//and remove the name of every contact and group that we find there from
//the expected contct list hashtable.
Iterator groups = rootGroup.subgroups();
while (groups.hasNext() )
{
ContactGroup group = (ContactGroup)groups.next();
List expectedContactsInGroup
= (List)expectedContactList.get(group.getGroupName());
// When sending the offline message
// the sever creates a group NotInContactList,
// beacuse the buddy we are sending message to is not in
// the contactlist. So this group must be ignored
if(!group.getGroupName().equals("NotInContactList"))
{
assertNotNull("Group " + group.getGroupName() +
" was returned by "
+
"the server but was not in the expected contact list."
, expectedContactsInGroup);
Iterator contactsIter = group.contacts();
while(contactsIter.hasNext())
{
String contactID = ((Contact)contactsIter.next()).
getAddress();
expectedContactsInGroup.remove(contactID);
}
//If we've removed all the sub contacts, remove the group too.
if(expectedContactsInGroup.size() == 0)
expectedContactList.remove(group.getGroupName());
}
}
//whatever we now have in the expected contact list snapshot are groups,
//that have been added by the testerAgent but that were not retrieved
//by the persistent presence operation set.
assertTrue("The following contacts were on the server sidec contact "
+"list, but were not returned by the pers. pres. op. set"
+ expectedContactList.toString()
, expectedContactList.isEmpty());
}
/**
* Creates a group in the server stored contact list, makes sure that the
* corresponding event has been generated and verifies that the group is
* in the list.
*
* @throws java.lang.Exception
*/
public void postTestCreateGroup()
throws Exception
{
// first clear the list
fixture.clearProvidersLists();
Object o = new Object();
synchronized(o)
{
o.wait(3000);
}
logger.trace("testing creation of server stored groups");
//first add a listener
GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
opSetPersPresence1
.addServerStoredGroupChangeListener(groupChangeCollector);
//create the group
opSetPersPresence1.createServerStoredContactGroup(
opSetPersPresence1.getServerStoredContactListRoot(), testGroupName);
groupChangeCollector.waitForEvent(10000);
opSetPersPresence1
.removeServerStoredGroupChangeListener(groupChangeCollector);
// check whether we got group created event
assertEquals("Collected Group Change events: ",
1, groupChangeCollector.collectedEvents.size());
assertEquals("Group name.", testGroupName,
((ServerStoredGroupEvent)groupChangeCollector.collectedEvents
.get(0)).getSourceGroup().getGroupName());
// check whether the group is retrievable
ContactGroup group = opSetPersPresence1.getServerStoredContactListRoot()
.getGroup(testGroupName);
assertNotNull("A newly created group was not in the contact list.",
group);
assertEquals("New group name", testGroupName, group.getGroupName());
// when opearting with groups . the group must have entries
// so changes to take effect. Otherwise group will be lost after loggingout
try
{
opSetPersPresence1.subscribe(group, fixture.userID2);
synchronized(o){o.wait(1500);}
}
catch (Exception ex)
{
fail("error adding entry to group : " +
group.getGroupName() + " " +
ex.getMessage());
}
}
/**
* Removes the group created in the server stored contact list by the create
* group test, makes sure that the corresponding event has been generated
* and verifies that the group is not in the list any more.
*/
public void postTestRemoveGroup()
{
logger.trace("testing removal of server stored groups");
//first add a listener
GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
opSetPersPresence1
.addServerStoredGroupChangeListener(groupChangeCollector);
//create the group
opSetPersPresence1.removeServerStoredContactGroup(
opSetPersPresence1.getServerStoredContactListRoot()
.getGroup(testGroupName2));
groupChangeCollector.waitForEvent(10000);
opSetPersPresence1
.removeServerStoredGroupChangeListener(groupChangeCollector);
// check whether we got group created event
assertEquals("Collected Group Change event",
1, groupChangeCollector.collectedEvents.size());
assertEquals("Group name.", testGroupName2,
((ServerStoredGroupEvent)groupChangeCollector.collectedEvents
.get(0)).getSourceGroup().getGroupName());
// check whether the group is still on the contact list
ContactGroup group = opSetPersPresence1.getServerStoredContactListRoot()
.getGroup(testGroupName2);
assertNull("A freshly removed group was still on the contact list.",
group);
}
/**
* Renames our test group and checks whether corresponding events are
* triggered. Verifies whether the group has really changed its name and
* whether it is findable by its new name. Also makes sure that it does
* not exist under its previous name any more.
*/
public void postTestRenameGroup()
{
logger.trace("Testing renaming groups.");
ContactGroup group = opSetPersPresence1.getServerStoredContactListRoot()
.getGroup(testGroupName);
//first add a listener
GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
opSetPersPresence1
.addServerStoredGroupChangeListener(groupChangeCollector);
//change the name and wait for a confirmation event
opSetPersPresence1.renameServerStoredContactGroup(group, testGroupName2);
groupChangeCollector.waitForEvent(10000);
opSetPersPresence1
.removeServerStoredGroupChangeListener(groupChangeCollector);
//examine the event
assertEquals("Collected Group Change event",
1, groupChangeCollector.collectedEvents.size());
assertEquals("Group name.", testGroupName2,
((ServerStoredGroupEvent)groupChangeCollector.collectedEvents
.get(0)).getSourceGroup().getGroupName());
// check whether the group is still on the contact list
ContactGroup oldGroup = opSetPersPresence1.getServerStoredContactListRoot()
.getGroup(testGroupName);
assertNull("A group was still findable by its old name after renaming.",
oldGroup);
//make sure that we could find the group by its new name.
ContactGroup newGroup = opSetPersPresence1.getServerStoredContactListRoot()
.getGroup(testGroupName2);
assertNotNull("Could not find a renamed group by its new name.",
newGroup);
}
/**
* Create the contact list. Later will be test to be sure that creating is ok
* @throws Exception
*/
public void prepareContactList()
throws Exception
{
fixture.clearProvidersLists();
Object o = new Object();
synchronized(o)
{
o.wait(3000);
}
String contactList = System.getProperty(
MsnProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME, null);
logger.debug("The "
+ MsnProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME
+ " property is set to=" + contactList);
if( contactList == null
|| contactList.trim().length() < 6)//at least 4 for a UIN, 1 for the
// dot and 1 for the grp name
throw new IllegalArgumentException(
"The " +
MsnProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME +
" property did not contain a contact list.");
StringTokenizer tokenizer = new StringTokenizer(contactList, " \n\t");
logger.debug("tokens contained by the CL tokenized="
+tokenizer.countTokens());
Hashtable contactListToCreate = new Hashtable();
//go over all group.uin tokens
while (tokenizer.hasMoreTokens())
{
String groupUinToken = tokenizer.nextToken();
int dotIndex = groupUinToken.indexOf(".");
if ( dotIndex == -1 )
{
throw new IllegalArgumentException(groupUinToken
+ " is not a valid Group.UIN token");
}
String groupName = groupUinToken.substring(0, dotIndex);
String uin = groupUinToken.substring(dotIndex + 1);
if( groupName.trim().length() < 1
|| uin.trim().length() < 4 )
{
throw new IllegalArgumentException(
groupName + " or " + uin +
" are not a valid group name or msn UIN.");
}
//check if we've already seen this group and if not - add it
List uinInThisGroup = (List)contactListToCreate.get(groupName);
if (uinInThisGroup == null)
{
uinInThisGroup = new ArrayList();
contactListToCreate.put(groupName, uinInThisGroup);
}
uinInThisGroup.add(uin);
}
// now init the list
Enumeration newGroupsEnum = contactListToCreate.keys();
//go over all groups in the contactsToAdd table
while (newGroupsEnum.hasMoreElements())
{
String groupName = (String) newGroupsEnum.nextElement();
logger.debug("Will add group " + groupName);
opSetPersPresence1.createServerStoredContactGroup(
opSetPersPresence1.getServerStoredContactListRoot(), groupName);
ContactGroup newlyCreatedGroup =
opSetPersPresence1.getServerStoredContactListRoot().getGroup(groupName);
Iterator contactsToAddToThisGroup
= ( (List) contactListToCreate.get(groupName)).iterator();
while (contactsToAddToThisGroup.hasNext())
{
String id = (String) contactsToAddToThisGroup.next();
logger.debug("Will add buddy " + id);
opSetPersPresence1.subscribe(newlyCreatedGroup, id);
}
}
//store the created contact list for later reference
MsnSlickFixture.preInstalledBuddyList = contactListToCreate;
}
/**
* The class would listen for and store received events delivered to
* <tt>ServerStoredGroupListener</tt>s.
*/
private class GroupChangeCollector implements ServerStoredGroupListener
{
public ArrayList collectedEvents = new ArrayList();
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whicever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
synchronized(this)
{
if(collectedEvents.size() > 0)
return;
try{
wait(waitFor);
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a subscription evt", ex);
}
}
}
/**
* Called whnever an indication is received that a new server stored
* group is created.
* @param evt a ServerStoredGroupChangeEvent containing a reference to
* the newly created group.
*/
public void groupCreated(ServerStoredGroupEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Called when an indication is received that the name of a server stored
* contact group has changed.
* @param evt a ServerStoredGroupChangeEvent containing the details of the
* name change.
*/
public void groupNameChanged(ServerStoredGroupEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Called whnever an indication is received that an existing server stored
* group has been removed.
* @param evt a ServerStoredGroupChangeEvent containing a reference to the
* newly created group.
*/
public void groupRemoved(ServerStoredGroupEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Called whnever an indication is received that an existing server
* stored group has been resolved.
* @param evt a ServerStoredGroupChangeEvent containing a reference to
* the resolved group.
*/
public void groupResolved(ServerStoredGroupEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
}
/**
* The class would listen for and store received subscription modification
* events.
*/
private class SubscriptionEventCollector implements SubscriptionListener
{
public ArrayList collectedEvents = new ArrayList();
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whicever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
logger.trace("Waiting for a persistent subscription event");
synchronized(this)
{
if(collectedEvents.size() > 0)
{
logger.trace("SubEvt already received. " + collectedEvents);
return;
}
try{
wait(waitFor);
if(collectedEvents.size() > 0)
logger.trace("Received a SubEvt in provider status.");
else
logger.trace("No SubEvt received for "+waitFor+"ms.");
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a subscription evt", ex);
}
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionCreated(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionRemoved(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionFailed(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionResolved(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionMoved(SubscriptionMovedEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void contactModified(ContactPropertyChangeEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
}
}

@ -0,0 +1,952 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.beans.*;
import java.util.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.msnconstants.*;
import net.java.sip.communicator.util.*;
/**
* Tests msn implementations of a Presence Operation Set. Tests in this class
* verify functionality such as: Changing local (our own) status and
* corresponding event dispatching; Querying status of contacts, Subscribing
* for presence notifications upong status changes of specific contacts.
* <p>
* Using a custom suite() method, we make sure that apart from standard test
* methods (those with a <tt>test</tt> prefix) we also execute those that
* we want run in a specific order like for example - postTestSubscribe() and
* postTestUnsubscribe().
* <p>
* @author Damian Minkov
*/
public class TestOperationSetPresence
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestOperationSetPresence.class);
private MsnSlickFixture fixture = new MsnSlickFixture();
private OperationSetPresence operationSetPresence1 = null;
private OperationSetPresence operationSetPresence2 = null;
private String statusMessageRoot = new String("Our status is now: ");
public TestOperationSetPresence(String name)
{
super(name);
}
protected void setUp() throws Exception
{
super.setUp();
fixture.setUp();
Map supportedOperationSets1 =
fixture.provider1.getSupportedOperationSets();
if ( supportedOperationSets1 == null
|| supportedOperationSets1.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this implementation. ");
//get the operation set presence here.
operationSetPresence1 =
(OperationSetPresence)supportedOperationSets1.get(
OperationSetPresence.class.getName());
//if the op set is null then the implementation doesn't offer a presence
//operation set which is unacceptable for msn.
if (operationSetPresence1 == null)
{
throw new NullPointerException(
"An implementation of the msn service must provide an "
+ "implementation of at least the one of the Presence "
+ "Operation Sets");
}
// do it once again for the second provider
Map supportedOperationSets2 =
fixture.provider2.getSupportedOperationSets();
if ( supportedOperationSets2 == null
|| supportedOperationSets2.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this msn implementation. ");
//get the operation set presence here.
operationSetPresence2 =
(OperationSetPresence)supportedOperationSets2.get(
OperationSetPresence.class.getName());
//if the op set is null then the implementation doesn't offer a presence
//operation set which is unacceptable for msn.
if (operationSetPresence2 == null)
{
throw new NullPointerException(
"An implementation of the msn service must provide an "
+ "implementation of at least the one of the Presence "
+ "Operation Sets");
}
}
protected void tearDown() throws Exception
{
super.tearDown();
fixture.tearDown();
}
/**
* Creates a test suite containing all tests of this class followed by
* test methods that we want executed in a specified order.
* @return Test
*/
public static Test suite()
{
//return an (almost) empty suite if we're running in offline mode.
if(MsnSlickFixture.onlineTestingDisabled)
{
TestSuite suite = new TestSuite();
//the only test around here that we could run without net
//connectivity
suite.addTest(
new TestOperationSetPresence(
"testSupportedStatusSetForCompleteness"));
return suite;
}
TestSuite suite = new TestSuite();
// clear the lists before subscribing users
suite.addTest(new TestOperationSetPresence("clearLists"));
// first postTestSubscribe. to be sure that contacts are in the
// list so we can further continue and test presences each other
suite.addTest(new TestOperationSetPresence("postTestSubscribe"));
// add other tests
suite.addTestSuite(TestOperationSetPresence.class);
// now test unsubscribe
suite.addTest(new TestOperationSetPresence("postTestUnsubscribe"));
return suite;
}
/**
* Verifies that all necessary msn test states are supported by the
* implementation.
*/
public void testSupportedStatusSetForCompleteness()
{
//first create a local list containing the presence status instances
//supported by the underlying implementation.
Iterator supportedStatusSetIter =
operationSetPresence1.getSupportedStatusSet();
List supportedStatusSet = new LinkedList();
while (supportedStatusSetIter.hasNext()){
supportedStatusSet.add(supportedStatusSetIter.next());
}
//create a copy of the MUST status set and remove any matching status
//that is also present in the supported set.
List requiredStatusSetCopy = (List)MsnStatusEnum.msnStatusSet.clone();
requiredStatusSetCopy.removeAll(supportedStatusSet);
//if we have anything left then the implementation is wrong.
int unsupported = requiredStatusSetCopy.size();
assertTrue( "There are " + unsupported + " statuses as follows:"
+ requiredStatusSetCopy,
unsupported == 0);
}
/**
* Verify that changing state to AWAY works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToAway() throws Exception
{
subtestStateTransition(MsnStatusEnum.AWAY);
}
/**
* Verify that changing state to NOT_AVAILABLE works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToNotAvailable() throws Exception
{
subtestStateTransition(MsnStatusEnum.BE_RIGHT_BACK);
}
/**
* Verify that changing state to DND works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToDnd() throws Exception
{
subtestStateTransition(MsnStatusEnum.BUSY);
}
/**
* Verify that changing state to FREE_FOR_CHAT works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToIdle() throws Exception
{
subtestStateTransition(MsnStatusEnum.IDLE);
}
/**
* Verify that changing state to ONLINE works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToOnline() throws Exception
{
subtestStateTransition(MsnStatusEnum.ONLINE);
}
/**
* Verify that changing state to OUT_TO_LUNCH works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToOutToLunch() throws Exception
{
subtestStateTransition(MsnStatusEnum.OUT_TO_LUNCH);
}
/**
* Verify that changing state to ON_THE_PHONE works as supposed to and that it
* generates the corresponding event.
* @throws Exception in case a failure occurs while the operation set
* is switching to the new state.
*/
public void testChangingStateToOnThePhone() throws Exception
{
subtestStateTransition(MsnStatusEnum.ON_THE_PHONE);
}
/**
* Used by methods testing state transiotions
*
* @param newStatus the MsnStatusEnum field corresponding to the status
* that we'd like the opeation set to enter.
*
* @throws Exception in case changing the state causes an exception
*/
public void subtestStateTransition( MsnStatusEnum newStatus)
throws Exception
{
logger.trace(" --=== beginning state transition test ===--");
PresenceStatus oldStatus = operationSetPresence1.getPresenceStatus();
logger.debug( "old status is=" + oldStatus.getStatusName()
+ " new status=" + newStatus.getStatusName());
//First register a listener to make sure that all corresponding
//events have been generated.
PresenceStatusEventCollector statusEventCollector
= new PresenceStatusEventCollector();
operationSetPresence1.addProviderPresenceStatusListener(
statusEventCollector);
//change the status
operationSetPresence1.publishPresenceStatus(newStatus, null);
pauseAfterStateChanges();
//test event notification.
statusEventCollector.waitForPresEvent(10000);
operationSetPresence1.removeProviderPresenceStatusListener(
statusEventCollector);
assertEquals("Events dispatched during an event transition.",
1, statusEventCollector.collectedPresEvents.size());
assertEquals("A status changed event contained wrong old status.",
oldStatus,
((ProviderPresenceStatusChangeEvent)
statusEventCollector.collectedPresEvents.get(0))
.getOldStatus());
assertEquals("A status changed event contained wrong new status.",
newStatus,
((ProviderPresenceStatusChangeEvent)
statusEventCollector.collectedPresEvents.get(0))
.getNewStatus());
// verify that the operation set itself is aware of the status change
assertEquals("opSet.getPresenceStatus() did not return properly.",
newStatus,
operationSetPresence1.getPresenceStatus());
MsnStatusEnum actualStatus = (MsnStatusEnum)
operationSetPresence2.queryContactStatus(fixture.userID1);
assertEquals("The underlying implementation did not switch to the "
+"requested presence status.",
newStatus,
actualStatus);
logger.trace(" --=== finished test ===--");
}
/**
* Give time changes to take effect
*/
private void pauseAfterStateChanges()
{
try
{
Thread.currentThread().sleep(1500);
}
catch (InterruptedException ex)
{
logger.debug("Pausing between state changes was interrupted", ex);
}
}
/**
* Verifies that querying status works fine. The tester agent would
* change status and the operation set would have to return the right status
* after every change.
*
* @throws java.lang.Exception if one of the transitions fails
*/
public void testQueryContactStatus()
throws Exception
{
// --- AWAY ---
logger.debug("Will Query an AWAY contact.");
subtestQueryContactStatus(MsnStatusEnum.AWAY,
MsnStatusEnum.AWAY);
// --- NA ---
logger.debug("Will Query an BRB contact.");
subtestQueryContactStatus(MsnStatusEnum.BE_RIGHT_BACK,
MsnStatusEnum.BE_RIGHT_BACK);
// --- DND ---
logger.debug("Will Query a Busy contact.");
subtestQueryContactStatus(MsnStatusEnum.BUSY,
MsnStatusEnum.BUSY);
// --- FFC ---
logger.debug("Will Query a Idle contact.");
subtestQueryContactStatus(MsnStatusEnum.IDLE,
MsnStatusEnum.IDLE);
// --- INVISIBLE ---
logger.debug("Will Query an Invisible contact.");
subtestQueryContactStatus(MsnStatusEnum.HIDE,
MsnStatusEnum.OFFLINE);
// --- Online ---
logger.debug("Will Query an Online contact.");
subtestQueryContactStatus(MsnStatusEnum.ONLINE,
MsnStatusEnum.ONLINE);
}
/**
* Used by functions testing the queryContactStatus method of the
* presence operation set.
* @param status the status as specified, that
* the tester agent should switch to.
* @param expectedReturn the PresenceStatus that the presence operation
* set should see the tester agent in once it has switched to taStatusLong.
*
* @throws java.lang.Exception if querying the status causes some exception.
*/
public void subtestQueryContactStatus(PresenceStatus status,
PresenceStatus expectedReturn)
throws Exception
{
operationSetPresence2.publishPresenceStatus(status, "status message");
pauseAfterStateChanges();
PresenceStatus actualReturn
= operationSetPresence1.queryContactStatus(fixture.userID2);
assertEquals("Querying a "
+ expectedReturn.getStatusName()
+ " state did not return as expected"
, expectedReturn, actualReturn);
}
/**
* The method would add a subscription for a contact, wait for a
* subscription event confirming the subscription, then change the status
* of the newly added contact (which is actually the testerAgent) and
* make sure that the corresponding notification events have been generated.
*
* @throws java.lang.Exception if an exception occurs during testing.
*/
public void postTestSubscribe()
throws Exception
{
logger.debug("Testing Subscription and Subscription Event Dispatch.");
SubscriptionEventCollector subEvtCollector
= new SubscriptionEventCollector();
operationSetPresence1.addSubsciptionListener(subEvtCollector);
synchronized (subEvtCollector){
operationSetPresence1.subscribe(fixture.userID2);
//we may already have the event, but it won't hurt to check.
subEvtCollector.waitForEvent(10000);
operationSetPresence1.removeSubscriptionListener(subEvtCollector);
}
assertEquals("Subscription event dispatching failed."
, 1, subEvtCollector.collectedEvents.size());
SubscriptionEvent subEvt =
(SubscriptionEvent)subEvtCollector.collectedEvents.get(0);
assertEquals("SubscriptionEvent Source:",
fixture.userID2,
((Contact)subEvt.getSource()).getAddress());
assertEquals("SubscriptionEvent Source Contact:",
fixture.userID2,
subEvt.getSourceContact().getAddress());
assertSame("SubscriptionEvent Source Provider:",
fixture.provider1,
subEvt.getSourceProvider());
subEvtCollector.collectedEvents.clear();
// make the user agent tester change its states and make sure we are
// notified
logger.debug("Testing presence notifications.");
MsnStatusEnum oldStatus
= (MsnStatusEnum)operationSetPresence2.getPresenceStatus();
MsnStatusEnum newStatus = MsnStatusEnum.IDLE;
//in case we are by any chance already in a FREE_FOR_CHAT status, we'll
//be changing to something else
if(oldStatus.equals(newStatus)){
newStatus = MsnStatusEnum.BUSY;
}
//now do the actual status notification testing
ContactPresenceEventCollector contactPresEvtCollector
= new ContactPresenceEventCollector(
fixture.userID2, newStatus);
operationSetPresence1.addContactPresenceStatusListener(
contactPresEvtCollector);
synchronized (contactPresEvtCollector){
operationSetPresence2.publishPresenceStatus(newStatus, "new status");
//we may already have the event, but it won't hurt to check.
contactPresEvtCollector.waitForEvent(10000);
operationSetPresence1
.removeContactPresenceStatusListener(contactPresEvtCollector);
}
assertEquals("Presence Notif. event dispatching failed."
, 1, contactPresEvtCollector.collectedEvents.size());
ContactPresenceStatusChangeEvent presEvt =
(ContactPresenceStatusChangeEvent)
contactPresEvtCollector.collectedEvents.get(0);
assertEquals("Presence Notif. event Source:",
fixture.userID2,
((Contact)presEvt.getSource()).getAddress());
assertEquals("Presence Notif. event Source Contact:",
fixture.userID2,
presEvt.getSourceContact().getAddress());
assertSame("Presence Notif. event Source Provider:",
fixture.provider1,
presEvt.getSourceProvider());
PresenceStatus reportedNewStatus = presEvt.getNewStatus();
PresenceStatus reportedOldStatus = presEvt.getOldStatus();
assertEquals( "Reported new PresenceStatus: ",
newStatus, reportedNewStatus );
//don't require equality between the reported old PresenceStatus and
//the actual presence status of the tester agent because a first
//notification is not supposed to have the old status as it really was.
assertNotNull( "Reported old PresenceStatus: ", reportedOldStatus );
try
{
// add the the user to the reverse side needed for status tests
subEvtCollector.collectedEvents.clear();
operationSetPresence2.addSubsciptionListener(subEvtCollector);
synchronized (subEvtCollector)
{
operationSetPresence2.subscribe(fixture.userID1);
//we may already have the event, but it won't hurt to check.
subEvtCollector.waitForEvent(10000);
operationSetPresence2.removeSubscriptionListener(
subEvtCollector);
}
}
catch (OperationFailedException ex)
{
// happens if the user is already subscribed
}
}
/**
* We unsubscribe from presence notification deliveries concerning
* testerAgent's presence status and verify that we receive the
* subscription removed event. We then make the tester agent change status
* and make sure that no notifications are delivered.
*
* @throws java.lang.Exception in case unsubscribing fails.
*/
public void postTestUnsubscribe()
throws Exception
{
logger.debug("Testing Unsubscribe and unsubscription event dispatch.");
// First create a subscription and verify that it really gets created.
SubscriptionEventCollector subEvtCollector
= new SubscriptionEventCollector();
operationSetPresence1.addSubsciptionListener(subEvtCollector);
Contact msnTesterAgentContact = operationSetPresence1
.findContactByID(fixture.userID2);
assertNotNull(
"Failed to find an existing subscription for the tester agent"
, msnTesterAgentContact);
synchronized(subEvtCollector){
operationSetPresence1.unsubscribe(msnTesterAgentContact);
subEvtCollector.waitForEvent(10000);
//don't want any more events
operationSetPresence1.removeSubscriptionListener(subEvtCollector);
}
assertEquals("Subscription event dispatching failed."
, 1, subEvtCollector.collectedEvents.size());
SubscriptionEvent subEvt =
(SubscriptionEvent)subEvtCollector.collectedEvents.get(0);
assertEquals("SubscriptionEvent Source:",
msnTesterAgentContact, subEvt.getSource());
assertEquals("SubscriptionEvent Source Contact:",
msnTesterAgentContact, subEvt.getSourceContact());
assertSame("SubscriptionEvent Source Provider:",
fixture.provider1,
subEvt.getSourceProvider());
subEvtCollector.collectedEvents.clear();
// make the user agent tester change its states and make sure we don't
// get notifications as we're now unsubscribed.
logger.debug("Testing (lack of) presence notifications.");
MsnStatusEnum oldStatus
= (MsnStatusEnum)operationSetPresence2.getPresenceStatus();
MsnStatusEnum newStatus = MsnStatusEnum.IDLE;
//in case we are by any chance already in a FREE_FOR_CHAT status, we'll
//be changing to something else
if(oldStatus.equals(newStatus)){
newStatus = MsnStatusEnum.BUSY;
}
//now do the actual status notification testing
ContactPresenceEventCollector contactPresEvtCollector
= new ContactPresenceEventCollector(fixture.userID2, null);
operationSetPresence1.addContactPresenceStatusListener(
contactPresEvtCollector);
synchronized (contactPresEvtCollector){
operationSetPresence2.publishPresenceStatus(newStatus, "new status");
//we may already have the event, but it won't hurt to check.
contactPresEvtCollector.waitForEvent(10000);
operationSetPresence1
.removeContactPresenceStatusListener(contactPresEvtCollector);
}
assertEquals("Presence Notifications were received after unsubscibing."
, 0, contactPresEvtCollector.collectedEvents.size());
}
public void clearLists()
throws Exception
{
logger.debug("Clear the two lists before tests");
Object o = new Object();
synchronized (o)
{
o.wait(10000);
}
// wait for a moment
// give time the impl to get the lists
logger.debug("start clearing");
fixture.clearProvidersLists();
synchronized(o)
{
o.wait(3000);
}
}
/**
* An event collector that would collect all events generated by a
* provider after a status change. The collector would also do a notidyAll
* every time it receives an event.
*/
private class PresenceStatusEventCollector
implements ProviderPresenceStatusListener
{
public ArrayList collectedPresEvents = new ArrayList();
public ArrayList collectedStatMsgEvents = new ArrayList();
public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedPresEvents.size()+")= "+evt);
collectedPresEvents.add(evt);
notifyAll();
}
}
public void providerStatusMessageChanged(PropertyChangeEvent evt)
{
synchronized(this)
{
logger.debug("Collected stat.msg. evt("
+collectedPresEvents.size()+")= "+evt);
collectedStatMsgEvents.add(evt);
notifyAll();
}
}
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whicever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForPresEvent(long waitFor)
{
logger.trace("Waiting for a change in provider status.");
synchronized(this)
{
if(collectedPresEvents.size() > 0){
logger.trace("Change already received. " + collectedPresEvents);
return;
}
try{
wait(waitFor);
if(collectedPresEvents.size() > 0)
logger.trace("Received a change in provider status.");
else
logger.trace("No change received for "+waitFor+"ms.");
}
catch (InterruptedException ex){
logger.debug("Interrupted while waiting for a provider evt"
, ex);
}
}
}
/**
* Blocks until at least one staus message event is received or until
* waitFor miliseconds pass (whichever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for a status message event before simply bailing out.
*/
public void waitForStatMsgEvent(long waitFor)
{
logger.trace("Waiting for a provider status message event.");
synchronized(this)
{
if(collectedStatMsgEvents.size() > 0){
logger.trace("Stat msg. evt already received. "
+ collectedStatMsgEvents);
return;
}
try{
wait(waitFor);
if(collectedStatMsgEvents.size() > 0)
logger.trace("Received a prov. stat. msg. evt.");
else
logger.trace("No prov. stat msg. received for "
+waitFor+"ms.");
}
catch (InterruptedException ex){
logger.debug("Interrupted while waiting for a status msg evt"
, ex);
}
}
}
}
/**
* The class would listen for and store received subscription modification
* events.
*/
private class SubscriptionEventCollector implements SubscriptionListener
{
public ArrayList collectedEvents = new ArrayList();
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whicever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
synchronized(this)
{
if(collectedEvents.size() > 0)
return;
try{
wait(waitFor);
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a subscription evt", ex);
}
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionCreated(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionRemoved(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void contactModified(ContactPropertyChangeEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionMoved(SubscriptionMovedEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionFailed(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
/**
* Stores the received subsctiption and notifies all waiting on this
* object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void subscriptionResolved(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
}
/**
* The class would listen for and store received events caused by changes
* in contact presence states.
*/
private class ContactPresenceEventCollector
implements ContactPresenceStatusListener
{
public ArrayList collectedEvents = new ArrayList();
private String trackedScreenName = null;
private MsnStatusEnum status = null;
ContactPresenceEventCollector(String screenname,
MsnStatusEnum wantedStatus)
{
this.trackedScreenName = screenname;
this.status = wantedStatus;
}
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whicever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
synchronized(this)
{
if(collectedEvents.size() > 0)
return;
try{
wait(waitFor);
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a subscription evt", ex);
}
}
}
/**
* Stores the received status change event and notifies all waiting on
* this object
* @param evt the SubscriptionEvent containing the corresponding contact
*/
public void contactPresenceStatusChanged(
ContactPresenceStatusChangeEvent evt)
{
synchronized(this)
{
//if the user has specified event details and the received
//event does not match - then ignore it.
if( this.trackedScreenName != null
&& !evt.getSourceContact().getAddress()
.equals(trackedScreenName))
return;
if( status != null
&& status != evt.getNewStatus())
return;
logger.debug("Collected evt("+collectedEvents.size()+")= "+evt);
collectedEvents.add(evt);
notifyAll();
}
}
}
/**
* Used to wait till buddy is removed from our contact list.
* Used in the authorization process tests
*/
private class UnsubscribeWait implements SubscriptionListener
{
public void waitForUnsubscribre(long waitFor)
{
synchronized(this)
{
try{
wait(waitFor);
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a subscription evt", ex);
}
}
}
public void subscriptionRemoved(SubscriptionEvent evt)
{
synchronized(this)
{
logger.debug("Got subscriptionRemoved " + evt);
notifyAll();
}
}
public void subscriptionCreated(SubscriptionEvent evt)
{}
public void subscriptionFailed(SubscriptionEvent evt)
{}
public void subscriptionMoved(SubscriptionMovedEvent evt)
{}
public void subscriptionResolved(SubscriptionEvent evt)
{}
public void contactModified(ContactPropertyChangeEvent evt)
{}
}
}

@ -0,0 +1,315 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.util.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Tests functionality of the typing notifications operation set.
*
* @author Damian Minkov
*/
public class TestOperationSetTypingNotifications
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestOperationSetTypingNotifications.class);
private MsnSlickFixture fixture = new MsnSlickFixture();
private OperationSetTypingNotifications opSetTypingNotifs1 = null;
private OperationSetPresence opSetPresence1 = null;
private OperationSetTypingNotifications opSetTypingNotifs2 = null;
private OperationSetPresence opSetPresence2 = null;
private OperationSetBasicInstantMessaging opSetBasicIM1 = null;
private OperationSetBasicInstantMessaging opSetBasicIM2 = null;
public TestOperationSetTypingNotifications(String name)
{
super(name);
}
protected void setUp() throws Exception
{
super.setUp();
fixture.setUp();
Map supportedOperationSets1 =
fixture.provider1.getSupportedOperationSets();
if ( supportedOperationSets1 == null
|| supportedOperationSets1.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this implementation. ");
//get the operation set presence here.
opSetTypingNotifs1 =
(OperationSetTypingNotifications)supportedOperationSets1.get(
OperationSetTypingNotifications.class.getName());
//if the op set is null then the implementation doesn't offer a typing.n
//operation set which is unacceptable.
if (opSetTypingNotifs1 == null)
{
throw new NullPointerException(
"No implementation for typing notifications was found");
}
opSetBasicIM1 =
(OperationSetBasicInstantMessaging)supportedOperationSets1.get(
OperationSetBasicInstantMessaging.class.getName());
if (opSetBasicIM1 == null)
{
throw new NullPointerException(
"No implementation for basic IM was found");
}
//we also need the presence op set in order to retrieve contacts.
opSetPresence1 =
(OperationSetPresence)supportedOperationSets1.get(
OperationSetPresence.class.getName());
//if the op set is null show that we're not happy.
if (opSetPresence1 == null)
{
throw new NullPointerException(
"An implementation of the service must provide an "
+ "implementation of at least one of the PresenceOperationSets");
}
Map supportedOperationSets2 =
fixture.provider2.getSupportedOperationSets();
if ( supportedOperationSets2 == null
|| supportedOperationSets2.size() < 1)
throw new NullPointerException(
"No OperationSet implementations are supported by "
+"this implementation. ");
//get the operation set presence here.
opSetTypingNotifs2 =
(OperationSetTypingNotifications)supportedOperationSets2.get(
OperationSetTypingNotifications.class.getName());
//if the op set is null then the implementation doesn't offer a typing.n
//operation set which is unacceptable for.
if (opSetTypingNotifs2 == null)
{
throw new NullPointerException(
"No implementation for typing notifications was found");
}
opSetBasicIM2 =
(OperationSetBasicInstantMessaging)supportedOperationSets2.get(
OperationSetBasicInstantMessaging.class.getName());
if (opSetBasicIM2 == null)
{
throw new NullPointerException(
"No implementation for basic IM was found");
}
//we also need the presence op set in order to retrieve contacts.
opSetPresence2 =
(OperationSetPresence)supportedOperationSets2.get(
OperationSetPresence.class.getName());
//if the op set is null show that we're not happy.
if (opSetPresence2 == null)
{
throw new NullPointerException(
"An implementation of the service must provide an "
+ "implementation of at least one of the PresenceOperationSets");
}
}
/**
* Create the list to be sure that contacts exchanging messages
* exists in each other lists
* @throws Exception
*/
public void prepareContactList() throws Exception
{
// be sure that contacts are in their lists
try{
opSetPresence1.subscribe(fixture.userID2);
}
catch (OperationFailedException ex){
// the contact already exist its OK
}
try{
opSetPresence2.subscribe(fixture.userID1);
}
catch (OperationFailedException ex1){
// the contact already exist its OK
}
Object o = new Object();
synchronized (o)
{
o.wait(2000);
}
}
protected void tearDown() throws Exception
{
super.tearDown();
fixture.tearDown();
}
/**
* Creates a test suite containing tests of this class in a specific order.
* We'll first execute a test where we receive a typing notification, and
* a volatile contact is created for the sender. we'll then be able to
* retrieve this volatile contact and them a notification on our turn.
* We need to do things this way as the contact corresponding to the tester
* agent has been removed in the previous test and we no longer have it
* in our contact list.
*
* @return Test a testsuite containing all tests to execute.
*/
public static Test suite()
{
TestSuite suite = new TestSuite();
suite.addTest(new TestOperationSetTypingNotifications(
"prepareContactList"));
//the following 2 need to be run in the specified order.
suite.addTest(new TestOperationSetTypingNotifications(
"testTypingNotificationsEventDelivery"));
return suite;
}
/**
* Sends a typing notification and verifies
* whether it is properly received by the tested implementation
*/
public void testTypingNotificationsEventDelivery()
{
TypingEventCollector evtCollector = new TypingEventCollector();
// send message so request for receiving notifications also to be set
Contact notifingContact =
opSetPresence1.findContactByID(fixture.userID2);
opSetBasicIM1.sendInstantMessage(notifingContact,
opSetBasicIM1.createMessage("ping"));
opSetTypingNotifs1.addTypingNotificationsListener(evtCollector);
Contact contactToNotify =
opSetPresence2.findContactByID(fixture.userID1);
opSetTypingNotifs2.sendTypingNotification(
contactToNotify, OperationSetTypingNotifications.STATE_TYPING);
evtCollector.waitForEvent(10000);
opSetTypingNotifs1.removeTypingNotificationsListener(evtCollector);
//check event dispatching
assertTrue("Number of typing events received was zero."
, evtCollector.collectedEvents.size() > 0);
TypingNotificationEvent evt = (TypingNotificationEvent)evtCollector
.collectedEvents.get(0);
assertEquals("Source of the typing notification event"
, fixture.userID2
, evt.getSourceContact().getAddress() );
assertEquals("Source of the typing notification event"
, OperationSetTypingNotifications.STATE_TYPING
, evt.getTypingState());
evtCollector.collectedEvents.clear();
opSetTypingNotifs1.addTypingNotificationsListener(evtCollector);
opSetTypingNotifs2.sendTypingNotification(
contactToNotify, OperationSetTypingNotifications.STATE_STOPPED);
evtCollector.waitForEvent(10000);
opSetTypingNotifs1.removeTypingNotificationsListener(evtCollector);
//check event dispatching
assertTrue("Number of typing events received was zero."
, evtCollector.collectedEvents.size() > 0);
evt = (TypingNotificationEvent)evtCollector.collectedEvents.get(0);
assertEquals("Source of the typing notification event"
, fixture.userID2
, evt.getSourceContact().getAddress() );
assertEquals("Source of the typing notification event"
, OperationSetTypingNotifications.STATE_STOPPED
, evt.getTypingState());
}
/**
* Simply collects allre received events and provides a mechanisim for
* waiting for the next event.
*/
private class TypingEventCollector implements TypingNotificationsListener
{
private List collectedEvents = new LinkedList();
/**
* Called to indicate that a remote <tt>Contact</tt> has sent us a typing
* notification. The method adds the <tt>event</tt> to the list of
* captured events.
* @param event a <tt>TypingNotificationEvent</tt> containing the sender
* of the notification and its type.
*/
public void typingNotificationReceifed(TypingNotificationEvent event)
{
logger.debug("Received a typing notification: " + event);
synchronized (this)
{
collectedEvents.add(event);
notifyAll();
}
}
/**
* Blocks until at least one event is received or until waitFor
* miliseconds pass (whicever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
synchronized(this){
if(collectedEvents.size() > 0)
return;
try{
wait(waitFor);
}
catch (InterruptedException ex){
logger.debug(
"Interrupted while waiting for a subscription evt", ex);
}
}
}
}
}

@ -0,0 +1,285 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.slick.protocol.msn;
import java.util.*;
import junit.framework.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Performs testing on protocol provider methods.
* @todo add more detailed docs once the tests are written.
* @author Damian Minkov
*/
public class TestProtocolProviderServiceMsnImpl
extends TestCase
{
private static final Logger logger =
Logger.getLogger(TestProtocolProviderServiceMsnImpl.class);
private MsnSlickFixture fixture = new MsnSlickFixture();
/**
* An event adapter that would collec registation state change events
*/
public RegistrationEventCollector regEvtCollector1
= new RegistrationEventCollector();
/**
* An event adapter that would collec registation state change events
*/
public RegistrationEventCollector regEvtCollector2
= new RegistrationEventCollector();
/**
* Creates a test encapsulator for the method with the specified name.
* @param name the name of the method this test should run.
*/
public TestProtocolProviderServiceMsnImpl(String name)
{
super(name);
}
/**
* Initializes the fixture.
* @throws Exception if super.setUp() throws one.
*/
protected void setUp() throws Exception
{
super.setUp();
fixture.setUp();
}
/**
* Tears the fixture down.
* @throws Exception if fixture.tearDown() fails.
*/
protected void tearDown() throws Exception
{
fixture.tearDown();
super.tearDown();
}
/**
* Makes sure that the instance of the Msn protocol provider that we're
* going to use for testing is properly initialized and registered with
* a Msn registrar. This MUST be called before any other online testing
* of the Msn provider so that we won't have to reregister for every single
* test.
* <p>
* The method also verifies that a registration event is fired upon
* succesful registration and collected by our event collector.
*
* @throws OperationFailedException if provider.register() fails.
*/
public void testRegister()
throws OperationFailedException
{
//add an event collector that will collect all events during the
//registration and allow us to later inspect them and make sure
//they were properly dispatched.
fixture.provider1.addRegistrationStateChangeListener(regEvtCollector1);
fixture.provider2.addRegistrationStateChangeListener(regEvtCollector2);
//register both our providers
fixture.provider1.register(new SecurityAuthorityImpl(
System.getProperty(MsnProtocolProviderServiceLick.ACCOUNT_1_PREFIX
+ ProtocolProviderFactory.PASSWORD).toCharArray()));
fixture.provider2.register(new SecurityAuthorityImpl(
System.getProperty(MsnProtocolProviderServiceLick.ACCOUNT_2_PREFIX
+ ProtocolProviderFactory.PASSWORD).toCharArray()));
//give it enough time to register. We won't really have to wait all this
//time since the registration event collector would notify us the moment
//we get signed on.
logger.debug("Waiting for registration to complete ...");
regEvtCollector1.waitForEvent(15000);
regEvtCollector2.waitForEvent(40000);
//make sure that the registration process trigerred the corresponding
//events.
assertTrue(
"No events were dispatched during the registration process."
,regEvtCollector1.collectedNewStates.size() > 0);
assertTrue(
"No registration event notifying of registration was dispatched. "
+"All events were: " + regEvtCollector1.collectedNewStates
,regEvtCollector1.collectedNewStates
.contains(RegistrationState.REGISTERED));
//now the same for provider 2
assertTrue(
"No events were dispatched during the registration process "
+"of provider2."
,regEvtCollector2.collectedNewStates.size() > 0);
assertTrue(
"No registration event notifying of registration was dispatched. "
+"All events were: " + regEvtCollector2.collectedNewStates
,regEvtCollector2.collectedNewStates
.contains(RegistrationState.REGISTERED));
fixture.provider1
.removeRegistrationStateChangeListener(regEvtCollector1);
fixture.provider2
.removeRegistrationStateChangeListener(regEvtCollector2);
}
/**
* Verifies that all operation sets have the type they are declarded to
* have.
*
* @throws java.lang.Exception if a class indicated in one of the keys
* could not be forName()ed.
*/
public void testOperationSetTypes() throws Exception
{
Map supportedOperationSets
= fixture.provider1.getSupportedOperationSets();
//make sure that keys (which are supposed to be class names) correspond
//what the class of the values recorded against them.
Iterator setNames = supportedOperationSets.keySet().iterator();
while (setNames.hasNext())
{
String setName = (String) setNames.next();
Object opSet = supportedOperationSets.get(setName);
assertTrue(opSet + " was not an instance of "
+ setName + " as declared"
, Class.forName(setName).isInstance(opSet));
}
}
/**
* A class that would plugin as a registration listener to a protocol
* provider and simply record all events that it sees and notifyAll()
* if it sees an event that notifies us of a completed
* registration.
*/
public class RegistrationEventCollector
implements RegistrationStateChangeListener
{
public List collectedNewStates = new LinkedList();
/**
* The method would simply register all received events so that they
* could be available for later inspection by the unit tests. In the
* case where a registraiton event notifying us of a completed
* registration is seen, the method would call notifyAll().
*
* @param evt ProviderStatusChangeEvent the event describing the status
* change.
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
logger.debug("Received a RegistrationStateChangeEvent: " + evt);
collectedNewStates.add(evt.getNewState());
if (evt.getNewState().equals(RegistrationState.REGISTERED))
{
logger.debug("We're registered and will notify those who wait");
synchronized (this)
{
notifyAll();
}
}
}
/**
* Blocks until an event notifying us of the awaited state change is
* received or until waitFor miliseconds pass (whichever happens first).
*
* @param waitFor the number of miliseconds that we should be waiting
* for an event before simply bailing out.
*/
public void waitForEvent(long waitFor)
{
logger.trace("Waiting for a RegistrationStateChangeEvent ");
synchronized (this)
{
if (collectedNewStates.contains(RegistrationState.REGISTERED))
{
logger.trace("Event already received. "
+ collectedNewStates);
return;
}
try
{
wait(waitFor);
if (collectedNewStates.size() > 0)
logger.trace(
"Received a RegistrationStateChangeEvent.");
else
logger.trace(
"No RegistrationStateChangeEvent received for "
+ waitFor + "ms.");
}
catch (InterruptedException ex)
{
logger.debug(
"Interrupted while waiting for a "
+"RegistrationStateChangeEvent"
, ex);
}
}
}
}
/**
* A very simple straight forward implementation of a security authority
* that would always return the same password (the one specified upon
* construction) when asked for credentials.
*/
public class SecurityAuthorityImpl
implements SecurityAuthority
{
/**
* The password to return when asked for credentials
*/
private char[] passwd = null;
/**
* Creates an instance of this class that would always return "passwd"
* when asked for credentials.
*
* @param passwd the password that this class should return when
* asked for credentials.
*/
public SecurityAuthorityImpl(char[] passwd)
{
this.passwd = passwd;
}
/**
* Returns a Credentials object associated with the specified realm.
* <p>
* @param realm The realm that the credentials are needed for.
* @param defaultValues the values to propose the user by default
* @return The credentials associated with the specified realm or null
* if none could be obtained.
*/
public UserCredentials obtainCredentials(String realm,
UserCredentials defaultValues)
{
defaultValues.setPassword(passwd);
return defaultValues;
}
}
}

@ -0,0 +1,15 @@
Bundle-Activator: net.java.sip.communicator.slick.protocol.msn.MsnProtocolProviderServiceLick
Bundle-Name: Msn Protocol Provider Service Leveraging Implementation Compatibility Kit
Bundle-Description: A Service Leveraging Implementation Compatibility Kit for the Msn implementation of the ProtocolProvider Service
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
Import-Package: net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.configuration.event,
junit.framework,
org.osgi.framework,
javax.net.ssl,
javax.xml.parsers,
net.java.sip.communicator.util,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.msnconstants,
net.java.sip.communicator.service.protocol.event
Loading…
Cancel
Save