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}