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));
                }
            }
        }
    }

}

Reply via email to