/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-12 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id$
*/
package org.exist.repo;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.log4j.Logger;
import org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.collections.IndexInfo;
import org.exist.config.ConfigurationException;
import org.exist.dom.BinaryDocument;
import org.exist.dom.QName;
import org.exist.memtree.*;
import org.exist.security.Permission;
import org.exist.security.PermissionDeniedException;
import org.exist.security.internal.aider.GroupAider;
import org.exist.security.internal.aider.UserAider;
import org.exist.security.xacml.AccessContext;
import org.exist.source.FileSource;
import org.exist.storage.DBBroker;
import org.exist.storage.lock.Lock;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.util.LockException;
import org.exist.util.MimeTable;
import org.exist.util.MimeType;
import org.exist.util.SyntaxException;
import org.exist.util.serializer.AttrList;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.*;
import org.exist.xquery.util.DocUtils;
import org.exist.xquery.value.DateTimeValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.Type;
import org.expath.pkg.repo.*;
import org.expath.pkg.repo.Package;
import org.expath.pkg.repo.deps.DependencyVersion;
import org.expath.pkg.repo.tui.BatchUserInteraction;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import java.io.*;
import java.util.Date;
import java.util.Stack;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* Deploy a .xar package into the database using the information provided
* in expath-pkg.xml and repo.xml.
*/
public class Deployment {
public final static String PROPERTY_APP_ROOT = "repo.root-collection";
private final static Logger LOG = Logger.getLogger(Deployment.class);
private final static String REPO_NAMESPACE = "http://exist-db.org/xquery/repo";
private final static String PKG_NAMESPACE = "http://expath.org/ns/pkg";
private final static QName SETUP_ELEMENT = new QName("setup", REPO_NAMESPACE);
private static final QName PRE_SETUP_ELEMENT = new QName("prepare", REPO_NAMESPACE);
private static final QName POST_SETUP_ELEMENT = new QName("finish", REPO_NAMESPACE);
private static final QName TARGET_COLL_ELEMENT = new QName("target", REPO_NAMESPACE);
private static final QName PERMISSIONS_ELEMENT = new QName("permissions", REPO_NAMESPACE);
private static final QName CLEANUP_ELEMENT = new QName("cleanup", REPO_NAMESPACE);
private static final QName DEPLOYED_ELEMENT = new QName("deployed", REPO_NAMESPACE);
private static final QName DEPENDENCY_ELEMENT = new QName("dependency", PKG_NAMESPACE);
private DBBroker broker;
private String user = null;
private String password = null;
private String group = null;
private int perms = -1;
private String permsStr = null;
public Deployment(DBBroker broker) {
this.broker = broker;
}
protected File getPackageDir(String pkgName, ExistRepository repo) throws PackageException {
File packageDir = null;
for (final Packages pp : repo.getParentRepo().listPackages()) {
final org.expath.pkg.repo.Package pkg = pp.latest();
if (pkg.getName().equals(pkgName)) {
packageDir = getPackageDir(pkg);
}
}
return packageDir;
}
protected File getPackageDir(Package pkg) {
final FileSystemStorage.FileSystemResolver resolver = (FileSystemStorage.FileSystemResolver) pkg.getResolver();
return resolver.resolveResourceAsFile(".");
}
protected org.expath.pkg.repo.Package getPackage(String pkgName, ExistRepository repo) throws PackageException {
for (final Packages pp : repo.getParentRepo().listPackages()) {
final org.expath.pkg.repo.Package pkg = pp.latest();
if (pkg.getName().equals(pkgName)) {
return pkg;
}
}
return null;
}
protected DocumentImpl getRepoXML(File packageDir) throws PackageException {
// find and parse the repo.xml descriptor
final File repoFile = new File(packageDir, "repo.xml");
if (!repoFile.canRead())
{return null;}
try {
return DocUtils.parse(broker.getBrokerPool(), null, new FileInputStream(repoFile));
} catch (final XPathException e) {
throw new PackageException("Failed to parse repo.xml: " + e.getMessage(), e);
} catch (final FileNotFoundException e) {
throw new PackageException("Failed to read repo.xml: " + e.getMessage(), e);
}
}
public String installAndDeploy(File xar, PackageLoader loader) throws PackageException, IOException {
return installAndDeploy(xar, loader, true);
}
/**
* Install and deploy a give xar archive. Dependencies are installed from
* the PackageLoader.
*
* @param xar the .xar file to install
* @param loader package loader to use
* @param enforceDeps when set to true, the method will throw an exception if a dependency could not be resolved
* or an older version of the required dependency is installed and needs to be replaced.
*/
public String installAndDeploy(File xar, PackageLoader loader, boolean enforceDeps) throws PackageException, IOException {
final DocumentImpl document = getDescriptor(xar);
final ElementImpl root = (ElementImpl) document.getDocumentElement();
final String name = root.getAttribute("name");
final String pkgVersion = root.getAttribute("version");
final ExistRepository repo = broker.getBrokerPool().getExpathRepo();
final Packages packages = repo.getParentRepo().getPackages(name);
if (packages != null && (!enforceDeps || pkgVersion.equals(packages.latest().getVersion()))) {
LOG.info("Application package " + name + " already installed. Skipping.");
return null;
}
InMemoryNodeSet deps;
try {
deps = findElements(root, DEPENDENCY_ELEMENT);
for (final SequenceIterator i = deps.iterate(); i.hasNext(); ) {
final Element dependency = (Element) i.nextItem();
final String pkgName = dependency.getAttribute("package");
final String versionStr = dependency.getAttribute("version");
final String semVer = dependency.getAttribute("semver");
final String semVerMin = dependency.getAttribute("semver-min");
final String semVerMax = dependency.getAttribute("semver-max");
PackageLoader.Version version = null;
if (semVer != null) {
version = new PackageLoader.Version(semVer, true);
} else if (semVerMax != null || semVerMin != null) {
version = new PackageLoader.Version(semVerMin, semVerMax);
} else if (pkgVersion != null) {
version = new PackageLoader.Version(versionStr, false);
}
if (pkgName != null) {
LOG.info("Package " + name + " depends on " + pkgName);
boolean isInstalled = false;
if (repo.getParentRepo().getPackages(pkgName) != null) {
LOG.debug("Package " + pkgName + " already installed");
Packages pkgs = repo.getParentRepo().getPackages(pkgName);
// check if installed package matches required version
if (pkgs != null) {
if (version != null) {
Package latest = pkgs.latest();
DependencyVersion depVersion = version.getDependencyVersion();
if (depVersion.isCompatible(latest.getVersion())) {
isInstalled = true;
} else {
LOG.debug("Package " + pkgName + " needs to be upgraded");
if (enforceDeps) {
throw new PackageException("Package requires version " + version.toString() +
" of package " + pkgName +
". Installed version is " + latest.getVersion() + ". Please upgrade!");
}
}
} else {
isInstalled = true;
}
if (isInstalled) {
LOG.debug("Package " + pkgName + " already installed");
}
}
}
if (!isInstalled && loader != null) {
final File depFile = loader.load(pkgName, version);
if (depFile != null) {
installAndDeploy(depFile, loader);
} else {
if (enforceDeps) {
LOG.warn("Missing dependency: package " + pkgName + " could not be resolved. This error " +
"is not fatal, but the package may not work as expected");
} else {
throw new PackageException("Missing dependency: package " + pkgName + " could not be resolved.");
}
}
}
}
}
} catch (final XPathException e) {
throw new PackageException("Invalid descriptor found in " + xar.getAbsolutePath());
}
// installing the xar into the expath repo
LOG.info("Installing package " + xar.getAbsolutePath());
final UserInteractionStrategy interact = new BatchUserInteraction();
final org.expath.pkg.repo.Package pkg = repo.getParentRepo().installPackage(xar, true, interact);
final ExistPkgInfo info = (ExistPkgInfo) pkg.getInfo("exist");
if (info != null && !info.getJars().isEmpty())
{ClasspathHelper.updateClasspath(broker.getBrokerPool(), pkg);}
broker.getBrokerPool().getXQueryPool().clear();
final String pkgName = pkg.getName();
// signal status
broker.getBrokerPool().reportStatus("Installing app: " + pkg.getAbbrev());
repo.reportAction(ExistRepository.Action.INSTALL, pkg.getName());
LOG.info("Deploying package " + pkgName);
return deploy(pkgName, repo, null);
}
public String undeploy(String pkgName, ExistRepository repo) throws PackageException {
final File packageDir = getPackageDir(pkgName, repo);
if (packageDir == null)
// fails silently if package dir is not found?
{return null;}
final DocumentImpl repoXML = getRepoXML(packageDir);
final Package pkg = getPackage(pkgName, repo);
if (repoXML != null) {
ElementImpl target = null;
try {
target = findElement(repoXML, TARGET_COLL_ELEMENT);
final ElementImpl cleanup = findElement(repoXML, CLEANUP_ELEMENT);
if (cleanup != null) {
runQuery(null, packageDir, cleanup.getStringValue(), false);
}
uninstall(pkg, target);
return target == null ? null : target.getStringValue();
} catch (final XPathException e) {
throw new PackageException("Error found while processing repo.xml: " + e.getMessage(), e);
} catch (final IOException e) {
throw new PackageException("Error found while processing repo.xml: " + e.getMessage(), e);
}
} else {
// we still may need to remove the copy of the package from /db/system/repo
uninstall(pkg, null);
}
return null;
}
public String deploy(String pkgName, ExistRepository repo, String userTarget) throws PackageException, IOException {
final File packageDir = getPackageDir(pkgName, repo);
if (packageDir == null)
{throw new PackageException("Package not found: " + pkgName);}
final DocumentImpl repoXML = getRepoXML(packageDir);
if (repoXML == null)
{return null;}
try {
// if there's a <setup> element, run the query it points to
final ElementImpl setup = findElement(repoXML, SETUP_ELEMENT);
String path = setup == null ? null : setup.getStringValue();
if (path != null && path.length() > 0) {
runQuery(null, packageDir, path, true);
return null;
} else {
// otherwise copy all child directories to the target collection
XmldbURI targetCollection = null;
if (userTarget != null) {
try {
targetCollection = XmldbURI.create(userTarget);
} catch (final Exception e) {
throw new PackageException("Bad collection URI: " + userTarget, e);
}
} else {
final ElementImpl target = findElement(repoXML, TARGET_COLL_ELEMENT);
if (target != null) {
final String targetPath = target.getStringValue();
if (targetPath.length() > 0) {
// determine target collection
try {
targetCollection = XmldbURI.create(getTargetCollection(targetPath));
} catch (final Exception e) {
throw new PackageException("Bad collection URI for <target> element: " + target.getStringValue(), e);
}
}
}
}
if (targetCollection == null) {
// no target means: package does not need to be deployed into database
// however, we need to preserve a copy for backup purposes
final Package pkg = getPackage(pkgName, repo);
final String pkgColl = pkg.getAbbrev() + "-" + pkg.getVersion();
targetCollection = XmldbURI.SYSTEM.append("repo/" + pkgColl);
}
final ElementImpl permissions = findElement(repoXML, PERMISSIONS_ELEMENT);
if (permissions != null) {
// get user, group and default permissions
user = permissions.getAttribute("user");
group = permissions.getAttribute("group");
password = permissions.getAttribute("password");
String mode = permissions.getAttribute("mode");
try {
perms = Integer.parseInt(mode, 8);
} catch (final NumberFormatException e) {
permsStr = mode;
if (!permsStr.matches("^[rwx-]{9}"))
{throw new PackageException("Bad format for mode attribute in <permissions>: " + mode);}
}
}
// run the pre-setup query if present
final ElementImpl preSetup = findElement(repoXML, PRE_SETUP_ELEMENT);
if (preSetup != null) {
path = preSetup.getStringValue();
if (path.length() > 0)
{runQuery(targetCollection, packageDir, path, true);}
}
// any required users and group should have been created by the pre-setup query.
// check for invalid users now.
checkUserSettings();
// install
scanDirectory(packageDir, targetCollection, true);
// run the post-setup query if present
final ElementImpl postSetup = findElement(repoXML, POST_SETUP_ELEMENT);
if (postSetup != null) {
path = postSetup.getStringValue();
if (path.length() > 0)
{runQuery(targetCollection, packageDir, path, false);}
}
storeRepoXML(repoXML, targetCollection);
// TODO: it should be save to clean up the file system after a package
// has been deployed. Might be enabled after 2.0
//cleanup(pkgName, repo);
return targetCollection.getCollectionPath();
}
} catch (final XPathException e) {
throw new PackageException("Error found while processing repo.xml: " + e.getMessage(), e);
}
}
/**
* After deployment, clean up the package directory and remove all files which have been
* stored into the db. They are not needed anymore. Only preserve the descriptors and the
* contents directory.
*
* @param pkgName
* @param repo
* @throws PackageException
*/
private void cleanup(String pkgName, ExistRepository repo) throws PackageException {
final Package pkg = getPackage(pkgName, repo);
final String abbrev = pkg.getAbbrev();
final File packageDir = getPackageDir(pkg);
if (packageDir == null) {
throw new PackageException("Cleanup: package dir for package " + pkgName + " not found");
}
File[] filesToDelete = packageDir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
String name = file.getName();
if (file.isDirectory()) {
return !(name.equals(abbrev) || name.equals("content"));
} else {
return !(name.equals("expath-pkg.xml") || name.equals("repo.xml") ||
"exist.xml".equals(name) || name.startsWith("icon"));
}
}
});
for (final File fileToDelete : filesToDelete) {
try {
FileUtils.forceDelete(fileToDelete);
} catch (final IOException e) {
LOG.warn("Cleanup: failed to delete file " + fileToDelete.getAbsolutePath() + " in package " +
pkgName);
}
}
}
private String getTargetCollection(String targetFromRepo) {
final String appRoot = (String) broker.getConfiguration().getProperty(PROPERTY_APP_ROOT);
if (appRoot != null) {
if (targetFromRepo.startsWith("/db/")) {
targetFromRepo = targetFromRepo.substring(4);
}
return appRoot + targetFromRepo;
}
if (targetFromRepo.startsWith("/db")) {
return targetFromRepo;
} else {
return "/db/" + targetFromRepo;
}
}
/**
* Delete the target collection of the package. If there's no repo.xml descriptor,
* target will be null.
*
* @param pkg
* @param target
* @throws PackageException
*/
private void uninstall(Package pkg, ElementImpl target)
throws PackageException {
// determine target collection
XmldbURI targetCollection;
if (target == null || target.getStringValue().length() == 0) {
final String pkgColl = pkg.getAbbrev() + "-" + pkg.getVersion();
targetCollection = XmldbURI.SYSTEM.append("repo/" + pkgColl);
} else {
final String targetPath = target.getStringValue();
try {
targetCollection = XmldbURI.create(getTargetCollection(targetPath));
} catch (final Exception e) {
throw new PackageException("Bad collection URI for <target> element: " + targetPath);
}
}
final TransactionManager mgr = broker.getBrokerPool().getTransactionManager();
final Txn txn = mgr.beginTransaction();
try {
Collection collection = broker.getOrCreateCollection(txn, targetCollection);
if (collection != null)
{broker.removeCollection(txn, collection);}
if (target != null) {
final XmldbURI configCollection = XmldbURI.CONFIG_COLLECTION_URI.append(targetCollection);
collection = broker.getOrCreateCollection(txn, configCollection);
if (collection != null)
{broker.removeCollection(txn, collection);}
}
mgr.commit(txn);
} catch (final Exception e) {
LOG.error("Exception occurred while removing package.", e);
mgr.abort(txn);
} finally {
mgr.close(txn);
}
}
/**
* Store repo.xml into the db. Adds the time of deployment to the descriptor.
*
* @param repoXML
* @param targetCollection
* @throws XPathException
*/
private void storeRepoXML(DocumentImpl repoXML, XmldbURI targetCollection)
throws PackageException, XPathException {
// Store repo.xml
final DateTimeValue time = new DateTimeValue(new Date());
final MemTreeBuilder builder = new MemTreeBuilder();
builder.startDocument();
final UpdatingDocumentReceiver receiver = new UpdatingDocumentReceiver(builder, time.getStringValue());
try {
repoXML.copyTo(broker, receiver);
} catch (final SAXException e) {
throw new PackageException("Error while updating repo.xml: " + e.getMessage());
}
builder.endDocument();
final DocumentImpl updatedXML = builder.getDocument();
final TransactionManager mgr = broker.getBrokerPool().getTransactionManager();
final Txn txn = mgr.beginTransaction();
try {
final Collection collection = broker.getOrCreateCollection(txn, targetCollection);
final XmldbURI name = XmldbURI.createInternal("repo.xml");
final IndexInfo info = collection.validateXMLResource(txn, broker, name, updatedXML);
final Permission permission = info.getDocument().getPermissions();
setPermissions(false, MimeType.XML_TYPE, permission);
collection.store(txn, broker, info, updatedXML, false);
mgr.commit(txn);
} catch (final Exception e) {
mgr.abort(txn);
} finally {
mgr.close(txn);
}
}
private void checkUserSettings() throws PackageException {
final org.exist.security.SecurityManager secman = broker.getBrokerPool().getSecurityManager();
try {
if (group != null && !secman.hasGroup(group)) {
final GroupAider aider = new GroupAider(group);
secman.addGroup(aider);
}
if (user != null && !secman.hasAccount(user)) {
final UserAider aider = new UserAider(user);
aider.setPassword(password);
if (group != null)
{aider.addGroup(group);}
secman.addAccount(aider);
}
} catch (final ConfigurationException e) {
throw new PackageException("Failed to create user: " + user, e);
} catch (final PermissionDeniedException e) {
throw new PackageException("Failed to create user: " + user, e);
} catch (final EXistException e) {
throw new PackageException("Failed to create user: " + user, e);
}
}
private Sequence runQuery(XmldbURI targetCollection, File tempDir, String fileName, boolean preInstall)
throws PackageException, IOException, XPathException {
final File xquery = new File(tempDir, fileName);
if (!xquery.canRead()) {
LOG.warn("The XQuery resource specified in the <setup> element was not found");
return Sequence.EMPTY_SEQUENCE;
}
final XQuery xqs = broker.getXQueryService();
final XQueryContext ctx = xqs.newContext(AccessContext.REST);
ctx.declareVariable("dir", tempDir.getAbsolutePath());
final File home = broker.getConfiguration().getExistHome();
ctx.declareVariable("home", home.getAbsolutePath());
if (targetCollection != null) {
ctx.declareVariable("target", targetCollection.toString());
ctx.setModuleLoadPath(XmldbURI.EMBEDDED_SERVER_URI + targetCollection.toString());
} else
{ctx.declareVariable("target", Sequence.EMPTY_SEQUENCE);}
if (preInstall)
// when running pre-setup scripts, base path should point to directory
// because the target collection does not yet exist
{ctx.setModuleLoadPath(tempDir.getAbsolutePath());}
CompiledXQuery compiled;
try {
compiled = xqs.compile(ctx, new FileSource(xquery, "UTF-8", false));
return xqs.execute(compiled, null);
} catch (final PermissionDeniedException e) {
throw new PackageException(e.getMessage(), e);
}
}
/**
* Scan a directory and import all files and sub directories into the target
* collection.
*
* @param directory
* @param target
*/
private void scanDirectory(File directory, XmldbURI target, boolean inRootDir) {
final TransactionManager mgr = broker.getBrokerPool().getTransactionManager();
final Txn txn = mgr.beginTransaction();
Collection collection = null;
try {
collection = broker.getOrCreateCollection(txn, target);
setPermissions(true, null, collection.getPermissionsNoLock());
broker.saveCollection(txn, collection);
mgr.commit(txn);
} catch (final Exception e) {
mgr.abort(txn);
} finally {
mgr.close(txn);
}
try {
// lock the collection while we store the files
// TODO: could be released after each operation
collection.getLock().acquire(Lock.WRITE_LOCK);
storeFiles(directory, collection, inRootDir);
} catch (final LockException e) {
e.printStackTrace();
} finally {
collection.getLock().release(Lock.WRITE_LOCK);
}
// scan sub directories
final File[] files = directory.listFiles();
for (final File file : files) {
if (file.isDirectory()) {
scanDirectory(file, target.append(file.getName()), false);
}
}
}
/**
* Import all files in the given directory into the target collection
*
* @param directory
* @param targetCollection
*/
private void storeFiles(File directory, Collection targetCollection, boolean inRootDir) {
final File[] files = directory.listFiles();
final MimeTable mimeTab = MimeTable.getInstance();
final TransactionManager mgr = broker.getBrokerPool().getTransactionManager();
for (final File file : files) {
if (inRootDir && "repo.xml".equals(file.getName()))
{continue;}
if (!file.isDirectory()) {
MimeType mime = mimeTab.getContentTypeFor(file.getName());
if (mime == null)
{mime = MimeType.BINARY_TYPE;}
final XmldbURI name = XmldbURI.create(file.getName());
final Txn txn = mgr.beginTransaction();
try {
if (mime.isXMLType()) {
final InputSource is = new InputSource(file.toURI().toASCIIString());
final IndexInfo info = targetCollection.validateXMLResource(txn, broker, name, is);
info.getDocument().getMetadata().setMimeType(mime.getName());
final Permission permission = info.getDocument().getPermissions();
setPermissions(false, mime, permission);
targetCollection.store(txn, broker, info, is, false);
} else {
final long size = file.length();
final FileInputStream is = new FileInputStream(file);
final BinaryDocument doc =
targetCollection.addBinaryResource(txn, broker, name, is, mime.getName(), size);
is.close();
final Permission permission = doc.getPermissions();
setPermissions(false, mime, permission);
doc.getMetadata().setMimeType(mime.getName());
broker.storeXMLResource(txn, doc);
}
mgr.commit(txn);
} catch (final Exception e) {
mgr.abort(txn);
e.printStackTrace();
} finally {
mgr.close(txn);
}
}
}
}
/**
* Set owner, group and permissions. For XQuery resources, always set the executable flag.
* @param mime
* @param permission
*/
private void setPermissions(boolean isCollection, MimeType mime, Permission permission) throws PermissionDeniedException {
if (user != null){
permission.setOwner(user);
}
if (group != null){
permission.setGroup(group);
}
int mode;
if (permsStr != null) {
try {
permission.setMode(permsStr);
mode = permission.getMode();
} catch (final SyntaxException e) {
LOG.warn("Incorrect permissions string: " + permsStr + ". Falling back to default.");
mode = permission.getMode();
}
} else if (perms > -1) {
mode = perms;
} else {
mode = permission.getMode();
}
if (isCollection || (mime != null && mime.getName().equals(MimeType.XQUERY_TYPE.getName()))) {
mode = mode | 0111;
}
permission.setMode(mode);
}
private ElementImpl findElement(NodeImpl root, QName qname) throws XPathException {
final InMemoryNodeSet setupNodes = new InMemoryNodeSet();
root.selectDescendants(false, new NameTest(Type.ELEMENT, qname), setupNodes);
if (setupNodes.getItemCount() == 0)
{return null;}
return (ElementImpl) setupNodes.itemAt(0);
}
private InMemoryNodeSet findElements(NodeImpl root, QName qname) throws XPathException {
final InMemoryNodeSet setupNodes = new InMemoryNodeSet();
root.selectDescendants(false, new NameTest(Type.ELEMENT, qname), setupNodes);
return setupNodes;
}
public String getNameFromDescriptor(File xar) throws IOException, PackageException {
final DocumentImpl doc = getDescriptor(xar);
final Element root = doc.getDocumentElement();
return root.getAttribute("name");
}
public DocumentImpl getDescriptor(File jar) throws IOException, PackageException {
final InputStream istream = new BufferedInputStream(new FileInputStream(jar));
final JarInputStream jis = new JarInputStream(istream);
JarEntry entry;
DocumentImpl doc = null;
while ((entry = jis.getNextJarEntry()) != null) {
if (!entry.isDirectory() && "expath-pkg.xml".equals(entry.getName())) {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
int c;
final byte[] b = new byte[4096];
while ((c = jis.read(b)) > 0) {
bos.write(b, 0, c);
}
bos.close();
final byte[] data = bos.toByteArray();
final ByteArrayInputStream bis = new ByteArrayInputStream(data);
try {
doc = DocUtils.parse(broker.getBrokerPool(), null, bis);
} catch (final XPathException e) {
throw new PackageException("Error while parsing expath-pkg.xml: " + e.getMessage(), e);
}
break;
}
}
jis.close();
return doc;
}
/**
* Update repo.xml while copying it. For security reasons, make sure
* any default password is removed before uploading.
*/
private class UpdatingDocumentReceiver extends DocumentBuilderReceiver {
private String time;
private Stack<String> stack = new Stack<String>();
public UpdatingDocumentReceiver( MemTreeBuilder builder, String time )
{
super( builder, false );
this.time = time;
}
@Override
public void startElement(QName qname, AttrList attribs) {
stack.push(qname.getLocalName());
AttrList newAttrs = attribs;
if (attribs != null && "permissions".equals(qname.getLocalName())) {
newAttrs = new AttrList();
for (int i = 0; i < attribs.getLength(); i++) {
if (!"password". equals(attribs.getQName(i).getLocalName())) {
newAttrs.addAttribute(attribs.getQName(i), attribs.getValue(i), attribs.getType(i));
}
}
}
if (!"deployed".equals(qname.getLocalName())) {
super.startElement(qname, newAttrs);
}
}
@Override
public void startElement(String namespaceURI, String localName,
String qName, Attributes attrs) throws SAXException {
stack.push(localName);
if (!"deployed".equals(localName))
{super.startElement(namespaceURI, localName, qName, attrs);}
}
@Override
public void endElement(QName qname) throws SAXException {
stack.pop();
if ("meta".equals(qname.getLocalName())) {
addDeployTime();
}
if (!"deployed".equals(qname.getLocalName())) {
super.endElement(qname);
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
stack.pop();
if ("meta".equals(localName)) {
addDeployTime();
}
if (!"deployed".equals(localName)) {
super.endElement(uri, localName, qName);
}
}
@Override
public void attribute(QName qname, String value) throws SAXException {
final String current = stack.peek();
if (!("permissions".equals(current) && "password".equals(qname.getLocalName()))) {
super.attribute(qname, value);
}
}
@Override
public void characters(char[] ch, int start, int len)
throws SAXException {
final String current = stack.peek();
if (!"deployed".equals(current))
{super.characters(ch, start, len);}
}
@Override
public void characters(CharSequence seq) throws SAXException {
final String current = stack.peek();
if (!"deployed".equals(current))
{super.characters(seq);}
}
private void addDeployTime() throws SAXException {
super.startElement(DEPLOYED_ELEMENT, null);
super.characters(time);
super.endElement(DEPLOYED_ELEMENT);
}
}
}