/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.enterprise.admin.cli.cluster; import com.sun.enterprise.admin.cli.remote.RemoteCLICommand; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; //import java.net.InetAddress; //import java.net.UnknownHostException; import java.util.*; import java.util.logging.Level; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.jvnet.hk2.annotations.Service; import org.glassfish.api.Param; import org.glassfish.api.admin.*; import static com.sun.enterprise.admin.cli.CLIConstants.*; import com.sun.enterprise.util.io.FileUtils; import java.io.FileInputStream; import java.util.logging.Logger; import org.glassfish.admin.payload.PayloadImpl; import org.glassfish.admin.payload.PayloadFilesManager.Perm; import org.glassfish.hk2.api.PerLookup; /** * This is a local command that unbundles the bundle generated by export-sync-bundle. * import-sync-bundle applies the content under ${com.sun.aas.instanceRoot}/ * directory. Synchronization cookie with DAS's timestamp should be created. * It also creates das.properties (if not present) under agent dir (ex. * installRoot/glassfish4/glassfish/nodes/<host-name>/agent/config/das.properties). * * Before running this command the instance should already have been registered in * DAS (server element created in DAS domain.xml) by running create-instance. * This command does not validate --node or instance_name. * * For upgrade - this command creates a new instance filesystem if it does not exist. * and completes DAS registration by setting rendezvousOccurred=true. * * For manual sync - this command creates a new instance filesystem or updates the * directories of an existing instance (remove existing application, generated, * config, docroot, lib dir first and explode the zip) and completes registration with DAS * by setting rendezvousOccurred=true. * * If setting of rendezvousOccurred=true with DAS fails, the command does not * not fail. Only a warning is printed out in the command output. We provide the * exact "asadmin set command" in this warning so that user can run * that command on DAS to change the rendezvousOccurred property for the server instance. * * Usage: * * import-sync-bundle [--node node_name] [--nodedir node_path] --file * xyz-sync-bundle.zip instance_name * * --node name of the node; this is optional. The command fails if there * is more than one node under the default location * (installRoot/glassfish4/glassfish/nodes/<host-name>/) * * --nodedir parent dir where node is created; this is optional. Default * location is installRoot/glassfish4/glassfish/nodes/ * * --file sync bundle created by export-sync-bundle * * instance_name name of the server instance */ @Service(name = "import-sync-bundle") @PerLookup public class ImportSyncBundleCommand extends LocalInstanceCommand { @Param(name = "file_name", primary = true) private String syncBundle; @Param(name = "instance") private String instanceName0; @Param(name = "node", optional = true, alias = "nodeagent") protected String _node; String DASHost; int DASPort = -1; String DASProtocol; boolean dasIsSecure; private File dasPropsFile; private Properties dasProperties; private File syncBundleFile = null; private File agentConfigDir; private File backupDir; private static final String RENDEZVOUS_PROPERTY_NAME = "rendezvousOccurred"; private String INSTANCE_DOTTED_NAME; private String RENDEZVOUS_DOTTED_NAME; //private String RENDEZVOUS_DOTTED_NAME_VALUE; //private boolean isDasRunning; /** */ @Override protected void validate() throws CommandException { if(ok(instanceName0)) instanceName = instanceName0; else throw new CommandException(Strings.get("Instance.badInstanceName")); syncBundleFile = new File(syncBundle); if (!syncBundleFile.isFile()) throw new CommandException(Strings.get("noFile", syncBundle)); if (!isRegisteredToDAS()) { throw new CommandException(Strings.get("import.sync.bundle.invalidInstance", instanceName)); } node = _node; super.validate(); // set ServerDirs init(); } private void init() throws CommandException { agentConfigDir = new File(nodeDirChild, "agent" + File.separator + "config"); dasPropsFile = new File(agentConfigDir, "das.properties"); if (dasPropsFile.isFile()) { setDasDefaults(dasPropsFile); } DASHost = programOpts.getHost(); DASPort = programOpts.getPort(); dasIsSecure = programOpts.isSecure(); DASProtocol = "http"; INSTANCE_DOTTED_NAME = "servers.server." + instanceName; RENDEZVOUS_DOTTED_NAME = INSTANCE_DOTTED_NAME + ".property." + RENDEZVOUS_PROPERTY_NAME; //RENDEZVOUS_DOTTED_NAME_VALUE = RENDEZVOUS_DOTTED_NAME + "=true"; } private boolean isRegisteredToDAS() throws CommandException { boolean isRegisteredOnDAS = false; InputStream input = null; XMLStreamReader reader = null; ZipFile zip = null; try { //find node from domain.xml zip = new ZipFile(syncBundleFile); ZipEntry entry = zip.getEntry("config/domain.xml"); if (entry != null) { input = zip.getInputStream(entry); reader = XMLInputFactory.newInstance().createXMLStreamReader(input); while (!isRegisteredOnDAS) { int event = reader.next(); if (event == XMLStreamReader.END_DOCUMENT) { break; } if (event == XMLStreamReader.START_ELEMENT && "server".equals(reader.getLocalName())) { // get the attributes for this <server> int num = reader.getAttributeCount(); Map<String, String> map = new HashMap<String, String>(); for (int i = 0; i < num; i++) { map.put(reader.getAttributeName(i).getLocalPart(), reader.getAttributeValue(i)); } String thisName = map.get("name"); if (instanceName != null && instanceName.equals(thisName)) { isRegisteredOnDAS = true; if (_node == null) { // if node not specified _node = map.get("node"); // find it in domain.xml } } } } } else { throw new CommandException(Strings.get("import.sync.bundle.domainXmlNotFound", syncBundle)); } } catch (IOException ex) { logger.log(Level.SEVERE, Strings.get("import.sync.bundle.inboundPayloadFailed", syncBundle, ex.getLocalizedMessage()), ex); throw new CommandException(Strings.get("import.sync.bundle.inboundPayloadFailed", syncBundle, ex.getLocalizedMessage()), ex); } catch (XMLStreamException xe) { logger.log(Level.SEVERE, Strings.get("import.sync.bundle.inboundPayloadFailed", syncBundle, xe.getLocalizedMessage()), xe); throw new CommandException(Strings.get("import.sync.bundle.inboundPayloadFailed", syncBundle, xe.getLocalizedMessage()), xe); } finally { if (input != null) { try { input.close(); } catch (IOException ex) { // ignored } } if (reader != null) { try { reader.close(); } catch (XMLStreamException ex) { // ignored } } if (zip != null) { try { zip.close(); } catch (Exception ex) { // ignored } } } return isRegisteredOnDAS; } /** */ @Override protected int executeCommand() throws CommandException { int exitCode = createDirectories(); if (exitCode == SUCCESS) { setRendezvousOccurred("true"); } return exitCode; } private int createDirectories() throws CommandException { if (!agentConfigDir.isDirectory()) { if (!agentConfigDir.mkdirs()) { throw new CommandException(Strings.get("import.sync.bundle.createDirectoryFailed", agentConfigDir.getPath())); } } writeProperties(); FileInputStream in = null; Payload.Inbound payload = null; try { in = new FileInputStream(syncBundle); payload = PayloadImpl.Inbound.newInstance("application/zip", in); } catch (IOException ex) { try { if (in != null) in.close(); } catch (IOException ioe) { logger.warning(Strings.get("import.sync.bundle.closeStreamFailed", syncBundle, ioe.getLocalizedMessage())); } throw new CommandException(Strings.get("import.sync.bundle.inboundPayloadFailed", syncBundle, ex.getLocalizedMessage()), ex); } backupInstanceDir(); File targetDir = this.getServerDirs().getServerDir(); if (!targetDir.mkdirs()) { restoreInstanceDir(); throw new CommandException(Strings.get("import.sync.bundle.createDirectoryFailed", targetDir.getPath())); } Perm perm = new Perm(targetDir, null, logger); try { perm.processParts(payload); } catch (Exception ex) { restoreInstanceDir(); String msg = Strings.get("import.sync.bundle.extractBundleFailed", syncBundle, targetDir.getAbsolutePath()); if (ex.getLocalizedMessage() != null) msg = msg + "\n" + ex.getLocalizedMessage(); throw new CommandException(msg, ex); } finally { try { if (in != null) in.close(); } catch (IOException ex) { logger.warning(Strings.get("import.sync.bundle.closeStreamFailed", syncBundle, ex.getLocalizedMessage())); } } deleteBackupDir(); return SUCCESS; } private void writeProperties() throws CommandException { try { if (!dasPropsFile.isFile()) { writeDasProperties(); } } catch (IOException ex) { throw new CommandException(Strings.get("Instance.cantWriteProperties", "das.properties", ex.getLocalizedMessage()), ex); } } private void writeDasProperties() throws IOException { if (dasPropsFile.createNewFile()) { dasProperties = new Properties(); dasProperties.setProperty(K_DAS_HOST, DASHost); dasProperties.setProperty(K_DAS_PORT, String.valueOf(DASPort)); dasProperties.setProperty(K_DAS_IS_SECURE, String.valueOf(dasIsSecure)); dasProperties.setProperty(K_DAS_PROTOCOL, DASProtocol); FileOutputStream fos = null; try { fos = new FileOutputStream(dasPropsFile); dasProperties.store(fos, Strings.get("Instance.dasPropertyComment")); } finally { if (fos != null) { fos.close(); } } } } private void backupInstanceDir() throws CommandException { File f = getServerDirs().getServerDir(); if (f != null && f.isDirectory()) { Random r = new Random(); setBackupDir(r.nextInt()); File backup = getBackupDir(); if (!f.renameTo(backup)) { logger.warning(Strings.get("import.sync.bundle.backupInstanceDirFailed", f.getAbsolutePath(), backup.getAbsolutePath())); if (FileUtils.whack(f)) { //Ask user first before deleting? logger.warning(Strings.get("import.sync.bundle.deletedInstanceDir", f.getAbsolutePath())); } } } } private void setBackupDir(int i) { File f = getServerDirs().getServerDir(); backupDir = new File(getServerDirs().getServerParentDir(), f.getName() + "_backup" + i); } private File getBackupDir() { return backupDir; } private void restoreInstanceDir() { File backup = getBackupDir(); if (backup != null && backup.isDirectory()) { File dir = getServerDirs().getServerDir(); boolean gone = ! FileUtils.deleteFileMaybe(getServerDirs().getServerDir()); if (!gone || !backup.renameTo(dir)) { logger.warning(Strings.get("import.sync.bundle.restoreInstanceDirFailed", backup.getAbsolutePath(), getServerDirs().getServerDir().getAbsolutePath())); } } } private void deleteBackupDir() { File backup = getBackupDir(); if (backup != null && backup.isDirectory()) { FileUtils.whack(backup); } } private void setRendezvousOccurred(String rendezVal) { String dottedName = RENDEZVOUS_DOTTED_NAME + "=" + rendezVal; try { RemoteCLICommand rc = new RemoteCLICommand("set", this.programOpts, this.env); rc.executeAndReturnOutput("set", dottedName); } catch (CommandException ex) { logger.warning(Strings.get("import.sync.bundle.completeRegistrationFailed", dottedName)); } } }