/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.server.plugins.url;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.util.ValidationEventCollector;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.enterprise.server.plugin.pc.content.ContentProviderPackageDetails;
import org.rhq.enterprise.server.plugin.pc.content.ContentProviderPackageDetailsKey;
import org.rhq.enterprise.server.xmlschema.XmlSchemas;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.ConfigurationType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.ListPropertyType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.MapPropertyType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.ObjectFactory;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.PackageDetailsKeyType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.PackageDetailsType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.PackageType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.ResourceVersionsType;
import org.rhq.enterprise.server.xmlschema.generated.contentsource.packagedetails.SimplePropertyType;
/**
* Parses the index XML file whose format follows the package details schema.
*
* @author John Mazzitelli
*/
public class XmlIndexParser implements IndexParser {
private final Log log = LogFactory.getLog(XmlIndexParser.class);
private static final String PLUGIN_SCHEMA_PATH = "rhq-contentsource-packagedetails.xsd";
public Map<String, RemotePackageInfo> parse(InputStream indexStream, UrlProvider contentSource) throws Exception {
return jaxbParse(indexStream, contentSource.getIndexUrl(), contentSource.getRootUrlString());
}
@SuppressWarnings("unchecked")
protected Map<String, RemotePackageInfo> jaxbParse(InputStream indexStream, URL indexUrl, String rootUrlString)
throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(XmlSchemas.PKG_CONTENTSOURCE_PACKAGEDETAILS);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
// Enable schema validation
URL pluginSchemaURL = XmlIndexParser.class.getClassLoader().getResource(PLUGIN_SCHEMA_PATH);
Schema pluginSchema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(pluginSchemaURL);
unmarshaller.setSchema(pluginSchema);
ValidationEventCollector vec = new ValidationEventCollector();
unmarshaller.setEventHandler(vec);
BufferedReader reader = new BufferedReader(new InputStreamReader(indexStream));
JAXBElement<PackageType> packagesXml = (JAXBElement<PackageType>) unmarshaller.unmarshal(reader);
for (ValidationEvent event : vec.getEvents()) {
log.debug("URL content source index [" + indexUrl + "] message {Severity: " + event.getSeverity()
+ ", Message: " + event.getMessage() + ", Exception: " + event.getLinkedException() + "}");
}
Map<String, RemotePackageInfo> fileList = new HashMap<String, RemotePackageInfo>();
List<PackageDetailsType> allPackages = packagesXml.getValue().getPackage();
for (PackageDetailsType pkg : allPackages) {
URL locationUrl = new URL(rootUrlString + pkg.getLocation());
ContentProviderPackageDetails details = translateXmlToDomain(pkg);
FullRemotePackageInfo rpi = new FullRemotePackageInfo(locationUrl, details);
fileList.put(stripLeadingSlash(rpi.getLocation()), rpi);
}
return fileList;
}
/**
* Translates the details XML type to its domain object.
* @param pkg the XML type object
* @return the domain object with the same data that the XML object had
* @throws Exception
*/
protected ContentProviderPackageDetails translateXmlToDomain(PackageDetailsType pkg) throws Exception {
PackageDetailsKeyType keyType = pkg.getPackageDetailsKey();
try {
String version = "[sha256=" + pkg.getSha256() + "]";
ContentProviderPackageDetailsKey key = new ContentProviderPackageDetailsKey(keyType.getName(), version,
keyType.getPackageTypeName(), keyType.getArchitectureName(), keyType
.getResourceTypeName(), keyType.getResourceTypePlugin());
ContentProviderPackageDetails details = new ContentProviderPackageDetails(key);
details.setDisplayName(pkg.getDisplayName());
details.setDisplayVersion(pkg.getDisplayVersion());
details.setShortDescription(pkg.getShortDescription());
details.setLongDescription(pkg.getLongDescription());
details.setClassification(pkg.getClassification());
details.setFileName(pkg.getFileName());
details.setFileSize(pkg.getFileSize());
details.setFileCreatedDate(pkg.getFileCreatedDate());
details.setSHA256(pkg.getSha256());
details.setMD5(pkg.getMd5());
details.setLicenseName(pkg.getLicenseName());
details.setLicenseVersion(pkg.getLicenseVersion());
if (pkg.getMetadata() != null) {
details.setMetadata(pkg.getMetadata().getBytes());
}
details.setLocation(pkg.getLocation());
ResourceVersionsType resourceVersions = pkg.getResourceVersions();
if (resourceVersions != null) {
details.setResourceVersions(new HashSet<String>(resourceVersions.getResourceVersion()));
}
ConfigurationType extraPropertiesXml = pkg.getExtraProperties();
if (extraPropertiesXml != null) {
Configuration config = new Configuration();
List<Object> configXml = extraPropertiesXml.getSimplePropertyOrListPropertyOrMapProperty();
for (Object object : configXml) {
if (object instanceof SimplePropertyType) {
config.put(translateSimpleProperty((SimplePropertyType) object, null));
} else if (object instanceof ListPropertyType) {
config.put(translateListProperty((ListPropertyType) object, null));
} else if (object instanceof MapPropertyType) {
config.put(translateMapProperty((MapPropertyType) object, null));
} else {
throw new IllegalStateException("Unknown JAXB type: " + object); // did the schema change?
}
}
details.setExtraProperties(config);
}
return details;
} catch (Exception e) {
log.error("Failed to process package [" + keyType.getName() + " v" + keyType.getVersion() + ']');
throw e;
}
}
protected Property translateSimpleProperty(SimplePropertyType object, String defaultName) {
String name = object.getName();
String value = object.getValue();
return new PropertySimple((name != null) ? name : defaultName, value);
}
protected Property translateListProperty(ListPropertyType object, String defaultName) {
String name = object.getName();
List<Object> listXml = object.getSimplePropertyOrListPropertyOrMapProperty();
PropertyList list = new PropertyList((name != null) ? name : defaultName);
for (Object listItem : listXml) {
if (listItem instanceof SimplePropertyType) {
list.add(translateSimpleProperty((SimplePropertyType) listItem, name));
} else if (listItem instanceof ListPropertyType) {
list.add(translateListProperty((ListPropertyType) listItem, name));
} else if (listItem instanceof MapPropertyType) {
list.add(translateMapProperty((MapPropertyType) listItem, name));
} else {
throw new IllegalStateException("Unknown JAXB type: " + object); // did the schema change?
}
}
return list;
}
protected Property translateMapProperty(MapPropertyType object, String defaultName) {
String name = object.getName();
List<Object> mapXml = object.getSimplePropertyOrListPropertyOrMapProperty();
PropertyMap map = new PropertyMap((name != null) ? name : defaultName);
for (Object mapItem : mapXml) {
if (mapItem instanceof SimplePropertyType) {
map.put(translateSimpleProperty((SimplePropertyType) mapItem, null));
} else if (mapItem instanceof ListPropertyType) {
map.put(translateListProperty((ListPropertyType) mapItem, null));
} else if (mapItem instanceof MapPropertyType) {
map.put(translateMapProperty((MapPropertyType) mapItem, null));
} else {
throw new IllegalStateException("Unknown JAXB type: " + object); // did the schema change?
}
}
return map;
}
protected String stripLeadingSlash(String str) {
while (str.startsWith("/")) {
str = (str.length() > 1) ? str.substring(1) : "";
}
return str;
}
/**
* A utility that can build an index file that contains metadata for content found
* in a given directory.
*
* <pre>
* java -cp "target\rhq-serverplugin-url-1.3.0-SNAPSHOT.jar;
* ..\..\..\..\core\client-api\target\rhq-core-client-api-1.3.0-SNAPSHOT.jar;
* ..\..\..\..\core\domain\target\rhq-core-domain-ejb3.jar;
* %HOMEPATH%\.m2\repository\commons-logging\commons-logging\1.1.0.jboss\commons-logging-1.1.0.jboss.jar"
* org.rhq.enterprise.server.plugins.url.XmlIndexParser
* C:\my\content\source\directory
* library
* noarch
* "JBossAS Server"
* JBossAS
* </pre>
*
* @param args
*/
public static void main(String args[]) {
try {
if (args.length != 5) {
System.err.println("Syntax: <directory> <packageTypeName> <archName> <resTypeName> <resTypePlugin>");
System.exit(1);
}
String directoryString = args[0];
File directory = new File(directoryString);
if (!directory.isDirectory()) {
System.err.println("You did not supply a valid directory name: " + directoryString);
System.exit(1);
}
PackageDetailsKeyType packageTypeInfo = new PackageDetailsKeyType();
packageTypeInfo.setPackageTypeName(args[1]);
packageTypeInfo.setArchitectureName(args[2]);
packageTypeInfo.setResourceTypeName(args[3]);
packageTypeInfo.setResourceTypePlugin(args[4]);
File index = new File(directory, "content-index.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(XmlSchemas.PKG_CONTENTSOURCE_PACKAGEDETAILS);
Marshaller marshaller = jaxbContext.createMarshaller();
PackageType packagesXml = new PackageType();
PrintWriter writer = new PrintWriter(index);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
try {
generatePackageIndex(directory, packagesXml.getPackage(), directory, packageTypeInfo);
marshaller.marshal(new ObjectFactory().createPackages(packagesXml), writer);
} finally {
writer.close();
}
} catch (Throwable t) {
System.err.println("Failed to generate content index file. Cause: " + t);
t.printStackTrace(System.err);
System.exit(1);
}
}
protected static void generatePackageIndex(File file, List<PackageDetailsType> list, File root,
PackageDetailsKeyType packageTypeInfo) throws Exception {
if (file.isDirectory()) {
File[] childFiles = file.listFiles();
for (File childFile : childFiles) {
generatePackageIndex(childFile, list, root, packageTypeInfo);
}
} else if (!file.getCanonicalPath().equals(new File(root, "content-index.xml").getCanonicalPath())) {
String relativeLocation = file.getCanonicalPath().substring(root.getCanonicalPath().length() + 1);
MessageDigestGenerator messageDigest = new MessageDigestGenerator(MessageDigestGenerator.SHA_256);
String sha256 = messageDigest.calcDigestString(file);
String version = "[sha256=" + sha256 + "]";
PackageDetailsKeyType detailsKeyType = new PackageDetailsKeyType();
detailsKeyType.setName(file.getName());
detailsKeyType.setVersion(version);
detailsKeyType.setPackageTypeName(packageTypeInfo.getPackageTypeName());
detailsKeyType.setArchitectureName(packageTypeInfo.getArchitectureName());
detailsKeyType.setResourceTypeName(packageTypeInfo.getResourceTypeName());
detailsKeyType.setResourceTypePlugin(packageTypeInfo.getResourceTypePlugin());
PackageDetailsType detailsType = new PackageDetailsType();
detailsType.setPackageDetailsKey(detailsKeyType);
detailsType.setDisplayName(file.getName());
detailsType.setFileName(file.getName());
detailsType.setFileSize(file.length());
detailsType.setFileCreatedDate(file.lastModified());
detailsType.setSha256(sha256);
detailsType.setLocation(relativeLocation);
list.add(detailsType);
}
return;
}
}