blob: 0b351598ea35b5d7f75a1ece286521794f472b13 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 IBM Corporation and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: IBM Corporation - initial API and implementation
* This file originally came from 'Eclipse Orbit' project then adapted to use
* in WTP and improved to use 'Manifest' to read manifest.mf, instead of reading
* it as a properties file.
******************************************************************************/
package org.eclipse.helios.tests;
import java.io.BufferedReader;
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.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.PatternSyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.tools.ant.BuildException;
import org.eclipse.internal.provisional.equinox.p2.jarprocessor.JarProcessor;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.BundleException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* @since 3.3
*/
public class TestLayoutTest {
private static final String EXTENSION_JAR = ".jar";
private static final String EXTENSION_PACEKD_JAR = ".pack.gz";
private static final String EXTENSION_ZIP = ".zip";
private static final String PROPERTY_BUNDLE_ID = "Bundle-SymbolicName";
private String configFilename = "config.properties";
private static final String KEY_DFT_BIN_JAR = "default.binary.jar";
private static final String KEY_DFT_SRC_JAR = "default.source.jar";
private static final String KEY_DFT_FEATURE = "default.feature";
private Properties config;
private List errors = new ArrayList();
private String directoryToCheck;
private String tempWorkingDir;
private String outputDirectory;
private ReportWriter reportWriter;
public static void main(String[] args) {
TestLayoutTest testlayout = new TestLayoutTest();
testlayout.setDirectoryToCheck("D:\\temptest");
testlayout.setTempWorkingDir("D:/temp");
try {
testlayout.testLayout();
}
catch (IOException e) {
e.printStackTrace();
}
}
private void addError(String message) {
errors.add(message);
}
public boolean testLayout() throws IOException {
boolean result = false;
try {
getReportWriter().writeln("Check files and layout in bundles and features.");
boolean featureFailures = testFeatureLayout();
boolean bundleFailures = testBundleLayout();
result = featureFailures || bundleFailures;
}
finally {
getReportWriter().close();
}
return result;
}
private boolean testBundleLayout() throws IOException {
errors = new ArrayList();
boolean failuresOccured = false;
String property = getDirectoryToCheck();
if (property == null) {
throw new BuildException("Need to set input directory to check against.");
}
String bundleDirectory = null;
if (property.endsWith("/")) {
bundleDirectory = property + "plugins";
}
else {
bundleDirectory = property + "/plugins";
}
File inputdir = new File(bundleDirectory);
if (!(inputdir.exists() && inputdir.isDirectory())) {
throw new BuildException("bundle direcotry (" + bundleDirectory + ") must be an existing directory.");
}
File[] children = inputdir.listFiles(new JARFileNameFilter());
int totalsize = children.length;
int checked = 0;
for (int i = 0; i < children.length; i++) {
File child = children[i];
String id = getBundleId(child);
if (id != null) {
if (id.endsWith(".source") || id.endsWith(".infopop") || id.endsWith(".doc.user") || id.endsWith(".doc") || id.endsWith(".doc.isv") || id.endsWith(".doc.dev") || id.endsWith(".doc.api") || id.endsWith("standard.schemas") || id.endsWith(".branding")) {
processBundle(child, getExpected(id, true));
checked++;
}
else {
processBundle(child, getExpected(id, false));
checked++;
}
}
}
getReportWriter().writeln();
getReportWriter().writeln(" Checking: " + bundleDirectory);
getReportWriter().writeln(" Checked " + checked + " of " + totalsize + ".");
getReportWriter().writeln(" Errors found: " + errors.size());
if (errors.size() > 0) {
Collections.sort(errors);
for (Iterator iter = errors.iterator(); iter.hasNext();) {
getReportWriter().writeln(iter.next());
}
failuresOccured = true;
}
return failuresOccured;
}
/*
* Check the configuration file and return a set of regular expressions
* which match the list of files that are expected to be in the bundle.
*/
private Set getExpected(String id, boolean source) {
// is the config cached?
if (config == null) {
getConfig();
}
String line = config.getProperty(id);
if (line == null) {
if (source) {
line = config.getProperty(KEY_DFT_SRC_JAR);
}
else {
line = config.getProperty(KEY_DFT_BIN_JAR);
}
}
if (line == null) {
throw new BuildException("Unable to load settings for: " + id);
}
Set result = new HashSet();
for (StringTokenizer tokenizer = new StringTokenizer(line, ","); tokenizer.hasMoreTokens();) {
result.add(tokenizer.nextToken().trim());
}
return result;
}
private Set getFeatureExpected(String id, boolean source, boolean zip) {
// is the config cached?
if (config == null) {
getConfig();
}
String line = config.getProperty(id);
if (line == null) {
line = config.getProperty(KEY_DFT_FEATURE);
}
if (line == null) {
throw new BuildException("Unable to load settings for: " + id);
}
Set result = new HashSet();
for (StringTokenizer tokenizer = new StringTokenizer(line, ","); tokenizer.hasMoreTokens();) {
result.add(tokenizer.nextToken().trim());
}
return result;
}
private void getConfig() {
config = new Properties();
InputStream input = null;
try {
// if we can read this file, it's been set by caller
File configFile = new File(getConfigFilename());
if (configFile.exists()) {
input = new FileInputStream(configFile);
}
else {
// else, use the default we "ship"
input = this.getClass().getResourceAsStream(getConfigFilename());
}
if (input == null) {
throw new BuildException("Unable to load configuration file.");
}
config.load(input);
}
catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (input != null) {
input.close();
}
}
catch (IOException e) {
// ignore
}
}
}
/*
* Process the bundle at the specified location, with the given set of
* expected results.
*/
private void processBundle(File file, Set expected) {
if (file.isDirectory()) {
String[] array = (String[]) expected.toArray(new String[expected.size()]);
processDir("", file, array);
for (int i = 0; i < array.length; i++) {
if (array[i] != null) {
addError("Missing " + array[i] + " in dir: " + file.getAbsolutePath());
}
}
}
else {
processArchive(file, (String[]) expected.toArray(new String[expected.size()]));
}
}
private void processFeature(File file, Set expected) {
if (file.isDirectory()) {
String[] array = (String[]) expected.toArray(new String[expected.size()]);
processDir("", file, array);
for (int i = 0; i < array.length; i++) {
if (array[i] != null) {
addError("Missing " + array[i] + " in dir: " + file.getAbsolutePath());
}
}
}
else {
processArchive(file, (String[]) expected.toArray(new String[expected.size()]));
}
}
/*
* The bundle is an archive. Make sure it has the right contents.
*/
private void processArchive(File file, String[] expected) {
ZipFile zip = null;
try {
zip = new ZipFile(file, ZipFile.OPEN_READ);
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
String name = entry.getName();
for (int i = 0; i < expected.length; i++) {
String pattern = expected[i];
if (pattern == null) {
continue;
}
try {
if (name.matches(pattern)) {
expected[i] = null;
}
}
catch (PatternSyntaxException ex) {
ex.printStackTrace();
}
}
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] != null) {
addError("Missing " + expected[i] + " in file: " + file.getName());
}
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if (zip != null) {
try {
zip.close();
}
catch (IOException e) {
// ignore
}
}
}
}
/*
* The bundle is in a directory.
*/
private void processDir(String root, File dir, String[] expected) {
File[] children = dir.listFiles();
for (int index = 0; index < children.length; index++) {
File child = children[index];
String name = root.length() == 0 ? child.getName() : root + '/' + child.getName();
if (child.isDirectory()) {
processDir(name, child, expected);
continue;
}
for (int i = 0; i < expected.length; i++) {
String pattern = expected[i];
if (pattern == null) {
continue;
}
try {
if (name.matches(pattern)) {
expected[i] = null;
}
}
catch (PatternSyntaxException ex) {
// ex.printStackTrace();
addError(ex.getMessage());
continue;
}
}
}
}
/*
* Return the bundle id from the manifest pointed to by the given input
* stream.
*/
private String getBundleIdFromManifest(InputStream input, String path) {
String id = null;
try {
Map attributes = ManifestElement.parseBundleManifest(input, null);
id = (String) attributes.get(PROPERTY_BUNDLE_ID);
if ((id == null) || (id.length() == 0)) {
addError("BundleSymbolicName header not set in manifest for bundle: " + path);
}
else {
// identifier can be followed by attributes such as
// 'singleton'
int pos = id.indexOf(';');
if (pos > 0) {
id = id.substring(0, pos);
}
}
}
catch (BundleException e) {
// e.printStackTrace();
addError(e.getMessage());
}
catch (IOException e) {
// e.printStackTrace();
addError(e.getMessage());
}
finally {
if (input != null) {
try {
input.close();
}
catch (IOException e) {
// ignore
}
}
}
return id;
}
/*
* Return the bundle identifier for the bundle contained in the given
* archive/directory.
*/
private String getBundleId(File file) {
String id = null;
if (file.isDirectory()) {
id = getBundleIdFromDir(file);
}
else if (file.getName().toLowerCase().endsWith(EXTENSION_ZIP)) {
id = getBundleIdFromZIP(file);
}
else if (file.getName().toLowerCase().endsWith(EXTENSION_JAR)) {
id = getBundleIdFromJAR(file);
}
return id;
}
private String getBundleIdFromZIP(File file) {
ZipFile zip = null;
try {
zip = new ZipFile(file);
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
if (entry.getName().matches("^.*/" + JarFile.MANIFEST_NAME)) {
InputStream input = zip.getInputStream(entry);
try {
return getBundleIdFromManifest(input, file.getAbsolutePath());
}
finally {
try {
input.close();
}
catch (IOException ex) {
// ignore
}
}
}
}
}
catch (IOException ex) {
// ex.printStackTrace();
addError(ex.getMessage());
}
finally {
try {
if (zip != null) {
zip.close();
}
}
catch (IOException ex) {
// ignore
}
}
addError("Bundle manifest (MANIFEST.MF) not found in bundle: " + file.getAbsolutePath());
return null;
}
/*
* The given file points to an expanded bundle on disc. Look into the
* bundle manifest file to find the bundle identifier.
*/
private String getBundleIdFromDir(File dir) {
String id = null;
File manifestFile = new File(dir, JarFile.MANIFEST_NAME);
if (!manifestFile.exists() || !manifestFile.isFile()) {
addError("Bundle manifest (MANIFEST.MF) not found at: " + manifestFile.getAbsolutePath());
}
else {
try {
id = getBundleIdFromManifest(new FileInputStream(manifestFile), manifestFile.getAbsolutePath());
}
catch (FileNotFoundException e) {
// e.printStackTrace();
addError(e.getMessage());
}
}
return id;
}
/*
* The given file points to a bundle contained in an archive. Look into
* the bundle manifest file to find the bundle identifier.
*/
private File getFileFromPACKEDJAR(File file) {
File tmpjar = null;
try {
JarProcessor jarprocessor = JarProcessor.getUnpackProcessor(null);
jarprocessor.setWorkingDirectory(getTempWorkingDir());
tmpjar = jarprocessor.processJar(file);
}
catch (IOException e) {
addError(e.getMessage());
}
return tmpjar;
}
public String getDirectoryToCheck() {
return directoryToCheck;
}
public void setDirectoryToCheck(String bundleDirToCheck) {
this.directoryToCheck = bundleDirToCheck;
}
public String getConfigFilename() {
return configFilename;
}
public void setConfigFilename(String configFilename) {
this.configFilename = configFilename;
}
/*
* The given file points to a bundle contained in an archive. Look into
* the bundle manifest file to find the bundle identifier.
*/
private String getBundleIdFromJAR(File file) {
InputStream input = null;
JarFile jar = null;
try {
jar = new JarFile(file, false, ZipFile.OPEN_READ);
JarEntry entry = jar.getJarEntry(JarFile.MANIFEST_NAME);
if (entry == null) {
addError("Bundle does not contain a MANIFEST.MF file: " + file.getAbsolutePath());
return null;
}
input = jar.getInputStream(entry);
return getBundleIdFromManifest(input, file.getAbsolutePath());
}
catch (IOException e) {
// e.printStackTrace();
addError(e.getMessage());
return null;
}
finally {
if (input != null) {
try {
input.close();
}
catch (IOException e) {
// ignore
}
}
if (jar != null) {
try {
jar.close();
}
catch (IOException e) {
// ignore
}
}
}
}
public String getTempWorkingDir() {
return tempWorkingDir;
}
public void setTempWorkingDir(String tempWorkingDir) {
this.tempWorkingDir = tempWorkingDir;
}
private boolean testFeatureLayout() throws IOException {
errors = new ArrayList();
boolean failuresOccurred = false;
String property = getDirectoryToCheck();
if (property == null) {
throw new BuildException("Need to set input directory to check against.");
}
String featureDirectory = null;
if (property.endsWith("/")) {
featureDirectory = property + "features";
}
else {
featureDirectory = property + "/features";
}
File inputdir = new File(featureDirectory);
if (!(inputdir.exists() && inputdir.isDirectory())) {
throw new BuildException("feature direcotry (" + featureDirectory + ") must be an existing directory.");
}
File[] children = inputdir.listFiles(new JARFileNameFilter());
int totalsize = children.length;
int checked = 0;
for (int i = 0; i < children.length; i++) {
File child = children[i];
if (child.getName().toLowerCase().endsWith(EXTENSION_PACEKD_JAR)) {
child = getFileFromPACKEDJAR(child);
}
if (child != null) {
String id = getFeatureId(child);
if (id != null) {
processFeature(child, getFeatureExpected(id, true, child.getName().endsWith(EXTENSION_ZIP)));
checked++;
}
}
}
getReportWriter().writeln();
getReportWriter().writeln(" Checking: " + featureDirectory);
getReportWriter().writeln(" Checked " + checked + " of " + totalsize + ".");
getReportWriter().writeln(" Errors found: " + errors.size());
if (errors.size() > 0) {
Collections.sort(errors);
for (Iterator iter = errors.iterator(); iter.hasNext();) {
getReportWriter().writeln(iter.next());
}
failuresOccurred = true;
}
return failuresOccurred;
}
private String getFeatureId(File file) {
String id = null;
if (file.isDirectory()) {
id = getFeatureIdFromDir(file);
}
else if (file.getName().toLowerCase().endsWith(EXTENSION_JAR)) {
id = getFeatureIdFromJAR(file);
}
return id;
}
private String getFeatureIdFromJAR(File file) {
InputStream input = null;
JarFile jar = null;
String id = null;
try {
jar = new JarFile(file, false, ZipFile.OPEN_READ);
JarEntry entry = jar.getJarEntry("feature.xml");
if (entry == null) {
addError("Feature jar does not contain a feature.xml file: " + file.getAbsolutePath());
}
input = jar.getInputStream(entry);
id = getFeatureFromFeatureXML(input);
}
catch (IOException e) {
// e.printStackTrace();
addError(e.getMessage());
}
catch (ParserConfigurationException e) {
addError(e.getMessage());
}
catch (SAXException e) {
addError(e.getMessage());
}
finally {
if (input != null) {
try {
input.close();
}
catch (IOException e) {
// ignore
}
}
if (jar != null) {
try {
jar.close();
}
catch (IOException e) {
// ignore
}
}
}
return id;
}
private String getFeatureIdFromDir(File dir) {
String id = null;
File featureFile = new File(dir, "feature.xml");
if (!featureFile.exists() || !featureFile.isFile()) {
addError("Feature.xml not found at: " + featureFile.getAbsolutePath());
}
else {
id = getFeatureFromFeatureXML(featureFile);
}
return id;
}
private String getFeatureFromFeatureXML(File file) {
Document document = getDOM(file);
return getFeatureIdFromDOM(document);
}
private String getFeatureIdFromDOM(Document document) {
String id = null;
if (document != null) {
NodeList featureElements = document.getElementsByTagName("feature");
Element featureElement = null;
if (featureElements.getLength() > 0) {
Node featureNode = featureElements.item(0);
if (featureNode instanceof Element) {
featureElement = (Element) featureNode;
}
}
if (featureElement != null) {
NamedNodeMap aNamedNodeMap = featureElement.getAttributes();
Node idAttribute = aNamedNodeMap.getNamedItem("id");
id = idAttribute.getNodeValue();
}
}
return id;
}
private String getFeatureFromFeatureXML(InputStream stream) throws ParserConfigurationException, SAXException, IOException {
Document document = getDOM(stream);
return getFeatureIdFromDOM(document);
}
private Document getDOM(File file) {
Document aDocument = null;
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
InputSource inputSource = new InputSource(reader);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
aDocument = builder.parse(inputSource);
}
catch (SAXException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (ParserConfigurationException e) {
e.printStackTrace();
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
// ignore this one
}
}
}
if (aDocument == null) {
String msg = "Error: could not parse xml in classpath file: " + file.getAbsolutePath();
throw new BuildException(msg);
}
return aDocument;
}
private Document getDOM(InputStream stream) throws ParserConfigurationException, SAXException, IOException {
Document aDocument = null;
InputSource inputSource = new InputSource(stream);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
aDocument = builder.parse(inputSource);
return aDocument;
}
public String getOutputDirectory() {
return outputDirectory;
}
public void setOutputDirectory(String outputDirectory) {
this.outputDirectory = outputDirectory;
}
public ReportWriter getReportWriter() {
if (reportWriter == null) {
String outputFilename = "layoutCheck.txt";
reportWriter = new ReportWriter(getOutputDirectory(), outputFilename);
}
return reportWriter;
}
}