blob: 57d168390e420a32bb54c3c18d42a4b80bfae7e4 [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.qi4j.sample.dcicargo.sample_b.communication.web.handling;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxFallbackButton;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.util.value.ValueMap;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.qi4j.sample.dcicargo.sample_b.communication.query.CommonQueries;
import org.qi4j.sample.dcicargo.sample_b.communication.query.HandlingQueries;
import org.qi4j.sample.dcicargo.sample_b.communication.web.BasePage;
import org.qi4j.sample.dcicargo.sample_b.context.interaction.handling.ProcessHandlingEvent;
import org.qi4j.sample.dcicargo.sample_b.data.structure.handling.HandlingEventType;
import org.qi4j.sample.dcicargo.sample_b.infrastructure.wicket.form.AbstractForm;
import org.qi4j.sample.dcicargo.sample_b.infrastructure.wicket.form.DateTextFieldWithPicker;
/**
* Incident Logging Application mockup page
*
* This is a mockup of an Incident Logging Application interface that external handling
* authorities would use to send us handling event data for cargos that they have handled.
*
* For simplicity we don't create a separate Incident Logging Application for now but instead
* let it reside inside our booking application. We act as a handling authority entering handling
* event data, and in the lower half of the page we mock the receipt of the data and show the
* validation results from our own validation of the incoming data.
*
* We could also instead (in future versions?) implement a web service endpoint receiving
* asynchronous messages from a separate incident logging application. We have prepared for
* this by separating the processing in three steps, with ProcessHandlingEvent coordinating
* the following steps:
*
* {@link ProcessHandlingEvent}
* 1. parsedHandlingEventData = parse( completion, trackingId, eventType, unLocode, voyage )
* 2. handlingEvent = register( parsedHandlingEventData )
* 3. inspect( handlingEvent )
*
* The last step updates the delivery status of the cargo.
*
* On this page we act as a handling authority sending handling event data to our booking
* application. We perform basic validation and parsing of incoming data, and in case of a
* valid registration attempt, synchronously send the data to the booking application for
* processing there.
*/
public class IncidentLoggingApplicationMockupPage extends BasePage
{
public IncidentLoggingApplicationMockupPage()
{
super( "handling" ); // Selects the Handling tab
add( new ReportHandlingEventForm() );
}
private final class ReportHandlingEventForm extends AbstractForm<Void>
{
FeedbackPanel feedback;
// Form values
Date completion;
String trackingId, unLocode, voyageNumber, eventType;
// Input
TextField<String> trackingIdInput, eventTypeInput, voyageInput, locationInput;
String trackingIdSelected, eventTypeSelected, voyageSelected, locationSelected;
DropDownChoice<String> trackingIdSelector, eventTypeSelector, voyageSelector, locationSelector;
// To avoid re-submitting same data
String lastSubmittedData;
public ReportHandlingEventForm()
{
final FeedbackPanel feedback = new FeedbackPanel( "feedback" );
add( feedback.setOutputMarkupId( true ) );
// Completion time
final DateTextFieldWithPicker completionDateInput = new DateTextFieldWithPicker( "completion", "Completion", this );
completionDateInput.earliestDate( new LocalDate() );
add( completionDateInput.setLabel( Model.of( "Completion" ) ) );
HandlingQueries fetch = new HandlingQueries();
// Tracking id
trackingIdInput = new TextField<String>( "trackingIdInput", new PropertyModel<String>( this, "trackingId" ) );
add( trackingIdInput.setRequired( true ).setLabel( Model.of( "Cargo" ) ).setOutputMarkupId( true ) );
trackingIdSelector = new DropDownChoice<String>( "trackingIdSelector",
new PropertyModel<String>( this, "trackingIdSelected" ),
fetch.cargoIds() );
trackingIdSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
{
@Override
protected void onUpdate( AjaxRequestTarget target )
{
trackingId = trackingIdSelected;
target.add( feedback, trackingIdInput, trackingIdSelector );
}
} );
add( trackingIdSelector.setOutputMarkupId( true ) );
// Event Type
eventTypeInput = new TextField<String>( "eventTypeInput", new PropertyModel<String>( this, "eventType" ) );
add( eventTypeInput.setRequired( true ).setLabel( Model.of( "Event Type" ) ).setOutputMarkupId( true ) );
eventTypeSelector = new DropDownChoice<String>( "eventTypeSelector",
new PropertyModel<String>( this, "eventTypeSelected" ),
fetch.eventTypes() );
eventTypeSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
{
@Override
protected void onUpdate( AjaxRequestTarget target )
{
eventType = eventTypeSelected;
target.add( feedback, eventTypeInput, eventTypeSelector );
}
} );
add( eventTypeSelector.setOutputMarkupId( true ) );
// Voyage (optional in some cases)
voyageInput = new TextField<String>( "voyageInput", new PropertyModel<String>( this, "voyageNumber" ) );
add( voyageInput.setLabel( Model.of( "Voyage" ) ).setOutputMarkupId( true ) );
voyageSelector = new DropDownChoice<String>( "voyageSelector",
new PropertyModel<String>( this, "voyageSelected" ),
fetch.voyages() );
voyageSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
{
@Override
protected void onUpdate( AjaxRequestTarget target )
{
voyageNumber = voyageSelected;
target.add( feedback, voyageInput, voyageSelector );
}
} );
add( voyageSelector.setOutputMarkupId( true ) );
// Location
locationInput = new TextField<String>( "locationInput", new PropertyModel<String>( this, "unLocode" ) );
add( locationInput.setRequired( true ).setLabel( Model.of( "Location" ) ).setOutputMarkupId( true ) );
locationSelector = new DropDownChoice<String>( "locationSelector",
new PropertyModel<String>( this, "locationSelected" ),
new CommonQueries().unLocodes() );
locationSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
{
@Override
protected void onUpdate( AjaxRequestTarget target )
{
unLocode = locationSelected;
target.add( feedback, locationInput, locationSelector );
}
} );
add( locationSelector.setOutputMarkupId( true ) );
// Submit and process
add( new AjaxFallbackButton( "register", this )
{
@Override
protected void onSubmit( AjaxRequestTarget target, Form<?> form )
{
try
{
// We want to allow making multiple _unique_ handling event registrations
if( sameDataIsSubmitted() )
{
throw new Exception( "Can't re-submit the same data." );
}
// We simulate receiving raw text data from incident logging applications
// Add current time to date to have same-dates in processing order (would register full time in real app)
Date adjustedCompletion = new Date( completion.getTime() + new DateTime().getMillisOfDay() );
String completionTimeString = new SimpleDateFormat( "yyyy-MM-dd HH:mm" ).format( adjustedCompletion );
// Parse "incoming" data (step 1 of ProcessHandlingEvent use case)
tbf.newTransient( ProcessHandlingEvent.class ).parse(
completionTimeString, trackingId, eventType, unLocode, voyageNumber );
/**
* We could redirect to Details, but it's more fun to update details in a separate
* window to follow the successive handling event registrations you make...
* */
// setResponsePage( CargoDetailsPage.class, new PageParameters().set( 0, trackingId ) );
try
{
HandlingEventType.valueOf( eventType );
}
catch( Exception e )
{
throw new Exception( "'" + eventType + "' is not a valid handling event type" );
}
ValueMap map = new ValueMap();
map.put( "type", eventType );
map.put( "location", unLocode );
if( voyageNumber != null )
{
map.put( "voyage", voyageNumber );
}
String msg = new StringResourceModel( "handlingEvent.${type}", this, new Model<ValueMap>( map ) )
.getObject();
feedback.info( "Registered handling event for cargo '" + trackingId + "': " + msg );
target.add( feedback );
}
catch( Exception e )
{
logger.warn( "Problem registering handling event: " + e.getMessage() );
feedback.error( e.getMessage() );
target.add( feedback );
}
}
@Override
protected void onError( final AjaxRequestTarget target, Form<?> form )
{
target.add( feedback );
focusFirstError( target );
}
} );
}
private boolean sameDataIsSubmitted()
{
String submittedData = completion.toString() + trackingId + unLocode + voyageNumber + eventType;
if( submittedData.equals( lastSubmittedData ) )
{
return true;
}
// Valid new data submitted
lastSubmittedData = submittedData;
return false;
}
}
}