| /* |
| * 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; |
| } |
| } |
| } |