package ring.deployer; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipFile; import org.xml.sax.SAXException; import org.xmldb.api.base.XMLDBException; import ring.main.RingModule; import ring.persistence.ExistDB; import ring.system.MUDConfig; /** * Deploys a mud file. MUDs are deployed when changes to their codebase or data have occurred. This * deployment module unpacks mud files and deploys them to the RingMUD server configuration root. * XML data is sent to the XML Database. During a MUD deploy, each entry in the .mud file is tested * for changes, and deployed accordingly. The Deployer makes use of an XMLDeployer and RegularDeployer * to implement these tests as well as deployment logic. * @author projectmoon * */ public class DeployModule implements RingModule { private DeployableMUDFile mudFile = null; private boolean codeUpdates = false; private boolean xmlUpdates = false; private ExistDB db = null; private String mudRoot; private String mudPath; //Cleanup related variables private List<String> deployedXMLDocuments = new ArrayList<String>(); public static void main(String[] args) { MUDConfig.loadProperties(); String[] args2 = new String[] { "/Users/projectmoon/Programs/git/ringmud/RingMUD-1.0.2.mud" }; new DeployModule().execute(args2); } @Override public void execute(String[] args) { try { //Create DeployableMUDFile from args[0]: This is the mud to be imported. ZipFile zip = new ZipFile(args[0]); mudFile = new DeployableMUDFile(zip); mudRoot = MUDConfig.MUDROOT + File.separator + "muds" + File.separator + mudFile.getName() + File.separator; mudPath = mudRoot + mudFile.getVersion(); //Set up database object ExistDB.setRootURI(mudFile.getName()); db = new ExistDB(); //Create DeployedMUD from info found in mudFile. DeployedMUD mud = DeployedMUDFactory.getMUD(mudFile.getName(), mudFile.getVersion()); //Add root to deployed XML docs. root is always there. deployedXMLDocuments.add("root"); //Check hash in property file. // If hashes are different || deployed mud == null: // setupDirectories() if ((mud == null) || (mud.getHash() != mudFile.getHash())) { setupDirectories(); } //Create collections for this MUD. createCollections(); //For each entry in the set: // If endsWith .xml, delegate to deployXML. // Else, delegate to deploy. for (DeployableFileEntry entry : mudFile.getEntries()) { if (entry.getEntryName().endsWith(".xml") || entry.getEntryName().endsWith(".XML")) { deployXMLDocument(entry); } else { deploy(entry); } } //Call cleanUpDatabase() to remove broken references and un-necessary documents. cleanUpDatabase(); //updateVersionFile() updateVersionFile(); //if codeUpdates: // Restart the currently running mud, or issue a warning that the MUD must be restarted. //if !codeUpdates: // Force all MUD objects in the currently running MUD to reload themselves from the database. if (codeUpdates && xmlUpdates) { System.out.println("There were data and code updates. You need to restart the MUD."); } else if (codeUpdates) { System.out.println("There were code updates. You need to restart the MUD."); } else if (xmlUpdates) { System.out.println("There were data updates. You need to restart the MUD."); } else { System.out.println("No changes detected in this deploy."); } } catch (IOException e) { e.printStackTrace(); } catch (XMLDBException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } /** * Discrete step for setting up the database for this deployal. * @throws XMLDBException */ private void createCollections() throws XMLDBException { db.createRingDatabase(); } /** * Discrete step for setting up deployment directories if necessary. */ private void setupDirectories() { //Create a new directory for the MUD in MUDROOT. //Create a new directory for the new version of the MUD in the above directory. //These will silently fail if directories exist. File f = new File(mudPath); if (f.mkdirs()) { System.out.println("Creating directory " + mudPath); } } /** * Deploys an XML document. Delegates to XMLDeployer. * @param entry * @throws XMLDBException * @throws SAXException * @throws IOException */ private void deployXMLDocument(DeployableFileEntry entry) throws XMLDBException, SAXException, IOException { deployedXMLDocuments.add(entry.getStrippedEntryName()); XMLDeployer deployer = new XMLDeployer(db, entry); deployer.deploy(); if (!xmlUpdates) { xmlUpdates = deployer.xmlUpdates(); } } /** * Deploys something that isn't an XML document. Delegates to RegularDeployer. * @param entry */ private void deploy(DeployableFileEntry entry) { //This deploys "everything else" that isn't XML data. String fullPath = mudPath + File.separator + stripMudPrefix(entry.getEntryName()); File file = new File(fullPath); RegularDeployer deployer = new RegularDeployer(file, entry); deployer.deploy(); if (!codeUpdates) { codeUpdates = deployer.codeUpdates(); } } /** * Discrete step to clean up the database of deleted XML documents and * broken references. */ private void cleanUpDatabase() throws XMLDBException { //Remove old documents: //Delegate to DocumentCleanup. DocumentCleanup cleanup = new DocumentCleanup(deployedXMLDocuments); cleanup.cleanup(); if (cleanup.getCleanupCount() > 0) { System.out.println("Removed " + cleanup.getCleanupCount() + " old XML document(s)."); } //Clean up database by removing broken references. //This will operate across all collections for the imported MUD. //Using XQuery, find a list of IDs that have ref=true attributes. //For each ID: // attempt to directly load its associated object. // if returned object == null: // delete referential definition // increment brokenRefCount //System.out.println("Removed " + brokenRefCount + " broken references."); } private void updateVersionFile() throws FileNotFoundException { //Write version file with "current= " + mudFile.getVersion() String path = mudRoot + "versions"; PrintWriter writer = new PrintWriter(path); writer.println("current=" + mudFile.getVersion()); writer.close(); } @Override public boolean usesDatabase() { return true; } private String stripMudPrefix(String entryName) { int start = entryName.indexOf("mud/") + "mud/".length() - 1; return entryName.substring(start + 1); } }