CALL BY: Pierre van Rooden CALL FOR: Applications 1 Full Backup export extension
Apps 1 currently has only one way to export data, the contextdepth writer. This hack proposes to add a second way to export data: the 'full bacjup'.
Full Backup exports all nodes in a mmabs ecloud, excluding a number of builders whose content is generated (such as syncnodes, versions, icaches, etc).
The module was written by Rob van Maris and offered for publication with his consent.
We used in in our own environment to export data from a testsite to production, and it werks pretty smooth. 'Full Backup' only exports.
Proposal is to include the full backup in the 1.7 release.
In 1.8, we expect to see an applications 2. However, full backup is, imo, so useful that is worthwhile to add this even though Applications 1 may be invalidated after the next (1.7) major release.
To test full backup, compile the files included in MMBase 1.7. Add a application whose contextdource bit raeds:
<contextsourcelist> <contextsource path="my_app/backup.xml" type="full" goal="backup"/> </contextsourcelist>
The content of the rest of the xml file is ignored, though you may need to supply some tags to satisfy the dtd (I myself just included it in our application file).
Note: due to a bug in the application 1 code, you cannot provide more than one contextsource. This is a bug and should be fixed if this hack succeeds.
START OF CALL: 14/11/2003 12:00
END OF CALL: 19/11/2003 12:00
[_] +1 (YEA)
[_] +0 (ABSTAIN )
[_] -1 (NAY), because :
[_] VETO, because:
-- Pierre van Rooden Mediapark, C 107 tel. +31 (0)35 6772815 "Never summon anything bigger than your head."
/*
This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. The license (Mozilla version 1.0) can be read at the MMBase site. See http://www.MMBase.org/license */ package org.mmbase.util; import java.io.*; import java.sql.SQLException; import java.util.*; import org.mmbase.module.core.*; import org.mmbase.util.*; import org.mmbase.util.logging.*; import org.mmbase.module.corebuilders.*; /** * This is used to export a full backup, by writing all nodes to XML. * Based on [EMAIL PROTECTED] org.mmbase.util.XMLContextDepthWriterII}. * * @author Rob van Maris * @see org.mmbase.util.XMLContextDepthWriterII */ public class XMLFullBackupWriter extends XMLContextDepthWriterII { /** * Logging instance */ private static Logger log = Logging.getLoggerInstance(XMLFullBackupWriter.class.getName()); /** * Writes all nodes to XML. * @param app A <code>XMLApplicationReader</code> initialised to read * the application's description (xml) file * @param targetpath The path where to save the application * @param mmb Reference to the MMbase processormodule. Used to retrieve the nodes to write. * @param resultmsgs Storage for messages which can be displayed to the user. * @return Returns true if succesful, false otherwise. */ public static boolean writeContext( XMLApplicationReader app, String targetpath, MMBase mmb, Vector resultmsgs) { try { // Create directory for data files. String subTargetPath = targetpath + "/" + app.getApplicationName() + "/"; File file = new File(subTargetPath); try { file.mkdirs(); } catch(Exception e) { log.error("Failed to create dir " + subTargetPath + ": " + e); } // Write the nodes writeNodes(subTargetPath, mmb, resultmsgs); resultmsgs.addElement("Full backup finished."); // // write DataSources // writeDataSources(app,nodes,targetpath,mmb,resultmsgs); // // write relationSources // writeRelationSources(app,relnodes,targetpath,mmb,resultmsgs); } catch (Exception e) { resultmsgs.addElement("Backup failed, exception: " + e); log.error("Backup failed: " + Logging.stackTrace(e)); } return true; } /** * Searches the MMBase cloud, colelcting all nodes (and corresponmding relation nodes) that belong to a specific * type, and which can be traced up to a certain depth of nodes to a starting node. * * @param startnodenr the number of the node to start with * @param maxdeoth the maximum depth a tree is traversed. A depth of 0 or less means only the sdtartnode is added. * A depth of one includes all teh nodes refernced by the startnode, etc. * Relation nodes are not counted when determining 'depth'. * @param fb a <code>HashSet</code> containing the set of types that are allowed for export * @param nodesdoneSet A <code>HashSet</code> which holds all nodes that are already 'done' or 'almost done'. this set is expanded in the method * nodes already in this set are skipped (optimization). After return, the set has been expanded * with all nodes found while traversing the cloud * @param mmb MMBase object used to retrieve builder information * @param resultmsgs * @todo update javadoc */ static void writeNodes(String subTargetPath, MMBase mmb, Vector resultmsgs) throws SQLException{ InsRel insrel = mmb.getInsRel(); // Get all loaded builders. Enumeration eBuilders = mmb.getMMObjects(); // For each builder, add its nodes to the sets. while (eBuilders.hasMoreElements()) { MMObjectBuilder builder = (MMObjectBuilder) eBuilders.nextElement(); // Skip virtual builders if (builder.isVirtual()) { continue; } // Skip nodes of these builders: if ( builder.getTableName().equals("reldef") || builder.getTableName().equals("typerel") || builder.getTableName().equals("versions") || builder.getTableName().equals("syncnodes") || builder.getTableName().equals("daymarks") || builder.getTableName().equals("oalias") || builder.getTableName().equals("icaches") // || builder.getTableName().equals("typedef") // || builder.getTableName().equals("object") // || builder.getTableName().equals("mmservers") ) { continue; } boolean isRelation = builder == insrel || builder.isExtensionOf(insrel); // Add this builder's nodes to set (by nodenumber). List nodes = builder.searchList("otype==" + builder.getObjectType()); writeNodes(subTargetPath, mmb, resultmsgs, builder, nodes, isRelation); } } /** * Writes the nodes of a particular type to the corresponding XML file. * @param builder The builder. * @param nodes The nodes, must type corresponding to the builder. * @param subTargetPath Path where the XML file is written. * @param mmb MMBase object used to retrieve builder information * @param resultmsgs Used to store messages that can be showmn to the user * @param isRelation Indicates whether the nodes to write are data (false) or relation (true) nodes */ static void writeNodes(String subTargetPath, MMBase mmb, Vector resultmsgs, MMObjectBuilder builder, List nodes, boolean isRelation) { // Create nodewriter for this builder NodeWriter nodeWriter = new NodeWriter(mmb, resultmsgs, subTargetPath, builder.getTableName(), isRelation); Iterator iNodes = nodes.iterator(); int nrWritten = 0; while (iNodes.hasNext()) { MMObjectNode node = (MMObjectNode) iNodes.next(); // Write node if it's of the correct type. if (node.parent == builder) { nodeWriter.write(node); nrWritten++; } } nodeWriter.done(); log.debug("Builder " + builder.getTableName() + ": " + nrWritten + (isRelation? " relations": " nodes") + " written."); } }
/* This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. The license (Mozilla version 1.0) can be read at the MMBase site. See http://www.MMBase.org/license */ package org.mmbase.util; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.*; import org.mmbase.util.xml.BuilderWriter; import org.mmbase.module.core.MMBase; import org.mmbase.module.core.MMBaseContext; import org.mmbase.module.core.MMObjectBuilder; import org.mmbase.util.logging.Logger; import org.mmbase.util.logging.Logging; /** * * @javadoc * @move org.mmbase.util.xml * @deprecation-used Can use Xerces functionality to write an XML, isn't it? * @version $Id: XMLApplicationWriter.java,v 1.23 2003/04/10 07:59:42 pierre Exp $ */ public class XMLApplicationWriter { private static Logger log = Logging.getLoggerInstance(XMLApplicationWriter.class.getName()); public static Vector writeXMLFile(XMLApplicationReader app, String targetpath, String goal, MMBase mmb) { Vector resultmsgs=new Vector(); // again this is a stupid class generating the xml file // the second part called the extractor is kind of neat // but very in early beta String name = app.getApplicationName(); String maintainer = app.getApplicationMaintainer(); int version = app.getApplicationVersion(); boolean deploy = app.getApplicationAutoDeploy(); String body = "<?xml version=\"1.0\"?>\n" + "<!DOCTYPE application PUBLIC \"-//MMBase/DTD application config 1.0//EN\" \"http://www.mmbase.org/dtd/application_1_0.dtd\">\n" + "<application name=\"" + name + "\" maintainer=\"" + maintainer + "\" version=\"" + version + "\" auto-deploy=\"" + deploy + "\">\n"; // write the needed builders body+=getRequirements(app); // write the needed builders body+=getNeededBuilders(app); // write the needed reldefs body+=getNeededRelDefs(app); // write the allowed relations body+=getAllowedRelations(app); // write the datasources body+=getDataSources(app); // write the relationsources body+=getRelationSources(app); // write the contextsources body+=getContextSources(app); // write the description body+=getDescription(app); // write the install-notice body+=getInstallNotice(app); // close the application file body+="</application>\n"; saveFile(targetpath+"/"+app.getApplicationName()+".xml",body); // now the tricky part starts figure out what nodes to write writeDateSources(app,targetpath,mmb,resultmsgs); // now write the context files itself writeContextSources(app,targetpath); // now as a backup write all the needed builders // that the application maker claimed we needed writeBuilders(app,targetpath,mmb); resultmsgs.addElement("Writing Application file : "+targetpath+"/"+app.getApplicationName()+".xml"); return resultmsgs; } static String getRequirements(XMLApplicationReader app) { String body="\t<requirements>\n"; List apps=app.getRequirements(); for (Iterator i=apps.iterator();i.hasNext();) { Map bset=(Map)i.next(); String name=(String)bset.get("name"); String maintainer=(String)bset.get("maintainer"); String version=(String)bset.get("version"); String type=(String)bset.get("type"); if (type==null) type="application"; body+="\t\t<requires type=\""+type+"\" name=\""+name+"\""; if (maintainer!=null) body+=" maintainer=\""+maintainer+"\""; if (version!=null) body+=" version=\""+version+"\""; body+=" />\n"; } body+="\t</requirements>\n\n"; return body; } static String getNeededBuilders(XMLApplicationReader app) { String body="\t<neededbuilderlist>\n"; Vector builders=app.getNeededBuilders(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String name=(String)bset.get("name"); String maintainer=(String)bset.get("maintainer"); String version=(String)bset.get("version"); body+="\t\t<builder maintainer=\""+maintainer+"\" version=\""+version+"\">"+name+"</builder>\n"; } body+="\t</neededbuilderlist>\n\n"; return(body); } static String getNeededRelDefs(XMLApplicationReader app) { String body="\t<neededreldeflist>\n"; Vector builders=app.getNeededRelDefs(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String source=(String)bset.get("source"); String target=(String)bset.get("target"); String dir=(String)bset.get("direction"); String guisourcename=(String)bset.get("guisourcename"); String guitargetname=(String)bset.get("guitargetname"); body+="\t\t<reldef source=\""+source+"\" target=\""+target+"\" direction=\""+dir+"\" guisourcename=\""+guisourcename+"\" guitargetname=\""+guitargetname+"\""; String builder=(String)bset.get("builder"); if (builder!=null) body+=" builder=\""+builder+"\""; body+=" />\n"; } body+="\t</neededreldeflist>\n\n"; return(body); } static String getAllowedRelations(XMLApplicationReader app) { String body="\t<allowedrelationlist>\n"; Vector builders=app.getAllowedRelations(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String from=(String)bset.get("from"); String to=(String)bset.get("to"); String type=(String)bset.get("type"); body+="\t\t<relation from=\""+from+"\" to=\""+to+"\" type=\""+type+"\" />\n"; } body+="\t</allowedrelationlist>\n\n"; return(body); } static String getDataSources(XMLApplicationReader app) { String body="\t<datasourcelist>\n"; Vector builders=app.getDataSources(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String path=(String)bset.get("path"); String builder=(String)bset.get("builder"); body+="\t\t<datasource builder=\""+builder+"\" path=\""+path+"\" />\n"; } body+="\t</datasourcelist>\n\n"; return(body); } static String getRelationSources(XMLApplicationReader app) { String body="\t<relationsourcelist>\n"; Vector builders=app.getRelationSources(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String path=(String)bset.get("path"); String builder=(String)bset.get("builder"); body+="\t\t<relationsource builder=\""+builder+"\" path=\""+path+"\" />\n"; } body+="\t</relationsourcelist>\n\n"; return(body); } static String getDescription(XMLApplicationReader app) { String body="\t<description><![CDATA["; String tmp=app.getDescription(); if (tmp!=null && !tmp.equals("")) body+=tmp; body+="]]></description>\n\n"; return(body); } static String getInstallNotice(XMLApplicationReader app) { String body="\t<install-notice><![CDATA["; String tmp=app.getInstallNotice(); if (tmp!=null && !tmp.equals("")) body+=tmp; body+="]]></install-notice>\n"; return(body); } static String getContextSources(XMLApplicationReader app) { String body="\t<contextsourcelist>\n"; Vector builders=app.getContextSources(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String path=(String)bset.get("path"); String type=(String)bset.get("type"); String goal=(String)bset.get("goal"); body+="\t\t<contextsource path=\""+path+"\" type=\""+type+"\" goal=\""+goal+"\"/>\n"; } body+="\t</contextsourcelist>\n\n"; return(body); } static boolean saveFile(String filename,String value) { File sfile = new File(filename); try { DataOutputStream scan = new DataOutputStream(new FileOutputStream(sfile)); scan.writeBytes(value); scan.flush(); scan.close(); } catch(Exception e) { log.error(Logging.stackTrace(e)); } return(true); } private static void writeDateSources(XMLApplicationReader app,String targetpath,MMBase mmb,Vector resultmsgs) { Vector builders=app.getContextSources(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String path=(String)bset.get("path"); String type=(String)bset.get("type"); String goal=(String)bset.get("goal"); path=MMBaseContext.getConfigPath()+("/applications/"+path); resultmsgs.addElement("save type : "+type); resultmsgs.addElement("save goal : "+goal); if (type.equals("depth")) { XMLContextDepthReader capp=new XMLContextDepthReader(path); XMLContextDepthWriterII.writeContext(app,capp,targetpath,mmb,resultmsgs); } else if (type.equals("full")) { XMLFullBackupWriter.writeContext(app, targetpath, mmb, resultmsgs); } } } private static void writeContextSources(XMLApplicationReader app,String targetpath) { Vector builders=app.getContextSources(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String path=(String)bset.get("path"); String type=(String)bset.get("type"); String goal=(String)bset.get("goal"); path=MMBaseContext.getConfigPath()+("/applications/"+path); log.debug("READ="+path+" type="+type); if (type.equals("depth")) { XMLContextDepthReader capp=new XMLContextDepthReader(path); XMLContextDepthWriterII.writeContextXML(capp,targetpath+"/"+(String)bset.get("path")); } } } private static void writeBuilders(XMLApplicationReader app,String targetpath,MMBase mmb) { // create the dir for the Data & resource files File file = new File(targetpath+"/"+app.getApplicationName()+"/builders"); try { file.mkdirs(); } catch(Exception e) { log.error("Can't create dir : "+targetpath+"/"+app.getApplicationName()+"/builders"); } Vector builders=app.getNeededBuilders(); for (Enumeration e=builders.elements();e.hasMoreElements();) { Hashtable bset=(Hashtable)e.nextElement(); String name=(String)bset.get("name"); MMObjectBuilder bul=mmb.getMMObject(name); if (bul!=null) { try { BuilderWriter builderOut=new BuilderWriter(bul); builderOut.setIncludeComments(true); builderOut.setExpandBuilder(false); builderOut.writeToFile(targetpath+"/"+app.getApplicationName()+"/builders/"+name+".xml"); } catch (Exception ex) { log.error(Logging.stackTrace(ex)); } } } } }
