de.swm.gwt.linker.server.Html5ManifestServletBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swm-gwt-client Show documentation
Show all versions of swm-gwt-client Show documentation
Plain GWT Swm erweiterungen, auch zur benutzung in mobilen Geraeten
package de.swm.gwt.linker.server;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import de.swm.gwt.linker.ManifestWriter;
import de.swm.gwt.linker.PermutationMapLinker;
import de.swm.gwt.linker.XMLPermutationProvider;
import de.swm.gwt.linker.XMLPermutationProviderException;
import de.swm.gwt.linker.server.propertyprovider.MgwtOsPropertyProvider;
import de.swm.gwt.linker.server.propertyprovider.PropertyProvider;
import de.swm.gwt.linker.server.propertyprovider.PropertyProviderException;
/**
* Base class for servlet which delivers application cache manifest for a specific GWT permutation.
* Modified by Alexander Vilbig (SWM Services GmbH) from original MGWT version to improve matching of user agent and locale properties.
*
* @author Daniel Kurka (see http://code.google.com/p/mgwt/)
*
*/
public class Html5ManifestServletBase extends HttpServlet {
private static final long serialVersionUID = -2540671294104865306L;
private XMLPermutationProvider permutationProvider;
private Map propertyProviders = new HashMap();
Map> permutationMap = null;
public Html5ManifestServletBase() {
permutationProvider = new XMLPermutationProvider();
}
protected void addPropertyProvider(PropertyProvider propertyProvider) {
propertyProviders.put(propertyProvider.getPropertyName(), propertyProvider);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String moduleName = getModuleName(req);
String baseUrl = getBaseUrl(req);
initPermutationMap(baseUrl, moduleName);
Set computedBindings = calculateBindinPropertiesForClient(req);
// remove locale and user agent property bindings if not present in permutation map
Set filteredBindings = computedBindings;
if (!containsPermutationProperty("locale")) {
log("Removing locale from matched properties...");
filteredBindings = filterProperty("locale", filteredBindings);
}
if (!containsPermutationProperty("user.agent")) {
log("Removing user agent from matched properties...");
filteredBindings = filterProperty("user.agent", filteredBindings);
}
log("Matching following binding properties: " + filteredBindings);
String strongName = determinePermutationStrongName(filteredBindings);
if (strongName == null) {
log("Setting locale to default in second round of matching...");
BindingProperty localeProperty = null;
for (BindingProperty property : filteredBindings) {
if (property.getName().equals("locale")) {
localeProperty = property;
}
}
if (localeProperty != null) {
filteredBindings.remove(localeProperty);
filteredBindings.add(new BindingProperty("locale", "default"));
}
strongName = determinePermutationStrongName(filteredBindings);
}
if (strongName != null) {
String manifest = readManifest(baseUrl + moduleName + "/" + strongName
+ PermutationMapLinker.PERMUTATION_MANIFEST_FILE_ENDING);
serveStringManifest(req, resp, manifest);
return;
}
boolean isIPhoneWithoutCookie = isIphoneWithoutCookie(computedBindings);
boolean isIPadWithoutCookie = isIpadWithoutCookie(computedBindings);
if (isIPhoneWithoutCookie || isIPadWithoutCookie) {
Set nonRetinaMatch = new HashSet();
Set retinaMatch = new HashSet();
if (isIPhoneWithoutCookie) {
computedBindings.remove(MgwtOsPropertyProvider.iPhone_undefined);
nonRetinaMatch.add(MgwtOsPropertyProvider.iPhone);
retinaMatch.add(MgwtOsPropertyProvider.retina);
}
if (isIPadWithoutCookie) {
computedBindings.remove(MgwtOsPropertyProvider.iPad_undefined);
nonRetinaMatch.add(MgwtOsPropertyProvider.iPad);
retinaMatch.add(MgwtOsPropertyProvider.iPad_retina);
}
nonRetinaMatch.addAll(computedBindings);
retinaMatch.addAll(computedBindings);
String moduleNameNonRetina = determinePermutationStrongName(nonRetinaMatch);
String moduleNameRetina = determinePermutationStrongName(retinaMatch);
if (moduleNameNonRetina != null && moduleNameRetina != null) {
// load files for both permutations
Set filesForPermutation = getFilesForPermutation(baseUrl, moduleName, moduleNameNonRetina);
filesForPermutation.addAll(getFilesForPermutation(baseUrl, moduleName, moduleNameRetina));
// dynamically write a new manifest..
ManifestWriter manifestWriter = new ManifestWriter();
String writeManifest = manifestWriter.writeManifest(new HashSet(), filesForPermutation);
serveStringManifest(req, resp, writeManifest);
return;
}
}
// if we got here we just don`t know the device react with 500 -> no
// manifest...
throw new ServletException("unkown device for bindings " + computedBindings);
}
protected String getBaseUrl(HttpServletRequest req) {
String base = req.getServletPath();
// cut off module
return base.substring(0, base.lastIndexOf("/") + 1);
}
public boolean isIphoneWithoutCookie(Set bps) {
for (BindingProperty bp : bps) {
if ("mgwt.os".equals(bp.getName())) {
if ("iphone_undefined".equals(bp.getValue())) {
// oh shit this is an iphone
// so now we need to serve two manifests
// retina...
// non retina
return true;
}
}
}
return false;
}
public boolean isIpadWithoutCookie(Set bps) {
for (BindingProperty bp : bps) {
if ("mgwt.os".equals(bp.getName())) {
if ("ipad_undefined".equals(bp.getValue())) {
// oh shit this is an ipad
// so now we need to serve two manifests
// retina...
// non retina
return true;
}
}
}
return false;
}
public Set getFilesForPermutation(String baseUrl, String moduleName, String permutation)
throws ServletException {
String fileName = baseUrl + moduleName + "/" + permutation + PermutationMapLinker.PERMUTATION_FILE_ENDING;
XMLPermutationProvider xmlPermutationProvider = new XMLPermutationProvider();
try {
File file = new File(getServletContext().getRealPath(fileName));
InputStream inputStream = new FileInputStream(file);
return xmlPermutationProvider.getPermutationFiles(inputStream);
} catch (XMLPermutationProviderException e) {
log("can not read permutation file");
throw new ServletException("can not read permutation file", e);
} catch (FileNotFoundException e) {
log("can not read permutation file");
throw new ServletException("can not read permutation file", e);
}
}
public String readManifest(String filePath) throws ServletException {
File manifestFile = new File(getServletContext().getRealPath(filePath));
StringWriter manifestWriter = new StringWriter();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(manifestFile));
String line = null;
while ((line = br.readLine()) != null) {
manifestWriter.append(line + "\n");
}
return manifestWriter.toString();
} catch (FileNotFoundException e) {
log("could not find manifest file", e);
throw new ServletException("can not find manifest file", e);
} catch (IOException e) {
log("error while reading manifest file", e);
throw new ServletException("error while reading manifest file", e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
}
}
}
}
/**
* @param req
* @return
* @throws PropertyProviderException
*/
public Set calculateBindinPropertiesForClient(HttpServletRequest req) throws ServletException {
try {
Set computedBindings = new HashSet();
Set> set = propertyProviders.entrySet();
for (Entry entry : set) {
String varValue = entry.getValue().getPropertyValue(req);
BindingProperty bindingProperty = new BindingProperty(entry.getKey(), varValue);
computedBindings.add(bindingProperty);
}
return computedBindings;
} catch (PropertyProviderException e) {
log("can not calculate properties for client", e);
throw new ServletException("can not calculate properties for client", e);
}
}
public void serveStringManifest(HttpServletRequest req, HttpServletResponse resp, String manifest)
throws ServletException {
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", new Date().getTime());
resp.setContentType("text/cache-manifest");
try {
InputStream is = new ByteArrayInputStream(manifest.getBytes("UTF-8"));
ServletOutputStream os = resp.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
return;
} catch (UnsupportedEncodingException e) {
log("can not write manifest to output stream", e);
throw new ServletException("can not write manifest to output stream", e);
} catch (IOException e) {
log("can not write manifest to output stream", e);
throw new ServletException("can not write manifest to output stream", e);
}
}
private String determinePermutationStrongName(Set computedBindings) throws ServletException {
if (permutationMap == null) {
throw new IllegalStateException("permutationMap can not be null");
}
if (computedBindings == null) {
throw new IllegalArgumentException("computedBindings can not be null");
}
for (Entry> entry : permutationMap.entrySet()) {
List value = entry.getValue();
if (value.containsAll(computedBindings)) {
log("Matching to permutation with strong name " + entry.getKey());
return entry.getKey();
}
}
return null;
}
private boolean containsPermutationProperty(String name) {
for (Entry> entry : permutationMap.entrySet()) {
List value = entry.getValue();
for (BindingProperty property : value) {
if (property.getName().equals(name)) {
return true;
}
}
}
return false;
}
private void initPermutationMap(String baseUrl, String moduleName) throws ServletException {
if (permutationMap != null) {
return;
}
if (moduleName == null) {
throw new IllegalArgumentException("moduleName can not be null");
}
String realPath = getServletContext().getRealPath(
baseUrl + moduleName + "/" + PermutationMapLinker.MANIFEST_MAP_FILE_NAME);
try {
FileInputStream fileInputStream = new FileInputStream(realPath);
permutationMap = permutationProvider.getBindingProperties(fileInputStream);
} catch (FileNotFoundException e) {
log("can not find file: '" + realPath + "'", e);
throw new ServletException("can not find permutation file", e);
} catch (XMLPermutationProviderException e) {
log("can not read xml file", e);
throw new ServletException("can not read permutation information", e);
}
}
// utility method to remove a specific binding property from a set of bindings
private Set filterProperty(String name, Set computedBindings) {
Set result = new HashSet();
for (BindingProperty property : computedBindings) {
if (!(property.getName().equals(name))) {
result.add(property);
}
}
return result;
}
public String getModuleName(HttpServletRequest req) throws ServletException {
if (req == null) {
throw new IllegalArgumentException("request can not be null");
}
// request url should be something like .../modulename.manifest" within
// the same folder of your host page...
Pattern pattern = Pattern.compile("/([a-zA-Z0-9]+)\\.manifest$");
Matcher matcher = pattern.matcher(req.getServletPath());
if (!matcher.find()) {
log("can not calculate module base from url: '" + req.getServletPath() + "'");
throw new ServletException("can not calculate module base from url: '" + req.getServletPath() + "'");
}
String module = matcher.group(1);
return module;
}
}