blob: f07a202cb850b347885ebc1a4149c10e31edb70f [file] [log] [blame]
/*
* 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.polygene.test.metrics;
import java.util.Collection;
import org.apache.polygene.api.activation.ActivationException;
import org.apache.polygene.api.activation.PassivationException;
import org.apache.polygene.api.association.ManyAssociation;
import org.apache.polygene.api.common.Visibility;
import org.apache.polygene.api.concern.Concerns;
import org.apache.polygene.api.entity.EntityBuilder;
import org.apache.polygene.api.identity.Identity;
import org.apache.polygene.api.identity.StringIdentity;
import org.apache.polygene.api.injection.scope.Structure;
import org.apache.polygene.api.metrics.TimingCapture;
import org.apache.polygene.api.metrics.TimingCaptureAllConcern;
import org.apache.polygene.api.metrics.TimingCaptureConcern;
import org.apache.polygene.api.mixin.Mixins;
import org.apache.polygene.api.property.Property;
import org.apache.polygene.api.service.ServiceActivation;
import org.apache.polygene.api.structure.Module;
import org.apache.polygene.api.unitofwork.NoSuchEntityException;
import org.apache.polygene.api.unitofwork.UnitOfWork;
import org.apache.polygene.api.unitofwork.concern.UnitOfWorkConcern;
import org.apache.polygene.api.unitofwork.concern.UnitOfWorkPropagation;
import org.apache.polygene.bootstrap.ApplicationAssembly;
import org.apache.polygene.bootstrap.Assembler;
import org.apache.polygene.bootstrap.Assemblers;
import org.apache.polygene.bootstrap.AssemblyException;
import org.apache.polygene.bootstrap.LayerAssembly;
import org.apache.polygene.bootstrap.ModuleAssembly;
import org.apache.polygene.test.AbstractPolygeneBaseTest;
import org.apache.polygene.test.EntityTestAssembler;
import org.apache.polygene.test.util.JmxFixture;
import org.junit.Test;
import static java.util.stream.Collectors.toList;
import static org.apache.polygene.api.unitofwork.concern.UnitOfWorkPropagation.Propagation.MANDATORY;
import static org.apache.polygene.api.usecase.UsecaseBuilder.newUsecase;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;
// TODO Test errors
public abstract class AbstractPolygeneMetricsTest extends AbstractPolygeneBaseTest
{
public interface Person
{
Property<String> name();
}
public interface PersonList
{
Identity LIST_ID = StringIdentity.identity( "person-list" );
ManyAssociation<Person> all();
}
@Concerns( { TimingCaptureAllConcern.class, UnitOfWorkConcern.class} )
@Mixins( CommandsMixin.class )
public interface Commands extends ServiceActivation
{
@UnitOfWorkPropagation( MANDATORY )
Person create( Identity id, String name );
@UnitOfWorkPropagation( MANDATORY )
void rename( Identity id, String newName );
@UnitOfWorkPropagation( MANDATORY )
void delete( Identity id );
}
public static class CommandsMixin implements Commands
{
@Structure
private Module module;
@Override
public void activateService() throws Exception
{
try (UnitOfWork uow = module.unitOfWorkFactory().newUnitOfWork( newUsecase( "Init Person List" ) ) )
{
try
{
uow.get( PersonList.class, PersonList.LIST_ID );
}
catch( NoSuchEntityException ex )
{
uow.newEntity( PersonList.class, PersonList.LIST_ID );
uow.complete();
}
}
}
@Override
public void passivateService()
{
}
@Override
public Person create( Identity id, String name )
{
UnitOfWork uow = module.unitOfWorkFactory().currentUnitOfWork();
PersonList list = uow.get( PersonList.class, PersonList.LIST_ID );
EntityBuilder<Person> builder = uow.newEntityBuilder( Person.class, id );
builder.instance().name().set( name );
Person person = builder.newInstance();
list.all().add( person );
return person;
}
@Override
public void rename( Identity id, String newName )
{
module.unitOfWorkFactory().currentUnitOfWork().get( Person.class, id ).name().set( newName );
}
@Override
public void delete( Identity id )
{
UnitOfWork uow = module.unitOfWorkFactory().currentUnitOfWork();
PersonList list = uow.get( PersonList.class, PersonList.LIST_ID );
Person person = uow.get( Person.class, id );
list.all().remove( person );
uow.remove( person );
}
}
@Concerns( { TimingCaptureConcern.class, UnitOfWorkConcern.class} )
@Mixins( QueriesMixin.class )
public interface Queries
{
@UnitOfWorkPropagation( MANDATORY )
Person byId( Identity id );
@TimingCapture
@UnitOfWorkPropagation( MANDATORY )
Iterable<Person> all();
}
public static class QueriesMixin implements Queries
{
@Structure
private Module module;
@Override
public Person byId( Identity id )
{
return module.unitOfWorkFactory().currentUnitOfWork().get( Person.class, id );
}
@Override
public Iterable<Person> all()
{
return module.unitOfWorkFactory().currentUnitOfWork()
.get( PersonList.class, PersonList.LIST_ID )
.all().toList();
}
}
@Override
protected final void defineApplication( ApplicationAssembly app )
{
app.setName( "app" );
LayerAssembly domain = app.layer( "domain" );
ModuleAssembly model = domain.module( "model" );
model.entities( Person.class, PersonList.class )
.visibleIn( Visibility.layer );
ModuleAssembly services = domain.module( "services" );
services.services( Commands.class, Queries.class )
.instantiateOnStartup()
.visibleIn( Visibility.application );
LayerAssembly config = app.layer( "config" );
ModuleAssembly configModule = config.module( "config" );
new EntityTestAssembler()
.visibleIn( Visibility.module )
.assemble( configModule );
LayerAssembly infra = app.layer( "infra" );
ModuleAssembly storage = infra.module( "storage" );
entityStoreAssembler( configModule, Visibility.application )
.visibleIn( Visibility.application )
.assemble( storage );
metricsAssembler()
.visibleIn( Visibility.application )
.assemble( infra.module( "metrics" ) );
domain.uses( infra );
infra.uses( config );
}
protected Assemblers.Visible<? extends Assembler> entityStoreAssembler( ModuleAssembly configModule, Visibility configVisibility ) throws AssemblyException
{
return new EntityTestAssembler().defaultServicesVisibleIn( Visibility.module );
}
protected abstract Assemblers.Visible<? extends Assembler> metricsAssembler();
protected Module metricsModule()
{
return application.findModule( "infra", "metrics" );
}
protected static final String UOW_TIMER_NAME = "app.domain.services.UnitOfWork.timer";
protected static final String ALL_NAME = "app.domain.services.AbstractPolygeneMetricsTest.Queries.all";
protected static final String CREATE_NAME = "app.domain.services.AbstractPolygeneMetricsTest.Commands.create";
protected static final String RENAME_NAME = "app.domain.services.AbstractPolygeneMetricsTest.Commands.rename";
protected static final String DELETE_NAME = "app.domain.services.AbstractPolygeneMetricsTest.Commands.delete";
protected final void assertUowTimer( MetricValuesProvider metrics ) throws PassivationException, ActivationException
{
Long initialUowCount = metrics.timerCount( UOW_TIMER_NAME );
runScenario1();
assertThat( UOW_TIMER_NAME + " count incremented by 3", metrics.timerCount( UOW_TIMER_NAME ), is( initialUowCount + 3L ) );
application.passivate();
application.activate();
assertThat( UOW_TIMER_NAME + " count reset on passivation", metrics.timerCount( UOW_TIMER_NAME ), equalTo( initialUowCount ) );
}
protected final void assertTimingCapture( MetricValuesProvider metrics ) throws PassivationException, ActivationException
{
// Initial state
assertThat( ALL_NAME + " count is 0 at start", metrics.timerCount( ALL_NAME ), is( 0L ) );
assertThat( CREATE_NAME + " count is 0 at start", metrics.timerCount( CREATE_NAME ), is( 0L ) );
assertThat( RENAME_NAME + " count is 0 at start", metrics.timerCount( RENAME_NAME ), is( 0L ) );
assertThat( DELETE_NAME+ " count is 0 at start", metrics.timerCount( DELETE_NAME ), is( 0L ) );
// Run scenario
runScenario1();
// Queries.byId() timings are not captured
assertThat( "Queries.byId() has no timer", metrics.registeredMetricNames(), not( contains( containsString( "byId" ) ) ) );
// Captured timings
assertThat( ALL_NAME + " count is 4 after scenario", metrics.timerCount( ALL_NAME ), is( 4L ) );
assertThat( CREATE_NAME + " count is 1 after scenario", metrics.timerCount( CREATE_NAME ), is( 1L ) );
assertThat( RENAME_NAME + " count is 1 after scenario", metrics.timerCount( RENAME_NAME ), is( 1L ) );
assertThat( DELETE_NAME + " count is 1 after scenario", metrics.timerCount( DELETE_NAME ), is( 1L ) );
// Reset on passivation
application.passivate();
application.activate();
assertThat( ALL_NAME + " count is 0 after restart", metrics.timerCount( ALL_NAME ), is( 0L ) );
assertThat( CREATE_NAME + " count is 0 after restart", metrics.timerCount( CREATE_NAME ), is( 0L ) );
assertThat( RENAME_NAME + " count is 0 after restart", metrics.timerCount( RENAME_NAME ), is( 0L ) );
assertThat( DELETE_NAME + " count is 0 after restart", metrics.timerCount( DELETE_NAME ), is( 0L ) );
}
protected final void runScenario1()
{
Module services = application.findModule( "domain", "services" );
Commands commands = services.findService( Commands.class ).get();
Queries queries = services.findService( Queries.class ).get();
Identity identity = StringIdentity.identity( "1" );
try (UnitOfWork uow = services.unitOfWorkFactory().newUnitOfWork( newUsecase( "Step 1" ) ) )
{
assertThat( queries.all().iterator().hasNext(), is( false ) );
assertThat( commands.create( identity, "Bob Geldof" ).name().get(), equalTo( "Bob Geldof" ) );
assertThat( queries.byId( identity ).name().get(), equalTo( "Bob Geldof" ) );
uow.complete();
}
try (UnitOfWork uow = services.unitOfWorkFactory().newUnitOfWork(newUsecase("Step 2")))
{
assertThat( queries.all().iterator().next().name().get(), equalTo( "Bob Geldof" ) );
assertThat( queries.byId( identity ).name().get(), equalTo( "Bob Geldof" ) );
commands.rename( identity, "Nina Hagen" );
assertThat( queries.all().iterator().next().name().get(), equalTo( "Nina Hagen" ) );
uow.complete();
}
try (UnitOfWork uow = services.unitOfWorkFactory().newUnitOfWork(newUsecase("Step 3")))
{
commands.delete( identity );
assertThat( queries.all().iterator().hasNext(), is( false ) );
uow.complete();
}
}
protected static class JmxMetricTestAdapter implements MetricValuesProvider
{
private final JmxFixture jmx = new JmxFixture( "metrics:name=" );
@Override
public long timerCount( String name )
{
if( jmx.objectExists( name ) ) {
return jmx.attributeValue( name, "Count", Long.class );
}
return 0L;
}
@Override
public Collection<String> registeredMetricNames()
{
return jmx.allObjectNames().stream()
.filter( objName -> objName.startsWith( jmx.prefix() ) )
.map( objName -> objName.substring( jmx.prefix().length() ) )
.collect( toList() );
}
}
@Test
public void uowTimerJmx() throws PassivationException, ActivationException
{
assertUowTimer( new JmxMetricTestAdapter() );
}
@Test
public void timingCaptureJmx() throws PassivationException, ActivationException
{
assertTimingCapture( new JmxMetricTestAdapter() );
}
}