/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
/**
* @author Sebastian Reinhardt
* The purpose of this tool is to enable an easy upgrade mechanism of the
* geotools dependencies in HALE. This class merges the different (and same)
* service files in the various "META-INF/services" folders of the geotools
* jar-files..
*/
public class Servicemerger {
String source;
String dest;
ArrayList<String> bndSpecifications;
boolean bndFlag;
/**
* @param source - the sourcepath of the geotools jar-files.
* @param dest - the destinationpath where the merged files should appear
* @param bnd - bnd-File with specified names of jar-files to merge
*/
public Servicemerger(String bnd){
this.bndSpecifications = new ArrayList<String>();
this.bndFlag = true;
try {
readBND(bnd);
merge(new File(this.source));
createJar(dest);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param source - the sourcepath of the geotools jar-files.
* @param dest - the destinationpath where the merged files should appear
* @param bnd - bnd-File with specified names of jar-files to merge
*/
public Servicemerger(String source, String dest){
this.source = source;
this.dest = dest;
this.bndFlag = false;
try {
merge(new File(this.source));
createJar(dest);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param bnd
* @throws IOException
*
*/
private void readBND(String bnd) throws IOException {
File bndFile = new File(bnd);
BufferedReader bndReader = new BufferedReader(
new FileReader(bndFile));
String readLine;
while((readLine = bndReader.readLine()).contains(",\\")){
if(readLine.contains("classpath")) continue;
bndSpecifications.add(readLine.substring(readLine.lastIndexOf("/") + 1, readLine.indexOf(",\\")));
}
//TODO
this.source = bndFile.getParent().concat( java.io.File.separator + readLine.substring(0, readLine.lastIndexOf("/")));
this.dest = this.source;
//!
bndSpecifications.add(readLine.substring(readLine.lastIndexOf("/") + 1));
bndReader.close();
}
/**
* Method for creating a jar file containing the merged servicefiles
* @param dest the destinationpath of the jar file
* @throws IOException
*/
private void createJar(String dest) throws IOException {
//set paths and files
File jar = new File(dest + java.io.File.separator + "_common_services.jar");
File destDir = new File(dest + java.io.File.separator +
"META-INF" + java.io.File.separator +
"services");
File[] jaredFiles = destDir.listFiles();
int BUFFER_SIZE = 10240 ;
try {
byte buffer [] = new byte [ BUFFER_SIZE ] ;
FileOutputStream stream = new FileOutputStream(jar) ;
JarOutputStream out = new JarOutputStream(stream) ;
for (int i = 0 ; i < jaredFiles.length; i++) {
if (jaredFiles[i] == null || !jaredFiles[i].exists()
|| jaredFiles[i].isDirectory()){
continue ;
}
// System.out.println("Adding " + jaredFiles[i].getName()) ;
//add files as JarEntrys to the JarFile
//must use "/" for pathname so it works with bnd
JarEntry entry = new JarEntry("META-INF" + "/" +
"services" + "/" + jaredFiles[i].getName()) ;
entry.setTime(jaredFiles[i].lastModified()) ;
out.putNextEntry(entry) ;
FileInputStream in = new FileInputStream(jaredFiles[i]) ;
while(true) {
int nRead = in.read(buffer, 0 , buffer.length) ;
if(nRead <= 0)
break ;
out.write(buffer, 0 , nRead) ;
}
in.close () ;
}
out.close();
stream.close();
System.out.println("Adding completed") ;
} catch(Exception ex){
ex.printStackTrace();
System.out.println("Error: " + ex.getMessage ()) ;
}
}
/**
* This method can be used, to list all files and subdirectorys
* in a specific directory
* @param dir the path of the directory
*/
public void listDir(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
System.out.print(files[i].getAbsolutePath());
if (files[i].isDirectory()) {
System.out.print(" (Ordner)\n");
listDir(files[i]); }
else {
System.out.print(" (Datei)\n");
}
}
}
}
/**
* Reads the inputstream of a file and transforms it into a String text
* Used to read files directly out of a jarfile
* @param input the stream of the file to read
* @return the content of the file as a text
* @throws IOException
*/
private String readFile(InputStream input) throws IOException{
String text = "";
InputStreamReader isr =
new InputStreamReader(input);
BufferedReader reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
text += (line + "\n");
}
reader.close();
return text;
}
/**
* creates the new merged file comparing the content of 2 files
* @param file the first file to be compared and merged
* @param text the content of the second file
* @throws IOException
*/
private void writeMergedFile(File file, String text) throws IOException{
// Create file if it does not exist
boolean success = file.createNewFile();
if (success)
{
// File did not exist and was created
BufferedWriter out = new BufferedWriter(new FileWriter(file));
out.write(text.toCharArray());
out.close();
}
//file existed, compare content and merge
else {
BufferedWriter out = new BufferedWriter(new FileWriter(file, true));
BufferedReader readFile = new BufferedReader(new FileReader(file));
BufferedReader readText = new BufferedReader(new StringReader(text));
String fileLine;
String textLine;
ArrayList<String> fileContent = new ArrayList<String>();
ArrayList<String> toBeWritten = new ArrayList<String>();
while ((fileLine = readFile.readLine()) != null){
fileContent.add(fileLine);
}
while ((textLine = readText.readLine()) != null){
if (!fileContent.contains(textLine)){
toBeWritten.add(textLine);
}
}
for(String s : toBeWritten){
out.write(s);
out.newLine();
}
out.flush();
out.close();
}
}
/**
* method for starting the creation and merging process.
* reads all the given jar files and their entrys.
* @param dir the directory of the jar files
* @throws IOException
*/
private void merge(File dir) throws IOException{
File[] files = dir.listFiles();
Map <String, File> visited = new HashMap<String, File>();
//are jar files in the given directory?
if (files != null) {
for (int i = 0; i < files.length; i++) {
//we only work with .jar files
JarFile jar = null;
if(this.bndFlag == false){
if(files[i].getName().endsWith(".jar")){
jar = new JarFile(files[i]);
}
}
else if(this.bndFlag == true){
if(files[i].getName().endsWith(".jar") && bndSpecifications.contains(files[i].getName())){
jar = new JarFile(files[i]);
}
}
if(jar != null){
Enumeration<JarEntry> jarenu = jar.entries();
//lets look at the entry of a specific jar file
while (jarenu.hasMoreElements()){
JarEntry entry = jarenu.nextElement();
//do the jar file contains a servicefile?
if(entry.getName().contains("META-INF")
&& entry.getName().contains("services")
&& !entry.isDirectory()){
if(visited.containsKey(entry.getName())){
File mergedFile = visited.get(entry.getName());
writeMergedFile(mergedFile, readFile(jar.getInputStream(entry)));
}
else {
File mergedFile = new File(dest + java.io.File.separator + entry.getName());
writeMergedFile(mergedFile, readFile(jar.getInputStream(entry)));
visited.put(entry.getName(), mergedFile);
}
}
}
}
}
}
}
/**
* main method to start the Servicemerger
* @param args 0 - the path of the original jar files, 1 - the destination of the new files
*/
public static void main(String[] args) {
if(args[0].equals("-bnd")){
Servicemerger sm = new Servicemerger(args[1]);
}
else {
Servicemerger sm = new Servicemerger(args[0], args[1]);
}
}
}