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.service; 023 024import com.google.gwt.user.server.rpc.RemoteServiceServlet; 025 026import java.io.IOException; 027import java.util.List; 028import java.util.Set; 029 030import de.novanic.eventservice.config.ConfigurationDependentFactory; 031import de.novanic.eventservice.client.config.EventServiceConfigurationTransferable; 032import de.novanic.eventservice.client.config.RemoteEventServiceConfigurationTransferable; 033import de.novanic.eventservice.service.connection.strategy.connector.ConnectionStrategyServerConnector; 034import de.novanic.eventservice.client.event.service.EventService; 035import de.novanic.eventservice.client.event.filter.EventFilter; 036import de.novanic.eventservice.client.event.Event; 037import de.novanic.eventservice.client.event.DomainEvent; 038import de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent; 039import de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener; 040import de.novanic.eventservice.client.event.domain.Domain; 041import de.novanic.eventservice.config.EventServiceConfiguration; 042import de.novanic.eventservice.service.connection.id.SessionConnectionIdGenerator; 043import de.novanic.eventservice.service.connection.strategy.connector.streaming.StreamingServerConnector; 044import de.novanic.eventservice.service.registry.EventRegistry; 045import de.novanic.eventservice.service.registry.EventRegistryFactory; 046import de.novanic.eventservice.logger.ServerLogger; 047import de.novanic.eventservice.logger.ServerLoggerFactory; 048import de.novanic.eventservice.config.EventServiceConfigurationFactory; 049import de.novanic.eventservice.config.level.ConfigLevelFactory; 050import de.novanic.eventservice.config.loader.WebDescriptorConfigurationLoader; 051 052import javax.servlet.ServletException; 053import javax.servlet.ServletConfig; 054import javax.servlet.ServletOutputStream; 055import javax.servlet.http.HttpServletRequest; 056import javax.servlet.http.HttpServletResponse; 057 058/** 059 * {@link de.novanic.eventservice.client.event.service.EventService} is the server side interface to register listen 060 * requests for domains and to add events. 061 * 062 * @author sstrohschein 063 * <br>Date: 05.06.2008 064 * <br>Time: 19:12:17 065 */ 066public class EventServiceImpl extends RemoteServiceServlet implements EventService 067{ 068 private static final ServerLogger LOG = ServerLoggerFactory.getServerLogger(EventServiceImpl.class.getName()); 069 private EventRegistry myEventRegistry; 070 private ConfigurationDependentFactory myConfigurationDependentFactory; 071 072 /** 073 * The init method should be called automatically before the servlet can be used and should called only one time. 074 * That method initialized the {@link de.novanic.eventservice.service.registry.EventRegistry}. 075 * @param aConfig servlet configuration 076 * @throws ServletException 077 */ 078 public void init(ServletConfig aConfig) throws ServletException { 079 super.init(aConfig); 080 myEventRegistry = initEventRegistry(aConfig); 081 EventServiceConfiguration theConfiguration = myEventRegistry.getConfiguration(); 082 myConfigurationDependentFactory = ConfigurationDependentFactory.getInstance(theConfiguration); 083 } 084 085 @Override 086 public void destroy() { 087 super.destroy(); 088 EventRegistryFactory.getInstance().resetEventRegistry(); 089 } 090 091 /** 092 * The GET method is used to stream data to the clients. 093 * @param aRequest request 094 * @param aResponse response (with the stream) 095 * @throws ServletException 096 * @throws IOException 097 */ 098 protected void doGet(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletException, IOException { 099 ConnectionStrategyServerConnector theConnectionStrategyServerConnector = myConfigurationDependentFactory.getConnectionStrategyServerConnector(); 100 if(theConnectionStrategyServerConnector instanceof StreamingServerConnector) { 101 final String theClientId = getClientId(aRequest); 102 StreamingServerConnector theStreamingServerConnector = (StreamingServerConnector)theConnectionStrategyServerConnector; 103 try { 104 //The streaming server connector has to be cloned, because it isn't stateless (a prepare method is required). 105 theStreamingServerConnector = (StreamingServerConnector)theStreamingServerConnector.clone(); 106 theStreamingServerConnector.prepare(aResponse); 107 listen(theStreamingServerConnector, theClientId); 108 } catch(EventServiceException e) { 109 throw new ServletException("Error on streaming events to the client\"" + theClientId + "\"!", e); 110 } catch(CloneNotSupportedException e) { 111 throw new ServletException("Error on cloning \"" + StreamingServerConnector.class.getName() + "\" for client \"" + theClientId + "\"!", e); 112 } finally { 113 ServletOutputStream theServletOutputStream = aResponse.getOutputStream(); 114 theServletOutputStream.close(); 115 } 116 } 117 } 118 119 /** 120 * Initializes the {@link de.novanic.eventservice.client.event.service.EventService}. 121 * @return EventServiceConfigurationTransferable a transferable configuration for the client side 122 */ 123 public EventServiceConfigurationTransferable initEventService() { 124 final String theClientId = generateClientId(); 125 final EventServiceConfiguration theConfiguration = myEventRegistry.getConfiguration(); 126 127 String theClientIdTransferable; 128 if(SessionConnectionIdGenerator.class.getName().equals(theConfiguration.getConnectionIdGeneratorClassName())) { 129 theClientIdTransferable = null; 130 } else { 131 theClientIdTransferable = theClientId; 132 } 133 LOG.info("Client \"" + theClientId + "\" initialized."); 134 return new RemoteEventServiceConfigurationTransferable(theConfiguration.getMinWaitingTime(), theConfiguration.getMaxWaitingTime(), 135 theConfiguration.getTimeoutTime(), theConfiguration.getReconnectAttemptCount(), theClientIdTransferable, theConfiguration.getConnectionStrategyClientConnectorClassName()); 136 } 137 138 /** 139 * Register listen for a domain. 140 * @param aDomain domain to listen to 141 */ 142 public void register(Domain aDomain) { 143 register(aDomain, null); 144 } 145 146 /** 147 * Register listen for a domain. 148 * @param aDomain domain to listen to 149 * @param anEventFilter EventFilter to filter events 150 */ 151 public void register(Domain aDomain, EventFilter anEventFilter) { 152 final String theClientId = getClientId(); 153 myEventRegistry.registerUser(aDomain, theClientId, anEventFilter); 154 } 155 156 /** 157 * Register listen for a domain. 158 * @param aDomains domains to listen to 159 */ 160 public void register(Set<Domain> aDomains) { 161 for(Domain aDomain : aDomains) { 162 register(aDomain); 163 } 164 } 165 166 /** 167 * Register listen for domains. 168 * @param aDomains domains to listen to 169 * @param anEventFilter EventFilter to filter events (applied to all domains) 170 */ 171 public void register(Set<Domain> aDomains, EventFilter anEventFilter) { 172 for(Domain aDomain : aDomains) { 173 register(aDomain, anEventFilter); 174 } 175 } 176 177 /** 178 * Registers an {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} which is triggered on a 179 * timeout or when a user/client leaves a {@link de.novanic.eventservice.client.event.domain.Domain}. An 180 * {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} is hold at the server side and can 181 * contain custom data. Other users/clients can use the custom data when the event is for example triggered by a timeout. 182 * @param anUnlistenScope scope of the unlisten events to receive 183 * @param anUnlistenEvent {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent} which should 184 * be transferred to other users/clients when a timeout occurs or a domain is leaved. 185 */ 186 public void registerUnlistenEvent(UnlistenEventListener.Scope anUnlistenScope, UnlistenEvent anUnlistenEvent) { 187 final String theClientId = getClientId(); 188 myEventRegistry.registerUnlistenEvent(theClientId, anUnlistenScope, anUnlistenEvent); 189 } 190 191 /** 192 * Registers an {@link EventFilter} for the domain. 193 * @param aDomain domain to register the EventFilter to 194 * @param anEventFilter EventFilter to filter events for the domain 195 */ 196 public void registerEventFilter(Domain aDomain, EventFilter anEventFilter) { 197 final String theClientId = getClientId(); 198 myEventRegistry.setEventFilter(aDomain, theClientId, anEventFilter); 199 } 200 201 /** 202 * Deregisters the {@link EventFilter} of the domain. 203 * @param aDomain domain to drop the EventFilters from 204 */ 205 public void deregisterEventFilter(Domain aDomain) { 206 final String theClientId = getClientId(); 207 myEventRegistry.removeEventFilter(aDomain, theClientId); 208 } 209 210 /** 211 * Returns the EventFilter for the user domain combination. 212 * @param aDomain domain 213 * @return EventFilter for the domain 214 */ 215 public EventFilter getEventFilter(Domain aDomain) { 216 final String theClientId = getClientId(); 217 return myEventRegistry.getEventFilter(aDomain, theClientId); 218 } 219 220 /** 221 * The listen method returns all events for the user (events for all domains where the user is registered and user 222 * specific events). If no events are available, the method waits a defined time before the events are returned. 223 * The client side calls the method with a defined interval to receive all events. If the client doesn't call the 224 * method in that interval, the user will be removed from the EventRegistry. The timeout time and the waiting time 225 * can be configured with {@link de.novanic.eventservice.config.EventServiceConfiguration}. 226 * The default listening method is long-polling, but that can be changed with changing the connection strategy. 227 * The connection strategy can be configured with {@link de.novanic.eventservice.config.ConfigParameter#CONNECTION_STRATEGY_CLIENT_CONNECTOR} 228 * for the client side part / connector and {@link de.novanic.eventservice.config.ConfigParameter#CONNECTION_STRATEGY_SERVER_CONNECTOR}. 229 * @return list of events 230 */ 231 public List<DomainEvent> listen() { 232 final String theClientId = getClientId(); 233 ConnectionStrategyServerConnector theConnectionStrategyServerConnector = myConfigurationDependentFactory.getConnectionStrategyServerConnector(); 234 LOG.debug("Listen (client id \"" + theClientId + "\")."); 235 return listen(theConnectionStrategyServerConnector, theClientId); 236 } 237 238 /** 239 * The listen method returns all events for the user (events for all domains where the user is registered and user 240 * specific events). If no events are available, the method waits a defined time before the events are returned. 241 * The client side calls the method with a defined interval to receive all events. If the client doesn't call the 242 * method in that interval, the user will be removed from the EventRegistry. The timeout time and the waiting time 243 * can be configured with {@link de.novanic.eventservice.config.EventServiceConfiguration}. 244 * @param aServerEventListener {@link de.novanic.eventservice.service.connection.strategy.connector.ConnectionStrategyServerConnector} for the listening method 245 * @param aClientId client / user 246 * @return list of events 247 */ 248 private List<DomainEvent> listen(ConnectionStrategyServerConnector aServerEventListener, String aClientId) { 249 LOG.debug("Listen (client id \"" + aClientId + "\")."); 250 return myEventRegistry.listen(aServerEventListener, aClientId); 251 } 252 253 /** 254 * Unlisten for events (for the current user) in all domains (deregisters the user from all domains). 255 */ 256 public void unlisten() { 257 final String theClientId = getClientId(); 258 LOG.debug("Unlisten (client id \"" + theClientId + "\")."); 259 myEventRegistry.unlisten(theClientId); 260 LOG.debug("Unlisten finished (client id \"" + theClientId + "\")."); 261 } 262 263 /** 264 * Unlisten for events (for the current user) in the domain and deregisters the user from the domain. 265 * @param aDomain domain to unlisten 266 */ 267 public void unlisten(Domain aDomain) { 268 final String theClientId = getClientId(); 269 LOG.debug("Unlisten (client id \"" + theClientId + "\")."); 270 myEventRegistry.unlisten(aDomain, theClientId); 271 LOG.debug("Unlisten finished (client id \"" + theClientId + "\")."); 272 } 273 274 /** 275 * Unlisten for events (for the current user) in the domains and deregisters the user from the domains. 276 * @param aDomains set of domains to unlisten 277 */ 278 public void unlisten(Set<Domain> aDomains) { 279 for(Domain theDomain: aDomains) { 280 unlisten(theDomain); 281 } 282 } 283 284 /** 285 * Checks if the user is registered for event listening. 286 * @param aDomain domain to check 287 * @return true when the user is registered for listening, otherwise false 288 */ 289 public boolean isUserRegistered(Domain aDomain) { 290 final String theClientId = getClientId(); 291 return myEventRegistry.isUserRegistered(aDomain, theClientId); 292 } 293 294 /** 295 * Adds an event for all users in the domain. 296 * @param aDomain domain to add the event 297 * @param anEvent event to add 298 */ 299 public void addEvent(Domain aDomain, Event anEvent) { 300 myEventRegistry.addEvent(aDomain, anEvent); 301 } 302 303 /** 304 * Adds an event only for the current user. 305 * @param anEvent event to add to the user 306 */ 307 public void addEventUserSpecific(Event anEvent) { 308 final String theClientId = getClientId(); 309 myEventRegistry.addEventUserSpecific(theClientId, anEvent); 310 } 311 312 /** 313 * Returns the domain names, where the user is listening to 314 * @return collection of domain names 315 */ 316 public Set<Domain> getActiveListenDomains() { 317 final String theClientId = getClientId(); 318 return myEventRegistry.getListenDomains(theClientId); 319 } 320 321 /** 322 * Registers the {@link de.novanic.eventservice.config.loader.WebDescriptorConfigurationLoader}, 323 * loads the first available configuration (with {@link de.novanic.eventservice.config.EventServiceConfigurationFactory}) 324 * and initializes the {@link de.novanic.eventservice.service.registry.EventRegistry}. 325 * @param aConfig servlet configuration 326 * @return initialized {@link de.novanic.eventservice.service.registry.EventRegistry} 327 */ 328 private EventRegistry initEventRegistry(ServletConfig aConfig) { 329 final WebDescriptorConfigurationLoader theWebDescriptorConfigurationLoader = new WebDescriptorConfigurationLoader(aConfig); 330 final EventServiceConfigurationFactory theEventServiceConfigurationFactory = EventServiceConfigurationFactory.getInstance(); 331 theEventServiceConfigurationFactory.addConfigurationLoader(ConfigLevelFactory.DEFAULT, theWebDescriptorConfigurationLoader); 332 333 final EventRegistryFactory theEventRegistryFactory = EventRegistryFactory.getInstance(); 334 EventRegistry theEventRegistry = theEventRegistryFactory.getEventRegistry(); 335 336 if(theWebDescriptorConfigurationLoader.isAvailable()) { 337 theEventServiceConfigurationFactory.loadEventServiceConfiguration(); 338 } 339 return theEventRegistry; 340 } 341 342 /** 343 * Returns the client id. 344 * @return client id 345 */ 346 protected String getClientId() { 347 return getClientId(getThreadLocalRequest()); 348 } 349 350 /** 351 * Returns the client id. 352 * @param aRequest request 353 * @return client id 354 */ 355 protected String getClientId(HttpServletRequest aRequest) { 356 return myConfigurationDependentFactory.getConnectionIdGenerator().getConnectionId(aRequest); 357 } 358 359 /** 360 * Generates and returns a new client id. 361 * @return client id 362 */ 363 protected String generateClientId() { 364 return myConfigurationDependentFactory.getConnectionIdGenerator().generateConnectionId(getThreadLocalRequest()); 365 } 366 367 /** 368 * This method is overridden because applications with various GWT versions got a {@link SecurityException} 369 * @throws SecurityException 370 */ 371 @Override 372 protected void checkPermutationStrongName() throws SecurityException {} 373}