
fiftyone.mobile.detection.webapp.ShareUsage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of 51Degrees.detection.webapp Show documentation
Show all versions of 51Degrees.detection.webapp Show documentation
51Degrees's WebApp library to be used with the core solution for extra functionality
The newest version!
package fiftyone.mobile.detection.webapp;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.TimeZone;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/* *********************************************************************
* This Source Code Form is copyright of 51Degrees Mobile Experts Limited.
* Copyright 2014 51Degrees Mobile Experts Limited, 5 Charlotte Close,
* Caversham, Reading, Berkshire, United Kingdom RG4 7BY
*
* This Source Code Form is the subject of the following patent
* applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte
* Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY:
* European Patent Application No. 13192291.6; and
* United States Patent Application Nos. 14/085,223 and 14/085,301.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0.
*
* If a copy of the MPL was not distributed with this file, You can obtain
* one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
* ********************************************************************* */
class ShareUsage implements Runnable {
/**
* Used to signal the thread to check weather to send data.
*/
private final Object wait = new Object();
/**
* Used to hold information from the jar file, default values set below.
*/
private static String _productName = "51Degrees.mobi - Detection - Java";
/**
* Used to stop the thread.
*/
private boolean stop = false;
/**
* URL to send new device data to.
*/
private final String newDevicesUrl;
/**
* Enumeration to specify the amount of detail to record about a device.
*/
private final NewDeviceDetails newDeviceDetail;
/**
* Queue to hold new device detail.
*/
private final LinkedBlockingQueue queue;
/**
* The format wanted for dates that are generated.
*/
private static final SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss");
/**
* Used to detect local devices.
*/
private static final String[] local = new String[]{"127.0.0.1",
"0:0:0:0:0:0:0:1"};
/**
* Creates a logger for this class
*/
final Logger logger = LoggerFactory.getLogger(ShareUsage.class);
/**
*
* Sets the enabled state of the class.
*
* @param newDevicesUrl Url of potential new host.
* @param newDeviceDetail Controls how much data is sent.
*/
public ShareUsage(final String newDevicesUrl,
final NewDeviceDetails newDeviceDetail) {
// Set TimeZone to UTC
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
this.newDevicesUrl = newDevicesUrl;
this.newDeviceDetail = newDeviceDetail;
this.queue = new LinkedBlockingQueue();
}
/**
* Attempts to send any device data that is still yet to be sent and then
* terminates the Thread.
*/
public void destroy() {
logger.trace("Destroying 51Degrees.mobi ShareUsage");
stop = true;
synchronized (wait) {
wait.notifyAll();
}
logger.trace("Destroyed 51Degrees.mobi ShareUsage");
}
/**
*
* Adds the request details to the queue for processing by the background
* thread.
*
* @param request The current server request.
* @throws XMLStreamException
* @throws IOException
*/
public void recordNewDevice(final HttpServletRequest request)
throws XMLStreamException, IOException {
final Enumeration HeaderNames = request.getHeaderNames();
final HashMap Headers = new HashMap();
while (HeaderNames.hasMoreElements()) {
final String n = (String) HeaderNames.nextElement();
Headers.put(n, request.getHeader(n));
}
recordNewDevice(Headers, request.getRemoteAddr(),
request.getRequestURI());
}
/**
*
* Adds the request details to the queue for processing by the background
* thread.
*
* @param Headers HashMap containing the request headers.
* @param HostAddress the Host Address.
* @param URI The URI of the request.
* @throws XMLStreamException
* @throws IOException
*/
public void recordNewDevice(final HashMap Headers,
final String HostAddress, String URI) throws XMLStreamException,
IOException {
// Get the new device details.
byte[] data = getContent(Headers, HostAddress, URI, newDeviceDetail);
if (data != null && data.length > 0) {
// Add the new details to the queue for later processing.
queue.add(data);
synchronized (wait) {
// Signal the background thread to check to see if it should
// send queued data.
wait.notify();
}
}
}
/**
*
* Sends all the data on the queue.
*
* @param stream Output stream to send data to.
* @throws IOException
* @throws XMLStreamException
*/
private void sendData(OutputStream stream) throws IOException,
XMLStreamException {
GZIPOutputStream compressed = new GZIPOutputStream(stream);
PrintWriter pw = new PrintWriter(compressed);
pw.print("");
pw.print("");
while (queue.size() > 0) {
byte[] item = queue.poll();
if (item != null && item.length > 0) {
pw.print(new String(item));
}
}
pw.print(" ");
pw.close();
compressed.close();
}
/**
* Runs the thread. Used to send the devices data back to 51Degrees.mobi
* after the NewDeviceQueueLength has been reached.
*/
@Override
public void run() {
do {
try {
synchronized (wait) {
// Wait for something to happen.
wait.wait();
}
// If there are enough items in the queue, or the thread is
// being
// stopped send the data.
int size = queue.size();
if (size >= Constants.NEW_DEVICE_QUEUE_LENGTH
|| (stop == true && queue.size() > 0)) {
// Prepare the request including all currently queued items.
HttpURLConnection request = (HttpURLConnection) new URL(
newDevicesUrl).openConnection();
request.setReadTimeout(Constants.NEW_URL_TIMEOUT);
request.setRequestMethod("POST");
request.setUseCaches(false);
request.setRequestProperty("Content-Type",
"text/xml; charset=ISO-8859-1");
request.setRequestProperty("Content-Encoding", "gzip");
request.setDoInput(true);
request.setDoOutput(true);
try {
sendData(request.getOutputStream());
} catch (UnknownHostException ex) {
stop = true;
}
// Get the response and record the content if it's valid. If
// it's not valid consider turning off the functionality.
InputStreamReader stream = new InputStreamReader(request.getInputStream());
if (stream.ready()) {
BufferedReader response = new BufferedReader(stream);
if (response != null) {
switch (request.getResponseCode()) {
case 200:// OK
// Ok response, do nothing
break;
case 408:// Request Timeout
// Could be temporary, do nothing.
break;
default:
// Turn off functionality.
stop = true;
break;
}
}
// Release the HttpWebResponse
response.close();
}
}
} catch (SecurityException ex) {
stop = true;
} catch (XMLStreamException ex) {
stop = true;
} catch (IOException ex) {
stop = true;
} catch (InterruptedException ex) {
stop = true;
} catch (IllegalStateException ex) {
// Probably means that the instance has stopped
// so stop any more thread processing.
stop = true;
}
} while (stop == false);
}
/**
*
* Indicates if the IP address is local.
*
* @param address Ip address to examine.
* @return true if local, else false.
*/
private static boolean isLocal(String address) {
for (String s : local) {
if (s.equals(address)) {
return true;
}
}
return false;
}
/**
*
* Records the information as XML data and converts to a byte array for
* storage.
*
* @param Headers A Map of the connection headers.
* @param HostAddress The address IP address of the host.
* @param URI The URI information.
* @param newDeviceDetail How much information to be recorded.
* @return The XML data as a byte array.
* @throws XMLStreamException
* @throws IOException
*/
private static byte[] getContent(HashMap Headers,
String HostAddress, String URI, NewDeviceDetails newDeviceDetail)
throws XMLStreamException, IOException {
// If the headers contain 51D as a setting or the request is to a web
// service then do not send the data.
boolean ignore = Headers.get("51D") != null || URI.endsWith("asmx");
if (ignore == false) {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
ByteArrayOutputStream ms = new ByteArrayOutputStream();
XMLStreamWriter writer = factory.createXMLStreamWriter(ms);
// Start writing the device
writer.writeStartElement("Device");
// Write the current date and time
writer.writeStartElement("DateSent");
writer.writeCharacters(dateFormat.format(new Date()).toString());
writer.writeEndElement();
writer.writeStartElement("Version");
writer.writeCharacters(Constants.VERSION);
writer.writeEndElement();
writer.writeStartElement("Product");
writer.writeCharacters(_productName);
writer.writeEndElement();
// Record either the IP address of the client if not local or the IP
// address of the machine.
// If it contains dots then its an IPV4
if (!isLocal(HostAddress)) {
writer.writeStartElement("ClientIP");
writer.writeCharacters(HostAddress);
writer.writeEndElement();
}
writer.writeStartElement("ServerIP");
writer.writeCharacters(HostAddress);
writer.writeEndElement();
for (String key : Headers.keySet()) {
// Determine if the field should be treated as a blank.
boolean blank = false;
for (String k : Constants.IGNORE_HEADER_FIELD_VALUES) {
blank = key.toLowerCase().contains(k.toLowerCase());
if (blank == true) {
break;
}
}
// Include all header values if maximumDetail is enabled, or
// header values related to the useragent or any header
// key containing profile or information helpful to determining
// mobile devices.
if (newDeviceDetail == NewDeviceDetails.MAXIMUM
|| key.equals("user-agent") || key.equals("host")
|| key.contains("profile") || blank) {
// Record the header content if it's not a cookie header.
if (blank) {
writer.writeStartElement("Header");
writer.writeAttribute("Name", key);
writer.writeEndElement();
} else {
writer.writeStartElement("Header");
writer.writeAttribute("Name", key);
writer.writeCData(Headers.get(key));
writer.writeEndElement();
}
}
}
writer.writeEndElement();
writer.close();
return ms.toByteArray();
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy