/**
* Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* - Apache License, version 2.0
* - Apache Software License, version 1.0
* - GNU Lesser General Public License, version 3
* - Mozilla Public License, versions 1.0, 1.1 and 2.0
* - Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* icense version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
package org.n52.ses.wsn.dissemination.updateinterval;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.namespace.QName;
import org.apache.muse.ws.addressing.EndpointReference;
import org.apache.muse.ws.notification.NotificationMessage;
import org.apache.muse.ws.notification.remote.NotificationConsumerClient;
import org.joda.time.Period;
import org.n52.ses.wsn.dissemination.AbstractDisseminationMethod;
import org.n52.ses.wsn.dissemination.DisseminationMethodFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Update Interval dissemination method.
*
* This implementation collects all messages within a given period
* and batches all messages in one or only sends the latest messages,
* depending on the policy configuration.
*
* @author matthes rieke
*
*/
public class UpdateIntervalDisseminationMethod extends AbstractDisseminationMethod {
public static final QName UPDATE_INTERVAL_NAME = new QName(DisseminationMethodFactory.SUBSCRIPTION_POLICY_NAMESPACE, "UpdateInterval");
public static final QName INTERVAL_DURATION_NAME = new QName(DisseminationMethodFactory.SUBSCRIPTION_POLICY_NAMESPACE, "IntervalDuration");
public static final QName DISSEMINATION_METHOD_NAME = new QName(DisseminationMethodFactory.SUBSCRIPTION_POLICY_NAMESPACE, "DisseminationMethod");
public static final QName NON_RELATED_NAME = new QName(DisseminationMethodFactory.SUBSCRIPTION_POLICY_NAMESPACE, "NonRelatedEventTreatment");
public static final QName NO_NEW_MESSAGES_NAME = new QName(DisseminationMethodFactory.SUBSCRIPTION_POLICY_NAMESPACE, "NoNewMessages");
public static enum DisseminationMethod { batching, latest };
public static enum NonRelatedEventTreatment { separate, ignore };
private static final Logger logger = LoggerFactory.getLogger(UpdateIntervalDisseminationMethod.class);
private long duration;
private MessageCollector messageCollector;
private boolean ignoreNonRelatedEvents;
private DisseminationMethod disseminationMethod;
private AtomicBoolean firstRun = new AtomicBoolean(true);
private NotificationConsumerClient soapClient;
private EndpointReference subscription;
private EndpointReference producer;
private EndpointReference consumer;
private Timer timer;
public UpdateIntervalDisseminationMethod(Node updateInterval, NotificationConsumerClient client,
EndpointReference subscriptionReference) {
List<Node> children = getChildElements(updateInterval);
for (int i = 0; i < children.size(); i++) {
parseChild(children.get(i));
}
this.soapClient = client;
storeReferences(client, subscriptionReference, client.getProducerReference(),
client.getConsumerReference());
this.messageCollector = createMessageCollector();
createIntervalThread();
}
private List<Node> getChildElements(Node updateInterval) {
List<Node> result = new ArrayList<Node>();
NodeList children = updateInterval.getChildNodes();
Node child;
for (int i = 0; i < children.getLength(); i++) {
child = children.item(i);
if (child instanceof Element) result.add(child);
}
return result;
}
private void parseChild(Node item) {
QName qn = new QName(item.getNamespaceURI(), item.getLocalName());
if (qn.equals(INTERVAL_DURATION_NAME)) {
this.duration = parseDuration(item.getTextContent());
} else if (qn.equals(DISSEMINATION_METHOD_NAME)) {
this.disseminationMethod = parseDisseminationMethod(item.getTextContent());
} else if (qn.equals(NON_RELATED_NAME)) {
this.ignoreNonRelatedEvents = isIgnoreNonRelatedEventsBehaviour(item.getTextContent());
}
}
private DisseminationMethod parseDisseminationMethod(String textContent) {
if (textContent.equals(DisseminationMethod.batching.toString())) {
// throw new UnsupportedOperationException("<DisseminationMethod>batching</DisseminationMethod> for Update Intervals is currently not supported.");
return DisseminationMethod.batching;
}
return DisseminationMethod.latest;
}
private boolean isIgnoreNonRelatedEventsBehaviour(String nonRelated) {
if (nonRelated.equals(NonRelatedEventTreatment.ignore.toString())) {
return true;
}
throw new UnsupportedOperationException("<NonRelatedEventTreatment>separate</NonRelatedEventTreatment> for Update Intervals is currently not supported.");
}
private MessageCollector createMessageCollector() {
return new MessageCollector(this.ignoreNonRelatedEvents, this.disseminationMethod == DisseminationMethod.batching);
}
private long parseDuration(String textContent) {
return new Period(textContent).toStandardSeconds().getSeconds() * 1000;
}
@Override
public boolean newMessage(NotificationMessage message,
NotificationConsumerClient client,
EndpointReference subscriptionReference,
EndpointReference producerReference,
EndpointReference consumerReference) {
synchronized (this) {
if (firstRun.getAndSet(false)) {
storeReferences(client, subscriptionReference, producerReference, consumerReference);
}
}
if (!validateReferences(client, subscriptionReference, producerReference, consumerReference)) {
logger.warn("Not the same reference objects for Update Interval dissemination! Skipping message.");
return false;
}
this.messageCollector.newMessage(message);
return true;
}
private boolean validateReferences(NotificationConsumerClient client,
EndpointReference subscriptionReference,
EndpointReference producerReference,
EndpointReference consumerReference) {
return this.soapClient.equals(client) && this.subscription.equals(subscriptionReference) &&
this.producer.equals(producerReference) && this.consumer.equals(consumerReference);
}
private void storeReferences(NotificationConsumerClient client,
EndpointReference subscriptionReference,
EndpointReference producerReference,
EndpointReference consumerReference) {
this.soapClient = client;
this.subscription = subscriptionReference;
this.producer = producerReference;
this.consumer = consumerReference;
}
public MessageCollector getMessageCollector() {
return messageCollector;
}
private void createIntervalThread() {
this.timer = new Timer();
timer.schedule( new IntervalCycle(), this.duration, this.duration);
}
private class IntervalCycle extends TimerTask {
@Override
public void run() {
NotificationMessage message = messageCollector.pullMessageForLastInterval();
if (message == null) {
message = createNoNewMessagesSystemMessage();
if (logger.isInfoEnabled())
logger.debug("Sending system message for no new messages within update interval.");
} else {
if (logger.isInfoEnabled())
logger.debug("Sending update interval message.");
}
sendMessage(message, soapClient, consumer, numberOfTries, producer, subscription);
}
private NotificationMessage createNoNewMessagesSystemMessage() {
return new NoNewMessagesMessage(subscription);
}
}
@Override
public void shutdown() {
if (this.timer != null) this.timer.cancel();
if (this.messageCollector != null) this.messageCollector.shutdown();
}
}