/* * Copyright 2017 Red Hat, Inc. and/or its affiliates. * * Licensed 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.kie.workbench.common.services.backend.builder.core; import java.io.ByteArrayInputStream; import java.util.Collection; import java.util.Map; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.inject.Instance; import javax.inject.Inject; import org.drools.compiler.kie.builder.impl.InternalKieModule; import org.guvnor.common.services.backend.exceptions.ExceptionUtilities; import org.guvnor.common.services.project.builder.model.BuildMessage; import org.guvnor.common.services.project.builder.model.BuildResults; import org.guvnor.common.services.project.builder.model.IncrementalBuildResults; import org.guvnor.common.services.project.builder.service.PostBuildHandler; import org.guvnor.common.services.project.model.GAV; import org.guvnor.common.services.project.model.POM; import org.guvnor.common.services.project.model.Project; import org.guvnor.common.services.project.service.DeploymentMode; import org.guvnor.common.services.project.service.POMService; import org.guvnor.common.services.shared.message.Level; import org.guvnor.m2repo.backend.server.ExtendedM2RepoService; import org.jboss.errai.security.shared.api.identity.User; import org.kie.workbench.common.services.shared.project.KieProject; import org.kie.workbench.common.services.shared.project.KieProjectService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.uberfire.backend.server.util.Paths; import org.uberfire.backend.vfs.Path; import org.uberfire.workbench.events.ResourceChange; @ApplicationScoped public class BuildHelper { private static final Logger logger = LoggerFactory.getLogger( BuildHelper.class ); private POMService pomService; private ExtendedM2RepoService m2RepoService; private LRUBuilderCache cache; private KieProjectService projectService; private DeploymentVerifier deploymentVerifier; private Instance< User > identity; private Instance< PostBuildHandler > handlers; public BuildHelper( ) { } @Inject public BuildHelper( final POMService pomService, final ExtendedM2RepoService m2RepoService, final KieProjectService projectService, final DeploymentVerifier deploymentVerifier, final LRUBuilderCache cache, final Instance< PostBuildHandler > handlers, final Instance< User > identity ) { this.pomService = pomService; this.m2RepoService = m2RepoService; this.projectService = projectService; this.deploymentVerifier = deploymentVerifier; this.cache = cache; this.handlers = handlers; this.identity = identity; } public BuildResult build( final Project project ) { try { cache.invalidateCache( project ); Builder builder = cache.assertBuilder( project ); final BuildResults results = builder.build( ); BuildMessage infoMsg = new BuildMessage( ); infoMsg.setLevel( Level.INFO ); infoMsg.setText( buildResultMessage( project, results ).toString( ) ); results.addBuildMessage( 0, infoMsg ); return new BuildResult( builder, results ); } catch ( Exception e ) { logger.error( e.getMessage( ), e ); return new BuildResult( null, buildExceptionResults( e, project.getPom( ).getGav( ) ) ); } } public IncrementalBuildResults addPackageResource( final Path resource ) { try { IncrementalBuildResults results = new IncrementalBuildResults( ); final KieProject project = projectService.resolveProject( resource ); if ( project == null ) { return results; } final Builder builder = cache.assertBuilder( project ); if ( !builder.isBuilt( ) ) { throw new IllegalStateException( "Incremental Build requires a full build be completed first." ); } else { results = builder.addResource( Paths.convert( resource ) ); } return results; } catch ( Exception e ) { logger.error( e.getMessage( ), e ); throw ExceptionUtilities.handleException( e ); } } public IncrementalBuildResults deletePackageResource( final Path resource ) { try { IncrementalBuildResults results = new IncrementalBuildResults( ); final KieProject project = projectService.resolveProject( resource ); if ( project == null ) { return results; } final Builder builder = cache.assertBuilder( project ); if ( !builder.isBuilt( ) ) { throw new IllegalStateException( "Incremental Build requires a full build be completed first." ); } else { results = builder.deleteResource( Paths.convert( resource ) ); } return results; } catch ( Exception e ) { logger.error( e.getMessage( ), e ); throw ExceptionUtilities.handleException( e ); } } public IncrementalBuildResults updatePackageResource( final Path resource ) { try { final Project project = projectService.resolveProject( resource ); if ( project == null ) { return new IncrementalBuildResults( ); } final Builder builder = cache.assertBuilder( project ); if ( !builder.isBuilt( ) ) { throw new IllegalStateException( "Incremental Build requires a full build be completed first." ); } return builder.updateResource( Paths.convert( resource ) ); } catch ( Exception e ) { logger.error( e.getMessage( ), e ); throw ExceptionUtilities.handleException( e ); } } public IncrementalBuildResults applyBatchResourceChanges( final Project project, final Map< Path, Collection< ResourceChange > > changes ) { IncrementalBuildResults results = new IncrementalBuildResults( ); try { if ( project == null ) { return results; } final Builder builder = cache.assertBuilder( project ); if ( !builder.isBuilt( ) ) { throw new IllegalStateException( "Incremental Build requires a full build be completed first." ); } else { results = builder.applyBatchResourceChanges( changes ); } return results; } catch ( Exception e ) { logger.error( e.getMessage( ), e ); throw ExceptionUtilities.handleException( e ); } } private StringBuffer buildResultMessage( final Project project, final BuildResults results ) { StringBuffer message = new StringBuffer( ); message.append( "Build of project '" ); message.append( project.getProjectName( ) ); message.append( "' (requested by " ); message.append( getIdentifier( ) ); message.append( ") completed.\n" ); message.append( " Build: " ); message.append( results.getErrorMessages( ).isEmpty( ) ? "SUCCESSFUL" : "FAILURE" ); return message; } /** * When an exception is produced by the builder service, this method is uses to generate an instance of * <code>org.guvnor.common.services.project.builder.model.BuildResults</code> in generated with the exception details. * @param e The error exception. * @param gav * @return An instance of BuildResults with the exception details. */ public BuildResults buildExceptionResults( Exception e, GAV gav ) { BuildResults exceptionResults = new BuildResults( gav ); BuildMessage exceptionMessage = new BuildMessage( ); exceptionMessage.setLevel( Level.ERROR ); exceptionMessage.setText( e.getMessage( ) ); exceptionResults.addBuildMessage( exceptionMessage ); return exceptionResults; } public BuildResults buildAndDeploy( final Project project ) { return buildAndDeploy( project, DeploymentMode.VALIDATED ); } public BuildResults buildAndDeploy( final Project project, final DeploymentMode mode ) { deploymentVerifier.verifyWithException( project, mode ); return doBuildAndDeploy( project, false ); } public BuildResults buildAndDeploy( final Project project, final boolean suppressHandlers ) { return buildAndDeploy( project, suppressHandlers, DeploymentMode.VALIDATED ); } public BuildResults buildAndDeploy( final Project project, final boolean suppressHandlers, final DeploymentMode mode ) { deploymentVerifier.verifyWithException( project, mode ); return doBuildAndDeploy( project, suppressHandlers ); } public class BuildResult { private Builder builder; private BuildResults buildResults; private IncrementalBuildResults incrementalBuildResults; public BuildResult( Builder builder, BuildResults buildResults ) { this.builder = builder; this.buildResults = buildResults; } public BuildResult( Builder builder, IncrementalBuildResults incrementalBuildResults ) { this.builder = builder; this.incrementalBuildResults = incrementalBuildResults; } public Builder getBuilder( ) { return builder; } public BuildResults getBuildResults( ) { return buildResults; } public IncrementalBuildResults getIncrementalBuildResults( ) { return incrementalBuildResults; } } private BuildResults doBuildAndDeploy( final Project project, final boolean suppressHandlers ) { try { //Build final BuildResults results = build( project ).getBuildResults(); StringBuffer message = new StringBuffer( ); message.append( "Build of project '" + project.getProjectName( ) + "' (requested by " + getIdentifier( ) + ") completed.\n" ); message.append( " Build: " + ( results.getErrorMessages( ).isEmpty( ) ? "SUCCESSFUL" : "FAILURE" ) ); //Deploy, if no errors final POM pom = pomService.load( project.getPomXMLPath( ) ); if ( results.getErrorMessages( ).isEmpty( ) ) { final Builder builder = cache.assertBuilder( project ); final InternalKieModule kieModule = ( InternalKieModule ) builder.getKieModule( ); final ByteArrayInputStream input = new ByteArrayInputStream( kieModule.getBytes( ) ); m2RepoService.deployJar( input, pom.getGav( ) ); message.append( " Maven: SUCCESSFUL" ); if ( !suppressHandlers ) { for ( PostBuildHandler handler : handlers ) { try { handler.process( results ); } catch ( Exception e ) { logger.warn( "PostBuildHandler {} failed due to {}", handler, e.getMessage( ) ); } } message.append( " Deploy: " + ( results.getErrorMessages( ).isEmpty( ) ? "SUCCESSFUL" : "FAILURE" ) ); } } return results; } catch ( Exception e ) { logger.error( e.getMessage( ), e ); // BZ-1007894: If throwing the exception, an error popup will be displayed, but it's not the expected behavior. The excepted one is to show the errors in problems widget. // So, instead of throwing the exception, a BuildResults instance is produced on the fly to simulate the error in the problems widget. return buildExceptionResults( e, project.getPom( ).getGav( ) ); } } private String getIdentifier( ) { if ( identity.isUnsatisfied( ) ) { return "system"; } try { return identity.get( ).getIdentifier( ); } catch ( ContextNotActiveException e ) { return "system"; } } }