/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* OpenIoT 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
* @author Julien Eberle
*/
package org.openiot.gsn.http.rest;
import org.openiot.gsn.beans.DataField;
import org.openiot.gsn.beans.StreamElement;
import java.io.IOException;
import java.io.Writer;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;
import com.thoughtworks.xstream.XStream;
public class WPPushDelivery implements DeliverySystem {
//the minimum amount of seconds between two notifications
//use that to limit the notification rate, especially for testing
private int DELAY = 60*15;
//1: tile notification, 2: toast notification, 3:raw notification
private int notificationClass = 3;
//a message to be used as text or referring to the underlying query
private String notificationMessage = "";
private XStream xstream = StreamElement4Rest.getXstream();
private boolean isClosed = false;
private static transient Logger logger = Logger.getLogger ( WPPushDelivery.class );
private HttpPost httpPost;
private DefaultHttpClient httpclient = new DefaultHttpClient();
private Writer writer;
private String lastmessage = "";
private long lastTimeSent = 0;
private double notificationId;
/**
* Builds a new Windows Phone Push Delivery class
* @param deliveryContactPoint: the unique url for Windows phone push service
* @param notificaitonId: the id for identifying the phone on GSN
* @param writer: where to write back the output structure
* @param notificationClass: 1: tile notification, 2: toast notification, 3:raw notification
* @param notificationMessage: a message to be used as text or referring to the underlying query
*/
public WPPushDelivery(String deliveryContactPoint,double notificaitonId, Writer writer,int notificationClass,String notificationMessage) {
httpPost = new HttpPost(deliveryContactPoint);
this.notificationClass = notificationClass;
this.writer = writer;
this.notificationId = notificaitonId;
this.notificationMessage = notificationMessage;
}
/**
* Write the structure of the data as xml in the writer given by the constructor.
* Can be useful in case of raw notification only.
* @param fields the field structure to write.
* @throws IOException in case the writer fails.
*/
public void writeStructure(DataField[] fields) throws IOException {
String xml = xstream.toXML(fields);
if (writer ==null)
throw new RuntimeException("The writer structure is null.");
writer.write(xml);
writer= null;
}
/**
* Generate the content of the notification according to the notification class
* and sends it to the phone. The content of the streamElement can be used in
* different ways and this method should be adapted to the specific application.
* @param se the streamElement
*/
public boolean writeStreamElement(StreamElement se) {
String xml = xstream.toXML(new StreamElement4Rest(se)); //raw notification
if (notificationClass == 2) { //toast notification
httpPost.setHeader("X-WindowsPhone-Target", "toast");
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>GSN Push Service</wp:Text1>" +
"<wp:Text2>"+ notificationMessage +"</wp:Text2>" +
"<wp:Param></wp:Param>" +
"</wp:Toast> " +
"</wp:Notification>";
}
if (notificationClass == 1) { //tile notification
httpPost.setHeader("X-WindowsPhone-Target", "token");
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Tile ID=\"tile_id\">" +
"<wp:BackgroundImage>/Images/front.png</wp:BackgroundImage>" +
"<wp:Count></wp:Count>" +
"<wp:Title>GSN Push Service</wp:Title>" +
"<wp:BackBackgroundImage>/Images/back.png</wp:BackBackgroundImage>" +
"<wp:BackTitle>" + notificationMessage+ "</wp:BackTitle>" +
"<wp:BackContent></wp:BackContent>" +
"</wp:Tile> " +
"</wp:Notification>";
}
if (xml.equalsIgnoreCase(lastmessage)){ // don't send twice the same message
return true;
}
if (se.getTimeStamp() < lastTimeSent + DELAY){// limit the rate for sending messages
return true;
}
lastmessage = xml;
lastTimeSent = se.getTimeStamp();
boolean success = sendData(xml);
isClosed = !success;
return success;
}
/**
* No need to keep the connection alive
*/
public boolean writeKeepAliveStreamElement() {
return true;
}
/**
* closing all connections
*/
public void close() {
httpclient.getConnectionManager().shutdown();
isClosed = true;
}
public boolean isClosed() {
return isClosed ;
}
/**
* Send the given xml string to the contact point.
* Take action according to the reply from Microsoft's servers (http://msdn.microsoft.com/en-us/library/ff941100%28VS.92%29.aspx).
*
* @param xml
* @return
*/
private boolean sendData(String xml) {
System.out.println(xml);
try {
httpPost.setHeader("X-NotificationClass",""+notificationClass);
httpPost.setEntity(new StringEntity(xml, HTTP.UTF_8));
HttpResponse response = httpclient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
String nStatus = "NA";
String dStatus = "NA";
String sStatus = "NA";
response.getEntity().getContent().close(); // releasing the connection to the http client's pool
if (response.containsHeader("X-NotificationStatus"))
nStatus = response.getFirstHeader("X-NotificationStatus").getValue();
if (response.containsHeader("X-DeviceConnectionStatus"))
dStatus = response.getFirstHeader("X-DeviceConnectionStatus").getValue();
if (response.containsHeader("X-SubscriptionStatus"))
sStatus = response.getFirstHeader("X-SubscriptionStatus").getValue();
logger.warn("Status for client "+notificationId+":(" +statusCode+")" + nStatus + ", " + dStatus + "," +sStatus);
if (statusCode != RestStreamHanlder.SUCCESS_200) {
return false;
}
if (nStatus.equalsIgnoreCase("QueueFull")){
lastTimeSent = System.currentTimeMillis()/1000 + 60*60*2;
}
if (nStatus.equalsIgnoreCase("Suppressed")){
return false;
}
return true;
} catch (Exception e) {
logger.warn(e.getMessage(),e);
return false;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WPPushDelivery that = (WPPushDelivery) o;
if (Double.compare(that.notificationId, notificationId) != 0) return false;
if (httpPost != null ? !httpPost.getURI().equals(that.httpPost.getURI()) : that.httpPost != null) return false;
return true;
}
@Override
public int hashCode() {
int result;
long temp;
result = httpPost != null ? httpPost.getURI().hashCode() : 0;
temp = notificationId != +0.0d ? Double.doubleToLongBits(notificationId) : 0L;
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}