001/*
002 * GWTEventService
003 * Copyright (c) 2011 and beyond, strawbill UG (haftungsbeschr?nkt)
004 *
005 * This is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU Lesser General Public License as
007 * published by the Free Software Foundation; either version 3 of
008 * the License, or (at your option) any later version.
009 * Other licensing for GWTEventService may also be possible on request.
010 * Please view the license.txt of the project for more information.
011 *
012 * This software is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this software; if not, write to the Free
019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021 */
022package de.novanic.eventservice.client.event;
023
024import com.google.gwt.user.client.rpc.AsyncCallback;
025import de.novanic.eventservice.client.config.EventServiceConfigurationTransferable;
026import de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector;
027import de.novanic.eventservice.client.event.command.ClientCommand;
028import de.novanic.eventservice.client.event.command.InitEventServiceCommand;
029import de.novanic.eventservice.client.event.command.schedule.ClientCommandSchedulerFactory;
030
031import java.util.LinkedList;
032import java.util.Queue;
033
034/**
035 * The RemoteEventServiceAccessor provides general methods for command execution and manages the
036 * {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector}.
037 *
038 * @author sstrohschein
039 *         <br>Date: 04.07.2010
040 *         <br>Time: 13:21:19
041 */
042public abstract class RemoteEventServiceAccessor
043{
044    private RemoteEventConnector myRemoteEventConnector;
045    private Queue<ClientCommand<?>> myClientCommandQueue;
046    private boolean isSessionInitialized;
047
048    /**
049     * Constructor of the AbstractRemoteEventService to manage the {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector}.
050     * @param aRemoteEventConnector {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector} for the connection
051     * between client side and server side
052     */
053    protected RemoteEventServiceAccessor(RemoteEventConnector aRemoteEventConnector) {
054        myRemoteEventConnector = aRemoteEventConnector;
055    }
056
057    /**
058     * Checks if the RemoteEventService is active (listening).
059     * @return true when active/listening, otherwise false
060     */
061    protected boolean isListenActive() {
062        return myRemoteEventConnector.isActive();
063    }
064
065    /**
066     * Starts the init command and schedules all other commands till the init command is finished. That must be done
067     * to avoid double initialized sessions (race condition GWT issue 1846).
068     * @param aClientCommand command to schedule
069     * @param <R> Return type of the command callback
070     */
071    protected <R> void schedule(final ClientCommand<R> aClientCommand) {
072        if(myClientCommandQueue == null) {
073            myClientCommandQueue = new LinkedList<ClientCommand<?>>();
074            InitEventServiceCommand theInitCommand = new InitEventServiceCommand(getRemoteEventConnector(), new InitCommandCallback());
075            theInitCommand.execute();
076        }
077        myClientCommandQueue.add(aClientCommand);
078        executeCommands();
079    }
080
081    /**
082     * Executes the scheduled commands ({@link ClientCommand}. The commands can be scheduled with
083     * {@link RemoteEventServiceAccessor#schedule(de.novanic.eventservice.client.event.command.ClientCommand)}.
084     */
085    private void executeCommands() {
086        if(isSessionInitialized) {
087            ClientCommand<?> theClientCommand;
088            while(myClientCommandQueue != null && (theClientCommand = myClientCommandQueue.poll()) != null) {
089                theClientCommand.execute();
090            }
091        }
092    }
093
094    /**
095     * Returns the managed {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector}.
096     * @return managed {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector}
097     */
098    protected RemoteEventConnector getRemoteEventConnector() {
099        return myRemoteEventConnector;
100    }
101
102    protected void reset() {
103        isSessionInitialized = false;
104        myClientCommandQueue = null;
105        getRemoteEventConnector().deactivate();
106    }
107
108    /**
109     * Callback for the init command.
110     */
111    private class InitCommandCallback implements AsyncCallback<EventServiceConfigurationTransferable>
112    {
113        /**
114         * Executes the scheduled commands on success.
115         * @param aConfiguration configuration for the client side
116         */
117        public void onSuccess(EventServiceConfigurationTransferable aConfiguration) {
118            getRemoteEventConnector().initListen(aConfiguration);
119            finishFirstCall();
120        }
121
122        /**
123         * Throws a runtime exception when the event service couldn't be activated / initialized.
124         * @param aThrowable throwable caused by a failed server call
125         */
126        public void onFailure(Throwable aThrowable) {
127            throw new RemoteEventServiceRuntimeException("Error on activating / initializing \"" + RemoteEventService.class.getName() + "\"!", aThrowable);
128        }
129
130        /**
131         * Executes the scheduled commands.
132         */
133        private void finishFirstCall() {
134            //Schedule the next command after the callback is finished. The timer is needed, because some browsers doesn't
135            //notice the server call cycle, when the next command is executed directly.
136            ClientCommandSchedulerFactory.getInstance().getClientCommandScheduler().schedule(new ClientCommand<Void>() {
137                public void execute() {
138                    isSessionInitialized = true;
139                    executeCommands();
140                }
141
142                public AsyncCallback<Void> getCommandCallback() { return null; }
143            });
144        }
145    }
146
147    /**
148     * Empty callback
149     */
150    protected static class VoidAsyncCallback implements AsyncCallback<Void>
151    {
152        public void onFailure(Throwable aThrowable) {}
153
154        public void onSuccess(Void aResult) {}
155    }
156}