uno.anahata.mapacho.servlet.JnlpFileHandler Maven / Gradle / Ivy
The newest version!
/*
* Copied form sun samples to override JnlpFileHandler implementation in jnlp-servlet.jar to
* allow for system property replacement in Jnlp template.
*
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Oracle nor the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
package uno.anahata.mapacho.servlet;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
/* The JNLP file handler implements a class that keeps
* track of JNLP files and their specializations
*/
@Slf4j
public class JnlpFileHandler {
private static final String JNLP_MIME_TYPE = "application/x-java-jnlp-file";
private ServletContext servletContext;
private HashMap cache = null;
/**
* Initialize JnlpFileHandlerAnahata for the specific ServletContext
*/
public JnlpFileHandler(ServletContext servletContext) {
servletContext = servletContext;
cache = new HashMap();
}
/* Main method to lookup an entry (NEW for JavaWebStart 1.5+) */
public synchronized DownloadResponse getJnlpFileEx(DownloadRequest dreq)
throws IOException {
String path = dreq.getPath();
File resourceFile = dreq.getFile();
if (resourceFile == null) {
throw new IOException("Real Path: " + dreq.getRealPath() + " for Path: " + dreq.getPath());
}
URL resource = dreq.getFile().toURI().toURL();
Date lastModified = dreq.getFileLastModified();
// fix for 4474854: use the request URL as key to look up jnlp file
// in hash map
String reqUrl = HttpUtils.getRequestURL(dreq.getHttpRequest()).toString();
// SQE: To support query string, we changed the hash key from Request URL to (Request URL + query string)
if (dreq.getQuery() != null) {
reqUrl += dreq.getQuery();
}
// Check if entry already exist in HashMap
DownloadResponse downloadResponse = (DownloadResponse) cache.get(reqUrl);
if (downloadResponse != null &&
(dreq.getIfModifiedSince() == null || !downloadResponse.getLastModified().after(dreq.getIfModifiedSince()))) {
// Entry found in cache, so return it
return downloadResponse;
}
// Read information from WAR file
long timeStamp;
String mimeType = JNLP_MIME_TYPE;
StringBuffer jnlpFileTemplate = new StringBuffer();
URLConnection conn = resource.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line = br.readLine();
if (line != null && line.startsWith("TS:")) {
timeStamp = parseTimeStamp(line.substring(3));
if (timeStamp == 0) {
timeStamp = lastModified.getTime();
}
line = br.readLine();
}
while (line != null) {
jnlpFileTemplate.append(line);
line = br.readLine();
}
String jnlpFileContent = specializeJnlpTemplate(dreq.getHttpRequest(), path, jnlpFileTemplate.toString());
// Convert to bytes as a UTF-8 encoding
byte[] byteContent = jnlpFileContent.getBytes("UTF-8");
// Create entry
downloadResponse = new ByteArrayDownloadResponse(byteContent, mimeType, dreq.getFileLastModified());
cache.put(reqUrl, downloadResponse);
return downloadResponse;
}
/* This method performs the following substituations
* $$name
* $$codebase
* $$context
*/
private String specializeJnlpTemplate(HttpServletRequest request, String respath, String jnlpTemplate) {
String urlprefix = getUrlPrefix(request);
int idx = respath.lastIndexOf('/'); //
String name = respath.substring(idx + 1); // Exclude /
String codebase = respath.substring(0, idx + 1); // Include /
jnlpTemplate = substitute(jnlpTemplate, "$$name", name);
// fix for 5039951: Add $$hostname macro
jnlpTemplate = substitute(jnlpTemplate, "$$hostname",
request.getServerName());
jnlpTemplate = substitute(jnlpTemplate, "$$codebase", urlprefix + request.getContextPath() + codebase);
jnlpTemplate = substitute(jnlpTemplate, "$$context", urlprefix + request.getContextPath());
// fix for 6256326: add $$site macro to sample jnlp servlet
jnlpTemplate = substitute(jnlpTemplate, "$$site", urlprefix);
//Anahata Addition
jnlpTemplate = substituteSystemProperties(jnlpTemplate);
return jnlpTemplate;
}
// This code is heavily inspired by the stuff in HttpUtils.getRequestURL
private String getUrlPrefix(HttpServletRequest req) {
StringBuffer url = new StringBuffer();
String scheme = req.getScheme();
int port = req.getServerPort();
url.append(scheme); // http, https
url.append("://");
url.append(req.getServerName());
if ((scheme.equals("http") && port != 80)
|| (scheme.equals("https") && port != 443)) {
url.append(':');
url.append(req.getServerPort());
}
return url.toString();
}
private String substitute(String target, String key, String value) {
int start = 0;
do {
int idx = target.indexOf(key, start);
if (idx == -1) {
return target;
}
target = target.substring(0, idx) + value + target.substring(idx + key.length());
start = idx + value.length();
} while (true);
}
/**
* Parses a ISO 8601 Timestamp. The format of the timestamp is:
*
* YYYY-MM-DD hh:mm:ss or YYYYMMDDhhmmss
*
* Hours (hh) is in 24h format. ss are optional. Time are by default relative to the current timezone. Timezone
* information can be specified by:
*
* - Appending a 'Z', e.g., 2001-12-19 12:00Z - Appending +hh:mm, +hhmm, +hh, -hh:mm -hhmm, -hh to indicate that the
* locale timezone used is either the specified amound before or after GMT. For example,
*
* 12:00Z = 13:00+1:00 = 0700-0500
*
* The method returns 0 if it cannot pass the string. Otherwise, it is the number of milliseconds size sometime in
* 1969.
*/
private long parseTimeStamp(String timestamp) {
int YYYY = 0;
int MM = 0;
int DD = 0;
int hh = 0;
int mm = 0;
int ss = 0;
timestamp = timestamp.trim();
try {
// Check what format is used
if (matchPattern("####-##-## ##:##", timestamp)) {
YYYY = getIntValue(timestamp, 0, 4);
MM = getIntValue(timestamp, 5, 7);
DD = getIntValue(timestamp, 8, 10);
hh = getIntValue(timestamp, 11, 13);
mm = getIntValue(timestamp, 14, 16);
timestamp = timestamp.substring(16);
if (matchPattern(":##", timestamp)) {
ss = getIntValue(timestamp, 1, 3);
timestamp = timestamp.substring(3);
}
} else if (matchPattern("############", timestamp)) {
YYYY = getIntValue(timestamp, 0, 4);
MM = getIntValue(timestamp, 4, 6);
DD = getIntValue(timestamp, 6, 8);
hh = getIntValue(timestamp, 8, 10);
mm = getIntValue(timestamp, 10, 12);
timestamp = timestamp.substring(12);
if (matchPattern("##", timestamp)) {
ss = getIntValue(timestamp, 0, 2);
timestamp = timestamp.substring(2);
}
} else {
// Unknown format
return 0;
}
} catch (NumberFormatException e) {
// Bad number
return 0;
}
String timezone = null;
// Remove timezone information
timestamp = timestamp.trim();
if (timestamp.equalsIgnoreCase("Z")) {
timezone = "GMT";
} else if (timestamp.startsWith("+") || timestamp.startsWith("-")) {
timezone = "GMT" + timestamp;
}
if (timezone == null) {
// Date is relative to current locale
Calendar cal = Calendar.getInstance();
cal.set(YYYY, MM - 1, DD, hh, mm, ss);
return cal.getTime().getTime();
} else {
// Date is relative to a timezone
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(timezone));
cal.set(YYYY, MM - 1, DD, hh, mm, ss);
return cal.getTime().getTime();
}
}
private int getIntValue(String key, int start, int end) {
return Integer.parseInt(key.substring(start, end));
}
private boolean matchPattern(String pattern, String key) {
// Key must be longer than pattern
if (key.length() < pattern.length()) {
return false;
}
for (int i = 0; i < pattern.length(); i++) {
char format = pattern.charAt(i);
char ch = key.charAt(i);
if (!((format == '#' && Character.isDigit(ch)) || (format == ch))) {
return false;
}
}
return true;
}
//--------------
// Added code
//--------------
private static final Pattern SYSTEM_PROPERTY_PATTERN = Pattern.compile("\\$\\$property.[\\S]+\\$\\$");
private String substituteSystemProperties(String target) {
Matcher matcher = SYSTEM_PROPERTY_PATTERN.matcher(target);
while (matcher.find()) {
String key = matcher.group();
int startIdx = key.indexOf(".") + 1;
String property = key.substring(startIdx);
property = property.substring(0, property.length() - 2);
String value = System.getProperty(property);
if (value == null) {
log.warn("No value for {} on System properties", property);
value = "";
}
target = substitute(target, key, value);
}
return target;
}
}