Issue #2756 new collaboration openfire plugin and http service

new servlet http service that communicates with openfire for configuration
new openfire plugin to reflect new http service
openfire and http service no longer need to be on same machine
moved http header parsing code from ogc to common util
updated collaboration http code to prefer XML instead of HTML


Former-commit-id: f6b2827434 [formerly ff23569984eee43927c10ca041ca7e0ca4d40a3b]
Former-commit-id: 5b4f1e8288
This commit is contained in:
Brian Clements 2014-02-11 16:06:03 -06:00
parent 996663ccee
commit a1d9e40bd4
64 changed files with 3138 additions and 1041 deletions

View file

@ -23,7 +23,7 @@ import java.util.regex.Pattern;
/** /**
* Used to store constants that are used to validate, analyze, and parse status * Used to store constants that are used to validate, analyze, and parse status
* and configuration messages associated with the AWIPS II httpd collaboration * and configuration messages associated with the AWIPS II http collaboration
* server. * server.
* *
* <pre> * <pre>
@ -34,6 +34,8 @@ import java.util.regex.Pattern;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 7, 2012 bkowal Initial creation * Aug 7, 2012 bkowal Initial creation
* Dec 18, 2013 2562 bclement removed preamble from patterns * Dec 18, 2013 2562 bclement removed preamble from patterns
* Feb 17, 2014 2756 bclement removed URL regex that was too specific
* renamed to remove httpd reference
* *
* </pre> * </pre>
* *
@ -41,7 +43,7 @@ import java.util.regex.Pattern;
* @version 1.0 * @version 1.0
*/ */
public interface IHttpdXmppMessage { public interface IHttpXmppMessage {
// Constant Strings // Constant Strings
public static final String URL_PARAMETER_NAME = "sessionDataHttpURL"; public static final String URL_PARAMETER_NAME = "sessionDataHttpURL";
@ -49,8 +51,6 @@ public interface IHttpdXmppMessage {
static final String SUFFIX_REGEX = " : .+"; static final String SUFFIX_REGEX = " : .+";
static final String COLLABORATION_URL_REGEX = "http://.+:[1-9][0-9]*/session_data/";
// Regex Patterns // Regex Patterns
public static final Pattern configErrorPattern = Pattern public static final Pattern configErrorPattern = Pattern
.compile(ERROR_PARAMETER_NAME + SUFFIX_REGEX); .compile(ERROR_PARAMETER_NAME + SUFFIX_REGEX);
@ -58,6 +58,4 @@ public interface IHttpdXmppMessage {
public static final Pattern configURLPattern = Pattern public static final Pattern configURLPattern = Pattern
.compile(URL_PARAMETER_NAME + SUFFIX_REGEX); .compile(URL_PARAMETER_NAME + SUFFIX_REGEX);
public static final Pattern urlPattern = Pattern
.compile(COLLABORATION_URL_REGEX);
} }

View file

@ -19,6 +19,9 @@
**/ **/
package com.raytheon.uf.viz.collaboration.comm.provider.session; package com.raytheon.uf.viz.collaboration.comm.provider.session;
import java.net.URI;
import java.net.URL;
import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Packet;
@ -27,9 +30,10 @@ import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.viz.collaboration.comm.Activator; 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.ISession;
import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpXmppMessage;
import com.raytheon.uf.viz.collaboration.comm.identity.event.IHttpdCollaborationConfigurationEvent; 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.event.ITextMessageEvent;
import com.raytheon.uf.viz.collaboration.comm.identity.user.IUser; import com.raytheon.uf.viz.collaboration.comm.identity.user.IUser;
import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload; import com.raytheon.uf.viz.collaboration.comm.provider.SessionPayload;
@ -38,6 +42,7 @@ import com.raytheon.uf.viz.collaboration.comm.provider.Tools;
import com.raytheon.uf.viz.collaboration.comm.provider.event.ChatMessageEvent; import com.raytheon.uf.viz.collaboration.comm.provider.event.ChatMessageEvent;
import com.raytheon.uf.viz.collaboration.comm.provider.event.HttpdCollaborationConfigurationEvent; import com.raytheon.uf.viz.collaboration.comm.provider.event.HttpdCollaborationConfigurationEvent;
import com.raytheon.uf.viz.collaboration.comm.provider.user.IDConverter; import com.raytheon.uf.viz.collaboration.comm.provider.user.IDConverter;
import com.raytheon.uf.viz.collaboration.comm.provider.user.UserId;
/** /**
* Listens for peer to peer messages and routes them appropriately. * Listens for peer to peer messages and routes them appropriately.
@ -54,6 +59,8 @@ import com.raytheon.uf.viz.collaboration.comm.provider.user.IDConverter;
* data now in packet extension * data now in packet extension
* Dec 19, 2013 2563 bclement removed wait for HTTP config, added reset * Dec 19, 2013 2563 bclement removed wait for HTTP config, added reset
* Feb 13, 2014 2751 bclement changed IQualifiedID objects to IUser * Feb 13, 2014 2751 bclement changed IQualifiedID objects to IUser
* Feb 17, 2014 2756 bclement null check for message from field
* moved url validation from regex to java utility
* *
* </pre> * </pre>
* *
@ -101,7 +108,14 @@ public class PeerToPeerCommHelper implements PacketListener {
public void processPacket(Packet packet) { public void processPacket(Packet packet) {
if (packet instanceof Message) { if (packet instanceof Message) {
Message msg = (Message) packet; Message msg = (Message) packet;
if (IDConverter.isFromRoom(msg.getFrom())) { String fromStr = msg.getFrom();
if (fromStr == null) {
// from server
UserId account = CollaborationConnection.getConnection()
.getUser();
fromStr = account.getHost();
}
if (IDConverter.isFromRoom(fromStr)) {
// venues will have their own listeners // venues will have their own listeners
return; return;
} }
@ -204,11 +218,11 @@ public class PeerToPeerCommHelper implements PacketListener {
*/ */
private void handleConfiguration(String body) { private void handleConfiguration(String body) {
// Determine if an error has occurred. // Determine if an error has occurred.
if (IHttpdXmppMessage.configErrorPattern.matcher(body).matches()) { if (IHttpXmppMessage.configErrorPattern.matcher(body).matches()) {
statusHandler.handle( statusHandler.handle(
UFStatus.Priority.ERROR, UFStatus.Priority.ERROR,
this.getCollaborationConfigurationParameterValue(body, this.getCollaborationConfigurationParameterValue(body,
IHttpdXmppMessage.ERROR_PARAMETER_NAME) IHttpXmppMessage.ERROR_PARAMETER_NAME)
+ ". Shared Display Sessions have been disabled."); + ". Shared Display Sessions have been disabled.");
this.disableSharedDisplaySession(); this.disableSharedDisplaySession();
// terminate execution // terminate execution
@ -216,7 +230,7 @@ public class PeerToPeerCommHelper implements PacketListener {
} }
// Validate the configuration. // Validate the configuration.
if (IHttpdXmppMessage.configURLPattern.matcher(body).matches() == false) { if (IHttpXmppMessage.configURLPattern.matcher(body).matches() == false) {
statusHandler statusHandler
.handle(UFStatus.Priority.PROBLEM, .handle(UFStatus.Priority.PROBLEM,
"Received invalid configuration from openfire. Shared Display Sessions have been disabled."); "Received invalid configuration from openfire. Shared Display Sessions have been disabled.");
@ -225,24 +239,32 @@ public class PeerToPeerCommHelper implements PacketListener {
} }
// Remove the parameter name. // Remove the parameter name.
String httpdCollaborationURL = this String httpCollaborationURL = this
.getCollaborationConfigurationParameterValue(body, .getCollaborationConfigurationParameterValue(body,
IHttpdXmppMessage.URL_PARAMETER_NAME); IHttpXmppMessage.URL_PARAMETER_NAME);
// validate the url. // validate the url.
if (IHttpdXmppMessage.urlPattern.matcher(httpdCollaborationURL) try {
.matches() == false) { URL u = new URL(httpCollaborationURL);
URI uri = u.toURI();
if (!uri.getScheme().equalsIgnoreCase("http")) {
throw new CollaborationException(
"Provided URL doesn't use the HTTP scheme");
}
} catch (Exception e) {
statusHandler.handle(UFStatus.Priority.PROBLEM, statusHandler.handle(UFStatus.Priority.PROBLEM,
"Received an invalid http url from openfire - " "Received an invalid http url from openfire - "
+ httpdCollaborationURL + httpCollaborationURL
+ ". Shared Display Sessions have been disabled."); + ". Shared Display Sessions have been disabled.",
e);
this.disableSharedDisplaySession(); this.disableSharedDisplaySession();
return; return;
} }
httpServer = httpdCollaborationURL;
httpServer = httpCollaborationURL;
// configuration is valid; publish it. // configuration is valid; publish it.
IHttpdCollaborationConfigurationEvent configurationEvent = new HttpdCollaborationConfigurationEvent( IHttpdCollaborationConfigurationEvent configurationEvent = new HttpdCollaborationConfigurationEvent(
httpdCollaborationURL); httpCollaborationURL);
manager.postEvent(configurationEvent); manager.postEvent(configurationEvent);
} }

View file

@ -62,6 +62,7 @@ import com.vividsolutions.jts.geom.Coordinate;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* May 23, 2012 mschenke Initial creation * May 23, 2012 mschenke Initial creation
* Jan 30, 2014 2698 bclement changed UserId to VenueParticipant * Jan 30, 2014 2698 bclement changed UserId to VenueParticipant
* Feb 13, 2014 2751 bclement VenueParticipant refactor
* *
* </pre> * </pre>
* *

View file

@ -19,18 +19,25 @@
**/ **/
package com.raytheon.uf.viz.collaboration.display.storage; package com.raytheon.uf.viz.collaboration.display.storage;
import java.io.ByteArrayInputStream;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicHeader;
import com.raytheon.uf.common.comm.HttpClient; import com.raytheon.uf.common.comm.HttpClient;
import com.raytheon.uf.common.comm.HttpClient.HttpClientResponse; import com.raytheon.uf.common.comm.HttpClient.HttpClientResponse;
@ -49,7 +56,9 @@ import com.raytheon.uf.viz.remote.graphics.events.DisposeObjectEvent;
import com.raytheon.uf.viz.remote.graphics.events.ICreationEvent; import com.raytheon.uf.viz.remote.graphics.events.ICreationEvent;
/** /**
* Class responsible for object event storage. Will persist/retrieve objects * Class responsible for object event storage. Will persist/retrieve objects.
* When listing remote directories, this client uses the HTTP Accepts header to
* prefer XML format, but also accepts HTML.
* *
* <pre> * <pre>
* *
@ -58,6 +67,7 @@ import com.raytheon.uf.viz.remote.graphics.events.ICreationEvent;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Apr 20, 2012 mschenke Initial creation * Apr 20, 2012 mschenke Initial creation
* Feb 17, 2014 2756 bclement added xml parsing for HTTP directory listing
* *
* </pre> * </pre>
* *
@ -90,12 +100,20 @@ public class CollaborationObjectEventStorage implements
return persistance; return persistance;
} }
public static final String ACCEPTS_HEADER = "Accepts";
public static final String XML_CONTENT_TYPE = "text/xml";
public static final String HTML_CONTENT_TYPE = "text/html";
private String sessionDataURL; private String sessionDataURL;
private HttpClient client; private HttpClient client;
private int displayId; private int displayId;
private final XMLInputFactory staxFactory = XMLInputFactory.newInstance();
private CollaborationObjectEventStorage(ISharedDisplaySession session, private CollaborationObjectEventStorage(ISharedDisplaySession session,
int displayId) { int displayId) {
this.displayId = displayId; this.displayId = displayId;
@ -116,6 +134,7 @@ public class CollaborationObjectEventStorage implements
public IPersistedEvent persistEvent(AbstractDispatchingObjectEvent event) public IPersistedEvent persistEvent(AbstractDispatchingObjectEvent event)
throws CollaborationException { throws CollaborationException {
if (event instanceof ICreationEvent) { if (event instanceof ICreationEvent) {
// TODO this is for pre 14.3 compatibility
createFolder(String.valueOf(event.getObjectId())); createFolder(String.valueOf(event.getObjectId()));
} else if (event instanceof DisposeObjectEvent) { } else if (event instanceof DisposeObjectEvent) {
// Do not delete anything off the server, users may still be // Do not delete anything off the server, users may still be
@ -205,6 +224,13 @@ public class CollaborationObjectEventStorage implements
} }
} }
/**
* Execute get request for object stored at event's resource path
*
* @param event
* @return null if object was deleted
* @throws CollaborationException
*/
private CollaborationHttpPersistedObject retreiveStoredObject( private CollaborationHttpPersistedObject retreiveStoredObject(
CollaborationHttpPersistedEvent event) CollaborationHttpPersistedEvent event)
throws CollaborationException { throws CollaborationException {
@ -213,9 +239,10 @@ public class CollaborationObjectEventStorage implements
HttpClientResponse response = executeRequest(get); HttpClientResponse response = executeRequest(get);
if (isSuccess(response.code)) { if (isSuccess(response.code)) {
try { try {
CollaborationHttpPersistedObject dataObject = (CollaborationHttpPersistedObject) SerializationUtil CollaborationHttpPersistedObject dataObject = SerializationUtil
.transformFromThrift(CompressionUtil .transformFromThrift(
.uncompress(response.data)); CollaborationHttpPersistedObject.class,
CompressionUtil.uncompress(response.data));
if (dataObject != null) { if (dataObject != null) {
dataObject.dataSize = response.data.length; dataObject.dataSize = response.data.length;
} }
@ -244,6 +271,8 @@ public class CollaborationObjectEventStorage implements
throws CollaborationException { throws CollaborationException {
String objectPath = objectId + "/"; String objectPath = objectId + "/";
HttpGet get = new HttpGet(sessionDataURL + objectPath); HttpGet get = new HttpGet(sessionDataURL + objectPath);
get.addHeader(new BasicHeader(ACCEPTS_HEADER, XML_CONTENT_TYPE + ","
+ HTML_CONTENT_TYPE));
HttpClientResponse response = executeRequest(get); HttpClientResponse response = executeRequest(get);
if (isSuccess(response.code) == false) { if (isSuccess(response.code) == false) {
if (isNotExists(response.code)) { if (isNotExists(response.code)) {
@ -252,38 +281,21 @@ public class CollaborationObjectEventStorage implements
throw new CollaborationException("Error retrieving object (" throw new CollaborationException("Error retrieving object ("
+ objectId + ") events, received code: " + response.code); + objectId + ") events, received code: " + response.code);
} }
CollaborationHttpPersistedEvent event = new CollaborationHttpPersistedEvent();
List<CollaborationHttpPersistedObject> objectEvents = new ArrayList<CollaborationHttpPersistedObject>();
// parse out links of objects // parse out links of objects
String htmlStr = new String(response.data); List<CollaborationHttpPersistedObject> objectEvents;
int searchIdx = 0; try {
String searchStrStart = "<a href=\""; if (response.contentType != null
String objectEnding = ".obj"; && response.contentType.toLowerCase().contains("xml")) {
String searchStrEnd = "\">"; objectEvents = parseXmlResponseData(objectPath, response.data);
int searchStrLen = searchStrStart.length(); } else {
while (searchIdx > -1) { objectEvents = parseHtmlResponseData(objectPath, response.data);
int previousIdx = searchIdx;
int foundAt = htmlStr.indexOf(searchStrStart, searchIdx);
// reset searchIdx to -1 until found
searchIdx = -1;
if (foundAt > previousIdx) {
foundAt += searchStrLen;
int endsAt = htmlStr.indexOf(searchStrEnd, foundAt);
if (endsAt > foundAt) {
String object = htmlStr.substring(foundAt, endsAt);
if (object.endsWith(objectEnding)) {
event.setResourcePath(objectPath + object);
CollaborationHttpPersistedObject eventObject = retreiveStoredObject(event);
if (eventObject != null) {
objectEvents.add(eventObject);
} else {
// Object was deleted, abort
return new AbstractDispatchingObjectEvent[0];
}
}
searchIdx = endsAt + 1;
}
} }
} catch (XMLStreamException e) {
throw new CollaborationException("Error parsing response data", e);
}
if (objectEvents == null) {
// Object was deleted, abort
return new AbstractDispatchingObjectEvent[0];
} }
// Sort by creation time // Sort by creation time
@ -306,10 +318,118 @@ public class CollaborationObjectEventStorage implements
return events; return events;
} }
/**
* Parse response from HTTP directory listing in xml format
*
* @param objectPath
* @param responseData
* @return null if object was deleted
* @throws XMLStreamException
* @throws CollaborationException
*/
private List<CollaborationHttpPersistedObject> parseXmlResponseData(
String objectPath, byte[] responseData) throws XMLStreamException,
CollaborationException {
CollaborationHttpPersistedEvent event = new CollaborationHttpPersistedEvent();
List<CollaborationHttpPersistedObject> objectEvents = new ArrayList<CollaborationHttpPersistedObject>();
String text;
boolean inFileTag = false;
XMLStreamReader reader = staxFactory
.createXMLStreamReader(new ByteArrayInputStream(responseData));
while (reader.hasNext()) {
int staxEvent = reader.next();
switch (staxEvent) {
case XMLStreamConstants.START_ELEMENT:
String localName = reader.getLocalName();
inFileTag = localName.equalsIgnoreCase("file");
break;
case XMLStreamConstants.CHARACTERS:
text = reader.getText().trim();
if (inFileTag && text.endsWith(".obj")) {
event.setResourcePath(objectPath + text);
CollaborationHttpPersistedObject eventObject = retreiveStoredObject(event);
if (eventObject != null) {
objectEvents.add(eventObject);
} else {
// Object was deleted, abort
return null;
}
}
}
}
return objectEvents;
}
/**
* Parse httpd HTML directory listing response
*
* @param objectPath
* @param responseData
* @return null if object was deleted
* @throws XMLStreamException
* @throws CollaborationException
*/
private List<CollaborationHttpPersistedObject> parseHtmlResponseData(
String objectPath, byte[] responseData) throws XMLStreamException,
CollaborationException {
CollaborationHttpPersistedEvent event = new CollaborationHttpPersistedEvent();
List<CollaborationHttpPersistedObject> objectEvents = new ArrayList<CollaborationHttpPersistedObject>();
String htmlStr = new String(responseData);
int searchIdx = 0;
String searchStrStart = "<a href=\"";
String objectEnding = ".obj";
String searchStrEnd = "\">";
int searchStrLen = searchStrStart.length();
while (searchIdx > -1) {
int previousIdx = searchIdx;
int foundAt = htmlStr.indexOf(searchStrStart, searchIdx);
// reset searchIdx to -1 until found
searchIdx = -1;
if (foundAt > previousIdx) {
foundAt += searchStrLen;
int endsAt = htmlStr.indexOf(searchStrEnd, foundAt);
if (endsAt > foundAt) {
String object = htmlStr.substring(foundAt, endsAt);
if (object.endsWith(objectEnding)) {
event.setResourcePath(objectPath + object);
CollaborationHttpPersistedObject eventObject = retreiveStoredObject(event);
if (eventObject != null) {
objectEvents.add(eventObject);
} else {
// Object was deleted, abort
return null;
}
}
searchIdx = endsAt + 1;
}
}
}
return objectEvents;
}
/**
* Perform a MKCOL operation on httpd with DAV module.
*
* @deprecated MKCOL is only required for DAV (pre 14.3)
*
* @param folderPath
* @throws CollaborationException
*/
@Deprecated
private void createFolder(String folderPath) throws CollaborationException { private void createFolder(String folderPath) throws CollaborationException {
createFolder(URI.create(sessionDataURL + folderPath)); createFolder(URI.create(sessionDataURL + folderPath));
} }
/**
* Perform a MKCOL operation on httpd with DAV module.
*
* @deprecated MKCOL is only required for DAV (pre 14.3)
*
* @param folderPath
* @throws CollaborationException
*/
@Deprecated
private void createFolder(URI folderPath) throws CollaborationException { private void createFolder(URI folderPath) throws CollaborationException {
HttpRequestBase mkcol = new HttpRequestBase() { HttpRequestBase mkcol = new HttpRequestBase() {
@Override @Override
@ -325,6 +445,12 @@ public class CollaborationObjectEventStorage implements
} }
} }
/**
* Delete all files at and below uri
*
* @param uri
* @throws CollaborationException
*/
private void deleteResource(URI uri) throws CollaborationException { private void deleteResource(URI uri) throws CollaborationException {
HttpClientResponse rsp = executeRequest(new HttpDelete(uri)); HttpClientResponse rsp = executeRequest(new HttpDelete(uri));
// If request was success or resource doesn't exist, we are good // If request was success or resource doesn't exist, we are good
@ -334,6 +460,13 @@ public class CollaborationObjectEventStorage implements
} }
} }
/**
* Execute HTTP request
*
* @param request
* @return
* @throws CollaborationException
*/
private HttpClientResponse executeRequest(HttpUriRequest request) private HttpClientResponse executeRequest(HttpUriRequest request)
throws CollaborationException { throws CollaborationException {
try { try {
@ -343,14 +476,30 @@ public class CollaborationObjectEventStorage implements
} }
} }
/**
* @param code
* @return true if code is a 200 level return code
*/
private boolean isSuccess(int code) { private boolean isSuccess(int code) {
return code >= 200 && code < 300; return code >= 200 && code < 300;
} }
/**
* @param code
* @return true if resource does not exist on server
*/
private boolean isNotExists(int code) { private boolean isNotExists(int code) {
return code == 404 || code == 410; return code == 404 || code == 410;
} }
/**
* @deprecated this error is related to MKCOL. MKCOL is only required for
* DAV (pre 14.3)
*
* @param code
* @return true if directory alread exists on server
*/
@Deprecated
private boolean isDirExists(int code) { private boolean isDirExists(int code) {
return code == 405 || code == 301; return code == 405 || code == 301;
} }

View file

@ -104,6 +104,7 @@ import com.raytheon.uf.common.util.ByteArrayOutputStreamPool.ByteArrayOutputStre
* Mar 11, 2013 1786 mpduff Add https capability. * Mar 11, 2013 1786 mpduff Add https capability.
* Jun 12, 2013 2102 njensen Better error handling when using * Jun 12, 2013 2102 njensen Better error handling when using
* DynamicSerializeStreamHandler * DynamicSerializeStreamHandler
* Feb 17, 2014 2756 bclement added content type to response object
* *
* </pre> * </pre>
* *
@ -116,9 +117,12 @@ public class HttpClient {
public final byte[] data; public final byte[] data;
private HttpClientResponse(int code, byte[] data) { public final String contentType;
private HttpClientResponse(int code, byte[] data, String contentType) {
this.code = code; this.code = code;
this.data = data != null ? data : new byte[0]; this.data = data != null ? data : new byte[0];
this.contentType = contentType;
} }
} }
@ -676,7 +680,7 @@ public class HttpClient {
byteResult = ((DefaultInternalStreamHandler) handlerCallback).byteResult; byteResult = ((DefaultInternalStreamHandler) handlerCallback).byteResult;
} }
return new HttpClientResponse(resp.getStatusLine().getStatusCode(), return new HttpClientResponse(resp.getStatusLine().getStatusCode(),
byteResult); byteResult, getContentType(resp));
} finally { } finally {
if (ongoing != null) { if (ongoing != null) {
ongoing.decrementAndGet(); ongoing.decrementAndGet();
@ -684,6 +688,24 @@ public class HttpClient {
} }
} }
/**
* Get content type of response
*
* @param response
* @return null if none found
*/
private static String getContentType(HttpResponse response) {
String rval = null;
HttpEntity entity = response.getEntity();
if (entity != null) {
Header contentType = entity.getContentType();
if (contentType != null) {
rval = contentType.getValue();
}
}
return rval;
}
/** /**
* Streams the response content to the handler callback and closes the http * Streams the response content to the handler callback and closes the http
* connection once finished. * connection once finished.

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.raytheon.uf.common.http</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,9 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Common Http
Bundle-SymbolicName: com.raytheon.uf.common.http
Bundle-Version: 1.14.0
Bundle-Vendor: RAYTHEON
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: com.raytheon.uf.common.http
Require-Bundle: org.apache.commons.lang;bundle-version="2.3.0"

View file

@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.

View file

@ -17,14 +17,14 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for * See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information. * further licensing information.
**/ **/
package com.raytheon.uf.edex.ogc.common.http; package com.raytheon.uf.common.http;
import java.util.Iterator; import java.util.Iterator;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Parses Accept-Encoding headers for HTTP requests * Parses Accept and Accept-Encoding headers for HTTP requests
* *
* <pre> * <pre>
* *
@ -33,6 +33,7 @@ import java.util.regex.Pattern;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Nov 8, 2013 2539 bclement Initial creation * Nov 8, 2013 2539 bclement Initial creation
* Feb 14, 2014 2756 bclement moved to common http from ogc common
* *
* </pre> * </pre>
* *

View file

@ -17,7 +17,7 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for * See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information. * further licensing information.
**/ **/
package com.raytheon.uf.edex.ogc.common.http; package com.raytheon.uf.common.http;
/** /**
* Represents a single HTTP accept(-encoding) header encoding with weight value * Represents a single HTTP accept(-encoding) header encoding with weight value
@ -29,6 +29,7 @@ package com.raytheon.uf.edex.ogc.common.http;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Nov 8, 2013 2539 bclement Initial creation * Nov 8, 2013 2539 bclement Initial creation
* Feb 14, 2014 2756 bclement moved to common http from ogc common
* *
* </pre> * </pre>
* *

View file

@ -7,7 +7,7 @@
* in and to this copyrighted software are as specified in DFARS * in and to this copyrighted software are as specified in DFARS
* 252.227-7014 which was made part of the above contract. * 252.227-7014 which was made part of the above contract.
*/ */
package com.raytheon.uf.edex.ogc.common.http; package com.raytheon.uf.common.http;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -18,7 +18,7 @@ import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
/** /**
* Data object representing a MIME type used in service requests * Data object representing a MIME type used in http requests
* *
* <pre> * <pre>
* *
@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Oct 29, 2012 bclement Initial creation * Oct 29, 2012 bclement Initial creation
* Feb 14, 2014 2756 bclement moved to common http from ogc common
* *
* </pre> * </pre>
* *
@ -50,6 +51,9 @@ public class MimeType {
private static final Pattern PARAM_PATTERN = Pattern.compile(";\\s*(" private static final Pattern PARAM_PATTERN = Pattern.compile(";\\s*("
+ TOKEN_CLASS + "+)=(" + TOKEN_CLASS + "+|\"\\S+\")"); + TOKEN_CLASS + "+)=(" + TOKEN_CLASS + "+|\"\\S+\")");
/**
* @param mime
*/
public MimeType(String mime){ public MimeType(String mime){
Matcher m = TYPE_PATTERN.matcher(mime); Matcher m = TYPE_PATTERN.matcher(mime);
if (m.matches()) { if (m.matches()) {
@ -70,6 +74,12 @@ public class MimeType {
} }
} }
/**
* Parse parameter string into name/value pairs
*
* @param paramStr
* @return
*/
private static Map<String, String> getParameters(String paramStr) { private static Map<String, String> getParameters(String paramStr) {
Matcher m = PARAM_PATTERN.matcher(paramStr); Matcher m = PARAM_PATTERN.matcher(paramStr);
Map<String, String> rval = new LinkedHashMap<String, String>(); Map<String, String> rval = new LinkedHashMap<String, String>();
@ -141,6 +151,10 @@ public class MimeType {
return true; return true;
} }
/**
* @param other
* @return true if the mime type equals this one ignoring any parameters
*/
public boolean equalsIgnoreParams(MimeType other) { public boolean equalsIgnoreParams(MimeType other) {
if (other == null) { if (other == null) {
return false; return false;
@ -166,10 +180,18 @@ public class MimeType {
return parameters.get(paramName.toLowerCase()); return parameters.get(paramName.toLowerCase());
} }
/**
* @return the number of parameters this mime type has
*/
public int getNumParams() { public int getNumParams() {
return parameters.size(); return parameters.size();
} }
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override @Override
public String toString(){ public String toString(){
StringBuilder sb = new StringBuilder(toStringWithoutParams()); StringBuilder sb = new StringBuilder(toStringWithoutParams());
@ -186,6 +208,11 @@ public class MimeType {
return sb.toString(); return sb.toString();
} }
/**
* Format mime type excluding any parameters
*
* @return
*/
public String toStringWithoutParams() { public String toStringWithoutParams() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(this.type).append("/"); sb.append(this.type).append("/");

View file

@ -143,4 +143,11 @@
version="0.0.0" version="0.0.0"
unpack="false"/> unpack="false"/>
<plugin
id="com.raytheon.uf.common.http"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature> </feature>

View file

@ -22,7 +22,8 @@ Require-Bundle: net.opengis;bundle-version="1.0.2",
org.apache.commons.cxf, org.apache.commons.cxf,
org.eclipse.jetty;bundle-version="7.6.9", org.eclipse.jetty;bundle-version="7.6.9",
com.raytheon.uf.common.dataplugin.level;bundle-version="1.12.1174", com.raytheon.uf.common.dataplugin.level;bundle-version="1.12.1174",
com.raytheon.uf.edex.log;bundle-version="1.12.1174" com.raytheon.uf.edex.log;bundle-version="1.12.1174",
com.raytheon.uf.common.http
Export-Package: com.raytheon.uf.edex.ogc.common, Export-Package: com.raytheon.uf.edex.ogc.common,
com.raytheon.uf.edex.ogc.common.db, com.raytheon.uf.edex.ogc.common.db,
com.raytheon.uf.edex.ogc.common.feature, com.raytheon.uf.edex.ogc.common.feature,

View file

@ -19,7 +19,7 @@
**/ **/
package com.raytheon.uf.edex.ogc.common; package com.raytheon.uf.edex.ogc.common;
import com.raytheon.uf.edex.ogc.common.http.MimeType; import com.raytheon.uf.common.http.MimeType;
/** /**
* Response wrapper for OGC web services * Response wrapper for OGC web services

View file

@ -14,9 +14,9 @@ import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.InvalidVersionException; import com.raytheon.uf.edex.ogc.common.InvalidVersionException;
import com.raytheon.uf.edex.ogc.common.Version; import com.raytheon.uf.edex.ogc.common.Version;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
/** /**
* Utility methods and constants for GML version parsing and comparison * Utility methods and constants for GML version parsing and comparison

View file

@ -29,12 +29,12 @@ import org.geotools.feature.FeatureCollection;
import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.simple.SimpleFeatureType;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.common.json.geo.GeoJsonUtil; import com.raytheon.uf.common.json.geo.GeoJsonUtil;
import com.raytheon.uf.common.json.geo.GeoJsonUtilSimpleImpl; import com.raytheon.uf.common.json.geo.GeoJsonUtilSimpleImpl;
import com.raytheon.uf.common.json.geo.MixedFeatureCollection; import com.raytheon.uf.common.json.geo.MixedFeatureCollection;
import com.raytheon.uf.edex.ogc.common.OgcResponse; import com.raytheon.uf.edex.ogc.common.OgcResponse;
import com.raytheon.uf.edex.ogc.common.OgcResponse.TYPE; import com.raytheon.uf.edex.ogc.common.OgcResponse.TYPE;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
/** /**

View file

@ -47,11 +47,11 @@ import org.opengis.feature.simple.SimpleFeatureType;
import sun.misc.IOUtils; import sun.misc.IOUtils;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.edex.ogc.common.OgcResponse; import com.raytheon.uf.edex.ogc.common.OgcResponse;
import com.raytheon.uf.edex.ogc.common.OgcResponse.TYPE; import com.raytheon.uf.edex.ogc.common.OgcResponse.TYPE;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
/** /**
* Convert simple features to shape files * Convert simple features to shape files

View file

@ -24,8 +24,8 @@ import java.util.List;
import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeature;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.OgcResponse; import com.raytheon.uf.edex.ogc.common.OgcResponse;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
/** /**
* Interface for converting simple features to different output formats * Interface for converting simple features to different output formats

View file

@ -38,6 +38,9 @@ import javax.xml.stream.XMLStreamReader;
import org.apache.commons.collections.map.CaseInsensitiveMap; import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import com.raytheon.uf.common.http.AcceptHeaderParser;
import com.raytheon.uf.common.http.AcceptHeaderValue;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.edex.ogc.common.OgcException; import com.raytheon.uf.edex.ogc.common.OgcException;

View file

@ -25,7 +25,8 @@ Require-Bundle: org.geotools;bundle-version="2.6.4",
org.w3.xmlschema;bundle-version="1.0.0", org.w3.xmlschema;bundle-version="1.0.0",
com.raytheon.uf.common.pointdata;bundle-version="1.12.1174", com.raytheon.uf.common.pointdata;bundle-version="1.12.1174",
org.apache.commons.collections;bundle-version="3.2.0", org.apache.commons.collections;bundle-version="3.2.0",
org.apache.commons.cxf;bundle-version="1.0.0" org.apache.commons.cxf;bundle-version="1.0.0",
com.raytheon.uf.common.http
Export-Package: com.raytheon.uf.edex.wfs, Export-Package: com.raytheon.uf.edex.wfs,
com.raytheon.uf.edex.wfs.feature, com.raytheon.uf.edex.wfs.feature,
com.raytheon.uf.edex.wfs.filter, com.raytheon.uf.edex.wfs.filter,

View file

@ -22,6 +22,7 @@ import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.edex.ogc.common.OgcBoundingBox; import com.raytheon.uf.edex.ogc.common.OgcBoundingBox;
@ -30,7 +31,6 @@ import com.raytheon.uf.edex.ogc.common.OgcNamespace;
import com.raytheon.uf.edex.ogc.common.OgcPrefix; import com.raytheon.uf.edex.ogc.common.OgcPrefix;
import com.raytheon.uf.edex.ogc.common.OgcResponse; import com.raytheon.uf.edex.ogc.common.OgcResponse;
import com.raytheon.uf.edex.ogc.common.OgcTimeRange; import com.raytheon.uf.edex.ogc.common.OgcTimeRange;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.http.OgcHttpHandler; import com.raytheon.uf.edex.ogc.common.http.OgcHttpHandler;
import com.raytheon.uf.edex.ogc.common.output.IOgcHttpResponse; import com.raytheon.uf.edex.ogc.common.output.IOgcHttpResponse;
import com.raytheon.uf.edex.ogc.common.output.OgcResponseOutput; import com.raytheon.uf.edex.ogc.common.output.OgcResponseOutput;

View file

@ -36,7 +36,7 @@ import net.opengis.wfs.v_1_1_0.ResultTypeType;
import net.opengis.wfs.v_2_0_0.GetPropertyValueType; import net.opengis.wfs.v_2_0_0.GetPropertyValueType;
import net.opengis.wfs.v_2_0_0.StoredQueryType; import net.opengis.wfs.v_2_0_0.StoredQueryType;
import com.raytheon.uf.edex.ogc.common.http.MimeType; import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.wfs.WfsException; import com.raytheon.uf.edex.wfs.WfsException;
import com.raytheon.uf.edex.wfs.WfsException.Code; import com.raytheon.uf.edex.wfs.WfsException.Code;
import com.raytheon.uf.edex.wfs.querystore.IStoredQueryCallback; import com.raytheon.uf.edex.wfs.querystore.IStoredQueryCallback;

View file

@ -19,8 +19,8 @@
**/ **/
package com.raytheon.uf.edex.wfs.request; package com.raytheon.uf.edex.wfs.request;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.OgcResponse; import com.raytheon.uf.edex.ogc.common.OgcResponse;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
/** /**
* Abstract base for WFS request wrappers * Abstract base for WFS request wrappers

View file

@ -56,11 +56,11 @@ import net.opengis.wfs.v_1_1_0.GMLObjectTypeType;
import net.opengis.wfs.v_1_1_0.OutputFormatListType; import net.opengis.wfs.v_1_1_0.OutputFormatListType;
import net.opengis.wfs.v_1_1_0.WFSCapabilitiesType; import net.opengis.wfs.v_1_1_0.WFSCapabilitiesType;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.OgcNamespace; import com.raytheon.uf.edex.ogc.common.OgcNamespace;
import com.raytheon.uf.edex.ogc.common.OgcOperationInfo; import com.raytheon.uf.edex.ogc.common.OgcOperationInfo;
import com.raytheon.uf.edex.ogc.common.OgcServiceInfo; import com.raytheon.uf.edex.ogc.common.OgcServiceInfo;
import com.raytheon.uf.edex.ogc.common.feature.GmlUtils; import com.raytheon.uf.edex.ogc.common.feature.GmlUtils;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
import com.raytheon.uf.edex.wfs.IWfsProvider.WfsOpType; import com.raytheon.uf.edex.wfs.IWfsProvider.WfsOpType;
import com.raytheon.uf.edex.wfs.WfsException; import com.raytheon.uf.edex.wfs.WfsException;
import com.raytheon.uf.edex.wfs.reg.WfsRegistryImpl; import com.raytheon.uf.edex.wfs.reg.WfsRegistryImpl;

View file

@ -45,6 +45,7 @@ import org.opengis.feature.simple.SimpleFeature;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.w3.xmlschema.Schema; import org.w3.xmlschema.Schema;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.core.EDEXUtil; import com.raytheon.uf.edex.core.EDEXUtil;
import com.raytheon.uf.edex.ogc.common.OgcException; import com.raytheon.uf.edex.ogc.common.OgcException;
import com.raytheon.uf.edex.ogc.common.OgcOperationInfo; import com.raytheon.uf.edex.ogc.common.OgcOperationInfo;
@ -55,7 +56,6 @@ import com.raytheon.uf.edex.ogc.common.OgcServiceInfo;
import com.raytheon.uf.edex.ogc.common.feature.GmlUtils; import com.raytheon.uf.edex.ogc.common.feature.GmlUtils;
import com.raytheon.uf.edex.ogc.common.feature.SimpleFeatureFormatter; import com.raytheon.uf.edex.ogc.common.feature.SimpleFeatureFormatter;
import com.raytheon.uf.edex.ogc.common.http.EndpointInfo; import com.raytheon.uf.edex.ogc.common.http.EndpointInfo;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.http.OgcHttpHandler; import com.raytheon.uf.edex.ogc.common.http.OgcHttpHandler;
import com.raytheon.uf.edex.ogc.common.output.IOgcHttpResponse; import com.raytheon.uf.edex.ogc.common.output.IOgcHttpResponse;
import com.raytheon.uf.edex.ogc.common.output.OgcResponseOutput; import com.raytheon.uf.edex.ogc.common.output.OgcResponseOutput;

View file

@ -54,11 +54,11 @@ import net.opengis.wfs.v_2_0_0.FeatureTypeListType;
import net.opengis.wfs.v_2_0_0.FeatureTypeType; import net.opengis.wfs.v_2_0_0.FeatureTypeType;
import net.opengis.wfs.v_2_0_0.WFSCapabilitiesType; import net.opengis.wfs.v_2_0_0.WFSCapabilitiesType;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.OgcNamespace; import com.raytheon.uf.edex.ogc.common.OgcNamespace;
import com.raytheon.uf.edex.ogc.common.OgcOperationInfo; import com.raytheon.uf.edex.ogc.common.OgcOperationInfo;
import com.raytheon.uf.edex.ogc.common.OgcServiceInfo; import com.raytheon.uf.edex.ogc.common.OgcServiceInfo;
import com.raytheon.uf.edex.ogc.common.feature.GmlUtils; import com.raytheon.uf.edex.ogc.common.feature.GmlUtils;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
import com.raytheon.uf.edex.wfs.IWfsProvider.WfsOpType; import com.raytheon.uf.edex.wfs.IWfsProvider.WfsOpType;
import com.raytheon.uf.edex.wfs.WfsException; import com.raytheon.uf.edex.wfs.WfsException;
import com.raytheon.uf.edex.wfs.reg.WfsRegistryImpl; import com.raytheon.uf.edex.wfs.reg.WfsRegistryImpl;

View file

@ -67,6 +67,7 @@ import org.w3.xmlschema.Schema;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import com.raytheon.uf.common.http.MimeType;
import com.raytheon.uf.edex.core.EDEXUtil; import com.raytheon.uf.edex.core.EDEXUtil;
import com.raytheon.uf.edex.ogc.common.OgcException; import com.raytheon.uf.edex.ogc.common.OgcException;
import com.raytheon.uf.edex.ogc.common.OgcOperationInfo; import com.raytheon.uf.edex.ogc.common.OgcOperationInfo;
@ -77,7 +78,6 @@ import com.raytheon.uf.edex.ogc.common.OgcServiceInfo;
import com.raytheon.uf.edex.ogc.common.feature.GmlUtils; import com.raytheon.uf.edex.ogc.common.feature.GmlUtils;
import com.raytheon.uf.edex.ogc.common.feature.SimpleFeatureFormatter; import com.raytheon.uf.edex.ogc.common.feature.SimpleFeatureFormatter;
import com.raytheon.uf.edex.ogc.common.http.EndpointInfo; import com.raytheon.uf.edex.ogc.common.http.EndpointInfo;
import com.raytheon.uf.edex.ogc.common.http.MimeType;
import com.raytheon.uf.edex.ogc.common.http.OgcHttpHandler; import com.raytheon.uf.edex.ogc.common.http.OgcHttpHandler;
import com.raytheon.uf.edex.ogc.common.output.IOgcHttpResponse; import com.raytheon.uf.edex.ogc.common.output.IOgcHttpResponse;
import com.raytheon.uf.edex.ogc.common.output.OgcResponseOutput; import com.raytheon.uf.edex.ogc.common.output.OgcResponseOutput;

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>collaboration.dataserver</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,10 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Dataserver
Bundle-SymbolicName: collaboration.dataserver
Bundle-Version: 1.14.0
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: org.eclipse.jetty,
com.raytheon.uf.common.util,
org.jivesoftware.smack,
com.raytheon.uf.common.http

View file

@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.

View file

@ -0,0 +1,2 @@
xmpp.username=dataserver
xmpp.password=password

View file

@ -0,0 +1,5 @@
port=9682
datapath=/session_data/
logdir=logs
logname=serverlog_yyyy_mm_dd.log
xmpp.server=localhost

View file

@ -0,0 +1,273 @@
/**
* 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.collaboration.dataserver;
import java.io.File;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Properties;
/**
* Global configuration for dataserver
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 5, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class Config{
public static final String HOST_KEY = "host";
public static final String PORT_KEY = "port";
public static final int PORT_DEFAULT = 9682;
public static final String DATAPATH_KEY = "datapath";
public static final String DATAPATH_DEFAULT = "/session_data/";
public static final String LOGDIR_KEY = "logdir";
public static final String LOGDIR_DEFAULT = "logs";
public static final String LOGNAME_KEY = "logname";
public static final String LOGNAME_DEFAULT = "serverlog_yyyy_mm_dd.log";
public static final String STOREDIR_KEY = "storedir";
public static final String STOREDIR_DEFAULT = "store";
public static final String LEGACY_MODE_KEY = "legacy.mode";
public static final boolean LEGACY_MODE_DEFAULT = true;
public static final String XMPP_USERNAME_KEY = "xmpp.username";
public static final String XMPP_PASSWORD_KEY = "xmpp.password";
public static final String XMPP_SERVER_KEY = "xmpp.server";
public static final String XMPP_SERVER_DEFAULT = "localhost";
public static final String config = System.getProperty(
"collaboration.dataserver.config", "config/settings.properties");
public static final String credentialsFile = System.getProperty(
"collaboration.dataserver.credentials",
"config/credentials.properties");
public static final Boolean useStdOut = Boolean
.getBoolean("collaboration.dataserver.stdout");
private static Properties _props = null;
private static Properties _credProps = null;
private Config() {
}
/**
* Get cached properties. Loads if not initialized.
*
* @return
*/
private static synchronized Properties getProperties() {
if (_props == null) {
_props = new Properties();
loadProperties(_props, config, false);
}
return _props;
}
/**
* Get cached credentials properties. Loads if not initialized.
*
* @return
*/
private static synchronized Properties getCredProperties()
throws RuntimeException {
if (_credProps == null) {
_credProps = new Properties();
loadProperties(_credProps, credentialsFile, true);
}
return _credProps;
}
/**
* Loads properties from file system. If fatal is true, runtime exception is
* thrown on error. Otherwise, an error message is logged and defaults are
* used.
*
* @param props
* @param propertyFile
* @param fatal
*/
private static void loadProperties(Properties props, String propertyFile,
boolean fatal) throws RuntimeException {
try {
props.load(new FileInputStream(new File(propertyFile)));
} catch (Exception e) {
System.err.println("Unable to open properties file: "
+ propertyFile + ". " + e.getLocalizedMessage());
if (fatal) {
throw new RuntimeException(
"Unable to continue without configuration: "
+ propertyFile, e);
} else {
e.printStackTrace();
System.err.println("Continuing using defaults");
}
}
}
/**
* Get integer value from properties
*
* @param key
* @param defaultValue
* @return
*/
public static int getInt(String key, int defaultValue) {
Properties props = getProperties();
String value = props.getProperty(key);
if (value == null) {
return defaultValue;
} else {
return Integer.parseInt(value);
}
}
/**
* Get string value from properties
*
* @param key
* @param defaultValue
* @return
*/
public static String getProp(String key, String defaultValue) {
return getProperties().getProperty(key, defaultValue);
}
/**
* Get boolean value from properties
*
* @param key
* @param defaultValue
* @return
*/
public static boolean getBool(String key, boolean defaultValue) {
Properties props = getProperties();
String value = props.getProperty(key);
if (value == null) {
return defaultValue;
} else {
return Boolean.parseBoolean(value);
}
}
/**
* @return username for XMPP account
* @throws RuntimeException
* if configuration for username cannot be found
*/
public static String getXmppUsername() throws RuntimeException {
return getRequiredCredential(XMPP_USERNAME_KEY);
}
/**
* @return password for XMPP account
* @throws RuntimeException
* if configuration for password cannot be found
*/
public static String getXmppPassword() throws RuntimeException {
return getRequiredCredential(XMPP_PASSWORD_KEY);
}
/**
* Get property that represents a web service path. Ensures a slash begins
* and ends the path.
*
* @param key
* @param defaultValue
* @return
*/
public static String getPath(String key, String defaultValue) {
String path = getProp(key, defaultValue).trim();
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/")) {
path = path + "/";
}
return path;
}
/**
* Get property from credentials file
*
* @param key
* @return
* @throws RuntimeException
* if credential configuration cannot be found
*/
private static String getRequiredCredential(String key)
throws RuntimeException {
Properties credProperties = getCredProperties();
String rval = credProperties.getProperty(key);
if (rval == null) {
throw new RuntimeException(
"Missing required credentials property: " + key);
}
return rval;
}
/**
* Get URL for data service. If host portion of URL is not found in config,
* this method will attempt to determine the canonical hostname for the
* machine
*
* @return
* @throws UnknownHostException
*/
public static String getDataserverUrl() throws UnknownHostException {
Properties props = getProperties();
String host = props.getProperty(HOST_KEY);
if (host == null) {
host = InetAddress.getLocalHost().getCanonicalHostName();
}
int port = getInt(PORT_KEY, PORT_DEFAULT);
String datapath = getPath(DATAPATH_KEY, DATAPATH_DEFAULT);
return "http://" + host + ":" + port + datapath;
}
}

View file

@ -0,0 +1,236 @@
/**
* 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.collaboration.dataserver;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import com.raytheon.uf.common.http.AcceptHeaderParser;
import com.raytheon.uf.common.http.AcceptHeaderValue;
/**
* Servlet for storing and retrieving collaboration data objects
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 5, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class DataService extends HttpServlet {
private static final long serialVersionUID = 3828421078339628468L;
private final File base;
private final FileManager manager;
private final Logger log = Log.getLogger(this.getClass());
public static final String XML_CONTENT_TYPE = "text/xml";
public static final String HTML_CONTENT_TYPE = "text/html";
public static final String BINARY_CONTENT_TYPE = "application/octet-stream";
public static final String ACCEPT_HEADER = "accept";
private final boolean legacyMode;
/**
* @param base
* base storage directory
*/
public DataService(File base) {
this.base = base;
if (!base.exists()) {
if (!base.mkdirs()) {
throw new IllegalStateException(
"Unable to create storage base directory: "
+ base.getAbsolutePath());
}
}
this.manager = new FileManager(base);
this.legacyMode = Config.getBool(Config.LEGACY_MODE_KEY,
Config.LEGACY_MODE_DEFAULT);
}
/**
*
*/
public DataService() {
this(new File(Config.getProp(Config.STOREDIR_KEY,
Config.STOREDIR_DEFAULT)));
}
/*
* (non-Javadoc)
*
* @see
* javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest
* , javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
File file = getFile(req);
if (!file.exists()) {
throw new RestException(HttpServletResponse.SC_NOT_FOUND,
"No Such Resource: " + file.getAbsolutePath());
}
ServletOutputStream out = resp.getOutputStream();
if (file.isDirectory()) {
if (acceptsXml(req)) {
resp.setContentType(XML_CONTENT_TYPE);
manager.readDirectoryAsXml(file, out);
} else {
resp.setContentType(HTML_CONTENT_TYPE);
manager.readDirectoryAsHtml(file, out);
}
} else {
resp.setContentType(BINARY_CONTENT_TYPE);
manager.readFile(file, out);
}
} catch (IOException e) {
log.warn("Problem handling GET", e);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (RestException e) {
log.debug(e.getLocalizedMessage(), e);
resp.sendError(e.getCode());
}
}
/**
* @param req
* @return true if request has an accepts header that lists xml content type
*/
private boolean acceptsXml(HttpServletRequest req) {
String header = req.getHeader(ACCEPT_HEADER);
if (header == null || header.trim().isEmpty()) {
return false;
}
for (AcceptHeaderValue value : new AcceptHeaderParser(header)) {
String type = value.getEncoding();
if (type.equalsIgnoreCase(XML_CONTENT_TYPE) && value.isAcceptable()) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest
* , javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO auth
try {
File file = getFile(req);
manager.writeFile(req.getInputStream(), file);
} catch (IOException e) {
log.warn("Problem handling PUT", e);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (RestException e) {
log.debug(e.getLocalizedMessage(), e);
resp.sendError(e.getCode());
}
}
/**
* Create file object from request path
*
* @param req
* @return
*/
private File getFile(HttpServletRequest req) {
String pathInfo = req.getPathInfo();
return new File(base, pathInfo);
}
/*
* (non-Javadoc)
*
* @see
* javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest
* , javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO auth
try {
File file = getFile(req);
if (!file.exists()) {
throw new RestException(HttpServletResponse.SC_NOT_FOUND,
"No Such Resource: " + file.getAbsolutePath());
}
manager.delete(file);
} catch (IOException e) {
log.warn("Problem handling DELETE", e);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (RestException e) {
log.debug(e.getLocalizedMessage(), e);
resp.sendError(e.getCode());
}
}
/*
* (non-Javadoc)
*
* @see
* javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest
* , javax.servlet.http.HttpServletResponse)
*/
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
// clients before 14.3 used DAV which uses MKCOL. This isn't needed
// since we create directories on put.
if (legacyMode && "MKCOL".equalsIgnoreCase(method)) {
resp.setStatus(HttpServletResponse.SC_OK);
return;
}
super.service(req, resp);
}
}

View file

@ -0,0 +1,129 @@
/**
* 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.collaboration.dataserver;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.TimeZone;
import org.eclipse.jetty.util.RolloverFileOutputStream;
/**
* Entry class for dataserver
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 5, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class DataserverMain {
private static final int CONNECTION_DELAY = 10000; // 10 seconds
private static final int SHUTDOWN_DELAY = 1000; // 1 second
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
try {
configureLogging();
} catch (IOException e) {
System.err.println("Unable to configure logging: "
+ e.getLocalizedMessage());
System.err
.println("Continuing using standard out and standard error");
}
final XmppServerConnection xmppConnection;
try {
xmppConnection = new XmppServerConnection();
} catch (Exception e) {
System.err
.println("Unable to connect to XMPP server, shutting down");
e.printStackTrace();
return;
}
final WebServerRunner webServer = new WebServerRunner();
new Thread(webServer).start();
wait(CONNECTION_DELAY);
new Thread(xmppConnection).start();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
System.out.println("Server shutting down");
xmppConnection.disconnect();
webServer.stop();
DataserverMain.wait(SHUTDOWN_DELAY);
}
});
}
/**
* sleep thread for specified milliseconds
*
* @param millis
*/
private static void wait(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* Set up rolling file log unless standard out is to be used
*
* @throws IOException
*/
private static void configureLogging() throws IOException {
if (Config.useStdOut){
return;
}
File logDir = new File(Config.getProp(Config.LOGDIR_KEY, Config.LOGDIR_DEFAULT));
if ( !logDir.exists()){
if (!logDir.mkdirs()) {
throw new IOException(
"Unable to create configured logging directory: "
+ logDir.getAbsolutePath());
}
} else if (!logDir.isDirectory()){
throw new IOException(
"Configured logging directory exists but is not a directory: "
+ logDir.getAbsolutePath());
}
String logFileName = logDir.getAbsolutePath() + File.separator
+ Config.getProp(Config.LOGNAME_KEY, Config.LOGNAME_DEFAULT);
RolloverFileOutputStream out = new RolloverFileOutputStream(
logFileName, true, 0, TimeZone.getTimeZone("GMT"));
System.setErr(new PrintStream(out));
System.setOut(new PrintStream(out));
}
}

View file

@ -0,0 +1,443 @@
/**
* 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.collaboration.dataserver;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javax.servlet.http.HttpServletResponse;
import com.raytheon.uf.common.util.concurrent.KeyLock;
import com.raytheon.uf.common.util.concurrent.KeyLocker;
/**
* Collaboration event object data storage that uses the file system. Uses a
* read/write lock policy based on file path. The paths are locked from the base
* to the target node and unlocked in reverse order.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 6, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class FileManager {
private static final int BUFFER_SIZE = 1024 * 256; // 256K
private static final ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>() {
/*
* (non-Javadoc)
*
* @see java.lang.ThreadLocal#initialValue()
*/
@Override
protected byte[] initialValue() {
return new byte[BUFFER_SIZE];
}
};
private static final KeyLocker<File> locker = new KeyLocker<File>();
private final File base;
/**
* @param base
* base storage directory
*/
public FileManager(File base) {
this.base = base;
}
/**
* simple container to associate a file lock with it's access mode
*/
private static class UsedLock {
public final KeyLock<File> lock;
public final boolean readOnly;
public UsedLock(KeyLock<File> lock, boolean readOnly) {
this.lock = lock;
this.readOnly = readOnly;
}
}
/**
* Write inputstream to file system
*
* @param in
* @param file
* @throws IOException
* @throws RestException
*/
public void writeFile(InputStream in, File file) throws IOException,
RestException {
OutputStream out = null;
List<UsedLock> locks = null;
KeyLock<File> targetLock = null;
try {
File parent = file.getParentFile();
// lock all files starting at child of base going to parent
locks = getCreateLocks(parent);
// lock target file for modification
targetLock = locker.getLock(file);
targetLock.lock();
createDirs(locks);
out = new FileOutputStream(file);
copy(in, out);
} finally {
// unlock in reverse order
if (targetLock != null) {
targetLock.unlock();
}
unlock(locks);
in.close();
if (out != null) {
out.flush();
out.close();
}
}
}
/**
* Each item in locks represents a node in a directory path. Attempts to
* create each node in order.
*
* @param locks
* @throws IOException
*/
private void createDirs(List<UsedLock> locks) throws IOException {
for (UsedLock lock : locks) {
File f = lock.lock.getKey();
if (!f.exists()) {
if (!f.mkdir()) {
throw new IOException("Unable to create file: "
+ f.getAbsolutePath());
}
}
}
}
/**
* Unlock each file lock in reverse order
*
* @param locks
*/
private void unlock(List<UsedLock> locks) {
if (locks != null) {
ListIterator<UsedLock> iter = locks.listIterator(locks.size());
while (iter.hasPrevious()) {
UsedLock ul = iter.previous();
if (ul.readOnly) {
ul.lock.readUnlock();
} else {
ul.lock.unlock();
}
}
}
}
/**
* Get a list of write file locks for each directory from the base to the
* target directory
*
* @param targetDirectory
* @return
* @throws RestException
*/
private List<UsedLock> getCreateLocks(File targetDirectory)
throws RestException {
List<KeyLock<File>> locks = new ArrayList<KeyLock<File>>();
// walk backwards getting locks for each node
while (!base.equals(targetDirectory)) {
locks.add(locker.getLock(targetDirectory));
targetDirectory = targetDirectory.getParentFile();
}
List<UsedLock> rval = new ArrayList<UsedLock>(locks.size());
// reverse iterate so we lock in the correct order
ListIterator<KeyLock<File>> iter = locks.listIterator(locks.size());
while (iter.hasPrevious()) {
KeyLock<File> lock = iter.previous();
File f = lock.getKey();
if (!f.exists()) {
lock.lock();
rval.add(new UsedLock(lock, false));
} else if (f.isDirectory()) {
lock.readLock();
rval.add(new UsedLock(lock, true));
} else {
// file exists but is not a directory
throw new RestException(HttpServletResponse.SC_CONFLICT,
"Resource already exists: " + f.getAbsolutePath());
}
}
return rval;
}
/**
* Output file to stream
*
* @param file
* @param out
* @throws IOException
* @throws RestException
*/
public void readFile(File file, OutputStream out) throws IOException,
RestException {
InputStream in = null;
List<UsedLock> locks = null;
try {
locks = getReadLocks(file);
if ( !file.exists()){
throw new RestException(HttpServletResponse.SC_NOT_FOUND,
"No Such File: " + file.getAbsoluteFile());
}
in = new FileInputStream(file);
copy(in, out);
} finally {
unlock(locks);
if (in != null) {
in.close();
}
out.close();
}
}
/**
* Get a list of read only file locks for each file from the base to the
* target file
*
* @param file
* @return
*/
private List<UsedLock> getReadLocks(File file) {
List<KeyLock<File>> locks = new ArrayList<KeyLock<File>>();
// walk backwards getting locks for each node
while (!base.equals(file)) {
locks.add(locker.getLock(file));
file = file.getParentFile();
}
List<UsedLock> rval = new ArrayList<UsedLock>(locks.size());
// reverse iterate so we lock in the correct order
ListIterator<KeyLock<File>> iter = locks.listIterator(locks.size());
while (iter.hasPrevious()) {
KeyLock<File> lock = iter.previous();
lock.readLock();
rval.add(new UsedLock(lock, true));
}
return rval;
}
/**
* Output contents of directory to stream in XML format
*
* @param directory
* @param out
* @throws IOException
* @throws RestException
*/
public void readDirectoryAsXml(File directory, OutputStream out)
throws IOException, RestException {
List<UsedLock> locks = null;
Writer w = null;
try {
locks = getReadLocks(directory);
if (!directory.exists()) {
// someone else modified it while waiting for lock
throw new RestException(HttpServletResponse.SC_NOT_FOUND,
"No Such Directory: " + directory.getAbsolutePath());
}
w = new OutputStreamWriter(out, "UTF-8");
w.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
w.write("<Contents xmlns=\"urn:uf:viz:collaboration\">");
for (File f : directory.listFiles()) {
if (f.isDirectory()) {
writeTextTag(w, "Directory", f.getName());
} else {
writeTextTag(w, "File", f.getName());
}
}
w.write("</Contents>");
} finally {
unlock(locks);
if (w != null) {
w.flush();
w.close();
}
}
}
/**
* Write tag with text element to writer
*
* @param w
* @param tagName
* @param text
* @throws IOException
*/
private void writeTextTag(Writer w, String tagName, String text)
throws IOException {
w.write("<");
w.write(tagName);
w.write(">");
w.write(text);
w.write("</");
w.write(tagName);
w.write(">");
}
/**
* Output contents of directory to stream in HTML format
*
* @param directory
* @param out
* @throws IOException
* @throws RestException
*/
public void readDirectoryAsHtml(File directory, OutputStream out)
throws IOException, RestException {
List<UsedLock> locks = null;
Writer w = null;
try {
locks = getReadLocks(directory);
if (!directory.exists()) {
// someone else modified it while waiting for lock
throw new RestException(HttpServletResponse.SC_NOT_FOUND,
"No Such Directory: " + directory.getAbsolutePath());
}
w = new OutputStreamWriter(out, "UTF-8");
w.write("<!DOCTYPE html>\n");
w.write("<html><body>");
for (File f : directory.listFiles()) {
String name = f.getName();
if (f.isDirectory() && !name.endsWith("/")) {
name = name + "/";
}
writeLinkTag(w, name);
}
w.write("</body></html>");
} finally {
unlock(locks);
if (w != null) {
w.flush();
w.close();
}
}
}
/**
* Write html link reference tag to writer
*
* @param w
* @param href
* @throws IOException
*/
private void writeLinkTag(Writer w, String href) throws IOException {
w.write("<a href=\"");
w.write(href);
w.write("\">");
w.write(href);
w.write("</a></br>");
}
/**
* Copy bytes from input to output
*
* @param in
* @param out
* @throws IOException
*/
public static void copy(InputStream in, OutputStream out)
throws IOException {
byte[] buff = localBuffer.get();
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
}
/**
* Delete target file and all children
*
* @param file
* @throws IOException
* @throws RestException
*/
public void delete(File file) throws IOException, RestException {
List<UsedLock> parentLocks = null;
KeyLock<File> targetLock = null;
try {
File parent = file.getParentFile();
parentLocks = getReadLocks(parent);
targetLock = locker.getLock(file);
targetLock.lock();
if (!file.exists()) {
throw new RestException(HttpServletResponse.SC_NOT_FOUND,
"No Such Resource: " + file.getAbsolutePath());
}
// we don't have to lock children since we have a write lock on the
// directory
deleteRecursive(file);
} finally {
if (targetLock != null) {
targetLock.unlock();
}
unlock(parentLocks);
}
}
/**
* Recursive method to delete file and children
*
* @param file
* @throws IOException
*/
private void deleteRecursive(File file) throws IOException {
if (file.isDirectory()) {
for (File sub : file.listFiles()) {
deleteRecursive(sub);
}
}
if (!file.delete()) {
throw new IOException("Unable to delete file: "
+ file.getAbsolutePath());
}
}
}

View file

@ -17,13 +17,10 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for * See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information. * further licensing information.
**/ **/
package com.raytheon.openfire.plugin.configuration.collaboration.util; package com.raytheon.collaboration.dataserver;
import java.io.File;
/** /**
* Various utility methods that are utilized by the Httpd Collaboration plugin * Exceptions for web service errors to be returned to the client
* that do not require a class to be instantiated.
* *
* <pre> * <pre>
* *
@ -31,33 +28,35 @@ import java.io.File;
* *
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 07, 2012 bkowal Initial creation * Feb 6, 2014 2756 bclement Initial creation
* Jan 06, 2013 2563 bclement removed config preamble
* *
* </pre> * </pre>
* *
* @author bkowal * @author bclement
* @version 1.0 * @version 1.0
*/ */
public class RestException extends Exception{
public abstract class HttpdCollaborationUtil { private static final long serialVersionUID = -3940227418233750698L;
public static String encodeErrorMessage(Throwable e) { private final int code;
String content = e.getMessage();
if (content == null) {
// final attempt
content = e.toString();
}
return "error : " + content;
/**
* @param code
* http status code
* @param message
*/
public RestException(int code, String message) {
super(message);
this.code = code;
} }
public static String endPathIfNecessary(String _path) { /**
if (_path.endsWith(File.separator)) { * @return the code
return _path; */
} public int getCode() {
return code;
return _path + File.separator;
} }
} }

View file

@ -0,0 +1,93 @@
/**
* 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.collaboration.dataserver;
import java.io.File;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
/**
* Start and run jetty webserver
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 14, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class WebServerRunner implements Runnable {
private Server server;
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
server = new Server(Config.getInt(Config.PORT_KEY,
Config.PORT_DEFAULT));
ServletContextHandler context = new ServletContextHandler(
ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
File base = new File(Config.getProp(Config.STOREDIR_KEY,
Config.STOREDIR_DEFAULT));
if (!base.exists()) {
base.mkdirs();
}
String datapath = Config.getPath(Config.DATAPATH_KEY,
Config.DATAPATH_DEFAULT);
context.addServlet(new ServletHolder(new DataService(base)), datapath
+ "*");
try {
server.start();
System.out.println("Server started");
server.join();
} catch (Exception e) {
System.err.println("Unable to start web server");
e.printStackTrace();
}
}
/**
* Shutdown web server
*/
public void stop() {
try {
server.stop();
} catch (Exception e) {
System.err.println("Unable to stop web server");
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,135 @@
/**
* 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.collaboration.dataserver;
import java.net.UnknownHostException;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.SyncPacketSend;
/**
* Starts and runs XMPP client thread for communication with XMPP server
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 14, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class XmppServerConnection implements Runnable {
private static final int PACKET_SEND_TIMEOUT = 5000; // 5 seconds
private final XMPPConnection conn;
private final String user;
private final String password;
private final String xmppServerAddress;
private final String dataServerUrl;
private final Logger log = Log.getLogger(this.getClass());
/**
* Creates connection and logs in
*
* @throws XMPPException
* @throws UnknownHostException
*/
public XmppServerConnection() throws XMPPException, UnknownHostException {
this.dataServerUrl = Config.getDataserverUrl();
this.user = Config.getXmppUsername();
this.password = Config.getXmppPassword();
this.xmppServerAddress = Config.getProp(Config.XMPP_SERVER_KEY,
Config.XMPP_SERVER_DEFAULT);
this.conn = new XMPPConnection(xmppServerAddress);
this.conn.connect();
this.conn.login(user, password);
log.debug("Connected to XMPP server at address: " + xmppServerAddress);
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
conn.addPacketListener(new PacketListener() {
@Override
public void processPacket(Packet packet) {
log.debug(packet.toXML());
}
}, new PacketFilter() {
@Override
public boolean accept(Packet packet) {
return true;
}
});
IQ packet = new IQ() {
@Override
public String getChildElementXML() {
return "<query xmlns=\"urn:uf:viz:collaboration:iq:http\">"
+ "<httpinfo xmlns=\"urn:uf:viz:collaboration\">"
+ "<url>" + dataServerUrl + "</url></httpinfo></query>";
}
};
packet.setType(Type.SET);
try {
Packet reply = SyncPacketSend.getReply(conn, packet,
PACKET_SEND_TIMEOUT);
log.debug("URL configuration set response: " + reply.toXML());
} catch (XMPPException e) {
log.warn("Problem sending URL configuration packet", e);
}
while (conn.isConnected()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.warn("Server connection thread interrupted", e);
disconnect();
}
}
}
/**
* disconnect from XMPP server
*/
public void disconnect() {
log.debug("Disconnecting from XMPP server");
conn.disconnect();
}
}

View file

@ -0,0 +1,12 @@
#!/bin/bash
(cd $(dirname "$0")
PIDFILE=collabserver.pid
if [[ -e $PIDFILE ]]
then
echo "PID file already exists at $PIDFILE, exiting"
exit 1
fi
nohup java -server -jar collabserver.jar &
echo $! > $PIDFILE
)

View file

@ -0,0 +1,10 @@
#!/bin/bash
(cd $(dirname "$0")
PIDFILE=collabserver.pid
if [[ -e $PIDFILE ]]
then
kill `cat $PIDFILE`
rm $PIDFILE
fi
)

View file

@ -2,10 +2,9 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2 Bundle-ManifestVersion: 2
Bundle-Name: CollaborationConfiguration Bundle-Name: CollaborationConfiguration
Bundle-SymbolicName: com.raytheon.openfire.plugin.configuration.collaboration;singleton:=true Bundle-SymbolicName: com.raytheon.openfire.plugin.configuration.collaboration;singleton:=true
Bundle-Version: 1.0.0.qualifier Bundle-Version: 1.14.0
Bundle-Vendor: RAYTHEON Bundle-Vendor: RAYTHEON
Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: org.jivesoftware.openfire;bundle-version="3.7.1", Require-Bundle: org.jivesoftware.openfire;bundle-version="3.7.1",
org.apache.commons.configuration;bundle-version="1.6.0",
org.apache.commons.collections;bundle-version="3.2.0", org.apache.commons.collections;bundle-version="3.2.0",
org.apache.http;bundle-version="4.1.2" org.apache.http;bundle-version="4.1.2"

View file

@ -0,0 +1,259 @@
/**
* 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.openfire.plugin.configuration.collaboration;
import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.jivesoftware.openfire.IQRouter;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.disco.IQDiscoInfoHandler;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.TaskEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import com.raytheon.openfire.plugin.configuration.collaboration.configuration.ConfigurationPacket;
import com.raytheon.openfire.plugin.configuration.collaboration.http.HttpStatusMonitor;
import com.raytheon.openfire.plugin.configuration.collaboration.iq.AbstractConfigHandler;
import com.raytheon.openfire.plugin.configuration.collaboration.iq.DataAuthHandler;
import com.raytheon.openfire.plugin.configuration.collaboration.iq.HttpAddressHandler;
import com.raytheon.openfire.plugin.configuration.collaboration.listener.CollaborationSessionEventListener;
/**
* The main plugin class for the AWIPS II Collaboration HTTP Configuration
* plugin; creates and configures an openfire listener and the http monitor.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 07, 2012 bkowal Initial creation
* Jan 06, 2013 2563 bclement replaced TaskEngine shutdown with cancel task
* added legacy format setter/accessor
* Feb 14, 2013 2756 bclement rename and refactor for operation with generic http
* server configured over XMPP
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpConfigurationPlugin implements Plugin {
private static final Logger logger = LoggerFactory
.getLogger(HttpConfigurationPlugin.class);
private static final String INTERVAL = "plugin.collaboration.http.interval";
private static final long DEFAULT_INTERVAL_MS = 60000;
private static final long MONITOR_DELAY_MS = 10000; /* 10 seconds */
private TaskEngine monitorTaskEngine;
private CollaborationSessionEventListener listener;
private HttpStatusMonitor httpStatusMonitor;
/**
*
*/
public HttpConfigurationPlugin() {
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
*/
@Override
public void destroyPlugin() {
if ((this.listener == null) == false) {
SessionEventDispatcher.removeListener(this.listener);
this.listener.dispose();
this.listener = null;
}
if ((this.monitorTaskEngine == null) == false) {
// we don't want to shutdown the engine since it is a singleton
// if we do, we can't reload the plugin
this.monitorTaskEngine
.cancelScheduledTask(this.httpStatusMonitor);
this.monitorTaskEngine = null;
}
}
/**
* Registers IQ configuration handlers with IQ router and adds features to
* info discovery
*
* @param server
* @param handlers
*/
private void registerConfigHandlers(XMPPServer server,
List<AbstractConfigHandler> handlers) {
IQRouter router = server.getIQRouter();
IQDiscoInfoHandler infoHandler = server.getIQDiscoInfoHandler();
for (AbstractConfigHandler handler : handlers) {
Iterator<String> iter = handler.getFeatures();
while (iter.hasNext()) {
infoHandler.addServerFeature(iter.next());
}
router.addHandler(handler);
}
}
/*
* (non-Javadoc)
*
* @see
* org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware
* .openfire.container.PluginManager, java.io.File)
*/
@Override
public void initializePlugin(PluginManager manager, File arg1) {
XMPPServer server = XMPPServer.getInstance();
DataAuthHandler authHandler = new DataAuthHandler();
HttpAddressHandler addressHandler = new HttpAddressHandler();
registerConfigHandlers(server,
Arrays.asList(authHandler, addressHandler));
/* Retrieve openfire components. */
JID serverId = new JID(server.getServerInfo().getXMPPDomain());
/* The http status monitor */
this.httpStatusMonitor = new HttpStatusMonitor(addressHandler, serverId);
/* Create a new listener. */
this.listener = new CollaborationSessionEventListener();
/* Initialize the listener. */
this.listener
.setHttpStatusChecker(this.httpStatusMonitor);
this.listener.setServerAddress(serverId);
this.listener.setRouter(server.getMessageRouter());
/* Make it possible for the listener to receive events. */
SessionEventDispatcher.addListener(this.listener);
this.scheduleStatusMonitor();
}
/**
* Schedules the http collaboration status monitor at the specified,
* configurable interval using the openfire TaskEngine; see
* {@link TaskEngine}
*/
private synchronized void scheduleStatusMonitor() {
logger.info("Scheduling the httpd collaboration status monitor ...");
if (this.monitorTaskEngine == null) {
this.monitorTaskEngine = TaskEngine.getInstance();
}
this.monitorTaskEngine.schedule(this.httpStatusMonitor,
MONITOR_DELAY_MS, this.getHttpMonitorInterval());
}
/**
* Stops the scheduled http collaboration status monitor if it has been
* scheduled.
*/
private synchronized void stopStatusMonitor() {
if (this.monitorTaskEngine == null) {
return;
}
this.monitorTaskEngine
.cancelScheduledTask(this.httpStatusMonitor);
}
/**
* Sets the configurable interval for the amount of time between executions
* of the http collaboration status monitor
*
* @param _interval
* the interval in milliseconds
*/
public void setHttpMonitorInterval(long _interval) {
long originalInterval = this.getHttpMonitorInterval();
if (_interval == originalInterval) {
return;
}
JiveGlobals.setProperty(INTERVAL, Long.toString(_interval));
this.stopStatusMonitor();
this.scheduleStatusMonitor();
}
/**
* Returns the interval from the openfire configuration
*
* @return the interval in milliseconds
*/
public long getHttpMonitorInterval() {
return JiveGlobals.getLongProperty(INTERVAL, DEFAULT_INTERVAL_MS);
}
/**
* Sets the global value for toggling pre 14.3 message format support
*
* @param legacy
*/
public void setLegacySupport(boolean legacy) {
JiveGlobals.setProperty(ConfigurationPacket.LEGACY_KEY,
Boolean.toString(legacy));
}
/**
* @return true if configured to support pre 14.3 message format
*/
public boolean hasLegacySupport() {
return JiveGlobals.getBooleanProperty(ConfigurationPacket.LEGACY_KEY,
true);
}
/**
* Set white list of accounts that belong to dataservers. The first server
* in the list is treated as the primary dataserver.
*
* @param whiteList
*/
public void setDataserverUsers(String whiteList) {
JiveGlobals
.setProperty(AbstractConfigHandler.DATASERVER_USERS_KEY, whiteList);
}
/**
* @return white list of accounts that belong to dataservers. The first
* server in the list is treated as the primary dataserver.
*/
public String getDataserverUsers() {
return JiveGlobals
.getProperty(AbstractConfigHandler.DATASERVER_USERS_KEY, "");
}
}

View file

@ -1,278 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.apache.commons.configuration.ConfigurationException;
import org.jivesoftware.openfire.MessageRouter;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.TaskEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import com.raytheon.openfire.plugin.configuration.collaboration.configuration.ConfigurationPacket;
import com.raytheon.openfire.plugin.configuration.collaboration.configuration.HttpdCollaborationConfiguration;
import com.raytheon.openfire.plugin.configuration.collaboration.httpd.HttpdCollaborationConfReader;
import com.raytheon.openfire.plugin.configuration.collaboration.httpd.HttpdCollaborationStatusMonitor;
import com.raytheon.openfire.plugin.configuration.collaboration.listener.HttpdCollaborationSessionEventListener;
/**
* The main plugin class for the AWIPS II Httpd Collaboration Configuration
* plugin; creates and configures an openfire listener and the httpd monitor.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 07, 2012 bkowal Initial creation
* Jan 06, 2013 2563 bclement replaced TaskEngine shutdown with cancel task
* added legacy format setter/accessor
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationConfigurationPlugin implements Plugin {
private static final Logger logger = LoggerFactory
.getLogger(HttpdCollaborationConfigurationPlugin.class);
private static final String LOCATION = "plugin.collaboration.httpd.location";
private static final String INTERVAL = "plugin.collaboration.httpd.interval";
private static final String DEFAULT_LOCATION = "/awips2/httpd_collaboration";
private static final long DEFAULT_INTERVAL_MS = 60000;
private static final long MONITOR_DELAY_MS = 60000; /* 1 Minute */
private static final String ABORT_ERROR_MESSAGE = "Aborting initialization of the HttpdCollaborationConfigurationPlugin plugin.";
private HttpdCollaborationSessionEventListener listener;
private HttpdCollaborationStatusMonitor httpdCollaborationStatusMonitor;
private TaskEngine monitorTaskEngine;
/**
*
*/
public HttpdCollaborationConfigurationPlugin() {
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
*/
@Override
public void destroyPlugin() {
if ((this.listener == null) == false) {
SessionEventDispatcher.removeListener(this.listener);
this.listener.dispose();
this.listener = null;
}
if ((this.monitorTaskEngine == null) == false) {
// we don't want to shutdown the engine since it is a singleton
// if we do, we can't reload the plugin
this.monitorTaskEngine
.cancelScheduledTask(this.httpdCollaborationStatusMonitor);
this.monitorTaskEngine = null;
}
}
/*
* (non-Javadoc)
*
* @see
* org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware
* .openfire.container.PluginManager, java.io.File)
*/
@Override
public void initializePlugin(PluginManager arg0, File arg1) {
/*
* Attempt to read the httpd collaboration configuration.
*/
HttpdCollaborationConfReader confReader = new HttpdCollaborationConfReader(
this.getHttpdCollaborationLocation());
try {
confReader.execute();
} catch (ConfigurationException e1) {
logger.error("Unable to read the httpd collaboration configuration.",
e1);
logger.error(ABORT_ERROR_MESSAGE);
return;
} catch (Exception e2) {
logger.error("An unexpected error has occurred.", e2);
logger.error(ABORT_ERROR_MESSAGE);
return;
}
/*
* Determine the hostname - there is a requirement in place that
* requires openfire and httpd-collaboration to be installed on the same
* machine.
*/
InetAddress address = null;
try {
address = InetAddress.getLocalHost();
} catch (UnknownHostException e1) {
logger.error("Unable to retrieve the hostname.", e1);
logger.error(ABORT_ERROR_MESSAGE);
return;
}
/* Persist the configuration information. */
HttpdCollaborationConfiguration httpdCollaborationConfiguration = new HttpdCollaborationConfiguration();
httpdCollaborationConfiguration.setSessionDataHost(address
.getHostName());
httpdCollaborationConfiguration.setSessionDataPort(confReader
.getListenPort());
/* The httpd-collaboration status monitor */
this.httpdCollaborationStatusMonitor = new HttpdCollaborationStatusMonitor(
this.getHttpdCollaborationLocation(),
httpdCollaborationConfiguration.getHttpdCollaborationURL(),
httpdCollaborationConfiguration.toString());
/* Retrieve openfire components. */
JID serverAddress = new JID(XMPPServer.getInstance().getServerInfo()
.getXMPPDomain());
MessageRouter router = XMPPServer.getInstance().getMessageRouter();
/* Create a new listener. */
this.listener = new HttpdCollaborationSessionEventListener(
httpdCollaborationConfiguration.toString());
/* Initialize the listener. */
this.listener
.setHttpdCollaborationStatusChecker(this.httpdCollaborationStatusMonitor);
this.listener.setServerAddress(serverAddress);
this.listener.setRouter(router);
/* Make it possible for the listener to receive events. */
SessionEventDispatcher.addListener(this.listener);
this.scheduleStatusMonitor();
}
/**
* Schedules the httpd collaboration status monitor at the specified,
* configurable interval using the openfire TaskEngine; see
* {@link TaskEngine}
*/
private synchronized void scheduleStatusMonitor() {
logger.info("Scheduling the httpd collaboration status monitor ...");
if (this.monitorTaskEngine == null) {
this.monitorTaskEngine = TaskEngine.getInstance();
}
this.monitorTaskEngine.schedule(this.httpdCollaborationStatusMonitor,
MONITOR_DELAY_MS, this.getHttpdMonitorInterval());
}
/**
* Stops the scheduled httpd collaboration status monitor if it has been
* scheduled.
*/
private synchronized void stopStatusMonitor() {
if (this.monitorTaskEngine == null) {
return;
}
this.monitorTaskEngine
.cancelScheduledTask(this.httpdCollaborationStatusMonitor);
}
/**
* Sets the configurable installation location of awips2-httpd-collaboration
*
* @param _location
* the location of awips2-httpd-collaboration
*/
public void setHttpdCollaborationLocation(String _location) {
JiveGlobals.setProperty(LOCATION, _location);
}
/**
* Returns the installation location of awips2-httpd-collaboration from the
* openfire configuration
*
* @return the installation root of awips2-httpd-collaboration
*/
public String getHttpdCollaborationLocation() {
return JiveGlobals.getProperty(LOCATION, DEFAULT_LOCATION);
}
/**
* Sets the configurable interval for the amount of time between executions
* of the httpd collaboration status monitor
*
* @param _interval
* the interval in milliseconds
*/
public void setHttpdMonitorInterval(long _interval) {
long originalInterval = this.getHttpdMonitorInterval();
if (_interval == originalInterval) {
return;
}
JiveGlobals.setProperty(INTERVAL, Long.toString(_interval));
this.stopStatusMonitor();
this.scheduleStatusMonitor();
}
/**
* Returns the interval from the openfire configuration
*
* @return the interval in milliseconds
*/
public long getHttpdMonitorInterval() {
return JiveGlobals.getLongProperty(INTERVAL, DEFAULT_INTERVAL_MS);
}
/**
* Sets the global value for toggling pre 14.3 message format support
*
* @param legacy
*/
public void setLegacySupport(boolean legacy) {
JiveGlobals.setProperty(ConfigurationPacket.LEGACY_KEY,
Boolean.toString(legacy));
}
/**
* @return true if configured to support pre 14.3 message format
*/
public boolean hasLegacySupport() {
return JiveGlobals.getBooleanProperty(ConfigurationPacket.LEGACY_KEY,
true);
}
}

View file

@ -19,11 +19,6 @@
**/ **/
package com.raytheon.openfire.plugin.configuration.collaboration.configuration; package com.raytheon.openfire.plugin.configuration.collaboration.configuration;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.dom4j.Element; import org.dom4j.Element;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
@ -32,6 +27,7 @@ import org.slf4j.LoggerFactory;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import org.xmpp.packet.PacketExtension; import org.xmpp.packet.PacketExtension;
/** /**
* Packet extension for collaboration configuration messages to clients * Packet extension for collaboration configuration messages to clients
* *
@ -42,6 +38,7 @@ import org.xmpp.packet.PacketExtension;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Dec 20, 2013 bclement Initial creation * Dec 20, 2013 bclement Initial creation
* Feb 14, 2014 2756 bclement moved preferences to separate file
* *
* </pre> * </pre>
* *
@ -54,33 +51,6 @@ public class ConfigurationPacket extends PacketExtension {
private static final Logger log = LoggerFactory private static final Logger log = LoggerFactory
.getLogger(ConfigurationPacket.class); .getLogger(ConfigurationPacket.class);
// format property file
public static final String CONFIG_FILE_KEY = "plugin.collaboration.packet.format.file";
private static final String DEFAULT_PLUGIN_CONFIG_FILE = "conf"
+ File.separator + "configurationPacketFormat.properties";
private static final Properties properties = new Properties();
static {
String confFile = JiveGlobals.getProperty(CONFIG_FILE_KEY,
DEFAULT_PLUGIN_CONFIG_FILE);
File f = new File(JiveGlobals.getHomeDirectory(), confFile);
if (f.exists()) {
try {
properties.load(new FileInputStream(f));
} catch (IOException e) {
// defaults will be used
log.error("Problem loading packet format configuration file: "
+ f.getAbsolutePath(), e);
}
} else {
log.info("Using default config packet format since there was no format file at "
+ f.getAbsolutePath());
}
}
// property keys // property keys
public static final String XMLNS_KEY = "plugin.collaboration.packet.xmlns"; public static final String XMLNS_KEY = "plugin.collaboration.packet.xmlns";
@ -128,11 +98,11 @@ public class ConfigurationPacket extends PacketExtension {
* configuration payload for packet * configuration payload for packet
* @return * @return
*/ */
private static Element create(String body) { public static Element create(String body) {
String element = properties.getProperty(ELEMENT_KEY, ELEMENT_DEFAULT); String element = PacketPreferences.get(ELEMENT_KEY, ELEMENT_DEFAULT);
String xmlns = properties.getProperty(XMLNS_KEY, XMLNS_DEFAULT); String xmlns = PacketPreferences.get(XMLNS_KEY, XMLNS_DEFAULT);
Element rval = docFactory.createElement(element, xmlns); Element rval = docFactory.createElement(element, xmlns);
String attributes = properties.getProperty(ATTRIBUTES_KEY, String attributes = PacketPreferences.get(ATTRIBUTES_KEY,
ATTRIBUTES_DEFAULT); ATTRIBUTES_DEFAULT);
for (String keyval : StringUtils.split(attributes, ',')) { for (String keyval : StringUtils.split(attributes, ',')) {
String[] separated = StringUtils.split(keyval, '='); String[] separated = StringUtils.split(keyval, '=');

View file

@ -1,83 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration.configuration;
/**
* Used to store and format the information retrieved from the
* httpd-collaboration httpd.conf file that will be returned to the client.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 07, 2012 bkowal Initial creation
* Jan 06, 2013 2563 bclement removed config preamble
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationConfiguration {
private static final String SESSION_DATA_URL_FORMAT_STRING = "http://%s:%s/session_data/";
private static final String URL_PARAMETER_NAME = "sessionDataHttpURL";
private String sessionDataHost;
private String sessionDataPort;
/**
*
*/
public HttpdCollaborationConfiguration() {
this.sessionDataHost = null;
this.sessionDataPort = null;
}
@Override
public String toString() {
return URL_PARAMETER_NAME + " : " + this.getHttpdCollaborationURL();
}
public String getHttpdCollaborationURL() {
return String.format(SESSION_DATA_URL_FORMAT_STRING,
this.sessionDataHost, this.sessionDataPort);
}
public String getSessionDataHost() {
return sessionDataHost;
}
public void setSessionDataHost(String sessionDataHost) {
this.sessionDataHost = sessionDataHost;
}
public String getSessionDataPort() {
return sessionDataPort;
}
public void setSessionDataPort(String sessionDataPort) {
this.sessionDataPort = sessionDataPort;
}
}

View file

@ -0,0 +1,91 @@
/**
* 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.openfire.plugin.configuration.collaboration.configuration;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Preferences wrapper for packet format
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 10, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class PacketPreferences {
private static final Logger log = LoggerFactory
.getLogger(PacketPreferences.class);
// format property file
public static final String CONFIG_FILE_KEY = "plugin.collaboration.packet.format.file";
private static final String DEFAULT_PLUGIN_CONFIG_FILE = "conf"
+ File.separator + "configurationPacketFormat.properties";
private static final Properties properties = new Properties();
static {
String confFile = JiveGlobals.getProperty(CONFIG_FILE_KEY,
DEFAULT_PLUGIN_CONFIG_FILE);
File f = new File(JiveGlobals.getHomeDirectory(), confFile);
if (f.exists()) {
try {
properties.load(new FileInputStream(f));
} catch (IOException e) {
// defaults will be used
log.error("Problem loading packet format configuration file: "
+ f.getAbsolutePath(), e);
}
} else {
log.info("Using default config packet format since there was no format file at "
+ f.getAbsolutePath());
}
}
/**
* Get property from configuration file. Returns defaultValue if key doesn't
* exist
*
* @param key
* @param defaultValue
* @return
*/
public static String get(String key, String defaultValue) {
return properties.getProperty(key, defaultValue);
}
}

View file

@ -1,73 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration.exception;
/**
* Implements an exception that is thrown whenever one of the conditions
* indicating httpd-collaboration is not running occurs.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 30, 2012 bkowal Initial creation
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationNotRunningException extends Throwable {
private static final long serialVersionUID = 6598229436212894638L;
private static final String MESSAGE_PREFIX = "The httpd-collaboration server is not running; ";
/**
*
*/
public HttpdCollaborationNotRunningException() {
}
/**
* @param message
*/
public HttpdCollaborationNotRunningException(String message) {
super(MESSAGE_PREFIX + message);
}
/**
* @param cause
*/
public HttpdCollaborationNotRunningException(Throwable cause) {
super(cause);
}
/**
* @param message
* @param cause
*/
public HttpdCollaborationNotRunningException(String message, Throwable cause) {
super(MESSAGE_PREFIX + message, cause);
}
}

View file

@ -1,74 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration.exception;
/**
* Implements an exception that is thrown whenever it is not possible to
* determine whether or not httpd-collaboration is currently running.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 30, 2012 bkowal Initial creation
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationStatusException extends Throwable {
private static final long serialVersionUID = -2446532142660776343L;
private static final String MESSAGE_PREFIX = "Unable to determine the status of the httpd-collaboration server; ";
/**
*
*/
public HttpdCollaborationStatusException() {
// TODO Auto-generated constructor stub
}
/**
* @param message
*/
public HttpdCollaborationStatusException(String message) {
super(MESSAGE_PREFIX + message);
}
/**
* @param cause
*/
public HttpdCollaborationStatusException(Throwable cause) {
super(cause);
}
/**
* @param message
* @param cause
*/
public HttpdCollaborationStatusException(String message, Throwable cause) {
super(MESSAGE_PREFIX + message, cause);
}
}

View file

@ -0,0 +1,259 @@
/**
* 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.openfire.plugin.configuration.collaboration.http;
import java.io.IOException;
import java.util.TimerTask;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.http.HttpStatus;
import org.jivesoftware.openfire.SessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import com.raytheon.openfire.plugin.configuration.collaboration.configuration.ConfigurationPacket;
import com.raytheon.openfire.plugin.configuration.collaboration.iq.AbstractConfigHandler;
import com.raytheon.openfire.plugin.configuration.collaboration.iq.HttpAddressHandler;
/**
* Runs a series of checks to determine if http collaboration is still running
* on-demand and on a scheduled basis. The checks include: verifying that a url
* is configured for the primary dataserver and executing an http GET request
* against the http dataserver.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 26, 2012 bkowal Initial creation
* Jan 06, 2013 2563 bclement replaced chat message with packet extension
* Feb 14, 2013 2756 bclement rename and refactor for operation with generic http
* server configured over XMPP
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpStatusMonitor extends TimerTask {
private static final Logger logger = LoggerFactory
.getLogger(HttpStatusMonitor.class);
private final SessionManager sessionManager;
private static final HttpClient httpClient = new HttpClient();
private GetMethod _getMethod = null;
private String _previousUrl = null;
private static enum Status {
URL_UNSET, SERVER_DOWN, SERVER_UP
}
private Status status = Status.URL_UNSET;
private final HttpAddressHandler addressHandler;
private final JID serverId;
/**
* @param addressHandler
* IQ hander responsible for http url configuration
* @param serverId
* id of server used as from address in messages
*/
public HttpStatusMonitor(HttpAddressHandler addressHandler, JID serverId) {
this.sessionManager = SessionManager.getInstance();
this.addressHandler = addressHandler;
this.serverId = serverId;
}
/**
* Get the current URL configuration string.
*
* @return null if no valid configuration is available
*/
public String getCurrentUrlConfig() {
String primary = AbstractConfigHandler.getPrimaryDataServer();
if (primary == null) {
logger.debug("No primary dataserver found");
urlUnset();
return null;
} else {
String url = addressHandler.getHttpAddress(primary);
if (url == null) {
logger.debug("No url set for dataserver: " + primary);
urlUnset();
return null;
} else {
return verifyHttpProcess(url);
}
}
}
/**
* Verify that the http server is online and accepting requests. Returns the
* valid URL configuration string.
*
* @param url
* url to verify
* @return null if successful request cannot be made
*/
private String verifyHttpProcess(String url) {
synchronized (httpClient) {
int statusCode = -1;
try {
GetMethod get = getCachedMethod(url);
statusCode = httpClient.executeMethod(get);
} catch (HttpException e1) {
String msg = "Unable to execute GET against the collaboration dataserver at "
+ url;
logger.error(msg, e1);
setOffline(msg);
} catch (IOException e2) {
String msg = "Unable to read the response from the collaboration dataserver at "
+ url;
logger.error(msg, e2);
setOffline(msg);
}
if ((statusCode == HttpStatus.SC_OK) == false) {
String msg = "Dataserver not is not available - received status "
+ statusCode;
logger.error(msg);
setOffline(msg);
return null;
} else {
String urlConfig = "sessionDataHttpURL : " + url;
setOnline(urlConfig);
return urlConfig;
}
}
}
/**
* Get the http GET method for url. Uses a simple cache.
*
* @param url
* @return
*/
private GetMethod getCachedMethod(String url) {
if (_previousUrl == null || !_previousUrl.equals(url)) {
logger.debug("Dataserver url changed from " + _previousUrl + " to "
+ url);
_getMethod = new GetMethod(url);
_previousUrl = url;
}
return _getMethod;
}
@Override
public void run() {
logger.debug("Verifying that httpd-collaboration is still available ...");
getCurrentUrlConfig();
}
/**
* Broadcast configuration message to all users
*
* @param body
* url or error configuration string
*/
private synchronized void broadcastMessage(String body) {
logger.debug("Broadcasting message: " + body);
Message message = ConfigurationPacket.createMessage(body);
message.setFrom(serverId);
this.sessionManager.broadcast(message);
}
/**
* Method to call when http server cannot be reached
*
* @param message
*/
private void setOffline(String message) {
switch (status) {
case SERVER_UP:
logger.debug("Status changing from " + status + " to "
+ Status.SERVER_DOWN);
broadcastMessage(formatError(message));
status = Status.SERVER_DOWN;
default:
logger.debug("SetOffline: Status not changed from " + status);
// no action since we don't want to spam with error messages
break;
}
}
/**
* Format error message to configuration format
*
* @param message
* @return
*/
private String formatError(String message) {
return "error : " + message;
}
/**
* Method to call when http server is configured and running
*
* @param urlConfig
*/
private void setOnline(String urlConfig) {
switch (status) {
case SERVER_DOWN:
case URL_UNSET:
logger.debug("Status changing from " + status + " to "
+ Status.SERVER_UP);
broadcastMessage(urlConfig);
status = Status.SERVER_UP;
break;
default:
logger.debug("SetOnline: Status not changed from " + status);
// no action
}
}
/**
* Method to call when http server url is not configured
*/
private void urlUnset() {
switch (status) {
case SERVER_UP:
logger.debug("Status changing from " + status + " to "
+ Status.URL_UNSET);
broadcastMessage(formatError("Dataserver does not have URL configured in chat server."));
status = Status.URL_UNSET;
default:
logger.debug("UrlUnset: Status not changed from " + status);
// no action since we don't want to spam with error messages
break;
}
}
}

View file

@ -1,65 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration.httpd;
import org.apache.commons.configuration.ConfigurationException;
import com.raytheon.openfire.plugin.configuration.collaboration.util.HttpdCollaborationUtil;
/**
* Reads the httpd.conf file for the configurable httpd-collaboration instance.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 7, 2012 bkowal Initial creation
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationConfReader {
private static String CONF_FILE;
private static final String LISTEN_PROPERTY = "Listen";
private HttpdCollaborationPropertiesConfiguration propertiesConfiguration;
/**
*
*/
public HttpdCollaborationConfReader(String _location) {
CONF_FILE = HttpdCollaborationUtil.endPathIfNecessary(_location)
+ "etc/httpd/conf/httpd.conf";
}
public void execute() throws ConfigurationException {
this.propertiesConfiguration = new HttpdCollaborationPropertiesConfiguration();
this.propertiesConfiguration.load(CONF_FILE);
}
public String getListenPort() {
return this.propertiesConfiguration.getString(LISTEN_PROPERTY);
}
}

View file

@ -1,84 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration.httpd;
import java.io.File;
import java.net.URL;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
/**
* This class was created because version 1.6 of commons configuration does not
* allow you to decide whether you want to allow includes or not. There is a
* 'setIncludesAllowed' method; however, it is protected.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 2012 bkowal Initial creation
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationPropertiesConfiguration extends PropertiesConfiguration {
/**
*
*/
public HttpdCollaborationPropertiesConfiguration() {
super();
}
/**
* @param fileName
* @throws ConfigurationException
*/
public HttpdCollaborationPropertiesConfiguration(String fileName)
throws ConfigurationException {
super(fileName);
}
/**
* @param file
* @throws ConfigurationException
*/
public HttpdCollaborationPropertiesConfiguration(File file) throws ConfigurationException {
super(file);
}
/**
* @param url
* @throws ConfigurationException
*/
public HttpdCollaborationPropertiesConfiguration(URL url) throws ConfigurationException {
super(url);
}
@Override
public void setBasePath(String basePath) {
super.setBasePath(basePath);
this.setIncludesAllowed(false);
}
}

View file

@ -1,180 +0,0 @@
/**
* 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.openfire.plugin.configuration.collaboration.httpd;
import java.io.File;
import java.io.IOException;
import java.util.TimerTask;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.http.HttpStatus;
import org.jivesoftware.openfire.SessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.Message;
import com.raytheon.openfire.plugin.configuration.collaboration.configuration.ConfigurationPacket;
import com.raytheon.openfire.plugin.configuration.collaboration.exception.HttpdCollaborationNotRunningException;
import com.raytheon.openfire.plugin.configuration.collaboration.exception.HttpdCollaborationStatusException;
import com.raytheon.openfire.plugin.configuration.collaboration.util.HttpdCollaborationUtil;
/**
* Runs a series of checks to determine if httpd-collaboration is still running
* on-demand and on a scheduled basis. The checks include: verifying that a pid
* file exists for httpd-collaboration and executing an http GET request against
* the httpd-collaboration server.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 26, 2012 bkowal Initial creation
* Jan 06, 2013 2563 bclement replaced chat message with packet extension
*
* </pre>
*
* @author bkowal
* @version 1.0
*/
public class HttpdCollaborationStatusMonitor extends TimerTask {
private static final Logger logger = LoggerFactory
.getLogger(HttpdCollaborationStatusMonitor.class);
private SessionManager sessionManager;
private boolean httpdCollaborationBecameUnavailable;
// provided by the configuration
private String HTTPD_COLLABORATION_CONFIGURATION;
private static String HTTPD_PID_FILE;
private static File pidFile;
private static final HttpClient httpClient = new HttpClient();
private GetMethod getMethod;
/**
*
*/
public HttpdCollaborationStatusMonitor(String _location,
String _httpdCollaborationURL,
String _httpdCollaborationConfiguration) {
HTTPD_PID_FILE = HttpdCollaborationUtil.endPathIfNecessary(_location)
+ "var/run/httpd.pid";
HTTPD_COLLABORATION_CONFIGURATION = _httpdCollaborationConfiguration;
this.sessionManager = SessionManager.getInstance();
this.httpdCollaborationBecameUnavailable = false;
pidFile = new File(HTTPD_PID_FILE);
this.getMethod = new GetMethod(
HttpdCollaborationUtil
.endPathIfNecessary(_httpdCollaborationURL));
}
public void statusHttpdCollaboration()
throws HttpdCollaborationNotRunningException,
HttpdCollaborationStatusException {
this.doesPidFileExist();
// "ping" the httpd process to ensure that it is actually running
this.verifyHttpdProcess();
}
private void doesPidFileExist()
throws HttpdCollaborationNotRunningException {
// verify the httpd-collaboration pid file exists.
if (pidFile.exists() == false) {
// httpd-collaboration is not running
throw new HttpdCollaborationNotRunningException(
"the httpd-collaboration pid file does not exist: "
+ pidFile.getAbsolutePath());
}
}
private void verifyHttpdProcess()
throws HttpdCollaborationNotRunningException,
HttpdCollaborationStatusException {
synchronized (this.getMethod) {
int statusCode = -1;
try {
statusCode = httpClient.executeMethod(getMethod);
} catch (HttpException e1) {
throw new HttpdCollaborationStatusException(
"Unable to execute GET against the httpd-collaboration server",
e1);
} catch (IOException e2) {
throw new HttpdCollaborationStatusException(
"Unable to read the response from the httpd-collaboration server",
e2);
}
if ((statusCode == HttpStatus.SC_OK) == false) {
throw new HttpdCollaborationNotRunningException(
"httpd-collaboration is not available - received status = "
+ statusCode);
}
}
}
@Override
public void run() {
logger.debug("Verifying that httpd-collaboration is still available ...");
String errorMessage = null;
try {
this.statusHttpdCollaboration();
} catch (HttpdCollaborationNotRunningException e1) {
logger.error("httpd-collaboration is not available", e1);
this.httpdCollaborationBecameUnavailable = true;
errorMessage = HttpdCollaborationUtil.encodeErrorMessage(e1);
} catch (HttpdCollaborationStatusException e2) {
logger.error(
"unable to determine if httpd-collaboration is still available!!!",
e2);
this.httpdCollaborationBecameUnavailable = true;
errorMessage = HttpdCollaborationUtil.encodeErrorMessage(e2);
}
if (errorMessage == null && this.httpdCollaborationBecameUnavailable) {
// If we reach this point, httpd-collaboration became
// unavailable at some point in time; re-enable shared displays
// in CAVE for all users.
this.broadcastMessage(HTTPD_COLLABORATION_CONFIGURATION);
this.httpdCollaborationBecameUnavailable = false;
} else {
// Broadcast to all users that httpd-collaboration is no longer
// available; shared displays in CAVE will be disabled
this.broadcastMessage(errorMessage);
}
}
private synchronized void broadcastMessage(String body) {
Message message = ConfigurationPacket.createMessage(body);
this.sessionManager.broadcast(message);
}
}

View file

@ -0,0 +1,301 @@
/**
* 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.openfire.plugin.configuration.collaboration.iq;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.tree.DefaultElement;
import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.PrivateStorage;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.disco.ServerFeaturesProvider;
import org.jivesoftware.openfire.handler.IQHandler;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.PacketError.Condition;
/**
* Base IQ handler for configuration over XMPP
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 12, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public abstract class AbstractConfigHandler extends IQHandler implements
ServerFeaturesProvider {
public static final String DATASERVER_USERS_KEY = "plugin.collaboration.dataserver.users";
public static final String QUERY_ELEMENT_NAME = "query";
public static final String COLLAB_XMLNS = "urn:uf:viz:collaboration";
private static final Logger log = LoggerFactory
.getLogger(AbstractConfigHandler.class);
protected final IQHandlerInfo info;
protected final List<String> features;
protected final PrivateStorage storage;
/**
* @param moduleName
* display name for handler
* @param features
* xmlns strings for features
* @param info
* qualified name registration information
*/
public AbstractConfigHandler(String moduleName, List<String> features,
IQHandlerInfo info) {
super(moduleName);
this.info = info;
this.features = features;
this.storage = XMPPServer.getInstance().getPrivateStorage();
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.handler.IQHandler#getInfo()
*/
@Override
public IQHandlerInfo getInfo() {
return info;
}
/*
* (non-Javadoc)
*
* @see
* org.jivesoftware.openfire.handler.IQHandler#handleIQ(org.xmpp.packet.IQ)
*/
@Override
public IQ handleIQ(IQ packet) throws UnauthorizedException {
Type type = packet.getType();
IQ rval;
switch (type) {
case get:
rval = handleGet(packet);
break;
case set:
rval = handleSet(packet);
break;
default:
rval = createError(packet, new PacketError(Condition.bad_request));
}
return rval;
}
/**
* Process get request
*
* @param packet
* request packet
* @return
* @throws UnauthorizedException
*/
abstract protected IQ handleGet(IQ packet) throws UnauthorizedException;
/**
* Process set request
*
* @param packet
* request packet
* @return
* @throws UnauthorizedException
*/
abstract protected IQ handleSet(IQ packet) throws UnauthorizedException;
/*
* (non-Javadoc)
*
* @see org.jivesoftware.openfire.disco.ServerFeaturesProvider#getFeatures()
*/
@Override
public Iterator<String> getFeatures() {
return features.iterator();
}
/**
* Create error response packet
*
* @param request
* @param error
* @return
*/
protected static IQ createError(IQ request, PacketError error) {
Element child = request.getChildElement();
child.setParent(null);
IQ rval = IQ.createResultIQ(request);
rval.setType(Type.error);
rval.setChildElement(child);
rval.setError(error);
return rval;
}
/**
* Get first child of parent found with qualified name
*
* @param parent
* @param name
* @param namespace
* @return
* @throws Exception
* if no child can be found
*/
@SuppressWarnings("unchecked")
protected static Element getChildElement(Element parent, String name,
String namespace) throws Exception {
Element rval = null;
Iterator<Element> iter = parent.elementIterator();
while (iter.hasNext()) {
Element child = iter.next();
if (child != null && child.getName().equals(name)
&& child.getNamespace().getURI().equals(namespace)) {
rval = child;
break;
}
}
if (rval == null) {
log.debug("Missing Data Element: " + parent.asXML());
String msg = "Missing Required Child Element: " + namespace + " "
+ name;
throw new Exception(msg);
}
return rval;
}
/**
* Remove all child elements from e
*
* @param e
*/
@SuppressWarnings("unchecked")
protected static void removeChildren(Element e) {
Iterator<Element> iter = e.elementIterator();
while (iter.hasNext()) {
e.remove(iter.next());
}
}
/**
* @param id
* @return true if id is in the list of dataserver users
*/
protected static boolean isDataServerUser(JID id) {
String usersStr = JiveGlobals.getProperty(DATASERVER_USERS_KEY, "");
// this will work if the dataserver user is set to a single account or a
// comma separated list or is blank
for (String user : StringUtils.split(usersStr, ',')) {
if (StringUtils.isBlank(user)) {
continue;
}
user = user.trim();
if (id.toBareJID().equals(user)) {
return true;
}
}
return false;
}
/**
* Return the primary dataserver which is the first item in the list of
* dataserver users
*
* @return null if none found
*/
public static String getPrimaryDataServer() {
String usersStr = JiveGlobals.getProperty(DATASERVER_USERS_KEY, "");
String[] users = StringUtils.split(usersStr, ',');
if (users.length < 1) {
return null;
}
// primary server is the only one or first in list
return users[0];
}
/**
* Retrieve data from private storage
*
* @param requestPacket
* request IQ for data
* @param id
* user id that data is stored under
* @param key
* empty xml element with qualified name
* @return
*/
protected IQ retrieve(IQ requestPacket, String id, Element key) {
Element queryElem = requestPacket.getChildElement();
Element respElem = storage.get(id, key);
IQ rval = IQ.createResultIQ(requestPacket);
queryElem.setParent(null);
removeChildren(queryElem);
queryElem.add(respElem);
rval.setChildElement(queryElem);
return rval;
}
/**
* Store to private data. NOTE: only child XML elements of the root data
* element will be retrievable. No attributes in the root element will be
* retrievable.
*
* @param id
* user id to store data under
* @param data
* xml element with data
*/
protected void store(String id, Element data) {
storage.add(id, data);
}
/**
* Create empty xml element using qualified name
*
* @param name
* @param namespace
* @return
*/
protected static Element createElement(String name, String namespace) {
return new DefaultElement(name, new Namespace(null, namespace));
}
}

View file

@ -0,0 +1,184 @@
/**
* 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.openfire.plugin.configuration.collaboration.iq;
import java.util.Arrays;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.pubsub.Node;
import org.jivesoftware.openfire.pubsub.PubSubModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.PacketError.Condition;
/**
* IQ handle for dataserver authorization and authentication. Stores client
* public keys and authorizes key use for pubsub nodes.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 10, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class DataAuthHandler extends AbstractConfigHandler {
public static final String AUTH_QUERY_XMLNS = "urn:uf:viz:collaboration:iq:auth";
public static final String INFO_ELEMENT_NAME = "authinfo";
public static final String PUBKEY_ELEMENT_NAME = "publickey";
public static final String ALG_ATTRIBUTE = "algorithm";
public static final String JID_ATTRIBUTE = "jid";
public static final String SESSIONID_ATTRIBUTE = "sessionid";
private static final Logger log = LoggerFactory
.getLogger(DataAuthHandler.class);
private final PubSubModule pubsub;
/**
* @param moduleName
*/
public DataAuthHandler() {
super("Collaboration Auth Query Handler", Arrays
.asList(AUTH_QUERY_XMLNS), new IQHandlerInfo(
QUERY_ELEMENT_NAME, AUTH_QUERY_XMLNS));
this.pubsub = XMPPServer.getInstance().getPubSubModule();
}
/*
* (non-Javadoc)
*
* @see com.raytheon.openfire.plugin.configuration.collaboration.iq.
* AbstractConfigHandler#handleGet(org.xmpp.packet.IQ)
*/
@Override
protected IQ handleGet(IQ packet) throws UnauthorizedException {
JID from = packet.getFrom();
String fromBare = from.toBareJID();
Element queryElem = packet.getChildElement();
String jid = queryElem.attributeValue(JID_ATTRIBUTE);
if (jid != null) {
if (!jid.equals(fromBare) && !isDataServerUser(from)) {
log.debug("User not authorized: " + from.toFullJID());
throw new UnauthorizedException(
"User not authorized for service: "
+ from.toFullJID());
}
} else {
jid = fromBare;
}
JID target = new JID(jid);
String sessionId = queryElem.attributeValue(SESSIONID_ATTRIBUTE);
if (StringUtils.isBlank(sessionId)) {
String msg = "Missing attribute: " + SESSIONID_ATTRIBUTE;
log.debug(msg);
return createError(packet, new PacketError(Condition.bad_request,
PacketError.Type.modify, msg));
}
Node node = pubsub.getNode(sessionId);
if (node == null) {
String msg = "No topic found for session: " + sessionId;
log.debug(msg);
return createError(packet, new PacketError(Condition.not_allowed,
PacketError.Type.cancel, msg));
}
if (!isOwner(jid, node)){
String msg = "User '" + jid + "' is not an owner of session '"
+ sessionId + "'";
log.debug(msg);
return createError(packet, new PacketError(Condition.not_allowed,
PacketError.Type.cancel, msg));
}
return retrieve(packet, target.getNode(),
createElement(INFO_ELEMENT_NAME, COLLAB_XMLNS));
}
/**
* @param jid
* @param node
* @return true if user with jid is owner of node
*/
private boolean isOwner(String jid, Node node) {
for (JID owner : node.getOwners()) {
if (jid.equals(owner.toBareJID())) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see com.raytheon.openfire.plugin.configuration.collaboration.iq.
* AbstractConfigHandler#handleSet(org.xmpp.packet.IQ)
*/
@Override
protected IQ handleSet(IQ packet) {
Element queryElem = packet.getChildElement();
Element infoElem;
Element pubkeyElem;
try {
infoElem = getChildElement(queryElem, INFO_ELEMENT_NAME,
COLLAB_XMLNS);
pubkeyElem = getChildElement(infoElem, PUBKEY_ELEMENT_NAME,
COLLAB_XMLNS);
} catch (Exception e) {
log.debug("Missing Required Element in packet: " + packet.toXML(),
e);
return createError(packet, new PacketError(Condition.bad_request,
PacketError.Type.modify, e.getLocalizedMessage()));
}
String pubKey = pubkeyElem.getText();
if (StringUtils.isBlank(pubKey)) {
log.debug("Missing Public Key Text");
return createError(packet, new PacketError(Condition.bad_request,
PacketError.Type.modify, "Missing Required Text Body"));
}
if (StringUtils.isBlank(pubkeyElem.attributeValue(ALG_ATTRIBUTE))) {
String msg = "Missing attribute: " + ALG_ATTRIBUTE;
return createError(packet, new PacketError(Condition.bad_request,
PacketError.Type.modify, msg));
}
store(packet.getFrom().getNode(), infoElem);
return IQ.createResultIQ(packet);
}
}

View file

@ -0,0 +1,177 @@
/**
* 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.openfire.plugin.configuration.collaboration.iq;
import java.util.Arrays;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.PacketError.Condition;
/**
* IQ handler for dataservers to store URL configuration
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb 11, 2014 2756 bclement Initial creation
*
* </pre>
*
* @author bclement
* @version 1.0
*/
public class HttpAddressHandler extends AbstractConfigHandler {
private static final Logger log = LoggerFactory
.getLogger(HttpAddressHandler.class);
public static final String HTTP_QUERY_XMLNS = "urn:uf:viz:collaboration:iq:http";
public static final String HTTP_ELEMENT_NAME = "httpinfo";
public static final String URL_ELEMENT_NAME = "url";
public HttpAddressHandler() {
super("Collaboration Dataserver HTTP Address Handler", Arrays
.asList(HTTP_QUERY_XMLNS), new IQHandlerInfo(
QUERY_ELEMENT_NAME, HTTP_QUERY_XMLNS));
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.openfire.plugin.configuration.collaboration.AbstractHandler
* #handleGet(org.xmpp.packet.IQ)
*/
@Override
protected IQ handleGet(IQ packet) throws UnauthorizedException {
String dataserver = getPrimaryDataServer();
if (dataserver == null) {
// no dataserver configured, return empty response
return IQ.createResultIQ(packet);
}
JID target = new JID(dataserver);
return retrieve(packet, target.getNode(),
createElement(HTTP_ELEMENT_NAME, COLLAB_XMLNS));
}
/**
* Set http address for dataserver user with id.
*
* @param id
* @param url
*/
public void setHttpAddress(String id, String url) {
Element httpElem = createElement(HTTP_ELEMENT_NAME, COLLAB_XMLNS);
Element urlElement = createElement(URL_ELEMENT_NAME, COLLAB_XMLNS);
urlElement.setText(url);
urlElement.setParent(null);
httpElem.add(urlElement);
JID target = new JID(id);
store(target.getNode(), httpElem);
}
/**
* Remove http address for dataserver user with id.
*
* @param id
* @param url
*/
public void removeHttpAddress(String id, String url) {
JID target = new JID(id);
store(target.getNode(), createElement(HTTP_ELEMENT_NAME, COLLAB_XMLNS));
}
/**
* Get http address for dataserver user with id.
*
* @param id
* @return
*/
public String getHttpAddress(String id) {
JID target = new JID(id);
Element respElem = storage.get(target.getNode(),
createElement(HTTP_ELEMENT_NAME, COLLAB_XMLNS));
Element urlElem;
try {
urlElem = getChildElement(respElem, URL_ELEMENT_NAME, COLLAB_XMLNS);
} catch (Exception e) {
// missing url element, no url configured
return null;
}
String rval = urlElem.getText();
if (StringUtils.isBlank(rval)) {
return null;
}
return rval.trim();
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.openfire.plugin.configuration.collaboration.AbstractHandler
* #handleSet(org.xmpp.packet.IQ)
*/
@Override
protected IQ handleSet(IQ packet) throws UnauthorizedException {
JID from = packet.getFrom();
if (!isDataServerUser(from)) {
String msg = "User not authorized for service: " + from.toFullJID();
log.debug(msg);
throw new UnauthorizedException(msg);
}
Element queryElem = packet.getChildElement();
Element infoElem;
Element urlElem;
try {
infoElem = getChildElement(queryElem, HTTP_ELEMENT_NAME,
COLLAB_XMLNS);
urlElem = getChildElement(infoElem, URL_ELEMENT_NAME, COLLAB_XMLNS);
} catch (Exception e) {
log.debug("Missing Required Element in packet: " + packet.toXML(),
e);
return createError(packet, new PacketError(Condition.bad_request,
PacketError.Type.modify, e.getLocalizedMessage()));
}
String url = urlElem.getText();
if (StringUtils.isBlank(url)) {
log.debug("Missing HTTP URL Text");
return createError(packet, new PacketError(Condition.bad_request,
PacketError.Type.modify, "Missing Required Text Body"));
}
store(packet.getFrom().getNode(), infoElem);
return IQ.createResultIQ(packet);
}
}

View file

@ -25,16 +25,11 @@ import org.jivesoftware.openfire.MessageRouter;
import org.jivesoftware.openfire.event.SessionEventListener; import org.jivesoftware.openfire.event.SessionEventListener;
import org.jivesoftware.openfire.session.Session; import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.TaskEngine; import org.jivesoftware.util.TaskEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import com.raytheon.openfire.plugin.configuration.collaboration.configuration.ConfigurationPacket; import com.raytheon.openfire.plugin.configuration.collaboration.configuration.ConfigurationPacket;
import com.raytheon.openfire.plugin.configuration.collaboration.exception.HttpdCollaborationNotRunningException; import com.raytheon.openfire.plugin.configuration.collaboration.http.HttpStatusMonitor;
import com.raytheon.openfire.plugin.configuration.collaboration.exception.HttpdCollaborationStatusException;
import com.raytheon.openfire.plugin.configuration.collaboration.httpd.HttpdCollaborationStatusMonitor;
import com.raytheon.openfire.plugin.configuration.collaboration.util.HttpdCollaborationUtil;
/** /**
* Impelements @{link SessionEventListener} to wait for new users to connect to * Impelements @{link SessionEventListener} to wait for new users to connect to
@ -48,23 +43,18 @@ import com.raytheon.openfire.plugin.configuration.collaboration.util.HttpdCollab
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 07, 2012 bkowal Initial creation * Aug 07, 2012 bkowal Initial creation
* Jan 06, 2013 2563 bclement replaced chat message with packet extension * Jan 06, 2013 2563 bclement replaced chat message with packet extension
* Feb 14, 2013 2756 bclement refactor for operation with generic http dataserver
* *
* </pre> * </pre>
* *
* @author bkowal * @author bkowal
* @version 1.0 * @version 1.0
*/ */
public class HttpdCollaborationSessionEventListener implements public class CollaborationSessionEventListener implements SessionEventListener {
SessionEventListener {
private static final Logger logger = LoggerFactory
.getLogger(HttpdCollaborationSessionEventListener.class);
// provided by the configuration
private String HTTPD_COLLABORATION_CONFIGURATION;
private static final int MSG_SEND_DELAY = 5000; private static final int MSG_SEND_DELAY = 5000;
private HttpdCollaborationStatusMonitor httpdCollaborationStatusMonitor; private HttpStatusMonitor statusMonitor;
private JID serverAddress; private JID serverAddress;
@ -73,9 +63,7 @@ public class HttpdCollaborationSessionEventListener implements
/** /**
* *
*/ */
public HttpdCollaborationSessionEventListener( public CollaborationSessionEventListener() {
String _httpdCollaborationConfiguration) {
HTTPD_COLLABORATION_CONFIGURATION = _httpdCollaborationConfiguration;
this.serverAddress = null; this.serverAddress = null;
this.router = null; this.router = null;
} }
@ -129,6 +117,10 @@ public class HttpdCollaborationSessionEventListener implements
*/ */
@Override @Override
public void sessionCreated(Session session) { public void sessionCreated(Session session) {
String body = this.composeMessageBody();
if (body == null) {
return;
}
final Message message = ConfigurationPacket.createMessage(this final Message message = ConfigurationPacket.createMessage(this
.composeMessageBody()); .composeMessageBody());
message.setTo(session.getAddress()); message.setTo(session.getAddress());
@ -157,27 +149,12 @@ public class HttpdCollaborationSessionEventListener implements
private String composeMessageBody() { private String composeMessageBody() {
// Verify that httpd-collaboration is / is still running. // Verify that httpd-collaboration is / is still running.
try { return statusMonitor.getCurrentUrlConfig();
this.httpdCollaborationStatusMonitor.statusHttpdCollaboration();
} catch (HttpdCollaborationNotRunningException e1) {
logger.error("httpd-collaboration is not running.", e1);
return HttpdCollaborationUtil.encodeErrorMessage(e1);
} catch (HttpdCollaborationStatusException e2) {
logger.error(
"failed to determine the status of httpd-collaboration", e2);
return HttpdCollaborationUtil.encodeErrorMessage(e2);
} catch (Exception e3) {
logger.error("Unexpected exception occurred!", e3);
return HttpdCollaborationUtil.encodeErrorMessage(e3);
}
return HTTPD_COLLABORATION_CONFIGURATION;
} }
public void setHttpdCollaborationStatusChecker( public void setHttpStatusChecker(HttpStatusMonitor httpStatusMonitor) {
HttpdCollaborationStatusMonitor httpdCollaborationStatusMonitor) { this.statusMonitor = httpStatusMonitor;
this.httpdCollaborationStatusMonitor = httpdCollaborationStatusMonitor; }
}
/** /**
* @param serverAddress * @param serverAddress

View file

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<plugin> <plugin>
<class>com.raytheon.openfire.plugin.configuration.collaboration.HttpdCollaborationConfigurationPlugin</class> <class>com.raytheon.openfire.plugin.configuration.collaboration.HttpConfigurationPlugin</class>
<name>Httpd Collaboration Configuration</name> <name>Http Collaboration Configuration</name>
<description> <description>
This plugin will provide httpd collaboration configuration information to CAVE This plugin will provide http collaboration configuration information to CAVE
via openfire whenever a user logs in to openfire. via openfire whenever a user logs in to openfire.
</description> </description>
<author>bkowal</author> <author>bkowal</author>
<version>1.0.0</version> <version>2.0.0</version>
<date>8/08/2012</date> <date>8/08/2012</date>
<minServerVersion>3.7.1</minServerVersion> <minServerVersion>3.7.1</minServerVersion>
<adminconsole> <adminconsole>
<tab id="tab-server"> <tab id="tab-server">
<sidebar id="sidebar-server-settings"> <sidebar id="sidebar-server-settings">
<id item="httpd-collaboration-monitor-admin" name="Httpd Collaboration Monitor Settings" <id item="http-collaboration-monitor-admin" name="Http Collaboration Monitor Settings"
url="httpd-collaboration-monitor-admin.jsp" url="http-collaboration-monitor-admin.jsp"
description="Click to administer settings for the Httpd Collaboration Monitor." /> description="Click to administer settings for the Http Collaboration Monitor." />
</sidebar> </sidebar>
</tab> </tab>
</adminconsole> </adminconsole>

View file

@ -15,7 +15,7 @@
extract dependency information from the plugin manifest. extract dependency information from the plugin manifest.
--> -->
<property name="plugin.dependent.libs" <property name="plugin.dependent.libs"
value="org.apache.commons.configuration,org.apache.commons.collections,org.apache.http" /> value="org.apache.commons.collections,org.apache.http" />
<import file="${build.dir.location}/${ant.abstract}" /> <import file="${build.dir.location}/${ant.abstract}" />
</project> </project>

View file

@ -1,6 +1,6 @@
<%@ page <%@ page
import="org.jivesoftware.openfire.XMPPServer, import="org.jivesoftware.openfire.XMPPServer,
com.raytheon.openfire.plugin.configuration.collaboration.HttpdCollaborationConfigurationPlugin, com.raytheon.openfire.plugin.configuration.collaboration.HttpConfigurationPlugin,
org.jivesoftware.util.ParamUtils, org.jivesoftware.util.ParamUtils,
java.util.HashMap, java.util.HashMap,
java.util.Map" java.util.Map"
@ -12,31 +12,33 @@
<% <%
final long DEFAULT_INTERVAL_S = 60; final long DEFAULT_INTERVAL_S = 60;
boolean save = ((request.getParameter("save") == null) == false); boolean save = ((request.getParameter("save") == null) == false);
// Currently, users are not allowed to modify the httpd-collaboration location
// because the rpm is not relocatable.
long interval = ParamUtils.getLongParameter(request, "txtInterval", DEFAULT_INTERVAL_S); long interval = ParamUtils.getLongParameter(request, "txtInterval", DEFAULT_INTERVAL_S);
boolean legacy = ParamUtils.getBooleanParameter(request, "chkLegacy", false); boolean legacy = ParamUtils.getBooleanParameter(request, "chkLegacy", false);
String dataserverUsers = ParamUtils.getParameter(request, "txtdataserverUsers", true);
if (dataserverUsers == null){
dataserverUsers = "";
}
HttpdCollaborationConfigurationPlugin plugin = (HttpdCollaborationConfigurationPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("com.raytheon.openfire.plugin.configuration.collaboration"); HttpConfigurationPlugin plugin = (HttpConfigurationPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("com.raytheon.openfire.plugin.configuration.collaboration");
if (save) if (save)
{ {
plugin.setHttpdMonitorInterval(interval * 1000); plugin.setHttpMonitorInterval(interval * 1000);
plugin.setLegacySupport(legacy); plugin.setLegacySupport(legacy);
response.sendRedirect("httpd-collaboration-monitor-admin.jsp?settingsSaved=true"); plugin.setDataserverUsers(dataserverUsers);
response.sendRedirect("http-collaboration-monitor-admin.jsp?settingsSaved=true");
return; return;
} }
String location = plugin.getHttpdCollaborationLocation(); interval = (plugin.getHttpMonitorInterval() / 1000);
interval = (plugin.getHttpdMonitorInterval() / 1000);
String legacyChkValue = plugin.hasLegacySupport() ? "checked" : ""; String legacyChkValue = plugin.hasLegacySupport() ? "checked" : "";
dataserverUsers = plugin.getDataserverUsers();
%> %>
<html> <html>
<head> <head>
<title>Httpd Collaboration Monitor Settings</title> <title>Http Collaboration Monitor Settings</title>
<meta name="pageID" content="httpd-collaboration-monitor-admin" /> <meta name="pageID" content="http-collaboration-monitor-admin" />
<script type="text/javascript"> <script type="text/javascript">
function validateInterval() function validateInterval()
@ -73,12 +75,32 @@
btnSubmit.disabled = true; btnSubmit.disabled = true;
} }
} }
function validateWhiteList()
{
var txtdataserverUsers = $('txtdataserverUsers');
var spanWhiteListError = $('spanWhiteListError');
var btnSubmit = $('btnSubmit');
var whiteList = txtdataserverUsers.value;
if (whiteList.match('^[^,@]+@[^,@]+(,[^,@]+@[^,@]+)*$'))
{
spanWhiteListError.style.display = 'none';
btnSubmit.disabled = false;
}
else
{
spanWhiteListError.style.display = 'block';
// Disable the submit button
btnSubmit.disabled = true;
}
}
</script> </script>
</head> </head>
<body> <body>
<form action="httpd-collaboration-monitor-admin.jsp?save" method="post"> <form name="collabPrefForm" action="http-collaboration-monitor-admin.jsp?save" method="post" onsubmit="return validTest();" >
<div class="jive-contentBoxHeader"> <div class="jive-contentBoxHeader">
Httpd Collaboration Monitor Settings Http Collaboration Monitor Settings
</div> </div>
<div class="jive-contextBox"> <div class="jive-contextBox">
<% if (ParamUtils.getBooleanParameter(request, "settingsSaved")) { %> <% if (ParamUtils.getBooleanParameter(request, "settingsSaved")) { %>
@ -97,17 +119,11 @@
<% } %> <% } %>
<p> <p>
Set how often (in seconds) the Httpd Collaboration Monitor should verify that the httpd-collaboration process is running. Set how often (in seconds) the Http Collaboration Monitor should verify that the dataserver is online.
</p> </p>
<table cellpadding="3" cellspacing="0" border="0" width="100%"> <table cellpadding="3" cellspacing="0" border="0" width="100%">
<tbody> <tbody>
<tr>
<td width="5%" valign="top">location:&nbsp;</td>
<td width="95%">
<%= location %>
</td>
</tr>
<tr> <tr>
<td width="5%" valign="top">interval:&nbsp;</td> <td width="5%" valign="top">interval:&nbsp;</td>
<td width="95%"> <td width="95%">
@ -135,6 +151,26 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
</br></br></br>
<p>
HTTP Dataserver users list. User ids that http servers can use to communication with openfire. Comma separated list of full user ids
the first id in the list will be used as the primary HTTP Dataserver. Only user ids that are in this list will be allowed to query for session public keys.
</p>
<table cellpadding="3" cellspacing="0" border="0" width="100%">
<tbody>
<tr>
<td width="5%" valign="top">Dataserver User Ids:&nbsp;</td>
<td width="95%">
<input type="text" id="txtdataserverUsers" name="txtdataserverUsers" onkeyup="validateWhiteList()"
value="<%= dataserverUsers %>">
<span id="spanWhiteListError" class="jive-error-text" style="display: none;">
The white list must be in the form 'user1@hostname,user2@hostname'
</span>
</td>
</tr>
</tbody>
</table>
</div> </div>
<input id="btnSubmit" type="submit" value="Save Settings" /> <input id="btnSubmit" type="submit" value="Save Settings" />
</form> </form>