/* * eXist Open Source Native XML Database * Copyright (C) 2001-2011 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 program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.backup; import org.exist.Namespaces; import org.exist.security.Permission; import org.exist.storage.DBBroker; import org.exist.storage.NativeBroker; import org.exist.storage.serializers.EXistOutputKeys; import org.exist.util.serializer.SAXSerializer; import org.exist.util.serializer.SerializerPool; import org.exist.xmldb.CollectionImpl; import org.exist.xmldb.EXistResource; import org.exist.xmldb.ExtendedResource; import org.exist.xmldb.UserManagementService; import org.exist.xmldb.XmldbURI; import org.exist.xquery.util.URIUtils; import org.exist.xquery.value.DateTimeValue; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.xmldb.api.DatabaseManager; import org.xmldb.api.base.Collection; import org.xmldb.api.base.Database; import org.xmldb.api.base.Resource; import org.xmldb.api.base.XMLDBException; import org.xmldb.api.modules.XMLResource; import javax.swing.*; import javax.xml.transform.OutputKeys; import java.awt.*; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Arrays; import java.util.Date; import java.util.Properties; public class Backup { private String target; private XmldbURI rootCollection; private String user; private String pass; private static final int currVersion = 1; public Properties defaultOutputProperties = new Properties(); { defaultOutputProperties.setProperty(OutputKeys.INDENT, "no"); defaultOutputProperties.setProperty(OutputKeys.ENCODING, "UTF-8"); defaultOutputProperties.setProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); defaultOutputProperties.setProperty(EXistOutputKeys.EXPAND_XINCLUDES, "no"); defaultOutputProperties.setProperty(EXistOutputKeys.PROCESS_XSL_PI, "no"); } public Properties contentsOutputProps = new Properties(); { contentsOutputProps.setProperty(OutputKeys.INDENT, "yes"); } public Backup(String user, String pass, String target, XmldbURI rootCollection) { this.user = user; this.pass = pass; this.target = target; this.rootCollection = rootCollection; } public Backup(String user, String pass, String target) { this(user, pass, target, XmldbURI.create("xmldb:exist://" + DBBroker.ROOT_COLLECTION)); } public Backup(String user, String pass, String target, XmldbURI rootCollection, Properties property) { this(user, pass, target, rootCollection); this.defaultOutputProperties.setProperty(OutputKeys.INDENT, property.getProperty("indent","no")); } public static String encode(String enco) { StringBuilder out = new StringBuilder(); char t; for (int y=0; y < enco.length(); y++) { t= enco.charAt(y); if (t == '"') { out.append("&22;"); } else if (t == '&') { out.append("&26;"); } else if (t == '*') { out.append("&2A;"); } else if (t ==':') { out.append("&3A;"); } else if (t =='<') { out.append("&3C;"); } else if (t =='>') { out.append("&3E;"); } else if (t =='?') { out.append("&3F;"); } else if (t =='\\') { out.append("&5C;"); } else if (t =='|') { out.append("&7C;"); } else { out.append(t); } } return out.toString(); } public static String decode(String enco) { StringBuilder out = new StringBuilder(); String temp=""; char t; for (int y=0; y < enco.length(); y++) { t= enco.charAt(y); if (t != '&') { out.append(t); } else { temp = enco.substring(y,y+4); if (temp.equals("&22;")) { out.append('"'); } else if (temp.equals("&26;")) { out.append('&'); } else if (temp.equals("&2A;")) { out.append('*'); } else if (temp.equals("&3A;")) { out.append(':'); } else if (temp.equals("&3C;")) { out.append('<'); } else if (temp.equals("&3E;")) { out.append(">"); } else if (temp.equals("&3F;")) { out.append('?'); } else if (temp.equals("&5C;")) { out.append('\\'); } else if (temp.equals("&7C;")) { out.append('|'); } else { } y=y+3; } } return out.toString(); } public void backup(boolean guiMode, JFrame parent) throws XMLDBException, IOException, SAXException { Collection current = DatabaseManager.getCollection(rootCollection.toString(), user, pass); if (guiMode) { BackupDialog dialog = new BackupDialog(parent, false); dialog.setSize(new Dimension(350, 150)); dialog.setVisible(true); BackupThread thread = new BackupThread(current, dialog); thread.start(); if(parent == null) { // if backup runs as a single dialog, wait for it (or app will terminate) while (thread.isAlive()) { synchronized (this) { try { wait(20); } catch (InterruptedException e) { } } } } } else backup(current, null); } private void backup(Collection current, BackupDialog dialog) throws XMLDBException, IOException, SAXException { String cname = current.getName(); if (cname.charAt(0) != '/') cname = "/" + cname; String path = target + encode(URIUtils.urlDecodeUtf8(cname)); BackupWriter output; if (target.endsWith(".zip")) output = new ZipWriter(target, encode(URIUtils.urlDecodeUtf8(cname))); else output = new FileSystemWriter(path); backup(current, output, dialog); output.close(); } private void backup(Collection current, BackupWriter output, BackupDialog dialog) throws XMLDBException, IOException, SAXException { if (current == null) return; current.setProperty(OutputKeys.ENCODING, defaultOutputProperties.getProperty(OutputKeys.ENCODING)); current.setProperty(OutputKeys.INDENT, defaultOutputProperties.getProperty(OutputKeys.INDENT)); current.setProperty(EXistOutputKeys.EXPAND_XINCLUDES, defaultOutputProperties.getProperty(EXistOutputKeys.EXPAND_XINCLUDES)); current.setProperty(EXistOutputKeys.PROCESS_XSL_PI, defaultOutputProperties.getProperty(EXistOutputKeys.PROCESS_XSL_PI)); // get resources and permissions String[] resources = current.listResources(); Arrays.sort(resources); UserManagementService mgtService = (UserManagementService) current.getService("UserManagementService", "1.0"); Permission perms[] = mgtService.listResourcePermissions(); Permission currentPerms = mgtService.getPermissions(current); if (dialog != null) { dialog.setCollection(current.getName()); dialog.setResourceCount(resources.length); } Writer contents = output.newContents(); // serializer writes to __contents__.xml SAXSerializer serializer = (SAXSerializer) SerializerPool.getInstance().borrowObject(SAXSerializer.class); serializer.setOutput(contents, contentsOutputProps); serializer.startDocument(); serializer.startPrefixMapping("", Namespaces.EXIST_NS); // write <collection> element CollectionImpl cur = (CollectionImpl)current; AttributesImpl attr = new AttributesImpl(); //The name should have come from an XmldbURI.toString() call attr.addAttribute(Namespaces.EXIST_NS, "name", "name", "CDATA", current.getName()); attr.addAttribute(Namespaces.EXIST_NS, "owner", "owner", "CDATA", currentPerms.getOwner()); attr.addAttribute(Namespaces.EXIST_NS, "group", "group", "CDATA", currentPerms.getOwnerGroup()); attr.addAttribute( Namespaces.EXIST_NS, "mode", "mode", "CDATA", Integer.toOctalString(currentPerms.getPermissions())); attr.addAttribute( Namespaces.EXIST_NS, "created", "created", "CDATA", ""+new DateTimeValue(cur.getCreationTime())); attr.addAttribute(Namespaces.EXIST_NS, "version", "version", "CDATA", String.valueOf(currVersion)); serializer.startElement(Namespaces.EXIST_NS, "collection", "collection", attr); // scan through resources Resource resource; OutputStream os; BufferedWriter writer; SAXSerializer contentSerializer; for (int i = 0; i < resources.length; i++) { try { if (resources[i].equals("__contents__.xml")) { //Skipping resources[i] continue; } resource = current.getResource(resources[i]); if (dialog != null) { dialog.setResource(resources[i]); dialog.setProgress(i); } os = output.newEntry(encode(URIUtils.urlDecodeUtf8(resources[i]))); if(resource instanceof ExtendedResource) { ((ExtendedResource)resource).getContentIntoAStream(os); } else { writer = new BufferedWriter( new OutputStreamWriter(os, "UTF-8")); // write resource to contentSerializer contentSerializer = (SAXSerializer) SerializerPool.getInstance().borrowObject(SAXSerializer.class); contentSerializer.setOutput(writer, defaultOutputProperties); ((EXistResource)resource).setLexicalHandler(contentSerializer); ((XMLResource)resource).getContentAsSAX(contentSerializer); SerializerPool.getInstance().returnObject(contentSerializer); writer.flush(); } output.closeEntry(); EXistResource ris = (EXistResource)resource; //store permissions attr.clear(); attr.addAttribute(Namespaces.EXIST_NS, "type", "type", "CDATA", resource.getResourceType()); attr.addAttribute(Namespaces.EXIST_NS, "name", "name", "CDATA", resources[i]); attr.addAttribute(Namespaces.EXIST_NS, "owner", "owner", "CDATA", perms[i].getOwner()); attr.addAttribute(Namespaces.EXIST_NS, "group", "group", "CDATA", perms[i].getOwnerGroup()); attr.addAttribute( Namespaces.EXIST_NS, "mode", "mode", "CDATA", Integer.toOctalString(perms[i].getPermissions())); Date date = ris.getCreationTime(); if (date != null) attr.addAttribute( Namespaces.EXIST_NS, "created", "created", "CDATA", ""+new DateTimeValue(date)); date = ris.getLastModificationTime(); if (date != null) attr.addAttribute( Namespaces.EXIST_NS, "modified", "modified", "CDATA", ""+new DateTimeValue(date)); attr.addAttribute( Namespaces.EXIST_NS, "filename", "filename", "CDATA", encode( URIUtils.urlDecodeUtf8(resources[i]) ) ); attr.addAttribute( Namespaces.EXIST_NS, "mimetype", "mimetype", "CDATA", encode( ((EXistResource)resource).getMimeType()) ); if (!resource.getResourceType().equals("BinaryResource")) { if (ris.getDocType() != null) { if (ris.getDocType().getName() != null) { attr.addAttribute(Namespaces.EXIST_NS, "namedoctype", "namedoctype", "CDATA", ris.getDocType().getName()); } if (ris.getDocType().getPublicId() != null) { attr.addAttribute(Namespaces.EXIST_NS, "publicid", "publicid", "CDATA", ris.getDocType().getPublicId()); } if (ris.getDocType().getSystemId() != null) { attr.addAttribute(Namespaces.EXIST_NS, "systemid", "systemid", "CDATA", ris.getDocType().getSystemId()); } } } serializer.startElement(Namespaces.EXIST_NS, "resource", "resource", attr); serializer.endElement(Namespaces.EXIST_NS, "resource", "resource"); } catch(XMLDBException e) { System.err.println("Failed to backup resource " + resources[i] + " from collection " + current.getName()); throw e; } } // write subcollections String[] collections = current.listChildCollections(); for (int i = 0; i < collections.length; i++) { if (current.getName().equals(NativeBroker.SYSTEM_COLLECTION) && collections[i].equals("temp")) continue; attr.clear(); attr.addAttribute(Namespaces.EXIST_NS, "name", "name", "CDATA", collections[i]); attr.addAttribute(Namespaces.EXIST_NS, "filename", "filename", "CDATA", encode(URIUtils.urlDecodeUtf8(collections[i]))); serializer.startElement(Namespaces.EXIST_NS, "subcollection", "subcollection", attr); serializer.endElement(Namespaces.EXIST_NS, "subcollection", "subcollection"); } // close <collection> serializer.endElement(Namespaces.EXIST_NS, "collection", "collection"); serializer.endPrefixMapping(""); serializer.endDocument(); output.closeContents(); SerializerPool.getInstance().returnObject(serializer); // descend into subcollections Collection child; for (int i = 0; i < collections.length; i++) { child = current.getChildCollection(collections[i]); if (child.getName().equals(NativeBroker.TEMP_COLLECTION)) continue; output.newCollection(encode(URIUtils.urlDecodeUtf8(collections[i]))); backup(child, output, dialog); output.closeCollection(); } } class BackupThread extends Thread { Collection collection_; BackupDialog dialog_; public BackupThread(Collection collection, BackupDialog dialog) { super(); collection_ = collection; dialog_ = dialog; } public void run() { try { backup(collection_, dialog_); dialog_.setVisible(false); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String args[]) { try { Class cl = Class.forName("org.exist.xmldb.DatabaseImpl"); Database database = (Database) cl.newInstance(); database.setProperty("create-database", "true"); DatabaseManager.registerDatabase(database); Backup backup = new Backup("admin", null, "backup", URIUtils.encodeXmldbUriFor(args[0])); backup.backup(false, null); } catch (Throwable e) { e.printStackTrace(); System.exit(0); } } }