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 com.google.gwt.user.client.rpc.ServiceDefTarget;
026import de.novanic.eventservice.client.ClientHandler;
027import de.novanic.eventservice.client.DefaultClientHandler;
028import de.novanic.eventservice.client.config.ConfigurationTransferableDependentFactory;
029import de.novanic.eventservice.client.connection.callback.AsyncCallbackWrapper;
030import de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector;
031import de.novanic.eventservice.client.event.command.ClientCommand;
032import de.novanic.eventservice.client.event.service.creator.DefaultEventServiceCreator;
033import de.novanic.eventservice.client.event.service.creator.EventServiceCreator;
034
035/**
036 * The RemoteEventServiceFactory is used to create the RemoteEventService and to ensure that only one instance of
037 * RemoteEventServiceFactory and RemoteEventService exists (singleton).
038 * The factory does also provide methods to create service mappings and handlers.
039 * @see DefaultRemoteEventService
040 *
041 * @author sstrohschein
042 * <br>Date: 08.06.2008
043 * <br>Time: 14:44:17
044 */
045public class RemoteEventServiceFactory
046{
047    private volatile RemoteEventService myRemoteEventService;
048
049    /**
050     * The RemoteEventServiceFactory should be created via the getInstance method.
051     * @see RemoteEventServiceFactory#getInstance()
052     */
053    protected RemoteEventServiceFactory() {}
054
055    /**
056     * Factory-Holder class to ensure thread-safe lazy-loading with IODH.
057     */
058    private static class RemoteEventServiceFactoryHolder {
059        private static RemoteEventServiceFactory INSTANCE = createRemoteEventServiceFactory();
060    }
061
062    /**
063     * This method should be used to create an instance of RemoteEventServiceFactory.
064     * RemoteEventServiceFactory is a singleton, so this method returns always the same instance of
065     * RemoteEventServiceFactory.
066     * @return RemoteEventServiceFactory (singleton)
067     */
068    public static RemoteEventServiceFactory getInstance() {
069        return RemoteEventServiceFactoryHolder.INSTANCE;
070    }
071
072    /**
073     * This method should be used to create an instance of RemoteEventService.
074     * RemoteEventService is a singleton, so this method returns always the same instance of RemoteEventService.
075     * @return RemoteEventService (singleton)
076     */
077    public RemoteEventService getRemoteEventService() {
078        if(myRemoteEventService != null) {
079            return myRemoteEventService;
080        }
081        EventServiceCreator theEventServiceCreator = DefaultEventServiceCreator.getInstance();
082        return getRemoteEventService(new GWTRemoteEventConnector(theEventServiceCreator));
083    }
084
085    /**
086     * This method should be used to create an instance of RemoteEventService.
087     * RemoteEventService is a singleton, so this method returns always the same instance of RemoteEventService.
088     * @param aRemoteEventConnector {@link de.novanic.eventservice.client.connection.strategy.connector.RemoteEventConnector}
089     * to specify the connection to the server side
090     * @return RemoteEventService (singleton)
091     */
092    public RemoteEventService getRemoteEventService(RemoteEventConnector aRemoteEventConnector) {
093        if(myRemoteEventService == null) {
094            synchronized(this) {
095                if(myRemoteEventService == null) {
096                    myRemoteEventService = new DefaultRemoteEventService(aRemoteEventConnector);
097                }
098            }
099        }
100        return myRemoteEventService;
101    }
102
103    /**
104     * Creates an instance of {@link RemoteEventServiceFactory} (everytime a new instance).
105     * @return new instance of {@link RemoteEventServiceFactory} 
106     */
107    private static RemoteEventServiceFactory createRemoteEventServiceFactory() {
108        return new RemoteEventServiceFactory();
109    }
110
111    /**
112     * Requests a {@link de.novanic.eventservice.client.ClientHandler} which contains the client-/connection-id and
113     * provides it via the callback. A server-call is only executed when no other server call was executed before because
114     * at least one server call has to be executed before to generate the client-/connection-id at the server-side.
115     * @param anAsyncCallback callback with a {@link de.novanic.eventservice.client.ClientHandler}
116     */
117    public void requestClientHandler(final AsyncCallback<ClientHandler> anAsyncCallback) {
118        RemoteEventServiceAccessor theRemoteEventServiceAccessor = (RemoteEventServiceAccessor)getRemoteEventService();
119        theRemoteEventServiceAccessor.schedule(new GetClientIdCommand(new AsyncCallbackWrapper<ClientHandler>(anAsyncCallback) {
120            public void onSuccess(ClientHandler aClientHandler) {
121                String theConnectionId = ConfigurationTransferableDependentFactory.getConfiguration().getConnectionId();
122                ClientHandler theClientHandler = new DefaultClientHandler(theConnectionId);
123                super.onSuccess(theClientHandler);
124            }
125        }));
126    }
127
128    /**
129     * Registers an user-/client-specific {@link de.novanic.eventservice.client.ClientHandler} to an existing service instance to provide the connection-/client-id with every request / server-call.
130     * That makes it possible to add user-specific events or domain-user-specific EventFilters dynamically when the {@link de.novanic.eventservice.client.ClientHandler}
131     * is provided. The {@link de.novanic.eventservice.client.ClientHandler} can be got from
132     * {@link de.novanic.eventservice.client.event.RemoteEventServiceFactory#requestClientHandler(com.google.gwt.user.client.rpc.AsyncCallback)}.
133     * The {@link de.novanic.eventservice.client.ClientHandler} could also be transferred manually to your custom service to manage or use the connection-/client-ids
134     * for user-specific events or domain-user-specific EventFilters.
135     * The service could extend from RemoteEventServiceServlet or use the EventExecutorService to execute events.
136     * Events (also user-specific) can also be executed directly from the client-side (see {@link de.novanic.eventservice.client.event.RemoteEventService}).
137     * @param anAsyncServiceInstance an async service (instance) which needs to add user-specific events or domain-user-specific EventFilters dynamically from the server-side
138     * @param aClientHandler {@link de.novanic.eventservice.client.ClientHandler} to provide the connection-/client-id for user-specific events or domain-user-specific EventFilters
139     * @return (re-)mapped service instance with a provided {@link de.novanic.eventservice.client.ClientHandler}
140     */
141    public void registerClientSpecificHandler(ServiceDefTarget anAsyncServiceInstance, ClientHandler aClientHandler) {
142        StringBuilder theServiceURLStringBuilder = new StringBuilder(anAsyncServiceInstance.getServiceEntryPoint());
143        theServiceURLStringBuilder.append("?id=");
144        theServiceURLStringBuilder.append(aClientHandler.getConnectionId());
145        
146        anAsyncServiceInstance.setServiceEntryPoint(theServiceURLStringBuilder.toString());
147    }
148
149    /**
150     * That method should only be used in TestCases, because it resets the factory and the factory can't ensure
151     * anymore that only one instance exists!
152     */
153    public static void reset() {
154        RemoteEventServiceFactoryHolder.INSTANCE = createRemoteEventServiceFactory();
155    }
156
157    /**
158     * That is a simple internal command which is used to ensure that the init command ({@link de.novanic.eventservice.client.event.command.InitEventServiceCommand})
159     * was already be executed. The initialization is needed, because the the connection-/client-id is generated at the service-side.
160     * That command doesn't execute a server-call directly. A server-call is only executed from {@link de.novanic.eventservice.client.event.command.InitEventServiceCommand}
161     * when it wasn't done before.
162     */
163    private class GetClientIdCommand implements ClientCommand<ClientHandler>
164    {
165        private AsyncCallback<ClientHandler> myCallback;
166
167        /**
168         * Creates a new command with a provided callback. The {@link com.google.gwt.user.client.rpc.AsyncCallback#onSuccess(Object)} method
169         * is called on the execution of this command.
170         * @param aCallback callback
171         */
172        private GetClientIdCommand(AsyncCallback<ClientHandler> aCallback) {
173            myCallback = aCallback;
174        }
175
176        /**
177         * The {@link com.google.gwt.user.client.rpc.AsyncCallback#onSuccess(Object)} method of the provided callback
178         * is called on the execution of this command.
179         */
180        public void execute() {
181            getCommandCallback().onSuccess(null);
182        }
183
184        /**
185         * Returns the provided callback.
186         * @return provided callback
187         */
188        public AsyncCallback<ClientHandler> getCommandCallback() {
189            return myCallback;
190        }
191    }
192}