/* * Copyright 2016 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. * * 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.datamodel.backend.server; import java.net.URISyntaxException; import java.net.URL; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import javax.enterprise.event.Event; import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.lang3.exception.ExceptionUtils; import org.guvnor.common.services.builder.ResourceChangeIncrementalBuilder; import org.guvnor.common.services.project.builder.events.InvalidateDMOProjectCacheEvent; import org.guvnor.common.services.project.builder.model.BuildResults; import org.guvnor.common.services.project.builder.service.BuildService; import org.guvnor.test.WeldJUnitRunner; import org.junit.Test; import org.junit.runner.RunWith; import org.kie.workbench.common.services.datamodel.backend.server.service.DataModelService; 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.io.IOService; import org.uberfire.rpc.SessionInfo; import static org.junit.Assert.*; @RunWith(WeldJUnitRunner.class) public class ProjectDataModelConcurrencyTest { private static final Logger logger = LoggerFactory.getLogger(ProjectDataModelConcurrencyTest.class); @Inject private Paths paths; @Inject private BuildResultsObserver buildResultsObserver; @Inject private BuildService buildService; @Inject private KieProjectService projectService; @Inject private DataModelService dataModelService; @Inject private ResourceChangeIncrementalBuilder buildChangeListener; @Inject private SessionInfo sessionInfo; @Inject @Named("ioStrategy") private IOService ioService; @Inject private Event<InvalidateDMOProjectCacheEvent> invalidateDMOProjectCacheEvent; @Test public void testConcurrentResourceUpdates() throws URISyntaxException { final URL pomUrl = this.getClass().getResource( "/DataModelBackendTest1/pom.xml" ); final org.uberfire.java.nio.file.Path nioPomPath = ioService.get( pomUrl.toURI() ); final Path pomPath = paths.convert( nioPomPath ); final URL resourceUrl = this.getClass().getResource( "/DataModelBackendTest1/src/main/resources/empty.rdrl" ); final org.uberfire.java.nio.file.Path nioResourcePath = ioService.get( resourceUrl.toURI() ); final Path resourcePath = paths.convert( nioResourcePath ); //Force full build before attempting incremental changes final KieProject project = projectService.resolveProject( resourcePath ); final BuildResults buildResults = buildService.build( project ); assertNotNull( buildResults ); assertEquals( 0, buildResults.getErrorMessages().size() ); assertEquals( 1, buildResults.getInformationMessages().size() ); //Perform incremental build final int THREADS = 200; final Result result = new Result(); ExecutorService es = Executors.newCachedThreadPool(); for ( int i = 0; i < THREADS; i++ ) { final int operation = ( i % 3 ); switch ( operation ) { case 0: es.execute( new Runnable() { @Override public void run() { try { logger.debug( "[Thread: " + Thread.currentThread().getName() + "] Request to update POM received" ); invalidateCaches( project, pomPath ); buildChangeListener.updateResource( pomPath ); logger.debug( "[Thread: " + Thread.currentThread().getName() + "] POM update completed" ); } catch ( Throwable e ) { result.setFailed( true ); result.setMessage( e.getMessage() ); ExceptionUtils.printRootCauseStackTrace( e ); } } } ); break; case 1: es.execute( new Runnable() { @Override public void run() { try { logger.debug( "[Thread: " + Thread.currentThread().getName() + "] Request to update Resource received" ); invalidateCaches( project, resourcePath ); buildChangeListener.addResource( resourcePath ); logger.debug( "[Thread: " + Thread.currentThread().getName() + "] Resource update completed" ); } catch ( Throwable e ) { result.setFailed( true ); result.setMessage( e.getMessage() ); ExceptionUtils.printRootCauseStackTrace( e ); } } } ); break; case 2: es.execute( new Runnable() { @Override public void run() { try { logger.debug( "[Thread: " + Thread.currentThread().getName() + "] Request for DataModel received" ); dataModelService.getDataModel( resourcePath ); logger.debug( "[Thread: " + Thread.currentThread().getName() + "] DataModel request completed" ); } catch ( Throwable e ) { result.setFailed( true ); result.setMessage( e.getMessage() ); ExceptionUtils.printRootCauseStackTrace( e ); } } } ); } } es.shutdown(); try { es.awaitTermination( 5, TimeUnit.MINUTES ); } catch ( InterruptedException e ) { } if ( result.isFailed() ) { fail( result.getMessage() ); } } private void invalidateCaches( final KieProject project, final Path resourcePath ) { invalidateDMOProjectCacheEvent.fire( new InvalidateDMOProjectCacheEvent( sessionInfo, project, resourcePath ) ); } private static class Result { private boolean failed = false; private String message = ""; public synchronized boolean isFailed() { return failed; } public synchronized void setFailed( boolean failed ) { this.failed = failed; } public synchronized String getMessage() { return message; } public synchronized void setMessage( String message ) { this.message = message; } } }