/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.web;
import com.hazelcast.impl.Util;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class Installer {
protected static final Logger logger = Logger.getLogger(Installer.class.getName());
private static final boolean DEBUG = false;
private String clusteredFilePrefix = "clustered-";
private boolean addHazelcastLib = true;
private boolean replaceOld = false;
private boolean appsSharingSessions = false;
private String version = "1.9.4";
private String libDir = "../lib/";
public static void main(final String[] args) {
final Installer installer = new Installer();
installer.install(args);
}
public Document createDocument(final InputStream in) {
Document doc = null;
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(in);
} catch (final Exception e) {
e.printStackTrace();
}
return doc;
}
public String getClusteredFilePrefix() {
return clusteredFilePrefix;
}
public boolean isAddHazelcastLib() {
return addHazelcastLib;
}
public boolean isAppsSharingSessions() {
return appsSharingSessions;
}
public boolean isReplaceOld() {
return replaceOld;
}
public final void modify(final File src, final File dest) {
try {
final ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
new FileOutputStream(dest)));
final ZipFile zipFile = new ZipFile(src);
final Enumeration entries = zipFile.entries();
String jarLocation = null;
if (src.getName().endsWith("war"))
jarLocation = "WEB-INF/lib/";
while (entries.hasMoreElements()) {
final ZipEntry entry = (ZipEntry) entries.nextElement();
log("entry name " + entry.getName());
out.putNextEntry(new ZipEntry(entry.getName()));
if (entry.isDirectory()) {
continue;
}
final String name = entry.getName();
if (jarLocation == null) {
if (name.endsWith(".jar")) {
final int slashIndex = name.lastIndexOf('/');
if (slashIndex == -1) {
jarLocation = "";
} else {
jarLocation = name.substring(0, slashIndex) + "/";
}
}
}
final InputStream in = zipFile.getInputStream(entry);
if (name.endsWith(".war")) {
modifyWar(in, out);
} else if (name.equals("WEB-INF/web.xml")) {
readModifyWrite(in, out);
} else {
Util.copyStream(in, out);
}
in.close();
}
if (jarLocation == null)
jarLocation = "";
if (isAddHazelcastLib()) {
log("Jar Location " + jarLocation);
String hazelcastJarName = "hazelcast-" + version + ".jar";
String hazelcastWMJarName = "hazelcast-wm-" + version + ".jar";
addFileToZipStream((libDir + hazelcastJarName), (jarLocation + hazelcastJarName), out);
addFileToZipStream((libDir + hazelcastWMJarName), (jarLocation + hazelcastWMJarName), out);
}
out.flush();
out.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
void addFileToZipStream(String fileName, String zipFilePath, ZipOutputStream out) throws Exception {
final ZipEntry hazelcastZip = new ZipEntry(zipFilePath);
out.putNextEntry(hazelcastZip);
final InputStream in = new FileInputStream(fileName);
Util.copyStream(in, out);
in.close();
}
public Document modifyWebXml(final Document doc) {
final Element docElement = doc.getDocumentElement();
final NodeList nodelist = docElement.getChildNodes();
final List<String> lsListeners = new ArrayList<String>();
Node firstFilter = null;
Node displayElement = null;
Node afterDisplayElement = null;
final int sessionTimeoutDefault = -23490375;
int sessionTimeout = sessionTimeoutDefault;
for (int i = 0; i < nodelist.getLength(); i++) {
final Node node = nodelist.item(i);
final String name = node.getNodeName();
if ("display-name".equals(name)) {
displayElement = node;
} else {
if ("listener".equals(name)) {
String className = null;
final NodeList nl = node.getChildNodes();
for (int a = 0; a < nl.getLength(); a++) {
final Node n = nl.item(a);
if (n.getNodeName().equals("listener-class")) {
className = n.getTextContent();
}
}
lsListeners.add(className);
docElement.removeChild(node);
} else if ("filter".equals(name)) {
if (firstFilter == null) {
firstFilter = node;
}
} else if ("session-config".equals(name)) {
final NodeList nl = node.getChildNodes();
for (int a = 0; a < nl.getLength(); a++) {
final Node n = nl.item(a);
if (n.getNodeName().equals("session-timeout")) {
try {
sessionTimeout = Integer.parseInt(n.getTextContent().trim());
} catch (final Exception e) {
e.printStackTrace();
}
}
}
}
if (displayElement != null && afterDisplayElement == null) {
afterDisplayElement = node;
}
}
}
final Element filter = doc.createElement("filter");
append(doc, filter, "filter-name", "hazel-filter");
append(doc, filter, "filter-class", "com.hazelcast.web.WebFilter");
Node initParam = append(doc, filter, "init-param", null);
append(doc, initParam, "param-name", "apps-sharing-sessions");
append(doc, initParam, "param-value", String.valueOf(appsSharingSessions));
if (sessionTimeout != sessionTimeoutDefault) {
initParam = append(doc, filter, "init-param", null);
append(doc, initParam, "param-name", "session-timeout");
append(doc, initParam, "param-value", "" + sessionTimeout);
}
Node first = firstFilter;
if (first == null) {
if (afterDisplayElement != null) {
first = afterDisplayElement;
}
}
if (first == null) {
first = docElement.getFirstChild();
}
docElement.insertBefore(filter, first);
final Element filterMapping = doc.createElement("filter-mapping");
append(doc, filterMapping, "filter-name", "hazel-filter");
append(doc, filterMapping, "url-pattern", "/*");
append(doc, filterMapping, "dispatcher", "FORWARD");
append(doc, filterMapping, "dispatcher", "INCLUDE");
append(doc, filterMapping, "dispatcher", "REQUEST");
final Element contextListener = doc.createElement("listener");
append(doc, contextListener, "listener-class",
"com.hazelcast.web.WebFilter$ContextListener");
docElement.insertBefore(filterMapping, after(docElement, "filter"));
docElement.insertBefore(contextListener, after(docElement, "filter-mapping"));
return doc;
}
public void readModifyWrite(final InputStream in, final OutputStream out) {
final Document finalDoc = modifyWebXml(createDocument(in));
Util.streamXML(finalDoc, out);
if (DEBUG)
Util.streamXML(finalDoc, System.out);
}
public void setAddHazelcastLib(final boolean addHazelcastLib) {
this.addHazelcastLib = addHazelcastLib;
}
public void setAppsSharingSessions(final boolean appsSharingSessions) {
this.appsSharingSessions = appsSharingSessions;
}
public void setClusteredFilePrefix(final String clusteredFilePrefix) {
this.clusteredFilePrefix = clusteredFilePrefix;
}
public void setReplaceOld(final boolean replaceOld) {
this.replaceOld = replaceOld;
}
public final void unzip(final File file) {
final String warFilename = file.getName();
final String destDirName = warFilename.substring(0, warFilename.lastIndexOf('.'));
Enumeration entries;
ZipFile zipFile;
final File destDir = new File(destDirName);
destDir.mkdirs();
try {
zipFile = new ZipFile(file);
entries = zipFile.entries();
while (entries.hasMoreElements()) {
final ZipEntry entry = (ZipEntry) entries.nextElement();
if (entry.isDirectory()) {
(new File(destDir, entry.getName())).mkdir();
continue;
}
final File entryFile = new File(destDir, entry.getName());
final InputStream in = zipFile.getInputStream(entry);
final OutputStream out = new BufferedOutputStream(new FileOutputStream(entryFile));
Util.copyStream(in, out);
in.close();
out.close();
}
zipFile.close();
} catch (final IOException ioe) {
ioe.printStackTrace();
}
}
private Node after(final Node parent, final String nodeName) {
final NodeList nodelist = parent.getChildNodes();
int index = -1;
for (int i = 0; i < nodelist.getLength(); i++) {
final Node node = nodelist.item(i);
final String name = node.getNodeName();
if (nodeName.equals(name)) {
index = i;
}
}
if (index == -1)
return null;
if (nodelist.getLength() > (index + 1)) {
return nodelist.item(index + 1);
}
return null;
}
private Node append(final Document doc, final Node parent, final String element,
final String value) {
final Element child = doc.createElement(element);
if (value != null)
child.setTextContent(value);
parent.appendChild(child);
return child;
}
private void install(final String[] args) {
if (args == null || args.length == 0) {
print("No application is specified!");
printHelp();
}
final Set<String> setApps = new HashSet<String>();
if (args != null) {
for (final String arg : args) {
if (arg.startsWith("-")) {
if (arg.equals("-apps-sharing-sessions")) {
appsSharingSessions = true;
addHazelcastLib = false;
} else if (arg.startsWith("-version")) {
version = arg.substring(arg.indexOf("-version") + "-version".length());
} else if (arg.startsWith("-libDir")) {
libDir = arg.substring(arg.indexOf("-libDir") + "-libDir".length());
} else if (arg.startsWith("-excludeJar")) {
addHazelcastLib = false;
}
} else {
setApps.add(arg);
}
}
}
for (final String appFilename : setApps) {
processApp(appFilename);
}
logger.log(Level.INFO, "Done!");
}
private void log(final Object obj) {
if (DEBUG)
logger.log(Level.INFO, obj.toString());
}
private void modifyWar(final InputStream in, final OutputStream os) {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final ZipOutputStream out = new ZipOutputStream(bos);
final ZipInputStream zin = new ZipInputStream(in);
try {
while (true) {
final ZipEntry entry = zin.getNextEntry();
if (entry == null)
break;
out.putNextEntry(new ZipEntry(entry.getName()));
log("war file " + entry.getName());
if (entry.isDirectory()) {
continue;
}
if (entry.getName().equals("WEB-INF/web.xml")) {
final ByteArrayOutputStream bosWebXml = new ByteArrayOutputStream();
Util.copyStream(zin, bosWebXml);
bosWebXml.flush();
final byte[] webxmlBytes = bosWebXml.toByteArray();
bosWebXml.close();
final ByteArrayInputStream binWebXml = new ByteArrayInputStream(webxmlBytes);
readModifyWrite(binWebXml, out);
binWebXml.close();
} else {
Util.copyStream(zin, out);
}
out.flush();
}
} catch (final Exception e) {
e.printStackTrace();
if (DEBUG)
System.exit(0);
} finally {
try {
out.flush();
out.close();
bos.writeTo(os);
} catch (final IOException e) {
e.printStackTrace();
}
}
}
private void print(final Object obj) {
logger.log(Level.INFO, obj.toString());
}
private void printHelp() {
print("clusterWebapp.bat <war-file-path or ear-file-path>");
}
private void processApp(final String appFilename) {
final File file = new File(appFilename);
final String clusteredFileName = clusteredFilePrefix + file.getName();
final File fileOriginal = new File(file.getParentFile(), clusteredFileName);
modify(file, fileOriginal);
if (isReplaceOld()) {
final boolean success = file.renameTo(fileOriginal);
if (success) {
logger.log(Level.INFO, "old Application File was replaced!");
}
}
logger.log(Level.INFO, "Done. New clustered application at "
+ fileOriginal.getAbsolutePath());
}
}