blob: a4523d437b3497ebd7c4e5242c80c3b92344e091 [file] [log] [blame]
/*
* Copyright 2011 Marc Grue.
*
* 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.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration;
import org.apache.zest.api.injection.scope.This;
import org.apache.zest.api.mixin.Mixins;
import org.apache.zest.api.query.Query;
import org.apache.zest.api.query.QueryBuilder;
import org.apache.zest.api.unitofwork.NoSuchEntityException;
import org.apache.zest.api.unitofwork.UnitOfWork;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.ProcessHandlingEvent;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.parsing.dto.ParsedHandlingEventData;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.AlreadyClaimedException;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.CannotRegisterHandlingEventException;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.DuplicateEventException;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.MissingVoyageNumberException;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.UnknownCargoException;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.UnknownLocationException;
import org.apache.zest.sample.dcicargo.sample_b.context.interaction.handling.registration.exception.UnknownVoyageException;
import org.apache.zest.sample.dcicargo.sample_b.data.aggregateroot.HandlingEventAggregateRoot;
import org.apache.zest.sample.dcicargo.sample_b.data.entity.HandlingEventEntity;
import org.apache.zest.sample.dcicargo.sample_b.data.factory.HandlingEventFactory;
import org.apache.zest.sample.dcicargo.sample_b.data.factory.exception.CannotCreateHandlingEventException;
import org.apache.zest.sample.dcicargo.sample_b.data.structure.cargo.Cargo;
import org.apache.zest.sample.dcicargo.sample_b.data.structure.handling.HandlingEvent;
import org.apache.zest.sample.dcicargo.sample_b.data.structure.handling.HandlingEventType;
import org.apache.zest.sample.dcicargo.sample_b.data.structure.location.Location;
import org.apache.zest.sample.dcicargo.sample_b.data.structure.tracking.TrackingId;
import org.apache.zest.sample.dcicargo.sample_b.data.structure.voyage.Voyage;
import org.apache.zest.sample.dcicargo.sample_b.infrastructure.dci.Context;
import org.apache.zest.sample.dcicargo.sample_b.infrastructure.dci.RoleMixin;
import static org.apache.zest.api.query.QueryExpressions.*;
import static org.apache.zest.sample.dcicargo.sample_b.data.aggregateroot.HandlingEventAggregateRoot.HANDLING_EVENTS_ID;
import static org.apache.zest.sample.dcicargo.sample_b.data.structure.handling.HandlingEventType.*;
/**
* Register Handling Event (subfunction use case)
*
* Second step in the ProcessHandlingEvent use case.
*
* Verifies handling event data received from ParseHandlingEventData against the database in
* order to create a valid HandlingEvent.
*
* If only the enclosing {@link ProcessHandlingEvent} summary use case is allowed to call this
* subfunction use case, we could draw an interesting parallel to the responsibilities of the
* DDD aggregate of enforcing invariants between its member objects. Whereas here it would be
* {@link ProcessHandlingEvent} enforcing its subfunction use cases to process correctly and
* ultimately abort the interactions chain when any of the subfunction interactions fail...
*
* As an example of "process integrity enforcement" we here validate that a cargo with the
* given TrackingId exists in our system. When we then inspect the cargo in the third and last
* step of the ProcessHandlingEvent summary use case, we presume having a valid cargo.
*
* How can we ensure that RegisterHandlingEvent is not mistakenly called directly out of the
* larger context of ProcessHandlingEvent?
*
* IMPORTANT:
* Compared to the DDD sample, we don't save a Cargo object with the HandlingEvent, but only
* the TrackingId! HandlingEvent never needs the full Cargo graph (not in the DDD sample either),
* so by saving only the TrackingId we get a much slimmer HandlingEvent object.
*/
public class RegisterHandlingEvent extends Context
{
EventRegistrarRole eventRegistrar;
ParsedHandlingEventData eventData;
HandlingEventType eventType;
String trackingIdString;
String unLocodeString;
String voyageNumberString;
public RegisterHandlingEvent( ParsedHandlingEventData parsedEventData )
{
eventRegistrar = rolePlayer( EventRegistrarRole.class, HandlingEventAggregateRoot.class, HANDLING_EVENTS_ID );
this.eventData = parsedEventData;
eventType = parsedEventData.handlingEventType().get();
trackingIdString = parsedEventData.trackingIdString().get();
unLocodeString = parsedEventData.unLocodeString().get();
voyageNumberString = parsedEventData.voyageNumberString().get();
}
public HandlingEvent getEvent()
throws CannotRegisterHandlingEventException
{
return eventRegistrar.registerAndGetHandlingEvent();
}
@Mixins( EventRegistrarRole.Mixin.class )
public interface EventRegistrarRole
{
void setContext( RegisterHandlingEvent context );
HandlingEvent registerAndGetHandlingEvent()
throws CannotRegisterHandlingEventException;
class Mixin
extends RoleMixin<RegisterHandlingEvent>
implements EventRegistrarRole
{
@This
HandlingEventFactory eventFactory;
public HandlingEvent registerAndGetHandlingEvent()
throws CannotRegisterHandlingEventException
{
UnitOfWork uow = uowf.currentUnitOfWork();
TrackingId trackingId;
Location location;
Voyage voyage = null;
// Step 1 - Find Cargo from tracking id string
try
{
trackingId = uow.get( Cargo.class, c.trackingIdString ).trackingId().get();
}
catch( NoSuchEntityException e )
{
throw new UnknownCargoException( c.eventData );
}
// Step 2 - Find Location from UnLocode string
try
{
location = uow.get( Location.class, c.unLocodeString );
}
catch( NoSuchEntityException e )
{
throw new UnknownLocationException( c.eventData );
}
// Step 3 - Find Voyage from voyage number string
if( c.eventType.requiresVoyage() )
{
if( c.voyageNumberString == null )
{
throw new MissingVoyageNumberException( c.eventData );
}
try
{
voyage = uow.get( Voyage.class, c.voyageNumberString );
}
catch( NoSuchEntityException e )
{
throw new UnknownVoyageException( c.eventData );
}
}
// Step 4 - Verify that cargo is not received, in customs or claimed more than once
if( c.eventType.equals( RECEIVE ) || c.eventType.equals( CUSTOMS ) || c.eventType.equals( CLAIM ) )
{
QueryBuilder<HandlingEventEntity> qb = qbf.newQueryBuilder( HandlingEventEntity.class )
.where(
and(
eq( templateFor( HandlingEvent.class ).trackingId().get().id(), c.trackingIdString ),
eq( templateFor( HandlingEvent.class ).handlingEventType(), c.eventType )
)
);
Query<HandlingEventEntity> duplicates = uowf.currentUnitOfWork().newQuery( qb );
if( duplicates.count() > 0 )
{
throw new DuplicateEventException( c.eventData );
}
}
// Step 5 - Verify that cargo is not handled after being claimed
if( !c.eventType.equals( CLAIM ) )
{
HandlingEvent eventTemplate = templateFor( HandlingEvent.class );
QueryBuilder<HandlingEventEntity> qb = qbf.newQueryBuilder( HandlingEventEntity.class )
.where(
and(
eq( eventTemplate.trackingId().get().id(), c.trackingIdString ),
eq( eventTemplate.handlingEventType(), CLAIM )
)
);
Query<HandlingEventEntity> alreadyClaimed = uowf.currentUnitOfWork().newQuery( qb );
if( alreadyClaimed.count() > 0 )
{
throw new AlreadyClaimedException( c.eventData );
}
}
// Step 6 - Create Handling Event in the system
try
{
return eventFactory.createHandlingEvent( c.eventData.registrationTime().get(),
c.eventData.completionTime().get(),
trackingId,
c.eventData.handlingEventType().get(),
location,
voyage );
}
catch( CannotCreateHandlingEventException e )
{
throw new CannotRegisterHandlingEventException( c.eventData );
}
}
}
}
}