# WicketQi4jApplication.java

    /*
     * 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_a.infrastructure;

    import javax.servlet.http.HttpServletRequest;
    import org.apache.wicket.Page;
    import org.apache.wicket.protocol.http.WebApplication;
    import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
    import org.apache.wicket.request.cycle.RequestCycle;
    import org.qi4j.api.Qi4j;
    import org.qi4j.api.composite.TransientBuilderFactory;
    import org.qi4j.api.injection.scope.Service;
    import org.qi4j.api.injection.scope.Structure;
    import org.qi4j.api.query.QueryBuilderFactory;
    import org.qi4j.api.structure.Application;
    import org.qi4j.api.structure.Module;
    import org.qi4j.api.unitofwork.ConcurrentEntityModificationException;
    import org.qi4j.api.unitofwork.UnitOfWork;
    import org.qi4j.api.unitofwork.UnitOfWorkCompletionException;
    import org.qi4j.api.unitofwork.UnitOfWorkFactory;
    import org.qi4j.api.usecase.UsecaseBuilder;
    import org.qi4j.api.value.ValueBuilderFactory;
    import org.qi4j.bootstrap.ApplicationAssembler;
    import org.qi4j.bootstrap.Energy4Java;
    import org.qi4j.sample.dcicargo.sample_a.infrastructure.conversion.EntityToDTOService;
    import org.qi4j.sample.dcicargo.sample_a.infrastructure.dci.Context;
    import org.qi4j.sample.dcicargo.sample_a.infrastructure.model.Queries;
    import org.qi4j.sample.dcicargo.sample_a.infrastructure.model.ReadOnlyModel;
    import org.qi4j.sample.dcicargo.sample_a.infrastructure.wicket.page.BaseWebPage;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    /**
     * Base Wicket Web Application containing the Zest application.
     */
    public class WicketQi4jApplication
        extends WebApplication
    {
        public Logger logger = LoggerFactory.getLogger( WicketQi4jApplication.class );

        protected Application qi4jApp;
        protected Module qi4jModule;

        @Structure
        protected UnitOfWorkFactory uowf;

        @Structure
        protected ValueBuilderFactory vbf;

        @Structure
        protected QueryBuilderFactory qbf;

        @Structure
        protected TransientBuilderFactory tbf;

        @Structure
        protected Qi4j qi4j;

        @Service
        protected EntityToDTOService valueConverter;

        /**
         * Zest Assembler
         *
         * To let the custom application class (DCISampleApplication_x) focus on starting up the
         * Wicket environment, I made a convention of having Zest Assembler files in an 'assembly'
         * folder beside the custom application class.
         *
         * There's always only one application file, but we could split the assemblage into several
         * files ie. one for each layer. In that case, the Assembler file would be distributing to
         * the individual LayerXAssembler classes.
         *
         * If you like, you can also override this method in the custom application class and simply
         * return an instance of YourAssembler:
         *
         * &#64;Override protected ApplicationAssembler getAssembler() {
         * return new YourAssemblerInAnyPath();
         * }
         */
        protected ApplicationAssembler getAssembler()
            throws Exception
        {
            String appPath = getClass().getCanonicalName();
            String expectedPathFromApplication = ".assembly.Assembler";
            String assemblerPath = appPath.substring( 0, appPath.lastIndexOf( "." ) ) + expectedPathFromApplication;
            try
            {
                return (ApplicationAssembler) Class.forName( assemblerPath ).newInstance();
            }
            catch( ClassNotFoundException e )
            {
                throw new Exception( "Couldn't find Zest assembler in path '" + assemblerPath + "'" );
            }
        }

        protected String defaultLayerName()
        {
            return "BOOTSTRAP";
        }

        protected String defaultModuleName()
        {
            return "BOOTSTRAP-Bootstrap";
        }

        // Override this to bootstrap the wicket application
        protected void wicketInit()
        {
        }

        @Override
        protected void init()
        {
            startQi4j();
            handleUnitOfWork();

            Context.prepareContextBaseClass( uowf );
            BaseWebPage.prepareBaseWebPageClass( tbf );
            ReadOnlyModel.prepareModelBaseClass( qi4jModule, qi4j, valueConverter );
            Queries.prepareQueriesBaseClass( uowf, qbf );

            wicketInit();
        }

        private void startQi4j()
        {
            try
            {
                logger.info( "Starting Zest application" );
                Energy4Java qi4j = new Energy4Java();
                qi4jApp = qi4j.newApplication( getAssembler() );
                qi4jApp.activate();
                qi4jModule = qi4jApp.findModule( defaultLayerName(), defaultModuleName() );

                // Zest injects @Structure and @Service elements into this application instance
                //qi4jModule.newObject( WicketQi4jApplication.class );
                qi4jModule.injectTo( this );

                logger.info( "Started Zest application" );
            }
            catch( Exception e )
            {
                logger.error( "Could not start Zest application." );
                e.printStackTrace();
                System.exit( 100 );
            }
        }

        private void handleUnitOfWork()
        {
            getRequestCycleListeners().add( new AbstractRequestCycleListener()
            {
                @Override
                public void onBeginRequest( final RequestCycle requestCycle )
                {
                    super.onBeginRequest( requestCycle );

                    logger.debug( "================================" );
                    logger.debug( "REQUEST start" );
                    logger.debug( requestCycle.getRequest().toString() );
                    logger.debug( requestCycle.getRequest().getRequestParameters().toString() );

                    UnitOfWork uow = uowf.newUnitOfWork( UsecaseBuilder.newUsecase( "REQUEST" ) );
                    logger.debug( "  ### NEW " + uow + "   ### MODULE: " + qi4jModule );
                }

                @Override
                public void onEndRequest( final RequestCycle requestCycle )
                {
                    UnitOfWork uow = uowf.currentUnitOfWork();
                    if( uow != null )
                    {
                        try
                        {
                            if( "POST".equals( ( (HttpServletRequest) requestCycle.getRequest()
                                .getContainerRequest() ).getMethod() ) )
                            {
                                // "Save"
                                logger.debug( "  ### COMPLETE " + uow + "   ### MODULE: " + qi4jModule );
                                uow.complete();
                            }
                            else
                            {
                                // GET requests
                                logger.debug( "  ### DISCARD " + uow + "   ### MODULE: " + qi4jModule );
                                uow.discard();
                            }
                        }
                        catch( ConcurrentEntityModificationException e )
                        {
                            logger.error( "  ### DISCARD " + uow + "   ### MODULE: " + qi4jModule );
                            uow.discard();
                            e.printStackTrace();
                        }
                        catch( UnitOfWorkCompletionException e )
                        {
                            logger.error( "  ### DISCARD " + uow + "   ### MODULE: " + qi4jModule );
                            uow.discard();
                            e.printStackTrace();
                        }
                    }
                    logger.debug( "REQUEST end" );
                    logger.debug( "------------------------------------" );
                }
            } );
        }

        // Since Zest can only add concrete classes in the assembly, we need to implement a (dummy) getHomePage()
        // method here. Override in wicket application class with a real returned page class.
        @Override
        public Class<? extends Page> getHomePage()
        {
            return null;
        }

        @Override
        protected void onDestroy()
        {
            if( qi4jApp == null )
            {
                return;
            }

            try
            {
                logger.info( "Passivating Zest application" );
                qi4jApp.passivate();
            }
            catch( Exception e )
            {
                e.printStackTrace();
            }
        }

        public String appVersion()
        {
            return qi4jApp.version();
        }
    }