/**
*
* Copyright
* 2009-2015 Jayway Products AB
* 2016-2017 Föreningen Sambruk
*
* Licensed under AGPL, Version 3.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.gnu.org/licenses/agpl.txt
*
* 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 se.streamsource.streamflow.web.context;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.codehaus.jackson.annotate.JsonAnyGetter;
import org.json.JSONObject;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.qi4j.api.Qi4j;
import org.qi4j.api.composite.TransientBuilder;
import org.qi4j.api.composite.TransientBuilderFactory;
import org.qi4j.api.composite.TransientComposite;
import org.qi4j.api.entity.EntityComposite;
import org.qi4j.api.object.ObjectBuilder;
import org.qi4j.api.object.ObjectBuilderFactory;
import org.qi4j.api.query.QueryBuilderFactory;
import org.qi4j.api.service.ServiceFinder;
import org.qi4j.api.unitofwork.UnitOfWork;
import org.qi4j.api.unitofwork.UnitOfWorkFactory;
import org.qi4j.api.value.ValueBuilderFactory;
import org.qi4j.api.value.ValueComposite;
import org.qi4j.bootstrap.ApplicationAssembler;
import org.qi4j.bootstrap.AssemblyException;
import org.qi4j.bootstrap.Energy4Java;
import org.qi4j.spi.Qi4jSPI;
import org.qi4j.spi.structure.ApplicationModelSPI;
import org.qi4j.spi.structure.ApplicationSPI;
import org.qi4j.spi.structure.ModuleSPI;
import se.streamsource.dci.api.RoleMap;
import se.streamsource.dci.value.EntityValue;
import se.streamsource.dci.value.StringValue;
import se.streamsource.dci.value.link.LinkValue;
import se.streamsource.dci.value.link.LinksValue;
import se.streamsource.streamflow.infrastructure.event.domain.DomainEvent;
import se.streamsource.streamflow.infrastructure.event.domain.source.helper.EventCollector;
import se.streamsource.streamflow.infrastructure.event.domain.source.helper.Events;
import se.streamsource.streamflow.test.StreamflowWebContextTestAssembler;
import se.streamsource.streamflow.web.domain.Describable;
/**
* Base class for roleMap tests. RoleMap tests should subclass this test and use the fluent API provided
* by this class to do testing of contexts.
*/
public abstract class ContextTest
{
protected static Qi4j api;
protected static Qi4jSPI spi;
protected static Energy4Java qi4j;
protected static ApplicationModelSPI applicationModel;
protected static ApplicationSPI application;
protected static TransientBuilderFactory transientBuilderFactory;
protected static ObjectBuilderFactory objectBuilderFactory;
protected static ValueBuilderFactory valueBuilderFactory;
protected static UnitOfWorkFactory unitOfWorkFactory;
protected static QueryBuilderFactory queryBuilderFactory;
protected static ServiceFinder serviceLocator;
protected static ModuleSPI moduleInstance;
private static EventCollector eventCollector = new EventCollector();
@BeforeClass
public static void setUp()
throws Exception
{
qi4j = new Energy4Java();
applicationModel = newApplication();
if (applicationModel == null)
{
// An AssemblyException has occurred that the Test wants to check for.
return;
}
application = applicationModel.newInstance( qi4j.spi() );
api = spi = qi4j.spi();
application.activate();
// Use the Context/Context module
moduleInstance = (ModuleSPI) application.findModule( "Context", "Context" );
transientBuilderFactory = moduleInstance.transientBuilderFactory();
objectBuilderFactory = moduleInstance.objectBuilderFactory();
valueBuilderFactory = moduleInstance.valueBuilderFactory();
unitOfWorkFactory = moduleInstance.unitOfWorkFactory();
queryBuilderFactory = moduleInstance.queryBuilderFactory();
serviceLocator = moduleInstance.serviceFinder();
}
/**
* This method is called when there was an AssemblyException in the creation of the Qi4j application model.
* <p>Override this method to catch valid failures to place into test suites.
*
* @param exception the exception thrown.
* @throws AssemblyException The default implementation of this method will simply re-throw the exception.
*/
protected static void assemblyException( AssemblyException exception )
throws AssemblyException
{
throw exception;
}
@AfterClass
public static void tearDown()
throws Exception
{
if (unitOfWorkFactory != null && unitOfWorkFactory.currentUnitOfWork() != null)
{
UnitOfWork current;
while ((current = unitOfWorkFactory.currentUnitOfWork()) != null)
{
if (current.isOpen())
{
current.discard();
} else
{
throw new InternalError( "I have seen a case where a UoW is on the stack, but not opened." );
}
}
new Exception( "UnitOfWork not properly cleaned up" ).printStackTrace();
}
if (application != null)
{
application.passivate();
}
// Set static variables to null to avoid leaks
api = null;
spi = null;
qi4j = null;
applicationModel = null;
application = null;
transientBuilderFactory = null;
objectBuilderFactory = null;
valueBuilderFactory = null;
unitOfWorkFactory = null;
queryBuilderFactory = null;
serviceLocator = null;
moduleInstance = null;
}
@Before
public void setupRoleMap()
{
RoleMap.setCurrentRoleMap( new RoleMap() );
}
@After
public void clearRoleMap()
{
RoleMap.clearCurrentRoleMap();
}
protected static ApplicationModelSPI newApplication() throws AssemblyException
{
ApplicationAssembler assembler = new StreamflowWebContextTestAssembler( Events.adapter( eventCollector ) );
try
{
return qi4j.newApplicationModel( assembler );
}
catch (AssemblyException e)
{
assemblyException( e );
return null;
}
}
// Helper methods
protected static <T> T context( Class<T> contextClass )
{
if (TransientComposite.class.isAssignableFrom( contextClass ))
{
TransientBuilder<T> builder = transientBuilderFactory.newTransientBuilder( contextClass );
for (Object rolePlayer : RoleMap.current().getAll(Object.class))
{
builder.use(rolePlayer);
}
return builder.newInstance();
}
else
{
ObjectBuilder<T> builder = objectBuilderFactory.newObjectBuilder( contextClass );
for (Object rolePlayer : RoleMap.current().getAll(Object.class))
{
builder.use(rolePlayer);
}
return builder.newInstance();
}
}
protected static <T> T playRole( T oldEntity )
{
if (oldEntity == null)
throw new NullPointerException( "No entity used to play role" );
T entity = unitOfWorkFactory.currentUnitOfWork().get( oldEntity );
RoleMap.current().set( entity );
return entity;
}
protected static <T> T playRole( Class<T> roleClass, String id )
{
T entity = unitOfWorkFactory.currentUnitOfWork().get( roleClass, id );
RoleMap.current().set( entity );
return entity;
}
protected static <T> T playRole( Class<T> roleClass, LinkValue link )
{
T entity = unitOfWorkFactory.currentUnitOfWork().get( roleClass, link.id().get() );
RoleMap.current().set( entity );
return entity;
}
protected static <T> T value( Class<T> valueClass, String json )
{
return valueBuilderFactory.newValueFromJSON( valueClass, json );
}
protected static StringValue stringValue( String value )
{
return value( StringValue.class, "{'string':'" + value + "'}" );
}
protected static EntityValue entityValueNull( )
{
return value( EntityValue.class, "{'entity':"+ JSONObject.NULL +"}" );
}
protected static EntityValue entityValue( String value )
{
return value( EntityValue.class, "{'entity':'" + value + "'}" );
}
protected static EntityValue entityValue( LinkValue link )
{
return value( EntityValue.class, "{'entity':'" + link.id().get() + "'}" );
}
protected static <T> T entity( LinkValue link )
{
return (T) unitOfWorkFactory.currentUnitOfWork().get( EntityComposite.class, link.id().get() );
}
protected static boolean valueContains( ValueComposite value, String jsonFragment )
{
return value.toJSON().contains( jsonFragment );
}
protected static void eventsOccurred( String... expectedEvents )
{
List<DomainEvent> events = eventCollector.events();
if (events == null)
events = Collections.emptyList();
List<String> eventNames = new ArrayList<String>();
for (DomainEvent event : events)
{
eventNames.add( event.name().get() );
}
// Assert.assertThat( eventNames.toArray( new String[eventNames.size()] ), CoreMatchers.equalTo( expectedEvents ) );
clearEvents();
}
protected static void clearEvents()
{
eventCollector.events().clear();
}
protected static LinkValue findLink( LinksValue links, String name )
{
String names = null;
for (LinkValue linkValue : links.links().get())
{
if (linkValue.text().get().equals( name ))
{
return linkValue;
} else
{
if (names == null)
names = linkValue.text().get();
else
names += "," + linkValue.text().get();
}
}
if (names == null)
throw new IllegalArgumentException( "No link found named " + name + ". List was empty" );
else
throw new IllegalArgumentException( "No link found named " + name + ". Available names:" + names );
}
protected static <T> T findDescribable( Iterable<T> iterable, String name )
{
String names = null;
for (T item : iterable)
{
String itemName = ((Describable) item).getDescription();
if (itemName.equals( name ))
{
return item;
} else
{
if (names == null)
names = itemName;
else
names += "," + itemName;
}
}
if (names == null)
throw new IllegalArgumentException( "No link found named " + name + ". List was empty" );
else
throw new IllegalArgumentException( "No link found named " + name + ". Available names:" + names );
}
}