diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IHttpdXmppMessage.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IHttpdXmppMessage.java index 7040c44636..6e8b5b1dc0 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IHttpdXmppMessage.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IHttpdXmppMessage.java @@ -21,8 +21,6 @@ package com.raytheon.uf.viz.collaboration.comm.identity.event; import java.util.regex.Pattern; -import com.raytheon.uf.viz.collaboration.comm.provider.Tools; - /** * Used to store constants that are used to validate, analyze, and parse status * and configuration messages associated with the AWIPS II httpd collaboration @@ -35,6 +33,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.Tools; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Aug 7, 2012 bkowal Initial creation + * Dec 18, 2013 2562 bclement removed preamble from patterns * * * @@ -48,20 +47,16 @@ public interface IHttpdXmppMessage { public static final String ERROR_PARAMETER_NAME = "error"; - // Regex - static final String PREABMLE_REGEX = Tools.CONFIG_PREAMBLE.replace("[", - "\\["); - - static final String SUFFIX_REGEX = " : .+" + Tools.DIRECTIVE_SUFFIX; + static final String SUFFIX_REGEX = " : .+"; static final String COLLABORATION_URL_REGEX = "http://.+:[1-9][0-9]*/session_data/"; // Regex Patterns public static final Pattern configErrorPattern = Pattern - .compile(PREABMLE_REGEX + ERROR_PARAMETER_NAME + SUFFIX_REGEX); + .compile(ERROR_PARAMETER_NAME + SUFFIX_REGEX); public static final Pattern configURLPattern = Pattern - .compile(PREABMLE_REGEX + URL_PARAMETER_NAME + SUFFIX_REGEX); + .compile(URL_PARAMETER_NAME + SUFFIX_REGEX); public static final Pattern urlPattern = Pattern .compile(COLLABORATION_URL_REGEX); diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IVenueInvitationEvent.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IVenueInvitationEvent.java index 306bc2222b..3d51d63e9f 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IVenueInvitationEvent.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/identity/event/IVenueInvitationEvent.java @@ -32,6 +32,7 @@ import com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Mar 21, 2012 jkorman Initial creation + * Dec 18, 2013 2562 bclement removed subject getter (subject in invite) * * * @@ -42,8 +43,9 @@ import com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID; public interface IVenueInvitationEvent { /** + * room id for venue * - * @return room id for venue + * @return id in {room}@conference.{host} format */ public IQualifiedID getRoomId(); @@ -53,13 +55,9 @@ public interface IVenueInvitationEvent { */ public IQualifiedID getInviter(); - - /** - * @return subject for venue, may be empty - */ - public String getSubject(); - /** + * Get detailed invitation which includes subject and message if provided + * * @return */ public VenueInvite getInvite(); diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java index 8a54bcae50..fcb5ccaf7c 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/CollaborationXmlManager.java @@ -19,11 +19,15 @@ **/ package com.raytheon.uf.viz.collaboration.comm.provider; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.UnmarshallerHandler; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; @@ -31,14 +35,15 @@ import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.Platform; +import org.xmlpull.v1.XmlPullParser; import com.raytheon.uf.common.serialization.JAXBManager; -import com.raytheon.uf.common.serialization.SerializationException; import com.raytheon.uf.common.serialization.jaxb.JAXBClassLocator; import com.raytheon.uf.common.serialization.jaxb.JaxbDummyObject; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.core.procedures.ProcedureXmlManager; import com.raytheon.uf.viz.core.reflect.SubClassLocator; @@ -53,13 +58,14 @@ import com.raytheon.uf.viz.core.reflect.SubClassLocator; * Date Ticket# Engineer Description * ------------- -------- ----------- -------------------------- * Oct 31, 2013 2491 bsteffen Initial creation + * Dec 18, 2013 2562 bclement extend jaxb manager, xpp/fragment support * * * * @author bsteffen * @version 1.0 */ -public class CollaborationXmlManager { +public class CollaborationXmlManager extends JAXBManager { private static final transient IUFStatusHandler statusHandler = UFStatus .getHandler(CollaborationXmlManager.class); @@ -68,13 +74,24 @@ public class CollaborationXmlManager { private static CollaborationXmlManager instance; - private final JAXBManager manager; - - private CollaborationXmlManager() { - manager = initManager(); + /** + * Not for external use. Get instance using static methods. + * + * @param jaxbClasses + * @throws JAXBException + */ + protected CollaborationXmlManager(Class[] jaxbClasses) + throws JAXBException { + super(jaxbClasses); } - private JAXBManager initManager() { + /** + * Create and configure an XML manager using xmlRoot objects defined in the + * extension point {@link CollaborationXmlManager#EXTENSION_ID} + * + * @return + */ + private static CollaborationXmlManager initManager() { List> baseClasses = new ArrayList>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtensionPoint point = registry.getExtensionPoint(EXTENSION_ID); @@ -110,7 +127,7 @@ public class CollaborationXmlManager { jaxbClasses[0] = JaxbDummyObject.class; try { - return new JAXBManager(jaxbClasses); + return new CollaborationXmlManager(jaxbClasses); } catch (JAXBException e) { statusHandler.handle(Priority.PROBLEM, ProcedureXmlManager.class.getSimpleName() @@ -120,34 +137,61 @@ public class CollaborationXmlManager { } - private JAXBManager getManager() throws SerializationException { - if (manager == null) { - throw new SerializationException( - ProcedureXmlManager.class.getSimpleName() - + " Failed to initialize."); - } - return manager; - } - - public Object unmarshal(String xml) throws SerializationException { + /** + * Unmarshal to object using Xml Pull Parser as input to JAXB + * + * @param parser + * @return + * @throws CollaborationException + */ + public Object unmarshalFromXPP(XmlPullParser parser) + throws CollaborationException { + Unmarshaller unmarshaller = null; try { - return getManager().unmarshalFromXml(xml); - } catch (JAXBException e) { - throw new SerializationException(e); + unmarshaller = getUnmarshaller(); + UnmarshallerHandler handler = unmarshaller.getUnmarshallerHandler(); + PullParserJaxbAdapter adapter = new PullParserJaxbAdapter(parser, handler); + return adapter.unmarshal(); + } catch (Exception e) { + throw new CollaborationException("Unable to unmarshal data", e); + } finally { + // TODO magic number 10 because QUEUE_SIZE isn't visible + if ((unmarshaller != null) && (unmarshallers.size() < 10)) { + unmarshallers.add(unmarshaller); + } } } - public String marshal(Object obj) throws SerializationException { + /** + * Marshal object to unformatted (not pretty-printed) XML fragment (no XML + * preamble) + * + * @param obj + * @return + * @throws JAXBException + */ + public String marshalToFragment(Object obj) throws JAXBException { + Marshaller msh = getMarshaller(); try { - return getManager().marshalToXml(obj); - } catch (JAXBException e) { - throw new SerializationException(e); + StringWriter writer = new StringWriter(); + msh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); + msh.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + msh.marshal(obj, writer); + return writer.toString(); + } finally { + // TODO magic number 10 because QUEUE_SIZE isn't visible + if ((msh != null) && (marshallers.size() < 10)) { + marshallers.add(msh); + } } } + /** + * @return singleton XML manager instance + */ public static synchronized CollaborationXmlManager getInstance() { if (instance == null) { - instance = new CollaborationXmlManager(); + instance = initManager(); } return instance; } diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/PullParserJaxbAdapter.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/PullParserJaxbAdapter.java new file mode 100644 index 0000000000..a4944047be --- /dev/null +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/PullParserJaxbAdapter.java @@ -0,0 +1,292 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.viz.collaboration.comm.provider; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import javax.xml.bind.JAXBException; +import javax.xml.bind.UnmarshallerHandler; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Adapts xml pull parser to JAXB content handler. Treats XML input as fragment + * (parsed tags do not inherit namespace of tags higher in parse stack) + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Dec 17, 2013 2562       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ + +public class PullParserJaxbAdapter { + + private final XmlPullParser parser; + + private final UnmarshallerHandler handler; + + private final Set parentNamespaces = new HashSet(); + + private final int[] startAndLength = new int[2]; + + /** + * Adapts pull parser to sax locator + */ + private final Locator locator = new Locator() { + + @Override + public String getSystemId() { + return null; + } + + @Override + public String getPublicId() { + return null; + } + + @Override + public int getLineNumber() { + return parser.getLineNumber(); + } + + @Override + public int getColumnNumber() { + return parser.getColumnNumber(); + } + }; + + /** + * Adapts pull parser to sax attributes + */ + private final Attributes attributes = new Attributes() { + + @Override + public String getValue(String uri, String localName) { + int index = getIndex(uri, localName); + return index < 0 ? null : getValue(index); + } + + @Override + public String getValue(String qName) { + int index = getIndex(qName); + return index < 0 ? null : getValue(index); + } + + @Override + public String getValue(int index) { + return parser.getAttributeValue(index); + } + + @Override + public String getURI(int index) { + return parser.getAttributeNamespace(index); + } + + @Override + public String getType(String uri, String localName) { + int index = getIndex(uri, localName); + return index < 0 ? null : getType(index); + } + + @Override + public String getType(String qName) { + int index = getIndex(qName); + return index < 0 ? null : getType(index); + } + + @Override + public String getType(int index) { + return parser.getAttributeType(index); + } + + @Override + public String getQName(int index) { + String name = parser.getAttributeName(index); + String prefix = parser.getAttributePrefix(index); + if (prefix != null) { + name = prefix + ":" + name; + } + return name; + } + + @Override + public String getLocalName(int index) { + return parser.getAttributeName(index); + } + + @Override + public int getLength() { + return parser.getAttributeCount(); + } + + @Override + public int getIndex(String uri, String localName) { + for (int i = 0, len = getLength(); i < len; ++i) { + if (getLocalName(i).equals(localName) && getURI(i).equals(uri)) { + return i; + } + } + return -1; + } + + @Override + public int getIndex(String qName) { + for (int i = 0, len = getLength(); i < len; ++i) { + if (getQName(i).equals(qName)) { + return i; + } + } + return -1; + } + }; + + /** + * @param parser + * @param handler + * JAXB unmarshaller content handler + */ + public PullParserJaxbAdapter(XmlPullParser parser, UnmarshallerHandler handler) { + this.parser = parser; + this.handler = handler; + handler.setDocumentLocator(locator); + } + + /** + * Unmarshal XML from pull parser and get the result from the handler. + * Treats XML input as fragment (parsed tags do not inherit namespace of + * tags higher in parse stack) + * + * @throws SAXException + * @throws XmlPullParserException + * @throws IOException + * @throws JAXBException + * @throws IllegalStateException + */ + public Object unmarshal() throws SAXException, XmlPullParserException, + IOException, IllegalStateException, JAXBException { + for (int i = 0, count = parser.getNamespaceCount(parser.getDepth() - 1); i < count; ++i) { + parentNamespaces.add(parser.getNamespaceUri(i)); + } + + handler.startDocument(); + parseElement(); + handler.endDocument(); + return handler.getResult(); + } + + /** + * Begin namespace prefix mapping + * + * @param start + * starting index in parser for mapping + * @param end + * ending index in parser (not included in mapping) + * @throws XmlPullParserException + * @throws SAXException + */ + private void startPrefix(int start, int end) throws XmlPullParserException, + SAXException { + for (int i = start; i < end; ++i) { + String prefix = parser.getNamespacePrefix(i); + if (prefix == null) { + prefix = ""; + } + String namespaceUri = parser.getNamespaceUri(i); + handler.startPrefixMapping(prefix, namespaceUri); + } + } + + /** + * End namespace prefix mapping, ends mapping in reverse order + * + * @param start + * starting index in parser for mapping + * @param end + * ending index in parser (not included in mapping) + * @throws XmlPullParserException + * @throws SAXException + */ + private void endPrefix(int start, int end) throws XmlPullParserException, + SAXException { + for (int i = end - 1; i >= start; --i) { + String prefix = parser.getNamespacePrefix(i); + if (prefix == null) { + prefix = ""; + } + handler.endPrefixMapping(prefix); + } + } + + /** + * Recursive parsing method + * + * @throws XmlPullParserException + * @throws SAXException + * @throws IOException + */ + private void parseElement() throws XmlPullParserException, SAXException, + IOException { + + int startNs = parser.getNamespaceCount(parser.getDepth() - 1); + int endNs = parser.getNamespaceCount(parser.getDepth()); + + String name = parser.getName(); + String prefix = parser.getPrefix(); + String qname = (prefix == null ? name : prefix + ":" + name); + String ns = parser.getNamespace(); + if (parentNamespaces.contains(ns)) { + // namespace inherited from parent tag, ignore to treat xml as + // fragment + ns = ""; + } + startPrefix(startNs, endNs); + handler.startElement(ns, name, qname, attributes); + + while (true) { + switch (parser.next()) { + case XmlPullParser.TEXT: + char[] text = parser.getTextCharacters(startAndLength); + handler.characters(text, startAndLength[0], startAndLength[1]); + break; + case XmlPullParser.START_TAG: + parseElement(); + break; + case XmlPullParser.END_TAG: + handler.endElement(ns, name, qname); + endPrefix(startNs, endNs); + return; + } + } + } +} diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SerializationMode.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SerializationMode.java index cc624dcc5d..aa8a2982c9 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SerializationMode.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SerializationMode.java @@ -19,7 +19,6 @@ **/ package com.raytheon.uf.viz.collaboration.comm.provider; -import java.io.Serializable; import java.lang.annotation.Annotation; import javax.xml.bind.annotation.XmlRootElement; @@ -27,7 +26,7 @@ import javax.xml.bind.annotation.XmlRootElement; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; /** - * + * Serialization mechanisms for collaboration data * *
  * 
@@ -36,6 +35,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * Mar 23, 2012            jkorman     Initial creation
+ * Dec 18, 2013  2562      bclement    removed java serialization mode
  * 
  * 
* @@ -44,15 +44,19 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ public enum SerializationMode { - THRIFT, JAXB, JAVA, STRING, NONE, ISNULL; + THRIFT, JAXB, STRING, NONE, ISNULL; + /** + * Uses reflection to determine serialization mode for object's type + * + * @param object + * @return {@link SerializationMode#NONE} if no match found + */ public static SerializationMode getMode(Object object) { SerializationMode mode = ISNULL; if (object != null) { if (object instanceof String) { mode = STRING; - } else if (object instanceof Serializable) { - mode = JAVA; } else { // We may override the serialization type Class clazz = object.getClass(); diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayload.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayload.java new file mode 100644 index 0000000000..c2d824320f --- /dev/null +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayload.java @@ -0,0 +1,223 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.viz.collaboration.comm.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.util.Base64; + +import com.raytheon.uf.common.serialization.SerializationUtil; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; + +/** + * XMPP packet extension for collaboration session data + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Dec 11, 2013 2562       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class SessionPayload implements PacketExtension { + + private static final IUFStatusHandler log = UFStatus + .getHandler(SessionPayload.class); + + public static enum PayloadType { + Config, Command, Invitation; + }; + + public static final String XMLNS = "urn:uf:viz:collaboration"; + + public static final String ELEMENT_NAME = "SessionData"; + + public static final String TYPE_ATTRIBUTE = "payloadtype"; + + public static final String ENCODING_ATTRIBUTE = "encoding"; + + private final PayloadType payloadType; + + private final SerializationMode mode; + + private final Object data; + + /** + * @param type + * type of message + * @param mode + * encoding for message + * @param data + * message object + */ + public SessionPayload(PayloadType type, SerializationMode mode, Object data) { + this.payloadType = type; + this.mode = mode; + this.data = data; + } + + /** + * Serialization mode will be determine by reflection on object + * + * @param type + * type of message + * @param data + * message object + */ + public SessionPayload(PayloadType type, Object data) { + this(type, SerializationMode.getMode(data), data); + } + + /** + * Create collaboration extension payload XML + * + * @param type + * type of message + * @param mode + * encoding for message + * @param data + * message object + * @return XML packet fragment + * @throws CollaborationException + */ + public static String createXml(PayloadType type, SerializationMode mode, + Object data) throws CollaborationException { + StringBuilder builder = new StringBuilder(); + builder = new StringBuilder(); + builder.append("<").append(ELEMENT_NAME).append(" "); + appendAttribute(builder, "xmlns", XMLNS); + appendAttribute(builder, TYPE_ATTRIBUTE, type.name()); + appendAttribute(builder, ENCODING_ATTRIBUTE, mode.name()); + builder.append(">"); + switch (mode) { + case THRIFT: + try { + byte[] arr = SerializationUtil.transformToThrift(data); + builder.append(Base64.encodeBytes(arr)); + } catch (Exception e) { + throw new CollaborationException( + "[THRIFT] Could not serialize object", e); + } + break; + case JAXB: + try { + CollaborationXmlManager jaxb = CollaborationXmlManager + .getInstance(); + String xml = jaxb.marshalToFragment(data); + builder.append(xml); + } catch (Exception je) { + throw new CollaborationException( + "[JAXB] Could not serialize object", je); + } + break; + case STRING: + builder.append(data.toString()); + break; + case NONE: + throw new CollaborationException("Serialization of " + + data.getClass().getName() + " not supported"); + case ISNULL: + break; + } + builder.append(""); + return builder.toString(); + } + + /** + * Format XML attribute name/value pair and append to string builder + * + * @param sb + * @param name + * @param value + */ + private static void appendAttribute(StringBuilder sb, String name, + String value) { + sb.append(name).append("='").append(value).append("' "); + } + + + /** + * @return the payloadType + */ + public PayloadType getPayloadType() { + return payloadType; + } + + /** + * @return the data encoding mode + */ + public SerializationMode getMode() { + return mode; + } + + /** + * @return the data object + */ + public Object getData() { + return data; + } + + /* + * (non-Javadoc) + * + * @see org.jivesoftware.smack.packet.PacketExtension#getElementName() + */ + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + /* + * (non-Javadoc) + * + * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace() + */ + @Override + public String getNamespace() { + return XMLNS; + } + + /* + * (non-Javadoc) + * + * @see org.jivesoftware.smack.packet.PacketExtension#toXML() + */ + @Override + public String toXML() { + try { + return createXml(payloadType, mode, data); + } catch (CollaborationException e) { + log.error("Unable to marshall payload to XML", e); + // this method will be called by smack and the result appended to a + // string buffer, this will result in an empty packet which will be + // ignored + return ""; + } + } + + +} diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayloadProvider.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayloadProvider.java new file mode 100644 index 0000000000..bb1677793d --- /dev/null +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/SessionPayloadProvider.java @@ -0,0 +1,185 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.viz.collaboration.comm.provider; + +import java.io.IOException; + +import javax.xml.bind.JAXBException; + +import org.apache.commons.lang.StringUtils; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.Base64; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import com.raytheon.uf.common.serialization.SerializationException; +import com.raytheon.uf.common.serialization.SerializationUtil; +import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; + +/** + * XMPP packet extension parsing provider for collaboration session data + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Dec 16, 2013 2562       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ + +public class SessionPayloadProvider implements PacketExtensionProvider { + + + /* (non-Javadoc) + * @see org.jivesoftware.smack.provider.PacketExtensionProvider#parseExtension(org.xmlpull.v1.XmlPullParser) + */ + @Override + public PacketExtension parseExtension(XmlPullParser parser) + throws Exception { + String typeString = parser.getAttributeValue(null, + SessionPayload.TYPE_ATTRIBUTE); + String modeString = parser.getAttributeValue(null, + SessionPayload.ENCODING_ATTRIBUTE); + checkAttribute(SessionPayload.TYPE_ATTRIBUTE, typeString); + checkAttribute(SessionPayload.ENCODING_ATTRIBUTE, modeString); + + SerializationMode mode; + try { + mode = SerializationMode.valueOf(modeString); + } catch (IllegalArgumentException e) { + throw new CollaborationException("Unsupported payload encoding: " + + modeString, e); + } + PayloadType t; + try { + t = PayloadType.valueOf(typeString); + } catch (IllegalArgumentException e) { + throw new CollaborationException("Unsupported IQ payload type: " + + typeString, e); + } + Object data; + switch (mode) { + case THRIFT: + String text = getText(parser); + data = unmarshalThrift(text); + break; + case JAXB: + data = unmarshalJaxb(parser); + break; + case STRING: + data = getText(parser); + break; + default: + throw new CollaborationException("Could not deserialize object"); + } + return new SessionPayload(t, mode, data); + } + + /** + * Unmarshal base64 encoded thrift data + * + * @param data + * @return + * @throws CollaborationException + */ + public static Object unmarshalThrift(String data) + throws CollaborationException { + try { + byte[] b = Base64.decode(data); + return SerializationUtil.transformFromThrift(Object.class, b); + } catch (SerializationException e) { + throw new CollaborationException("Could not deserialize object", e); + } + } + + /** + * Unmarshal XML sub tags using JAXB + * + * @param parser + * @return + * @throws XmlPullParserException + * @throws IOException + * @throws CollaborationException + * @throws JAXBException + */ + private static Object unmarshalJaxb(XmlPullParser parser) + throws XmlPullParserException, IOException, CollaborationException, + JAXBException { + int tag = parser.next(); + if (tag != XmlPullParser.START_TAG) { + throw new CollaborationException( + "Encountered JAXB payload without XML as data"); + } + CollaborationXmlManager manager = CollaborationXmlManager.getInstance(); + return manager.unmarshalFromXPP(parser); + } + + /** + * Get any text elements under current tag + * + * @param parser + * @return + * @throws XmlPullParserException + * @throws IOException + */ + private static String getText(XmlPullParser parser) + throws XmlPullParserException, IOException { + boolean done = false; + StringBuilder payloadText = new StringBuilder(); + int tag = parser.next(); + while (!done) { + if (tag == XmlPullParser.END_TAG + && parser.getName().equals(SessionPayload.ELEMENT_NAME)) { + done = true; + continue; + } else if (parser.getEventType() == XmlPullParser.TEXT) { + payloadText.append(parser.getText()); + } + tag = parser.next(); + } + return payloadText.toString(); + } + + + /** + * Assert that value is not null or empty + * + * @param name + * used for error message + * @param value + * @throws CollaborationException + * if value is null or empty + */ + public static void checkAttribute(String name, String value) + throws CollaborationException { + if (StringUtils.isBlank(value)) { + throw new CollaborationException("Missing attribute: " + name); + } + } + +} diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/Tools.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/Tools.java index 49ee328314..3d33498cd3 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/Tools.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/Tools.java @@ -27,11 +27,6 @@ import java.util.regex.Pattern; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.util.Base64; -import com.raytheon.uf.common.serialization.SerializationException; -import com.raytheon.uf.common.serialization.SerializationUtil; -import com.raytheon.uf.viz.collaboration.comm.compression.CompressionUtil; -import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; - /** * Provides some utility methods for parsing and serializing/deserializing data. * @@ -44,7 +39,9 @@ import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; * Mar 07, 2012 jkorman Initial creation * Oct 31, 2013 2491 bsteffen Use CollaborationXmlManager for xml * serialization. - * Dec 6, 2013 2561 bclement removed ECF + * Dec 6, 2013 2561 bclement removed ECF + * Dec 16, 2013 2562 bclement moved compression to smack, + * moved marshalling to session payload * * * @@ -55,10 +52,6 @@ import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; public abstract class Tools { - public static final String TAG_INVITE = "[[INVITEID#"; - - public static final String TAG_INVITE_ID = TAG_INVITE + "%s]]%s"; - public static final String PROP_SESSION_ID = "sessionId"; public static final String CMD_PREAMBLE = "[[COMMAND#"; @@ -67,27 +60,6 @@ public abstract class Tools { public static final String DIRECTIVE_SUFFIX = "]]"; - private static final String ENV_THRIFT = CMD_PREAMBLE - + SerializationMode.THRIFT.name() + "]]"; - - private static final String ENV_THRIFT_COMPRESSED = CMD_PREAMBLE - + SerializationMode.THRIFT.name() + "-COMPRESSED]]"; - - private static final String ENV_JAXB = CMD_PREAMBLE - + SerializationMode.JAXB.name() + "]]"; - - private static final String ENV_JAXB_COMPRESSED = CMD_PREAMBLE - + SerializationMode.JAXB.name() + "-COMPRESSED]]"; - - private static final String ENV_STRING = CMD_PREAMBLE - + SerializationMode.STRING.name() + "]]"; - - private static final String ENV_JAVA = CMD_PREAMBLE - + SerializationMode.JAVA.name() + "]]"; - - private static final String ENV_NONE = CMD_PREAMBLE - + SerializationMode.NONE.name() + "]]"; - public static final String VENUE_SUBJECT_PROP = "subject"; public static final String NAME_DELIM = "@"; @@ -96,18 +68,6 @@ public abstract class Tools { public static final String RESOURCE_DELIM = "/"; - public static boolean COMPRESSION_OFF = false; - - static { - try { - COMPRESSION_OFF = Boolean - .getBoolean("collaboration.compressionOff"); - } catch (Exception e) { - // must not have permission to access system properties. ignore and - // use default. - } - } - public static final Pattern JID_RESERVED_CHARACTERS = Pattern .compile("[ \"&'/:<>@]"); @@ -235,159 +195,6 @@ public abstract class Tools { return Base64.encodeBytes(message); } - /** - * - * @param data - * @return - */ - public static String marshallData(Object data) - throws CollaborationException { - String marshalledData = null; - if (data != null) { - StringBuilder sb = new StringBuilder(); - byte[] marshalledBinary = null; - SerializationMode mode = SerializationMode.getMode(data); - switch (mode) { - case THRIFT: { - try { - if (COMPRESSION_OFF) { - marshalledBinary = SerializationUtil - .transformToThrift(data); - sb.append(ENV_THRIFT); - } else { - /* - * compress(thrift(data)) - */ - byte[] marshalledThrift = SerializationUtil - .transformToThrift(data); - marshalledBinary = CompressionUtil - .compress(marshalledThrift); - sb.append(ENV_THRIFT_COMPRESSED); - } - } catch (Exception e) { - throw new CollaborationException( - "[THRIFT] Could not serialize object", e); - } - break; - } - case JAXB: { - try { - CollaborationXmlManager jaxb = CollaborationXmlManager - .getInstance(); - if (COMPRESSION_OFF) { - String s = jaxb.marshal(data); - if (s != null) { - sb.append(ENV_JAXB); - sb.append(s); - } - } else { - String rawString = jaxb.marshal(data); - marshalledBinary = CompressionUtil.compress(rawString - .getBytes()); - sb.append(ENV_JAXB_COMPRESSED); - } - } catch (Exception je) { - throw new CollaborationException( - "[JAXB] Could not serialize object", je); - } - break; - } - case JAVA: { - break; - } - case STRING: { - sb.append(ENV_STRING); - sb.append(data); - break; - } - case NONE: { - throw new CollaborationException("Serialization of " - + data.getClass().getName() + " not supported"); - } - case ISNULL: { - break; - } - } - if (marshalledBinary != null) { - sb.append(encodeToBase64(marshalledBinary)); - } - if (sb.length() > 0) { - marshalledData = sb.toString(); - } - } - return marshalledData; - } - - /** - * - * @param data - * @return - * @throws CollaborationException - */ - public static Object unMarshallData(String data) - throws CollaborationException { - Object unMarshalledData = null; - if (data != null) { - // look for the envelope header first - if (data.startsWith(ENV_THRIFT)) { - String s = data.substring(ENV_THRIFT.length()); - try { - byte[] b = decodeFromBase64(s); - unMarshalledData = SerializationUtil.transformFromThrift( - Object.class, b); - } catch (SerializationException e) { - throw new CollaborationException( - "Could not deserialize object", e); - } - } else if (data.startsWith(ENV_THRIFT_COMPRESSED)) { - String s = data.substring(ENV_THRIFT_COMPRESSED.length()); - try { - byte[] rawBytes = decodeFromBase64(s); - byte[] uncompressedBytes = CompressionUtil - .uncompress(rawBytes); - - unMarshalledData = SerializationUtil.transformFromThrift( - Object.class, uncompressedBytes); - // unMarshalledData = SerializationUtil - // .transformFromThrift(createCompressionInputStream(rawBytes)); - } catch (Exception e) { - throw new CollaborationException( - "Could not deserialize object", e); - } - } else if (data.startsWith(ENV_JAXB)) { - String s = data.substring(ENV_JAXB.length()); - try { - unMarshalledData = CollaborationXmlManager.getInstance() - .unmarshal(s); - } catch (SerializationException je) { - throw new CollaborationException( - "[JAXB] Could not deserialize object", je); - } - } else if (data.startsWith(ENV_JAXB_COMPRESSED)) { - String rawString = data.substring(ENV_JAXB_COMPRESSED.length()); - try { - byte[] rawBytes = decodeFromBase64(rawString); - unMarshalledData = CollaborationXmlManager.getInstance() - .unmarshal( - new String(CompressionUtil - .uncompress(rawBytes))); - // unMarshalledData = SerializationUtil - // .unmarshalFromXml(createCompressionInputStream(rawBytes)); - } catch (Exception je) { - throw new CollaborationException( - "[JAXB] Could not deserialize object", je); - } - } else if (data.startsWith(ENV_STRING)) { - unMarshalledData = data.substring(ENV_STRING.length()); - } else if (data.startsWith(ENV_JAVA)) { - throw new CollaborationException("Could not deserialize object"); - } else if (data.startsWith(ENV_NONE)) { - throw new CollaborationException("Could not deserialize object"); - } - } - return unMarshalledData; - } - /** * Add properties to packet * diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/event/VenueInvitationEvent.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/event/VenueInvitationEvent.java index 8e46034156..5f8988e934 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/event/VenueInvitationEvent.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/event/VenueInvitationEvent.java @@ -33,6 +33,7 @@ import com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Mar 27, 2012 jkorman Initial creation + * Dec 18, 2013 2562 bclement removed subject (subject in invite) * * * @@ -46,8 +47,6 @@ public class VenueInvitationEvent implements IVenueInvitationEvent { private IQualifiedID invitor; - private String subject; - private VenueInvite invite; /** @@ -58,10 +57,9 @@ public class VenueInvitationEvent implements IVenueInvitationEvent { * @param body */ public VenueInvitationEvent(IQualifiedID venueId, IQualifiedID invitor, - String subject, VenueInvite invite) { + VenueInvite invite) { this.venueId = venueId; this.invitor = invitor; - this.subject = subject; this.invite = invite; } @@ -83,14 +81,6 @@ public class VenueInvitationEvent implements IVenueInvitationEvent { return invitor; } - /** - * @see com.raytheon.uf.viz.collaboration.comm.identity.event.IVenueInvitationEvent#getSubject() - */ - @Override - public String getSubject() { - return subject; - } - public VenueInvite getInvite() { return invite; } diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java index a21addcb90..3022a3184a 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/CollaborationConnection.java @@ -25,7 +25,9 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import org.apache.commons.lang.StringUtils; import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.RosterListener; @@ -36,6 +38,7 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence.Mode; import org.jivesoftware.smack.packet.Presence.Type; +import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smackx.muc.InvitationListener; import org.jivesoftware.smackx.muc.MultiUserChat; import org.jivesoftware.smackx.muc.RoomInfo; @@ -57,6 +60,9 @@ import com.raytheon.uf.viz.collaboration.comm.identity.info.IVenueInfo; import com.raytheon.uf.viz.collaboration.comm.identity.invite.SharedDisplayVenueInvite; import com.raytheon.uf.viz.collaboration.comm.identity.invite.VenueInvite; import com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayloadProvider; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.RosterChangeEvent; import com.raytheon.uf.viz.collaboration.comm.provider.event.VenueInvitationEvent; @@ -93,6 +99,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueId; * Feb 24, 2012 jkorman Initial creation * Apr 18, 2012 njensen Major cleanup * Dec 6, 2013 2561 bclement removed ECF + * Dec 18, 2013 2562 bclement added smack compression, fixed invite parsing * * * @@ -103,6 +110,12 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.VenueId; public class CollaborationConnection implements IEventPublisher { + static { + ProviderManager.getInstance().addExtensionProvider( + SessionPayload.ELEMENT_NAME, SessionPayload.XMLNS, + new SessionPayloadProvider()); + } + private static final transient IUFStatusHandler statusHandler = UFStatus .getHandler(CollaborationConnection.class); @@ -130,6 +143,17 @@ public class CollaborationConnection implements IEventPublisher { private XMPPConnection connection; + public static boolean COMPRESS = true; + + static { + try { + COMPRESS = Boolean.getBoolean("collaboration.compression"); + } catch (Exception e) { + // must not have permission to access system properties. ignore and + // use default. + } + } + private CollaborationConnection(CollaborationConnectionData connectionData) throws CollaborationException { this.connectionData = connectionData; @@ -148,7 +172,11 @@ public class CollaborationConnection implements IEventPublisher { eventBus = new EventBus(); sessions = new HashMap(); - connection = new XMPPConnection(connectionData.getServer()); + ConnectionConfiguration conConfig = new ConnectionConfiguration( + connectionData.getServer()); + conConfig.setCompressionEnabled(COMPRESS); + + connection = new XMPPConnection(conConfig); this.user = new UserId(connectionData.getUserName(), connectionData.getServer()); @@ -410,7 +438,7 @@ public class CollaborationConnection implements IEventPublisher { Collection info = new ArrayList(); if (isConnected()) { Iterator joinedRooms = MultiUserChat.getJoinedRooms( - connection, user.getNormalizedId()); + connection, connection.getUser()); while (joinedRooms.hasNext()) { String room = joinedRooms.next(); RoomInfo roomInfo; @@ -512,46 +540,66 @@ public class CollaborationConnection implements IEventPublisher { if (isConnected()) { MultiUserChat.addInvitationListener(connection, new InvitationListener() { - @Override public void invitationReceived(Connection conn, String room, String inviter, String reason, String password, Message message) { + // TODO handle password protected rooms IQualifiedID venueId = new VenueId(); venueId.setName(Tools.parseName(room)); - UserId invitor = IDConverter.convertFrom(inviter); - VenueInvite received; - try { - received = (VenueInvite) Tools - .unMarshallData(reason); - // TODO handle invite from generic client - String subject = message.getSubject(); - if (received != null && subject == null) { - subject = received.getSubject(); - if (subject == null) { - RoomInfo roomInfo = MultiUserChat - .getRoomInfo(conn, room); - subject = roomInfo.getSubject(); - } - - IVenueInvitationEvent invite = new VenueInvitationEvent( - venueId, invitor, subject, received); - eventBus.post(invite); + if (message != null) { + SessionPayload payload = (SessionPayload) message + .getExtension(SessionPayload.XMLNS); + if (payload != null) { + handleCollabInvite(venueId, invitor, + payload); + return; } - } catch (Exception e) { - statusHandler - .handle(Priority.PROBLEM, - "Error handling received invite message", - e); } - + if ( reason.startsWith(Tools.CMD_PREAMBLE)){ + reason = "Shared display invitation from incompatible version of CAVE. " + + "Session will be chat-only if invitation is accepted"; + } + handleChatRoomInvite(venueId, invitor, reason, + message); } }); } } + private void handleChatRoomInvite(IQualifiedID venueId, UserId invitor, + String reason, Message message) { + VenueInvite invite = new VenueInvite(); + if (!StringUtils.isBlank(reason)) { + invite.setMessage(reason); + } else if (!StringUtils.isBlank(message.getBody())) { + invite.setMessage(message.getBody()); + } else { + invite.setMessage(""); + } + invite.setSubject(message.getSubject()); + IVenueInvitationEvent event = new VenueInvitationEvent(venueId, + invitor, invite); + eventBus.post(event); + } + + private void handleCollabInvite(IQualifiedID venueId, UserId invitor, + SessionPayload payload) { + Object obj = payload.getData(); + if (obj == null + || !payload.getPayloadType().equals(PayloadType.Invitation) + || !(obj instanceof VenueInvite)) { + statusHandler.warn("Received unsupported invite payload"); + return; + } + VenueInvite invite = (VenueInvite) obj; + IVenueInvitationEvent event = new VenueInvitationEvent(venueId, + invitor, invite); + eventBus.post(event); + } + /** * Register an event handler with this * diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java index fb455d2f1e..2e255d97ed 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/PeerToPeerCommHelper.java @@ -27,12 +27,12 @@ import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.viz.collaboration.comm.Activator; -import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; import com.raytheon.uf.viz.collaboration.comm.identity.ISession; import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpdCollaborationConfigurationEvent; import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpdXmppMessage; import com.raytheon.uf.viz.collaboration.comm.identity.event.ITextMessageEvent; import com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; import com.raytheon.uf.viz.collaboration.comm.provider.TextMessage; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.ChatMessageEvent; @@ -50,6 +50,8 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.IDConverter; * ------------ ---------- ----------- -------------------------- * Mar 28, 2012 jkorman Initial creation * Dec 6, 2013 2561 bclement removed ECF + * Dec 18, 2013 2562 bclement added timeout for HTTP config, + * data now in packet extension * * * @@ -65,14 +67,30 @@ public class PeerToPeerCommHelper implements PacketListener { private static Object httpServerLockObj = new Object(); private static String httpServer; + + private static long HTTP_SERVER_TIMEOUT = 10 * 1000; // 10 seconds + /** + * Get HTTP server address. This method blocks until the address has been + * received from the chat server or a timeout has expired. + * + * @return + */ public static String getCollaborationHttpServer() { /** * Wait for initialization of field httpServer. */ synchronized (httpServerLockObj) { + long start = System.currentTimeMillis(); try { while (httpServer == null) { + if (System.currentTimeMillis() - start > HTTP_SERVER_TIMEOUT) { + // TODO should we get fallback server address from + // localization? + statusHandler + .error("HTTP URL configuration not received from server"); + break; + } httpServerLockObj.wait(500); } } catch (InterruptedException e) { @@ -111,37 +129,48 @@ public class PeerToPeerCommHelper implements PacketListener { return; } String body = msg.getBody(); - Activator.getDefault().getNetworkStats() - .log(Activator.PEER_TO_PEER, 0, body.length()); if (body != null) { - if (body.startsWith(Tools.CMD_PREAMBLE)) { - routeData(msg); - } else if (body.startsWith(Tools.CONFIG_PREAMBLE)) { + Activator.getDefault().getNetworkStats() + .log(Activator.PEER_TO_PEER, 0, body.length()); + if (body.startsWith(Tools.CONFIG_PREAMBLE)) { + // TODO Legacy config support + body = body.substring(Tools.CONFIG_PREAMBLE.length(), + body.length() - Tools.DIRECTIVE_SUFFIX.length()); this.handleConfiguration(body); } else { // anything else pass to the normal text routeMessage(msg); } + } else { + SessionPayload payload = (SessionPayload) msg + .getExtension(SessionPayload.XMLNS); + if (payload != null) { + switch (payload.getPayloadType()) { + case Command: + routeData(payload, + (String) msg.getProperty(Tools.PROP_SESSION_ID)); + break; + case Config: + handleConfiguration(payload.getData().toString()); + break; + default: + // do nothing + } + } } } - } + /** + * Post data as event either to associated session, or general event bus * - * @param message + * @param payload + * @param sessionId */ - private void routeData(Message message) { - Object object = null; - try { - object = Tools.unMarshallData(message.getBody()); - } catch (CollaborationException e) { - statusHandler.handle(Priority.PROBLEM, - "Error unmarshalling PeerToPeer data", e); - } + private void routeData(SessionPayload payload, String sessionId) { + Object object = payload.getData(); if (object != null) { - String sessionId = (String) message - .getProperty(Tools.PROP_SESSION_ID); if (sessionId == null) { manager.postEvent(object); } else { @@ -158,6 +187,7 @@ public class PeerToPeerCommHelper implements PacketListener { } /** + * Post text message to chat * * @param message */ @@ -190,6 +220,11 @@ public class PeerToPeerCommHelper implements PacketListener { } } + /** + * Parse server configuration and notify general event bus of config event + * + * @param body + */ private void handleConfiguration(String body) { // Determine if an error has occurred. if (IHttpdXmppMessage.configErrorPattern.matcher(body).matches()) { @@ -237,18 +272,23 @@ public class PeerToPeerCommHelper implements PacketListener { manager.postEvent(configurationEvent); } + /** + * Parse config parameter value from key:value string + * + * @param body + * @param parameterName + * @return + */ private String getCollaborationConfigurationParameterValue(String body, String parameterName) { - // Eliminate the preamble. - String encodedConfiguration = body.replace(Tools.CONFIG_PREAMBLE, ""); - // Eliminate the suffix: ]] - encodedConfiguration = encodedConfiguration.substring(0, - encodedConfiguration.length() - 2); // Remove the parameter name. - return encodedConfiguration.replace(parameterName + " :", "").trim(); + return body.replace(parameterName + " :", "").trim(); } + /** + * Notify general event bus that shared display is disabled + */ private void disableSharedDisplaySession() { // ensure that the shared session displays will be disabled IHttpdCollaborationConfigurationEvent configurationEvent = new HttpdCollaborationConfigurationEvent( diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java index 352769147e..1495dc390d 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/SharedDisplaySession.java @@ -19,11 +19,45 @@ **/ package com.raytheon.uf.viz.collaboration.comm.provider.session; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.packet.DiscoverInfo; +import org.jivesoftware.smackx.pubsub.AccessModel; +import org.jivesoftware.smackx.pubsub.Affiliation; +import org.jivesoftware.smackx.pubsub.ConfigureForm; +import org.jivesoftware.smackx.pubsub.FormType; +import org.jivesoftware.smackx.pubsub.Item; +import org.jivesoftware.smackx.pubsub.ItemDeleteEvent; +import org.jivesoftware.smackx.pubsub.ItemPublishEvent; +import org.jivesoftware.smackx.pubsub.LeafNode; +import org.jivesoftware.smackx.pubsub.NodeType; +import org.jivesoftware.smackx.pubsub.PayloadItem; +import org.jivesoftware.smackx.pubsub.PubSubManager; +import org.jivesoftware.smackx.pubsub.PublishModel; +import org.jivesoftware.smackx.pubsub.Subscription; +import org.jivesoftware.smackx.pubsub.Subscription.State; +import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener; +import org.jivesoftware.smackx.pubsub.listener.ItemEventListener; +import org.jivesoftware.smackx.pubsub.packet.SyncPacketSend; + import com.google.common.eventbus.EventBus; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.viz.collaboration.comm.identity.CollaborationException; +import com.raytheon.uf.viz.collaboration.comm.identity.ISession; import com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession; +import com.raytheon.uf.viz.collaboration.comm.identity.info.IVenueInfo; import com.raytheon.uf.viz.collaboration.comm.identity.user.SharedDisplayRole; -import com.raytheon.uf.viz.collaboration.comm.provider.TextMessage; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId; @@ -37,6 +71,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Apr 18, 2012 njensen Initial creation + * Dec 18, 2013 2562 bclement moved data to packet extension * * * @@ -45,46 +80,122 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId; */ public class SharedDisplaySession extends VenueSession implements - ISharedDisplaySession { + ISharedDisplaySession, ItemEventListener, ItemDeleteListener { + + private static final transient IUFStatusHandler log = UFStatus + .getHandler(SharedDisplaySession.class); private UserId sessionLeader = null; private UserId dataProvider = null; + private PubSubManager pubsubMgr; + + private LeafNode topic; + + private XMPPConnection conn; + public SharedDisplaySession(EventBus externalBus, CollaborationConnection manager) throws CollaborationException { super(externalBus, manager); + init(); } public SharedDisplaySession(EventBus externalBus, CollaborationConnection manager, String sessionId) throws CollaborationException { super(externalBus, manager, sessionId); + init(); } - @Override - public void sendObjectToVenue(Object obj) throws CollaborationException { - if (obj != null) { - String message = Tools.marshallData(obj); - if (message != null) { - sendMessageToVenue(message); + /** + * perform setup/management tasks before connecting to venue + */ + private void init() { + this.conn = getConnection().getXmppConnection(); + pubsubMgr = new PubSubManager(conn); + cleanupOldTopics(); + } + + /** + * Delete orphaned topic from sessions this user started that are not + * currently running on this client + */ + private void cleanupOldTopics() { + Collection sessions = getConnection().getSessions(); + Set sessionIds = new HashSet(sessions.size()); + for ( ISession s : sessions){ + sessionIds.add(s.getSessionId()); + } + List affiliations; + try { + affiliations = pubsubMgr.getAffiliations(); + } catch (XMPPException e) { + // openfire returns a 404 if there are no topic affiliations for + // this user. Smack turns that into an exception. + log.debug("Unable to get affiliations from server: " + + e.getLocalizedMessage()); + return; + } + for (Affiliation aff : affiliations) { + if (aff.getType().equals( + org.jivesoftware.smackx.pubsub.Affiliation.Type.owner)) { + if (!sessionIds.contains(aff.getNodeId())) { + // TODO should we check to see if the topic is a certain age + // before cleanup? + log.info("Deleting old topic: " + aff.getNodeId()); + try { + pubsubMgr.deleteNode(aff.getNodeId()); + } catch (XMPPException e) { + log.error( + "Problem cleaning up old topic: " + + aff.getNodeId(), e); + } + } } } } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession + * #sendObjectToVenue(java.lang.Object) + */ + @Override + public void sendObjectToVenue(Object obj) throws CollaborationException { + if (obj == null) { + return; + } + SessionPayload payload = new SessionPayload(PayloadType.Command, obj); + topic.publish(new PayloadItem(payload)); + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession + * #sendObjectToPeer + * (com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID, + * java.lang.Object) + */ @Override public void sendObjectToPeer( com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID participant, Object obj) throws CollaborationException { - PeerToPeerChat session = getP2PSession(); - if (session != null) { - String message = Tools.marshallData(obj); - if (message != null) { - TextMessage msg = new TextMessage(participant, message); - msg.setProperty(Tools.PROP_SESSION_ID, getSessionId()); - session.sendPeerToPeer(msg); - } + // TODO should only send to CAVE clients + if (obj == null) { + return; } + SessionPayload payload = new SessionPayload(PayloadType.Command, obj); + Message msg = new Message(participant.getFQName(), Type.normal); + msg.addExtension(payload); + msg.setFrom(conn.getUser()); + msg.setProperty(Tools.PROP_SESSION_ID, getSessionId()); + conn.sendPacket(msg); } /** @@ -125,13 +236,240 @@ public class SharedDisplaySession extends VenueSession implements return result; } + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession + * #setCurrentSessionLeader + * (com.raytheon.uf.viz.collaboration.comm.provider.user.UserId) + */ @Override public void setCurrentSessionLeader(UserId id) { sessionLeader = id; } + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.identity.ISharedDisplaySession + * #setCurrentDataProvider + * (com.raytheon.uf.viz.collaboration.comm.provider.user.UserId) + */ @Override public void setCurrentDataProvider(UserId id) { dataProvider = id; } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.provider.session.VenueSession# + * configureVenue(java.lang.String) + */ + @Override + protected IVenueInfo configureVenue(String venueName) + throws CollaborationException { + IVenueInfo rval = super.configureVenue(venueName); + try { + configureSubscription(); + } catch (XMPPException e) { + throw new CollaborationException( + "Unable to configure subscription", e); + } + return rval; + } + + /** + * Setup data subscription for this client + * + * @throws XMPPException + * @throws CollaborationException + */ + private void configureSubscription() throws XMPPException, + CollaborationException { + topic = pubsubMgr.getNode(getSessionId()); + topic.addItemEventListener(this); + topic.addItemDeleteListener(this); + Subscription sub = findSubscription(getUserID()); + if (sub == null) { + sub = topic.subscribe(conn.getUser()); + } + if ( !sub.getState().equals(State.subscribed)){ + String msg = String.format( + "Problem subscribing to topic %s. Current state: %s", + topic.getId(), sub.getState().toString()); + throw new CollaborationException(msg); + } + } + + /** + * Find subscriptions this user has on the current topic + * + * @param user + * @return null if none found + * @throws XMPPException + */ + protected Subscription findSubscription(UserId user) throws XMPPException { + Subscription rval = null; + for (Subscription sub : topic.getSubscriptions()) { + if (user.isSameUser(sub.getJid())) { + log.info("found sub: " + sub.toXML()); + rval = sub; + } + } + return rval; + } + + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.provider.session.VenueSession# + * createVenue(java.lang.String, java.lang.String) + */ + @Override + protected IVenueInfo createVenue(String venueName, String subject) + throws CollaborationException { + IVenueInfo rval = super.createVenue(venueName, subject); + try { + createNode(getSessionId()); + } catch (XMPPException e) { + throw new CollaborationException("Unable to create topic", e); + } + try { + configureSubscription(); + } catch (XMPPException e) { + throw new CollaborationException( + "Unable to configure subscription", e); + } + return rval; + } + + /** + * Create and configure new topic for data subscriptions + * + * @param topicName + * @throws XMPPException + */ + private void createNode(String topicName) throws XMPPException { + ConfigureForm form = new ConfigureForm(FormType.submit); + form.setAccessModel(AccessModel.open); + form.setDeliverPayloads(true); + form.setNodeType(NodeType.leaf); + form.setTitle(topicName); + form.setSubscribe(true); + form.setPublishModel(PublishModel.open); + form.setPresenceBasedDelivery(false); + form.setPersistentItems(false); + form.setNotifyDelete(true); + form.setNotifyConfig(true); + form.setNotifyRetract(true); + form.setMaxPayloadSize(Short.MAX_VALUE); + pubsubMgr.createNode(topicName, form); + } + + /* + * (non-Javadoc) + * + * @see org.jivesoftware.smackx.pubsub.listener.ItemEventListener# + * handlePublishedItems(org.jivesoftware.smackx.pubsub.ItemPublishEvent) + */ + @Override + public void handlePublishedItems(ItemPublishEvent items) { + for (Item i : items.getItems()) { + if (i instanceof PayloadItem) { + PacketExtension payload = ((PayloadItem) i).getPayload(); + handlePayload(payload); + } + } + } + + /** + * Process session data payload + * + * @param payload + */ + private void handlePayload(PacketExtension payload) { + if (payload instanceof SessionPayload) { + SessionPayload sp = (SessionPayload) payload; + Object obj = sp.getData(); + postEvent(obj); + } + } + + /** + * @return true if session data topic exists + */ + private boolean topicExists() { + try { + DiscoverInfo info = new DiscoverInfo(); + info.setTo("pubsub." + conn.getServiceName()); + info.setNode(topic.getId()); + SyncPacketSend.getReply(conn, info); + return true; + } catch (XMPPException e) { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.provider.session.VenueSession# + * close() + */ + @Override + public void close() { + super.close(); + if (pubsubMgr == null || topic == null || !topicExists()) { + return; + } + try { + Subscription sub = findSubscription(getUserID()); + if (sub == null) { + return; + } + topic.unsubscribe(sub.getJid(), sub.getId()); + topic.removeItemDeleteListener(this); + topic.removeItemEventListener(this); + if (hasRole(SharedDisplayRole.SESSION_LEADER)) { + pubsubMgr.deleteNode(topic.getId()); + } + topic = null; + pubsubMgr = null; + } catch (XMPPException e) { + log.error("Unable to close subscription", e); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener#handleDeletedItems + * (org.jivesoftware.smackx.pubsub.ItemDeleteEvent) + */ + @Override + public void handleDeletedItems(ItemDeleteEvent items) { + // TODO this method should be able to tell the client if the topic is + // deleted, either smack or openfire doesn't support that feature, so we + // use topicExists() instead + } + + /* + * (non-Javadoc) + * + * @see + * org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener#handlePurge() + */ + @Override + public void handlePurge() { + // we don't persist data packets to topics + } + } diff --git a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java index eb018271e9..a1c114c34d 100644 --- a/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java +++ b/cave/com.raytheon.uf.viz.collaboration.comm/src/com/raytheon/uf/viz/collaboration/comm/provider/session/VenueSession.java @@ -19,17 +19,22 @@ **/ package com.raytheon.uf.viz.collaboration.comm.provider.session; +import java.util.Iterator; import java.util.List; +import org.apache.commons.lang.StringUtils; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smackx.Form; +import org.jivesoftware.smackx.ServiceDiscoveryManager; import org.jivesoftware.smackx.muc.MultiUserChat; import org.jivesoftware.smackx.muc.ParticipantStatusListener; +import org.jivesoftware.smackx.packet.DiscoverItems; import com.google.common.eventbus.EventBus; import com.raytheon.uf.common.status.IUFStatusHandler; @@ -44,6 +49,8 @@ import com.raytheon.uf.viz.collaboration.comm.identity.info.IVenue; import com.raytheon.uf.viz.collaboration.comm.identity.info.IVenueInfo; import com.raytheon.uf.viz.collaboration.comm.identity.invite.VenueInvite; import com.raytheon.uf.viz.collaboration.comm.provider.CollaborationMessage; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; +import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload.PayloadType; import com.raytheon.uf.viz.collaboration.comm.provider.TextMessage; import com.raytheon.uf.viz.collaboration.comm.provider.Tools; import com.raytheon.uf.viz.collaboration.comm.provider.event.VenueParticipantEvent; @@ -75,6 +82,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId; * Feb 24, 2012 jkorman Initial creation * Apr 17, 2012 njensen Major refactor * Dec 6, 2013 2561 bclement removed ECF + * Dec 18, 2013 2562 bclement moved data to packet extension * * * @@ -90,8 +98,6 @@ public class VenueSession extends BaseSession implements IVenueSession { private static final transient IUFStatusHandler statusHandler = UFStatus .getHandler(VenueSession.class); - private static final String SEND_CMD = "[[COMMAND"; - private static final String SEND_TXT = "[[TEXT]]"; public static final String SEND_HISTORY = "[[HISTORY]]"; @@ -120,8 +126,8 @@ public class VenueSession extends BaseSession implements IVenueSession { * @param container * @param eventBus */ - protected VenueSession(EventBus externalBus, - CollaborationConnection manager) throws CollaborationException { + protected VenueSession(EventBus externalBus, CollaborationConnection manager) + throws CollaborationException { super(externalBus, manager); } @@ -179,8 +185,20 @@ public class VenueSession extends BaseSession implements IVenueSession { @Override public void sendInvitation(UserId id, VenueInvite invite) throws CollaborationException { - String msgBody = Tools.marshallData(invite); - muc.invite(id.getNormalizedId(), msgBody); + SessionPayload payload = new SessionPayload(PayloadType.Invitation, + invite); + Message msg = new Message(); + msg.setTo(id.getNormalizedId()); + msg.setFrom(getUserID().getNormalizedId()); + msg.setType(Type.normal); + msg.addExtension(payload); + String reason = ""; + if (!StringUtils.isBlank(invite.getMessage())) { + reason = invite.getMessage(); + } else if (!StringUtils.isBlank(invite.getSubject())) { + reason = invite.getSubject(); + } + muc.invite(msg, id.getNormalizedId(), reason); } /** @@ -209,21 +227,12 @@ public class VenueSession extends BaseSession implements IVenueSession { @Override public void sendChatMessage(String message) throws CollaborationException { - this.sendMessageToVenue(message); - } - - protected void sendMessageToVenue(String message) - throws CollaborationException { // Assume success if ((muc != null) && (message != null)) { Activator.getDefault().getNetworkStats() .log(Activator.VENUE, message.length(), 0); try { - if (message.startsWith(SEND_CMD)) { - muc.sendMessage(message); - } else { - muc.sendMessage(SEND_TXT + message); - } + muc.sendMessage(message); } catch (XMPPException e) { throw new CollaborationException("Error sending messge", e); } @@ -256,7 +265,7 @@ public class VenueSession extends BaseSession implements IVenueSession { * @param roomName * @return */ - private String getRoomId(String host, String roomName){ + private String getRoomId(String host, String roomName) { return roomName + "@conference." + host; } @@ -275,10 +284,12 @@ public class VenueSession extends BaseSession implements IVenueSession { CollaborationConnection manager = getSessionManager(); XMPPConnection conn = manager.getXmppConnection(); String roomId = getRoomId(conn.getHost(), venueName); + if (roomExistsOnServer(roomId)) { + throw new CollaborationException("Session name already in use"); + } this.muc = new MultiUserChat(conn, roomId); createListeners(); UserId user = manager.getUser(); - // TODO check if room already exists muc.create(user.getName()); muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT)); muc.changeSubject(subject); @@ -291,6 +302,29 @@ public class VenueSession extends BaseSession implements IVenueSession { } } + /** + * @param roomId + * @return true if room exists on server + * @throws XMPPException + */ + protected boolean roomExistsOnServer(String roomId) throws XMPPException { + String host = Tools.parseHost(roomId); + CollaborationConnection manager = getSessionManager(); + XMPPConnection conn = manager.getXmppConnection(); + ServiceDiscoveryManager serviceDiscoveryManager = new ServiceDiscoveryManager( + conn); + DiscoverItems result = serviceDiscoveryManager.discoverItems(host); + + for (Iterator items = result.getItems(); items + .hasNext();) { + DiscoverItems.Item item = items.next(); + if (roomId.equals(item.getEntityID())) { + return true; + } + } + return false; + } + /** * register chat room listeners with muc */ @@ -476,6 +510,9 @@ public class VenueSession extends BaseSession implements IVenueSession { if (!body.startsWith(SEND_HISTORY) && fromUser.isSameUser(account)) { // ignore from ourselves except for history return false; + } else if (body.startsWith(Tools.CMD_PREAMBLE) + || body.startsWith(Tools.CONFIG_PREAMBLE)) { + return false; } return true; @@ -490,28 +527,7 @@ public class VenueSession extends BaseSession implements IVenueSession { String body = message.getBody(); if (body != null) { - if (body.startsWith(SEND_CMD)) { - Object o = null; - try { - o = Tools.unMarshallData(body); - if (o != null) { - this.postEvent(o); - } - } catch (CollaborationException ce) { - statusHandler.error( - "Error deserializing received message on venue " - + muc.getRoom(), ce); - } - } else if (body.startsWith(SEND_TXT)) { - body = body.substring(SEND_TXT.length()); - message.setBody(body); - - TextMessage msg = new TextMessage(message.getTo(), - message.getBody()); - msg.setFrom(message.getFrom()); - - this.postEvent(msg); - } else if (body.startsWith(SEND_HISTORY)) { + if (body.startsWith(SEND_HISTORY)) { String[] vars = body.split("\\|"); String timeString = vars[0] .substring(SEND_HISTORY.length()); @@ -523,7 +539,7 @@ public class VenueSession extends BaseSession implements IVenueSession { // three pipe characters String moddedBody = body.substring(SEND_HISTORY.length() + timeString.length() + username.length() - + site.length() + SEND_TXT.length() + 3); + + site.length() + 3); message.setBody(moddedBody); TextMessage msg = new TextMessage(message.getFrom(), message.getBody()); @@ -535,9 +551,6 @@ public class VenueSession extends BaseSession implements IVenueSession { msg.setStatus(SEND_HISTORY); this.postEvent(msg); } else { - // attempt to handle outside clients as text only since the - // SEND_TXT won't be appended to the first portion of the - // body message.setBody(body); TextMessage msg = new TextMessage(message.getTo(), message.getBody()); @@ -561,12 +574,22 @@ public class VenueSession extends BaseSession implements IVenueSession { String body = msg.getBody(); if (body != null) { + if (body.startsWith(SEND_TXT)) { + body = body.substring(SEND_TXT.length()); + } message = new CollaborationMessage(null, body); message.setFrom(IDConverter.convertFromRoom(muc, msg.getFrom())); } return message; } + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.viz.collaboration.comm.identity.IVenueSession#sendPresence + * (org.jivesoftware.smack.packet.Presence) + */ @Override public void sendPresence(Presence presence) throws CollaborationException { presence.setTo(venue.getInfo().getVenueID()); diff --git a/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingResource.java b/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingResource.java index 1e180b2ecf..d51c57a9bc 100644 --- a/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingResource.java +++ b/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingResource.java @@ -287,7 +287,6 @@ public class CollaborationDrawingResource extends event.setType(lockingDrawing ? CollaborationEventType.LOCK_USERS : CollaborationEventType.UNLOCK_USERS); sendEvent(event); - handleDrawEvent(event); } } } diff --git a/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingToolLayer.java b/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingToolLayer.java index 75a399efe7..da2a48347e 100644 --- a/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingToolLayer.java +++ b/cave/com.raytheon.uf.viz.collaboration.display/src/com/raytheon/uf/viz/collaboration/display/rsc/telestrator/CollaborationDrawingToolLayer.java @@ -153,7 +153,6 @@ public class CollaborationDrawingToolLayer extends DrawingToolLayer { event.setCoordinates(new ArrayList(coordinates)); resource.sendEvent(event); coordinates.clear(); - resource.handleDrawEvent(event); } private void sendSimpleEvent(CollaborationEventType type) { @@ -162,7 +161,6 @@ public class CollaborationDrawingToolLayer extends DrawingToolLayer { event.setType(type); event.setUserName(resource.getMyUser()); resource.sendEvent(event); - resource.handleDrawEvent(event); } public Stack> getUndoStack() { diff --git a/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/ConnectionSubscriber.java b/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/ConnectionSubscriber.java index a1093405d1..13890d6bce 100644 --- a/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/ConnectionSubscriber.java +++ b/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/ConnectionSubscriber.java @@ -39,6 +39,7 @@ import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpdCollaboration import com.raytheon.uf.viz.collaboration.comm.identity.event.ITextMessageEvent; import com.raytheon.uf.viz.collaboration.comm.identity.event.IVenueInvitationEvent; import com.raytheon.uf.viz.collaboration.comm.identity.invite.SharedDisplayVenueInvite; +import com.raytheon.uf.viz.collaboration.comm.identity.invite.VenueInvite; import com.raytheon.uf.viz.collaboration.comm.identity.user.IQualifiedID; import com.raytheon.uf.viz.collaboration.comm.identity.user.SharedDisplayRole; import com.raytheon.uf.viz.collaboration.comm.provider.TextMessage; @@ -65,6 +66,7 @@ import com.raytheon.viz.ui.views.CaveWorkbenchPageManager; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jun 8, 2012 njensen Initial creation + * Dec 18, 2013 2562 bclement fixed venue invite * * * @@ -159,17 +161,17 @@ public class ConnectionSubscriber { } @Subscribe - public void handleInvitationEvent(IVenueInvitationEvent event) { - final IVenueInvitationEvent invitation = event; + public void handleInvitationEvent(final IVenueInvitationEvent event) { VizApp.runAsync(new Runnable() { @Override public void run() { - IQualifiedID inviter = invitation.getInviter(); - IQualifiedID room = invitation.getRoomId(); + IQualifiedID inviter = event.getInviter(); + IQualifiedID room = event.getRoomId(); Shell shell = new Shell(Display.getCurrent()); StringBuilder sb = new StringBuilder(); - boolean sharedDisplay = invitation.getInvite() instanceof SharedDisplayVenueInvite; + VenueInvite invite = event.getInvite(); + boolean sharedDisplay = invite instanceof SharedDisplayVenueInvite; sb.append("You are invited to a "); if (sharedDisplay) { sb.append("collaboration session."); @@ -177,8 +179,8 @@ public class ConnectionSubscriber { sb.append("chat room."); } InviteDialog inviteBox = new InviteDialog(shell, inviter - .getName(), invitation.getSubject(), room.getName(), sb - .toString(), invitation.getInvite().getMessage()); + .getName(), invite.getSubject(), room.getName(), sb + .toString(), invite.getMessage()); if (!(Boolean) inviteBox.open()) { return; } @@ -186,33 +188,27 @@ public class ConnectionSubscriber { CollaborationConnection connection = CollaborationConnection .getConnection(); try { - IVenueSession session = connection - .joinCollaborationVenue(invitation); - String sessionId = session.getSessionId(); + IVenueSession session; if (sharedDisplay) { + session = connection.joinCollaborationVenue(event); ISharedDisplaySession displaySession = (ISharedDisplaySession) session; SessionColorManager man = new SessionColorManager(); SharedDisplaySessionMgr.joinSession(displaySession, SharedDisplayRole.PARTICIPANT, man); CaveWorkbenchPageManager.getActiveInstance().showView( - CollaborationSessionView.ID, sessionId, + CollaborationSessionView.ID, session.getSessionId(), IWorkbenchPage.VIEW_ACTIVATE); } else { + session = connection.joinTextOnlyVenue(room.getName()); CaveWorkbenchPageManager.getActiveInstance().showView( - SessionView.ID, sessionId, + SessionView.ID, session.getSessionId(), IWorkbenchPage.VIEW_ACTIVATE); } } catch (CollaborationException e) { - // TODO Auto-generated catch block. Please revise as - // appropriate. - statusHandler.handle(Priority.PROBLEM, - e.getLocalizedMessage(), e); + statusHandler.error("Unable to join session venue", e); } catch (PartInitException e) { - // TODO Auto-generated catch block. Please revise as - // appropriate. - statusHandler.handle(Priority.PROBLEM, - e.getLocalizedMessage(), e); + statusHandler.error("Unable to display session view", e); } } }); diff --git a/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/session/CollaborationSessionView.java b/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/session/CollaborationSessionView.java index 7b1f5a8c78..6ec152be9d 100644 --- a/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/session/CollaborationSessionView.java +++ b/cave/com.raytheon.uf.viz.collaboration.ui/src/com/raytheon/uf/viz/collaboration/ui/session/CollaborationSessionView.java @@ -210,7 +210,6 @@ public class CollaborationSessionView extends SessionView implements ColorChangeEvent event = new ColorChangeEvent(entry, rgb); try { session.sendObjectToVenue(event); - CollaborationSessionView.this.modifyColors(event); } catch (CollaborationException e) { statusHandler.handle(Priority.PROBLEM, "Unable to send color change to venue", e); diff --git a/cots/org.jivesoftware.smack/META-INF/MANIFEST.MF b/cots/org.jivesoftware.smack/META-INF/MANIFEST.MF index cfe7bfdae6..5803e7befd 100644 --- a/cots/org.jivesoftware.smack/META-INF/MANIFEST.MF +++ b/cots/org.jivesoftware.smack/META-INF/MANIFEST.MF @@ -58,4 +58,5 @@ Export-Package: org.jivesoftware.smack, org.jivesoftware.smackx.workgroup.packet, org.jivesoftware.smackx.workgroup.settings, org.jivesoftware.smackx.workgroup.user, - org.jivesoftware.smackx.workgroup.util + org.jivesoftware.smackx.workgroup.util, + org.xmlpull.v1