| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.kalumet.agent.updater; |
| |
| import org.apache.kalumet.FileManipulator; |
| import org.apache.kalumet.FileManipulatorException; |
| import org.apache.kalumet.KalumetException; |
| import org.apache.kalumet.agent.Configuration; |
| import org.apache.kalumet.agent.utils.EventUtils; |
| import org.apache.kalumet.controller.core.JEEApplicationServerController; |
| import org.apache.kalumet.controller.core.JEEApplicationServerControllerFactory; |
| import org.apache.kalumet.model.Agent; |
| import org.apache.kalumet.model.Archive; |
| import org.apache.kalumet.model.Environment; |
| import org.apache.kalumet.model.JEEApplication; |
| import org.apache.kalumet.model.JEEApplicationServer; |
| import org.apache.kalumet.model.Kalumet; |
| import org.apache.kalumet.model.update.UpdateLog; |
| import org.apache.kalumet.model.update.UpdateMessage; |
| import org.apache.kalumet.utils.NotifierUtils; |
| import org.apache.kalumet.utils.PublisherUtils; |
| import org.apache.kalumet.utils.VariableUtils; |
| import org.apache.kalumet.ws.client.ArchiveClient; |
| import org.apache.kalumet.ws.client.ClientException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * JEE application archive updater. |
| */ |
| public class ArchiveUpdater |
| { |
| |
| private static final transient Logger LOGGER = LoggerFactory.getLogger( ArchiveUpdater.class ); |
| |
| /** |
| * Wrapper method to update JEE application archive. |
| * |
| * @param environmentName the target environment name. |
| * @param serverName the target JEE application server name. |
| * @param applicationName the target JEE application name. |
| * @param archiveName the target archive name. |
| * @param delegation flag indicating if the update is a delegation from another agent (true), or a client call (false). |
| * @throws UpdateException in case of update failure. |
| */ |
| public static void update( String environmentName, String serverName, String applicationName, String archiveName, |
| boolean delegation ) |
| throws KalumetException |
| { |
| LOGGER.info( "Archive {} update requested by WS", archiveName ); |
| |
| // load configuration. |
| LOGGER.debug( "Loading configuration" ); |
| Kalumet kalumet = Kalumet.digeste( Configuration.CONFIG_LOCATION ); |
| Environment environment = kalumet.getEnvironment( environmentName ); |
| if ( environment == null ) |
| { |
| LOGGER.error( "Environment {} is not found in the configuration", environmentName ); |
| throw new KalumetException( "Environment " + environmentName + " is not found in the configuration" ); |
| } |
| JEEApplicationServer server = environment.getJEEApplicationServers().getJEEApplicationServer( serverName ); |
| if ( server == null ) |
| { |
| LOGGER.error( "JEE application server {} is not found in environment {}", serverName, environmentName ); |
| throw new KalumetException( |
| "JEE application server " + serverName + " is not found in environment " + environmentName ); |
| } |
| JEEApplication application = server.getJEEApplication( applicationName ); |
| if ( application == null ) |
| { |
| LOGGER.error( "JEE application {} is not found in JEE application server {}", applicationName, serverName ); |
| throw new KalumetException( |
| "JEE application " + applicationName + " is not found in JEE application server " + serverName ); |
| } |
| Archive archive = application.getArchive( archiveName ); |
| if ( archive == null ) |
| { |
| LOGGER.error( "Archive {} is not found in JEE application {}", archiveName, applicationName ); |
| throw new KalumetException( |
| "Archive " + archiveName + " is not found in JEE application " + applicationName ); |
| } |
| |
| // update configuration cache |
| LOGGER.debug( "Updating configuration cache" ); |
| Configuration.CONFIG_CACHE = kalumet; |
| |
| // post journal event |
| EventUtils.post( environment, "UPDATE", "Archive " + archiveName + " update requested by WS" ); |
| // create an update logger |
| UpdateLog updateLog = |
| new UpdateLog( "Archive " + archiveName + " update in progress ...", environment.getName(), environment ); |
| |
| if ( !delegation ) |
| { |
| // the update is a client call |
| LOGGER.info( "Send a notification and waiting for the count down" ); |
| EventUtils.post( environment, "UPDATE", "Send a notification and waiting for the count down" ); |
| NotifierUtils.waitAndNotify( environment ); |
| } |
| |
| try |
| { |
| // call the updater method |
| ArchiveUpdater.update( environment, server, application, archive, updateLog ); |
| } |
| catch ( Exception e ) |
| { |
| LOGGER.error( "Archive {} update failed", archiveName, e ); |
| EventUtils.post( environment, "ERROR", "Archive " + archiveName + " update failed: " + e.getMessage() ); |
| if ( !delegation ) |
| { |
| updateLog.setStatus( "Archive " + archiveName + " update failed" ); |
| updateLog.addUpdateMessage( |
| new UpdateMessage( "error", "Archive " + archiveName + " update failed: " + e.getMessage() ) ); |
| LOGGER.info( "Publishing update report" ); |
| PublisherUtils.publish( environment ); |
| } |
| throw new UpdateException( "Archive " + archiveName + " update failed", e ); |
| } |
| |
| // update completed |
| LOGGER.info( "Archive {} updated", archive.getName() ); |
| EventUtils.post( environment, "UPDATE", "Archive " + archive.getName() + " updated" ); |
| |
| if ( !delegation ) |
| { |
| if ( updateLog.isUpdated() ) |
| { |
| updateLog.setStatus( "Archive " + archive.getName() + " updated" ); |
| } |
| else |
| { |
| updateLog.setStatus( "Archive " + archive.getName() + " already up to date" ); |
| } |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Archive " + archive.getName() + " updated" ) ); |
| LOGGER.info( "Publishing update report" ); |
| PublisherUtils.publish( environment ); |
| } |
| } |
| |
| /** |
| * Updates a archive. |
| * |
| * @param environment the target <code>Environment</code>. |
| * @param server the target <code>JEEApplicationServer</code>. |
| * @param application the target JZEE <code>Application</code>. |
| * @param archive the target <code>Archive</code>. |
| * @param updateLog the <code>UpdateLog</code> to use. |
| */ |
| public static void update( Environment environment, JEEApplicationServer server, JEEApplication application, |
| Archive archive, UpdateLog updateLog ) |
| throws UpdateException |
| { |
| LOGGER.info( "Updating archive {}", archive.getName() ); |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Updating archive " + archive.getName() ) ); |
| EventUtils.post( environment, "UPDATE", "Updating archive " + archive.getName() ); |
| |
| if ( !archive.isActive() ) |
| { |
| LOGGER.info( "Archive {} is inactive, so not updated", archive.getName() ); |
| updateLog.addUpdateMessage( |
| new UpdateMessage( "info", "Archive " + archive.getName() + " is inactive, so not updated" ) ); |
| EventUtils.post( environment, "UPDATE", "Archive " + archive.getName() + " is inactive, so not updated" ); |
| return; |
| } |
| |
| // check to delegate the update |
| if ( archive.getAgent() != null && archive.getAgent().trim().length() > 0 && !archive.getAgent().equals( |
| Configuration.AGENT_ID ) ) |
| { |
| LOGGER.info( "Delegating archive {} update to agent {}", archive.getName(), archive.getAgent() ); |
| EventUtils.post( environment, "UPDATE", |
| "Delegating archive " + archive.getName() + " update to agent " + archive.getAgent() ); |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Delegating archive " + archive.getName() |
| + " update to agent " + archive.getAgent() ) ); |
| Agent delegationAgent = Configuration.CONFIG_CACHE.getAgent( archive.getAgent() ); |
| if ( delegationAgent == null ) |
| { |
| LOGGER.error( "Agent {} is not found in the configuration", archive.getAgent() ); |
| throw new UpdateException( "Agent " + archive.getAgent() + " is not found in the configuration" ); |
| } |
| try |
| { |
| LOGGER.debug( "Call archive WS" ); |
| ArchiveClient client = new ArchiveClient( delegationAgent.getHostname(), delegationAgent.getPort() ); |
| client.update( environment.getName(), server.getName(), application.getName(), archive.getName(), |
| true ); |
| } |
| catch ( ClientException clientException ) |
| { |
| LOGGER.error( "Archive " + archive.getName() + " update failed", clientException ); |
| throw new UpdateException( "Archive " + archive.getName() + " update failed", clientException ); |
| } |
| return; |
| } |
| |
| // construct the archiveUri |
| String archiveUri = VariableUtils.replace( archive.getUri(), environment.getVariables() ); |
| if ( !FileManipulator.protocolExists( archiveUri ) ) |
| { |
| // the archive URI is relative (no prefix protocol), use the |
| // application URI to construct the VFS URI |
| LOGGER.debug( "Archive URI is relative (no protocol prefix) to JEE application URI" ); |
| archiveUri = |
| FileManipulator.format( VariableUtils.replace( application.getUri(), environment.getVariables() ) ) |
| + "!/" + archiveUri; |
| } |
| // get the application cache directory |
| String applicationCacheDir = null; |
| // initialize the file manipulator instance |
| FileManipulator fileManipulator = null; |
| try |
| { |
| applicationCacheDir = FileManipulator.createJEEApplicationCacheDir( environment, application ); |
| fileManipulator = new FileManipulator(); |
| } |
| catch ( FileManipulatorException e ) |
| { |
| LOGGER.error( "Can't create JEE application {} cache directory", application.getName(), e ); |
| throw new UpdateException( "Can't create JEE application " + application.getName() + " cache directory", |
| e ); |
| } |
| finally |
| { |
| if ( fileManipulator != null ) |
| { |
| fileManipulator.close(); |
| } |
| } |
| // define the archive cache location |
| String archiveCache = applicationCacheDir + "/" + archive.getName(); |
| // define the archive installation URI |
| String archiveInstallation = null; |
| if ( archive.getPath() == null || archive.getPath().trim().length() < 1 ) |
| { |
| LOGGER.error( "Archive {} path is not defined", archive.getName() ); |
| throw new UpdateException( "Archive " + archive.getName() + " path is not defined" ); |
| } |
| // the archive path is defined, use it |
| archiveInstallation = VariableUtils.replace( archive.getPath(), environment.getVariables() ); |
| // get the JEE application server controller |
| LOGGER.debug( "Getting the JEE application server controller" ); |
| JEEApplicationServerController controller = null; |
| try |
| { |
| controller = JEEApplicationServerControllerFactory.getController( environment, server ); |
| } |
| catch ( KalumetException e ) |
| { |
| LOGGER.error( "Can't get the JEE application server {} controller", server.getName(), e ); |
| throw new UpdateException( "Can't get the JEE application server " + server.getName() + " controller", e ); |
| } |
| // check if the archive is already deployed |
| try |
| { |
| if ( controller.isJEEApplicationDeployed( archiveInstallation, archive.getName() ) ) |
| { |
| // the archive is already deployed, check for update |
| LOGGER.info( "Archive {} is already deployed, check for update", archive.getName() ); |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Archive " + archive.getName() |
| + " is already deployed, check for update" ) ); |
| EventUtils.post( environment, "UPDATE", |
| "Archive " + archive.getName() + " is already deployed, check for update" ); |
| if ( !fileManipulator.checksumEquals( archiveUri, archiveCache ) ) |
| { |
| // the archive file is different from the copy in the |
| // application directory, perform an update |
| // update the cache |
| fileManipulator.copy( archiveUri, archiveCache ); |
| LOGGER.info( "Archive {} (located {}) is different from the cache, performing update", |
| archive.getName(), archiveUri ); |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Archive " + archive.getName() + " (located " |
| + archiveUri + ") is different from the cache, performing update" ) ); |
| EventUtils.post( environment, "UPDATE", "Archive " + archive.getName() + " (located " + archiveUri |
| + ") is different from the cache, performing update" ); |
| updateLog.addUpdateMessage( |
| new UpdateMessage( "info", "Copy " + archiveUri + " to " + archiveInstallation ) ); |
| EventUtils.post( environment, "UPDATE", "Copy " + archiveUri + " to " + archiveInstallation ); |
| // update the archive path |
| fileManipulator.copy( archiveUri, archiveInstallation ); |
| // undeploy the archive |
| LOGGER.info( "Undeploying archive {}", archive.getName() ); |
| updateLog.addUpdateMessage( |
| new UpdateMessage( "info", "Undeploying archive " + archive.getName() ) ); |
| EventUtils.post( environment, "UPDATE", "Undeploying archive " + archive.getName() ); |
| controller.undeployJEEApplication( archiveInstallation, archive.getName() ); |
| // deploy the archive |
| LOGGER.info( "Deploying archive {}", archive.getName() ); |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Deploying archive " + archive.getName() ) ); |
| EventUtils.post( environment, "UPDATE", "Deploying archive " + archive.getName() ); |
| controller.deployJEEApplication( archiveInstallation, archive.getName(), |
| archive.getClassloaderorder(), archive.getClassloaderpolicy(), |
| VariableUtils.replace( archive.getVhost(), |
| environment.getVariables() ) ); |
| LOGGER.info( "Archive {} updated", archive.getName() ); |
| updateLog.addUpdateMessage( |
| new UpdateMessage( "info", "Archive " + archive.getName() + " updated" ) ); |
| EventUtils.post( environment, "UPDATE", "Archive " + archive.getName() + " updated" ); |
| updateLog.setUpdated( true ); |
| } |
| } |
| else |
| { |
| // the archive is not deployed |
| LOGGER.info( "Archive {} is not deployed, deploying it", archive.getName() ); |
| updateLog.addUpdateMessage( |
| new UpdateMessage( "info", "Archive " + archive.getName() + " is not deployed, deploying it" ) ); |
| EventUtils.post( environment, "UPDATE", |
| "Archive " + archive.getName() + " is not deployed, deploying it" ); |
| // copy the archive agent locally |
| fileManipulator.copy( archiveUri, archiveCache ); |
| // copy the archive to the destination path |
| fileManipulator.copy( archiveUri, archiveInstallation ); |
| // deploy the archive |
| LOGGER.info( "Deploying archive {}", archive.getName() ); |
| updateLog.addUpdateMessage( new UpdateMessage( "info", "Deploying archive " + archive.getName() ) ); |
| EventUtils.post( environment, "UPDATE", "Deploying archive " + archive.getName() ); |
| controller.deployJEEApplication( archiveInstallation, archive.getName(), archive.getClassloaderorder(), |
| archive.getClassloaderpolicy(), |
| VariableUtils.replace( archive.getVhost(), |
| environment.getVariables() ) ); |
| updateLog.setUpdated( true ); |
| } |
| // as some JEE application server (like IBM WebSphere) change the archive file during deployment, update |
| // the local archive with a original copy (for next update) |
| LOGGER.debug( "Restoring the original archive (before deployment) from {}", archiveUri ); |
| fileManipulator.copy( archiveUri, archiveCache ); |
| // check if the JEE application is deployed (it should be) |
| if ( !controller.isJEEApplicationDeployed( archiveInstallation, archive.getName() ) ) |
| { |
| LOGGER.error( |
| "Archive {} is not deployed whereas it should be. Please check the JEE application server logs", |
| archive.getName() ); |
| throw new UpdateException( "Archive " + archive.getName() |
| + " is not deployed whereas it should be. Please check the JEE application server logs" ); |
| } |
| } |
| catch ( Exception e ) |
| { |
| // the archive update has failed |
| LOGGER.error( "Archive {} update failed", archive.getName(), e ); |
| try |
| { |
| fileManipulator.delete( archiveCache ); |
| } |
| catch ( FileManipulatorException fileManipulatorException ) |
| { |
| LOGGER.warn( "Can't delete " + archiveCache, fileManipulatorException ); |
| } |
| throw new UpdateException( "Archive " + archive.getName() + " update failed", e ); |
| } |
| } |
| |
| /** |
| * Wrapper method to check if a archive is up to date or not via WS. |
| * |
| * @param environmentName the target environment name. |
| * @param serverName the target JEE application server name. |
| * @param applicationName the target JEE application name. |
| * @param archiveName the target archive name. |
| * @return true if the archive is up to date, false else. |
| * @throws KalumetException in case of status check failure |
| */ |
| public static boolean check( String environmentName, String serverName, String applicationName, String archiveName ) |
| throws KalumetException |
| { |
| LOGGER.info( "Checking status of archive {} via WS", archiveName ); |
| // load configuration |
| LOGGER.debug( "Load configuration" ); |
| Kalumet kalumet = Kalumet.digeste( Configuration.CONFIG_LOCATION ); |
| Environment environment = kalumet.getEnvironment( environmentName ); |
| if ( environment == null ) |
| { |
| LOGGER.error( "Environment {} is not found in the configuration", environmentName ); |
| throw new KalumetException( "Environment " + environmentName + " is not found in the configuration" ); |
| } |
| JEEApplicationServer applicationServer = |
| environment.getJEEApplicationServers().getJEEApplicationServer( serverName ); |
| if ( applicationServer == null ) |
| { |
| LOGGER.error( "JEE application server {} is not found in environment {}", serverName, environmentName ); |
| throw new KalumetException( |
| "JEE application server " + serverName + " is not found in environment " + environmentName ); |
| } |
| JEEApplication application = applicationServer.getJEEApplication( applicationName ); |
| if ( application == null ) |
| { |
| LOGGER.error( "JEE application {} is not found in JEE application server {}", applicationName, serverName ); |
| throw new KalumetException( |
| "JEE application " + applicationName + " is not found in JEE application server " + serverName ); |
| } |
| Archive archive = application.getArchive( archiveName ); |
| if ( archive == null ) |
| { |
| LOGGER.error( "Archive {} is not found in JEE application {}", archiveName, applicationName ); |
| throw new KalumetException( |
| "Archive " + archiveName + " is not found in JEE application " + applicationName ); |
| } |
| |
| // check if the check should be delegated to another agent |
| if ( archive.getAgent() != null && archive.getAgent().trim().length() > 0 && !archive.getAgent().equals( |
| Configuration.AGENT_ID ) ) |
| { |
| // the check needs to be delegated to another agent |
| LOGGER.info( "Delegating archive {} status check to agent {}", archive.getName(), archive.getAgent() ); |
| Agent agentDelegation = kalumet.getAgent( archive.getAgent() ); |
| if ( agentDelegation == null ) |
| { |
| LOGGER.error( "Agent {} is not found in the configuration", archive.getName() ); |
| throw new KalumetException( "Agent " + archive.getAgent() + " is not found in the configuration" ); |
| } |
| // call the service |
| ArchiveClient client = new ArchiveClient( agentDelegation.getHostname(), agentDelegation.getPort() ); |
| return client.check( environmentName, serverName, applicationName, archiveName ); |
| } |
| |
| // get JEE application server controller |
| LOGGER.debug( "Getting JEE application server controller" ); |
| JEEApplicationServerController controller = |
| JEEApplicationServerControllerFactory.getController( environment, applicationServer ); |
| |
| FileManipulator fileManipulator = null; |
| try |
| { |
| fileManipulator = new FileManipulator(); |
| |
| // get application cache directory |
| LOGGER.debug( "Getting application cache directory" ); |
| String applicationCacheDirectory = FileManipulator.createJEEApplicationCacheDir( environment, application ); |
| |
| // construct the archive URI |
| String archiveUri = VariableUtils.replace( archive.getUri(), environment.getVariables() ); |
| if ( !FileManipulator.protocolExists( archiveUri ) ) |
| { |
| // the archive URI is relative (doesn't contain the protocol prefix), construct the URI using the application URI |
| archiveUri = |
| VariableUtils.replace( application.getUri(), environment.getVariables() ) + "!/" + archiveUri; |
| } |
| |
| // get the archive cache |
| String archiveCache = applicationCacheDirectory + "/" + archive.getName(); |
| |
| // get the archive installation path |
| if ( archive.getPath() == null || archive.getPath().trim().length() < 1 ) |
| { |
| LOGGER.error( "Archive {} path is not defined", archive.getName() ); |
| throw new KalumetException( "Archive " + archive.getName() + " path is not defined" ); |
| } |
| String archiveInstallation = VariableUtils.replace( archive.getPath(), environment.getVariables() ); |
| |
| if ( controller.isJEEApplicationDeployed( archiveInstallation, archive.getName() ) ) |
| { |
| // check if the archive is deployed or not |
| if ( fileManipulator.checksumEquals( archiveUri, archiveCache ) ) |
| { |
| // archive URI and cache are the same |
| LOGGER.debug( "Archive URI and agent cache are the same" ); |
| return true; |
| } |
| } |
| } |
| finally |
| { |
| if ( fileManipulator != null ) |
| { |
| fileManipulator.close(); |
| } |
| } |
| |
| return false; |
| } |
| |
| } |