001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services 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 Lesser 015 * 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 library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.gravity; 023 024import java.io.Serializable; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.Iterator; 028import java.util.Map; 029import java.util.Timer; 030import java.util.TimerTask; 031import java.util.concurrent.ConcurrentHashMap; 032 033import javax.management.ObjectName; 034 035import org.granite.clustering.DistributedData; 036import org.granite.config.GraniteConfig; 037import org.granite.config.flex.Destination; 038import org.granite.config.flex.ServicesConfig; 039import org.granite.context.GraniteContext; 040import org.granite.context.SimpleGraniteContext; 041import org.granite.gravity.adapters.AdapterFactory; 042import org.granite.gravity.adapters.ServiceAdapter; 043import org.granite.gravity.security.GravityDestinationSecurizer; 044import org.granite.gravity.security.GravityInvocationContext; 045import org.granite.gravity.udp.UdpReceiverFactory; 046import org.granite.jmx.MBeanServerLocator; 047import org.granite.jmx.OpenMBean; 048import org.granite.logging.Logger; 049import org.granite.messaging.amf.process.AMF3MessageInterceptor; 050import org.granite.messaging.jmf.SharedContext; 051import org.granite.messaging.service.security.SecurityService; 052import org.granite.messaging.service.security.SecurityServiceException; 053import org.granite.messaging.webapp.ServletGraniteContext; 054import org.granite.scan.ServiceLoader; 055import org.granite.util.TypeUtil; 056import org.granite.util.UUIDUtil; 057 058import flex.messaging.messages.AcknowledgeMessage; 059import flex.messaging.messages.AsyncMessage; 060import flex.messaging.messages.CommandMessage; 061import flex.messaging.messages.ErrorMessage; 062import flex.messaging.messages.Message; 063 064/** 065 * @author William DRAI 066 * @author Franck WOLFF 067 */ 068public class DefaultGravity implements Gravity, DefaultGravityMBean { 069 070 /////////////////////////////////////////////////////////////////////////// 071 // Fields. 072 073 private static final Logger log = Logger.getLogger(Gravity.class); 074 075 private final Map<String, Object> applicationMap = new HashMap<String, Object>(); 076 private final ConcurrentHashMap<String, TimeChannel<?>> channels = new ConcurrentHashMap<String, TimeChannel<?>>(); 077 078 private GravityConfig gravityConfig = null; 079 private ServicesConfig servicesConfig = null; 080 private GraniteConfig graniteConfig = null; 081 private SharedContext sharedContext = null; 082 083 private Channel serverChannel = null; 084 private AdapterFactory adapterFactory = null; 085 private GravityPool gravityPool = null; 086 087 private UdpReceiverFactory udpReceiverFactory = null; 088 089 private Timer channelsTimer; 090 private boolean started; 091 092 /////////////////////////////////////////////////////////////////////////// 093 // Constructor. 094 095 public DefaultGravity(GravityConfig gravityConfig, ServicesConfig servicesConfig, GraniteConfig graniteConfig, SharedContext sharedContext) { 096 if (gravityConfig == null || servicesConfig == null || graniteConfig == null) 097 throw new NullPointerException("All arguments must be non null."); 098 099 this.gravityConfig = gravityConfig; 100 this.servicesConfig = servicesConfig; 101 this.graniteConfig = graniteConfig; 102 this.sharedContext = sharedContext; 103 } 104 105 /////////////////////////////////////////////////////////////////////////// 106 // Properties. 107 108 public GravityConfig getGravityConfig() { 109 return gravityConfig; 110 } 111 112 public ServicesConfig getServicesConfig() { 113 return servicesConfig; 114 } 115 116 public GraniteConfig getGraniteConfig() { 117 return graniteConfig; 118 } 119 120 public SharedContext getSharedContext() { 121 return sharedContext; 122 } 123 124 public boolean isStarted() { 125 return started; 126 } 127 128 public ServiceAdapter getServiceAdapter(String messageType, String destinationId) { 129 return adapterFactory.getServiceAdapter(messageType, destinationId); 130 } 131 132 /////////////////////////////////////////////////////////////////////////// 133 // Starting/stopping. 134 135 public void start() throws Exception { 136 log.info("Starting Gravity..."); 137 synchronized (this) { 138 if (!started) { 139 adapterFactory = new AdapterFactory(this); 140 internalStart(); 141 serverChannel = new ServerChannel(this, ServerChannel.class.getName(), null, null); 142 143 if (gravityConfig.isUseUdp()) { 144 ServiceLoader<UdpReceiverFactory> loader = ServiceLoader.load(UdpReceiverFactory.class); 145 Iterator<UdpReceiverFactory> factories = loader.iterator(); 146 if (factories.hasNext()) { 147 udpReceiverFactory = factories.next(); 148 udpReceiverFactory.setPort(gravityConfig.getUdpPort()); 149 udpReceiverFactory.setNio(gravityConfig.isUdpNio()); 150 udpReceiverFactory.setConnected(gravityConfig.isUdpConnected()); 151 udpReceiverFactory.setSendBufferSize(gravityConfig.getUdpSendBufferSize()); 152 udpReceiverFactory.start(); 153 } 154 else 155 log.warn("UDP receiver factory not found"); 156 } 157 158 started = true; 159 } 160 } 161 log.info("Gravity successfully started."); 162 } 163 164 protected void internalStart() { 165 gravityPool = new GravityPool(gravityConfig); 166 channelsTimer = new Timer(); 167 168 if (graniteConfig.isRegisterMBeans()) { 169 try { 170 ObjectName name = new ObjectName("org.graniteds:type=Gravity,context=" + graniteConfig.getMBeanContextName()); 171 log.info("Registering MBean: %s", name); 172 OpenMBean mBean = OpenMBean.createMBean(this); 173 MBeanServerLocator.getInstance().register(mBean, name, true); 174 } 175 catch (Exception e) { 176 log.error(e, "Could not register Gravity MBean for context: %s", graniteConfig.getMBeanContextName()); 177 } 178 } 179 } 180 181 public void restart() throws Exception { 182 synchronized (this) { 183 stop(); 184 start(); 185 } 186 } 187 188 public void reconfigure(GravityConfig gravityConfig, GraniteConfig graniteConfig) { 189 this.gravityConfig = gravityConfig; 190 this.graniteConfig = graniteConfig; 191 if (gravityPool != null) 192 gravityPool.reconfigure(gravityConfig); 193 } 194 195 public void stop() throws Exception { 196 stop(true); 197 } 198 199 public void stop(boolean now) throws Exception { 200 log.info("Stopping Gravity (now=%s)...", now); 201 synchronized (this) { 202 if (adapterFactory != null) { 203 try { 204 adapterFactory.stopAll(); 205 } catch (Exception e) { 206 log.error(e, "Error while stopping adapter factory"); 207 } 208 adapterFactory = null; 209 } 210 211 if (serverChannel != null) { 212 try { 213 removeChannel(serverChannel.getId(), false); 214 } catch (Exception e) { 215 log.error(e, "Error while removing server channel: %s", serverChannel); 216 } 217 serverChannel = null; 218 } 219 220 if (channelsTimer != null) { 221 try { 222 channelsTimer.cancel(); 223 } catch (Exception e) { 224 log.error(e, "Error while cancelling channels timer"); 225 } 226 channelsTimer = null; 227 } 228 229 if (gravityPool != null) { 230 try { 231 if (now) 232 gravityPool.shutdownNow(); 233 else 234 gravityPool.shutdown(); 235 } 236 catch (Exception e) { 237 log.error(e, "Error while stopping thread pool"); 238 } 239 gravityPool = null; 240 } 241 242 if (udpReceiverFactory != null) { 243 try { 244 udpReceiverFactory.stop(); 245 } 246 catch (Exception e) { 247 log.error(e, "Error while stopping udp receiver factory"); 248 } 249 udpReceiverFactory = null; 250 } 251 252 started = false; 253 } 254 log.info("Gravity sucessfully stopped."); 255 } 256 257 /////////////////////////////////////////////////////////////////////////// 258 // GravityMBean attributes implementation. 259 260 public String getGravityFactoryName() { 261 return gravityConfig.getGravityFactory(); 262 } 263 264 public long getChannelIdleTimeoutMillis() { 265 return gravityConfig.getChannelIdleTimeoutMillis(); 266 } 267 public void setChannelIdleTimeoutMillis(long channelIdleTimeoutMillis) { 268 gravityConfig.setChannelIdleTimeoutMillis(channelIdleTimeoutMillis); 269 } 270 271 public boolean isRetryOnError() { 272 return gravityConfig.isRetryOnError(); 273 } 274 public void setRetryOnError(boolean retryOnError) { 275 gravityConfig.setRetryOnError(retryOnError); 276 } 277 278 public long getLongPollingTimeoutMillis() { 279 return gravityConfig.getLongPollingTimeoutMillis(); 280 } 281 public void setLongPollingTimeoutMillis(long longPollingTimeoutMillis) { 282 gravityConfig.setLongPollingTimeoutMillis(longPollingTimeoutMillis); 283 } 284 285 public int getMaxMessagesQueuedPerChannel() { 286 return gravityConfig.getMaxMessagesQueuedPerChannel(); 287 } 288 public void setMaxMessagesQueuedPerChannel(int maxMessagesQueuedPerChannel) { 289 gravityConfig.setMaxMessagesQueuedPerChannel(maxMessagesQueuedPerChannel); 290 } 291 292 public long getReconnectIntervalMillis() { 293 return gravityConfig.getReconnectIntervalMillis(); 294 } 295 296 public int getReconnectMaxAttempts() { 297 return gravityConfig.getReconnectMaxAttempts(); 298 } 299 300 public int getCorePoolSize() { 301 if (gravityPool != null) 302 return gravityPool.getCorePoolSize(); 303 return gravityConfig.getCorePoolSize(); 304 } 305 306 public void setCorePoolSize(int corePoolSize) { 307 gravityConfig.setCorePoolSize(corePoolSize); 308 if (gravityPool != null) 309 gravityPool.setCorePoolSize(corePoolSize); 310 } 311 312 public long getKeepAliveTimeMillis() { 313 if (gravityPool != null) 314 return gravityPool.getKeepAliveTimeMillis(); 315 return gravityConfig.getKeepAliveTimeMillis(); 316 } 317 public void setKeepAliveTimeMillis(long keepAliveTimeMillis) { 318 gravityConfig.setKeepAliveTimeMillis(keepAliveTimeMillis); 319 if (gravityPool != null) 320 gravityPool.setKeepAliveTimeMillis(keepAliveTimeMillis); 321 } 322 323 public int getMaximumPoolSize() { 324 if (gravityPool != null) 325 return gravityPool.getMaximumPoolSize(); 326 return gravityConfig.getMaximumPoolSize(); 327 } 328 public void setMaximumPoolSize(int maximumPoolSize) { 329 gravityConfig.setMaximumPoolSize(maximumPoolSize); 330 if (gravityPool != null) 331 gravityPool.setMaximumPoolSize(maximumPoolSize); 332 } 333 334 public int getQueueCapacity() { 335 if (gravityPool != null) 336 return gravityPool.getQueueCapacity(); 337 return gravityConfig.getQueueCapacity(); 338 } 339 340 public int getQueueRemainingCapacity() { 341 if (gravityPool != null) 342 return gravityPool.getQueueRemainingCapacity(); 343 return gravityConfig.getQueueCapacity(); 344 } 345 346 public int getQueueSize() { 347 if (gravityPool != null) 348 return gravityPool.getQueueSize(); 349 return 0; 350 } 351 352 public boolean hasUdpReceiverFactory() { 353 return udpReceiverFactory != null; 354 } 355 356 public UdpReceiverFactory getUdpReceiverFactory() { 357 return udpReceiverFactory; 358 } 359 360 /////////////////////////////////////////////////////////////////////////// 361 // Channel's operations. 362 363 protected <C extends Channel> C createChannel(ChannelFactory<C> channelFactory, String clientId) { 364 C channel = null; 365 if (clientId != null) { 366 channel = getChannel(channelFactory, clientId); 367 if (channel != null) 368 return channel; 369 } 370 371 String clientType = GraniteContext.getCurrentInstance().getClientType(); 372 channel = channelFactory.newChannel(UUIDUtil.randomUUID(), clientType); 373 TimeChannel<C> timeChannel = new TimeChannel<C>(channel); 374 for (int i = 0; channels.putIfAbsent(channel.getId(), timeChannel) != null; i++) { 375 if (i >= 10) 376 throw new RuntimeException("Could not find random new clientId after 10 iterations"); 377 channel.destroy(false); 378 channel = channelFactory.newChannel(UUIDUtil.randomUUID(), clientType); 379 timeChannel = new TimeChannel<C>(channel); 380 } 381 382 String channelId = channel.getId(); 383 384 // Save channel id in distributed data (clustering). 385 try { 386 DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); 387 if (gdd != null) { 388 log.debug("Saving channel id in distributed data: %s", channelId); 389 gdd.addChannelId(channelId, channelFactory.getClass().getName(), clientType); 390 } 391 } 392 catch (Exception e) { 393 log.error(e, "Could not add channel id in distributed data: %s", channelId); 394 } 395 396 // Initialize timer task. 397 access(channelId); 398 399 return channel; 400 } 401 402 @SuppressWarnings("unchecked") 403 public <C extends Channel> C getChannel(ChannelFactory<C> channelFactory, String clientId) { 404 if (clientId == null) 405 return null; 406 407 TimeChannel<C> timeChannel = (TimeChannel<C>)channels.get(clientId); 408 if (timeChannel == null) { 409 // Look for existing channel id/subscriptions in distributed data (clustering). 410 log.debug("Lookup channel %s in distributed data", clientId); 411 try { 412 DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); 413 if (gdd != null && gdd.hasChannelId(clientId)) { 414 log.debug("Found channel id in distributed data: %s", clientId); 415 String channelFactoryClassName = gdd.getChannelFactoryClassName(clientId); 416 String clientType = gdd.getChannelClientType(clientId); 417 channelFactory = (ChannelFactory<C>)TypeUtil.newInstance(channelFactoryClassName, new Class<?>[] { Gravity.class }, new Object[] { this }); 418 C channel = channelFactory.newChannel(clientId, clientType); 419 timeChannel = new TimeChannel<C>(channel); 420 if (channels.putIfAbsent(clientId, timeChannel) == null) { 421 for (CommandMessage subscription : gdd.getSubscriptions(clientId)) { 422 log.debug("Resubscribing channel: %s - %s", clientId, subscription); 423 handleSubscribeMessage(channelFactory, subscription, false); 424 } 425 access(clientId); 426 } 427 } 428 } 429 catch (Exception e) { 430 log.error(e, "Could not recreate channel/subscriptions from distributed data: %s", clientId); 431 } 432 } 433 434 return (timeChannel != null ? timeChannel.getChannel() : null); 435 } 436 437 public Channel removeChannel(String channelId, boolean timeout) { 438 if (channelId == null) 439 return null; 440 441 // Remove existing channel id/subscriptions in distributed data (clustering). 442 try { 443 DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); 444 if (gdd != null) { 445 log.debug("Removing channel id from distributed data: %s", channelId); 446 gdd.removeChannelId(channelId); 447 } 448 } 449 catch (Exception e) { 450 log.error(e, "Could not remove channel id from distributed data: %s", channelId); 451 } 452 453 TimeChannel<?> timeChannel = channels.get(channelId); 454 Channel channel = null; 455 if (timeChannel != null) { 456 try { 457 if (timeChannel.getTimerTask() != null) 458 timeChannel.getTimerTask().cancel(); 459 } 460 catch (Exception e) { 461 // Should never happen... 462 } 463 464 channel = timeChannel.getChannel(); 465 466 try { 467 for (Subscription subscription : channel.getSubscriptions()) { 468 try { 469 Message message = subscription.getUnsubscribeMessage(); 470 handleMessage(channel.getFactory(), message, true); 471 } 472 catch (Exception e) { 473 log.error(e, "Error while unsubscribing channel: %s from subscription: %s", channel, subscription); 474 } 475 } 476 } 477 finally { 478 channels.remove(channelId); 479 channel.destroy(timeout); 480 } 481 } 482 return channel; 483 } 484 485 public boolean access(String channelId) { 486 if (channelId != null) { 487 TimeChannel<?> timeChannel = channels.get(channelId); 488 if (timeChannel != null) { 489 synchronized (timeChannel) { 490 TimerTask timerTask = timeChannel.getTimerTask(); 491 if (timerTask != null) { 492 log.debug("Canceling TimerTask: %s", timerTask); 493 timerTask.cancel(); 494 timeChannel.setTimerTask(null); 495 } 496 497 timerTask = new ChannelTimerTask(this, channelId); 498 timeChannel.setTimerTask(timerTask); 499 500 long timeout = gravityConfig.getChannelIdleTimeoutMillis(); 501 log.debug("Scheduling TimerTask: %s for %s ms.", timerTask, timeout); 502 channelsTimer.schedule(timerTask, timeout); 503 return true; 504 } 505 } 506 } 507 return false; 508 } 509 510 public void execute(AsyncChannelRunner runner) { 511 if (gravityPool == null) { 512 runner.reset(); 513 throw new NullPointerException("Gravity not started or pool disabled"); 514 } 515 gravityPool.execute(runner); 516 } 517 518 public boolean cancel(AsyncChannelRunner runner) { 519 if (gravityPool == null) { 520 runner.reset(); 521 throw new NullPointerException("Gravity not started or pool disabled"); 522 } 523 return gravityPool.remove(runner); 524 } 525 526 /////////////////////////////////////////////////////////////////////////// 527 // Incoming message handling. 528 529 public Message handleMessage(final ChannelFactory<?> channelFactory, Message message) { 530 return handleMessage(channelFactory, message, false); 531 } 532 533 public Message handleMessage(final ChannelFactory<?> channelFactory, final Message message, boolean skipInterceptor) { 534 535 AMF3MessageInterceptor interceptor = null; 536 if (!skipInterceptor) 537 interceptor = GraniteContext.getCurrentInstance().getGraniteConfig().getAmf3MessageInterceptor(); 538 539 Message reply = null; 540 boolean publish = false; 541 542 try { 543 if (interceptor != null) 544 interceptor.before(message); 545 546 if (message instanceof CommandMessage) { 547 CommandMessage command = (CommandMessage)message; 548 549 switch (command.getOperation()) { 550 551 case CommandMessage.LOGIN_OPERATION: 552 case CommandMessage.LOGOUT_OPERATION: 553 return handleSecurityMessage(command); 554 555 case CommandMessage.CLIENT_PING_OPERATION: 556 return handlePingMessage(channelFactory, command); 557 case CommandMessage.CONNECT_OPERATION: 558 return handleConnectMessage(channelFactory, command); 559 case CommandMessage.DISCONNECT_OPERATION: 560 return handleDisconnectMessage(channelFactory, command); 561 case CommandMessage.SUBSCRIBE_OPERATION: 562 return handleSubscribeMessage(channelFactory, command); 563 case CommandMessage.UNSUBSCRIBE_OPERATION: 564 return handleUnsubscribeMessage(channelFactory, command); 565 566 default: 567 throw new UnsupportedOperationException("Unsupported command operation: " + command); 568 } 569 } 570 571 reply = handlePublishMessage(channelFactory, (AsyncMessage)message); 572 publish = true; 573 } 574 finally { 575 if (interceptor != null) 576 interceptor.after(message, reply); 577 } 578 579 if (reply != null) { 580 GraniteContext context = GraniteContext.getCurrentInstance(); 581 if (context.getSessionId() != null) { 582 reply.setHeader("org.granite.sessionId", context.getSessionId()); 583 if (publish && context instanceof ServletGraniteContext && ((ServletGraniteContext)context).getSession(false) != null) { 584 long serverTime = new Date().getTime(); 585 ((ServletGraniteContext)context).getSession().setAttribute(GraniteContext.SESSION_LAST_ACCESSED_TIME_KEY, serverTime); 586 reply.setHeader("org.granite.time", serverTime); 587 reply.setHeader("org.granite.sessionExp", ((ServletGraniteContext)context).getSession().getMaxInactiveInterval()); 588 } 589 } 590 } 591 592 return reply; 593 } 594 595 /////////////////////////////////////////////////////////////////////////// 596 // Other Public API methods. 597 598 public GraniteContext initThread(String sessionId, String clientType) { 599 GraniteContext context = GraniteContext.getCurrentInstance(); 600 if (context == null) 601 context = SimpleGraniteContext.createThreadInstance(graniteConfig, servicesConfig, sessionId, applicationMap, clientType); 602 return context; 603 } 604 605 public void releaseThread() { 606 GraniteContext.release(); 607 } 608 609 public Message publishMessage(AsyncMessage message) { 610 return publishMessage(serverChannel, message); 611 } 612 613 public Message publishMessage(Channel fromChannel, AsyncMessage message) { 614 initThread(null, fromChannel != null ? fromChannel.getClientType() : serverChannel.getClientType()); 615 616 return handlePublishMessage(null, message, fromChannel != null ? fromChannel : serverChannel); 617 } 618 619 private Message handlePingMessage(ChannelFactory<?> channelFactory, CommandMessage message) { 620 621 Channel channel = createChannel(channelFactory, (String)message.getClientId()); 622 623 AsyncMessage reply = new AcknowledgeMessage(message); 624 reply.setClientId(channel.getId()); 625 Map<String, Object> advice = new HashMap<String, Object>(); 626 advice.put(RECONNECT_INTERVAL_MS_KEY, Long.valueOf(gravityConfig.getReconnectIntervalMillis())); 627 advice.put(RECONNECT_MAX_ATTEMPTS_KEY, Long.valueOf(gravityConfig.getReconnectMaxAttempts())); 628 advice.put(ENCODE_MESSAGE_BODY_KEY, Boolean.valueOf(gravityConfig.isEncodeMessageBody())); 629 reply.setBody(advice); 630 reply.setDestination(message.getDestination()); 631 632 log.debug("handshake.handle: reply=%s", reply); 633 634 return reply; 635 } 636 637 private Message handleSecurityMessage(CommandMessage message) { 638 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig(); 639 640 Message response = null; 641 642 if (!config.hasSecurityService()) 643 log.warn("Ignored security operation (no security settings in granite-config.xml): %s", message); 644 else if (!config.getSecurityService().acceptsContext()) 645 log.info("Ignored security operation (security service does not handle this kind of granite context)", message); 646 else { 647 SecurityService securityService = config.getSecurityService(); 648 try { 649 if (message.isLoginOperation()) 650 securityService.login(message.getBody(), (String)message.getHeader(Message.CREDENTIALS_CHARSET_HEADER)); 651 else 652 securityService.logout(); 653 } 654 catch (Exception e) { 655 if (e instanceof SecurityServiceException) 656 log.debug(e, "Could not process security operation: %s", message); 657 else 658 log.error(e, "Could not process security operation: %s", message); 659 response = new ErrorMessage(message, e, true); 660 } 661 } 662 663 if (response == null) { 664 response = new AcknowledgeMessage(message, true); 665 // For SDK 2.0.1_Hotfix2. 666 if (message.isSecurityOperation()) 667 response.setBody("success"); 668 } 669 670 return response; 671 } 672 673 private Message handleConnectMessage(final ChannelFactory<?> channelFactory, CommandMessage message) { 674 Channel client = getChannel(channelFactory, (String)message.getClientId()); 675 676 if (client == null) 677 return handleUnknownClientMessage(message); 678 679 return null; 680 } 681 682 private Message handleDisconnectMessage(final ChannelFactory<?> channelFactory, CommandMessage message) { 683 Channel client = getChannel(channelFactory, (String)message.getClientId()); 684 if (client == null) 685 return handleUnknownClientMessage(message); 686 687 removeChannel(client.getId(), false); 688 689 AcknowledgeMessage reply = new AcknowledgeMessage(message); 690 reply.setDestination(message.getDestination()); 691 reply.setClientId(client.getId()); 692 return reply; 693 } 694 695 private Message handleSubscribeMessage(final ChannelFactory<?> channelFactory, final CommandMessage message) { 696 return handleSubscribeMessage(channelFactory, message, true); 697 } 698 699 private Message handleSubscribeMessage(final ChannelFactory<?> channelFactory, final CommandMessage message, final boolean saveMessageInSession) { 700 701 final GraniteContext context = GraniteContext.getCurrentInstance(); 702 703 // Get and check destination. 704 final Destination destination = context.getServicesConfig().findDestinationById( 705 message.getMessageRefType(), 706 message.getDestination() 707 ); 708 709 if (destination == null) 710 return getInvalidDestinationError(message); 711 712 713 GravityInvocationContext invocationContext = new GravityInvocationContext(message, destination) { 714 @Override 715 public Object invoke() throws Exception { 716 // Subscribe... 717 Channel channel = getChannel(channelFactory, (String)message.getClientId()); 718 if (channel == null) 719 return handleUnknownClientMessage(message); 720 721 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); 722 if (subscriptionId == null) { 723 subscriptionId = UUIDUtil.randomUUID(); 724 message.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId); 725 } 726 727 DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); 728 if (gdd != null) { 729 if (!gdd.hasChannelId(channel.getId())) { 730 gdd.addChannelId(channel.getId(), channel.getFactory().getClass().getName(), context.getClientType()); 731 log.debug("Stored channel %s in distributed data", channel.getId()); 732 } 733 734 if (Boolean.TRUE.toString().equals(destination.getProperties().get("session-selector"))) { 735 String selector = gdd.getDestinationSelector(destination.getId()); 736 log.debug("Session selector found: %s", selector); 737 if (selector != null) 738 message.setHeader(CommandMessage.SELECTOR_HEADER, selector); 739 } 740 } 741 742 ServiceAdapter adapter = adapterFactory.getServiceAdapter(message); 743 744 AsyncMessage reply = (AsyncMessage)adapter.manage(channel, message); 745 746 postManage(channel); 747 748 if (saveMessageInSession && !(reply instanceof ErrorMessage)) { 749 // Save subscription message in distributed data (clustering). 750 try { 751 if (gdd != null) { 752 log.debug("Saving new subscription message for channel: %s - %s", channel.getId(), message); 753 gdd.addSubcription(channel.getId(), message); 754 } 755 } 756 catch (Exception e) { 757 log.error(e, "Could not add subscription in distributed data: %s - %s", channel.getId(), subscriptionId); 758 } 759 } 760 761 reply.setDestination(message.getDestination()); 762 reply.setClientId(channel.getId()); 763 reply.getHeaders().putAll(message.getHeaders()); 764 765 if (gdd != null && message.getDestination() != null) { 766 gdd.setDestinationClientId(message.getDestination(), channel.getId()); 767 gdd.setDestinationSubscriptionId(message.getDestination(), subscriptionId); 768 } 769 770 return reply; 771 } 772 }; 773 774 // Check security 1 (destination). 775 if (destination.getSecurizer() instanceof GravityDestinationSecurizer) { 776 try { 777 ((GravityDestinationSecurizer)destination.getSecurizer()).canSubscribe(invocationContext); 778 } 779 catch (Exception e) { 780 return new ErrorMessage(message, e); 781 } 782 } 783 784 // Check security 2 (security service). 785 GraniteConfig config = context.getGraniteConfig(); 786 try { 787 if (config.hasSecurityService() && config.getSecurityService().acceptsContext()) 788 return (Message)config.getSecurityService().authorize(invocationContext); 789 790 return (Message)invocationContext.invoke(); 791 } 792 catch (Exception e) { 793 return new ErrorMessage(message, e, true); 794 } 795 } 796 797 private Message handleUnsubscribeMessage(final ChannelFactory<?> channelFactory, CommandMessage message) { 798 Channel channel = getChannel(channelFactory, (String)message.getClientId()); 799 if (channel == null) 800 return handleUnknownClientMessage(message); 801 802 AsyncMessage reply = null; 803 804 ServiceAdapter adapter = adapterFactory.getServiceAdapter(message); 805 806 reply = (AcknowledgeMessage)adapter.manage(channel, message); 807 808 postManage(channel); 809 810 if (!(reply instanceof ErrorMessage)) { 811 // Remove subscription message in distributed data (clustering). 812 try { 813 DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); 814 if (gdd != null) { 815 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); 816 log.debug("Removing subscription message from channel info: %s - %s", channel.getId(), subscriptionId); 817 gdd.removeSubcription(channel.getId(), subscriptionId); 818 } 819 } 820 catch (Exception e) { 821 log.error( 822 e, "Could not remove subscription from distributed data: %s - %s", 823 channel.getId(), message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER) 824 ); 825 } 826 } 827 828 reply.setDestination(message.getDestination()); 829 reply.setClientId(channel.getId()); 830 reply.getHeaders().putAll(message.getHeaders()); 831 832 return reply; 833 } 834 835 protected void postManage(Channel channel) { 836 } 837 838 private Message handlePublishMessage(final ChannelFactory<?> channelFactory, final AsyncMessage message) { 839 return handlePublishMessage(channelFactory, message, null); 840 } 841 842 private Message handlePublishMessage(final ChannelFactory<?> channelFactory, final AsyncMessage message, final Channel channel) { 843 844 GraniteContext context = GraniteContext.getCurrentInstance(); 845 846 // Get and check destination. 847 Destination destination = context.getServicesConfig().findDestinationById( 848 message.getClass().getName(), 849 message.getDestination() 850 ); 851 852 if (destination == null) 853 return getInvalidDestinationError(message); 854 855 if (message.getMessageId() == null) 856 message.setMessageId(UUIDUtil.randomUUID()); 857 message.setTimestamp(System.currentTimeMillis()); 858 if (channel != null) 859 message.setClientId(channel.getId()); 860 861 GravityInvocationContext invocationContext = new GravityInvocationContext(message, destination) { 862 @Override 863 public Object invoke() throws Exception { 864 // Publish... 865 Channel fromChannel = channel; 866 if (fromChannel == null) 867 fromChannel = getChannel(channelFactory, (String)message.getClientId()); 868 if (fromChannel == null) 869 return handleUnknownClientMessage(message); 870 871 ServiceAdapter adapter = adapterFactory.getServiceAdapter(message); 872 873 AsyncMessage reply = (AsyncMessage)adapter.invoke(fromChannel, message); 874 875 reply.setDestination(message.getDestination()); 876 reply.setClientId(fromChannel.getId()); 877 878 return reply; 879 } 880 }; 881 882 // Check security 1 (destination). 883 if (destination.getSecurizer() instanceof GravityDestinationSecurizer) { 884 try { 885 ((GravityDestinationSecurizer)destination.getSecurizer()).canPublish(invocationContext); 886 } 887 catch (Exception e) { 888 return new ErrorMessage(message, e, true); 889 } 890 } 891 892 // Check security 2 (security service). 893 GraniteConfig config = context.getGraniteConfig(); 894 try { 895 if (config.hasSecurityService() && config.getSecurityService().acceptsContext()) 896 return (Message)config.getSecurityService().authorize(invocationContext); 897 898 return (Message)invocationContext.invoke(); 899 } 900 catch (Exception e) { 901 return new ErrorMessage(message, e, true); 902 } 903 } 904 905 private Message handleUnknownClientMessage(Message message) { 906 ErrorMessage reply = new ErrorMessage(message, true); 907 reply.setFaultCode("Server.Call.UnknownClient"); 908 reply.setFaultString("Unknown client"); 909 return reply; 910 } 911 912 /////////////////////////////////////////////////////////////////////////// 913 // Utilities. 914 915 private ErrorMessage getInvalidDestinationError(Message message) { 916 917 String messageType = message.getClass().getName(); 918 if (message instanceof CommandMessage) 919 messageType += '[' + ((CommandMessage)message).getMessageRefType() + ']'; 920 921 ErrorMessage reply = new ErrorMessage(message, true); 922 reply.setFaultCode("Server.Messaging.InvalidDestination"); 923 reply.setFaultString( 924 "No configured destination for id: " + message.getDestination() + 925 " and message type: " + messageType 926 ); 927 return reply; 928 } 929 930 private static class ServerChannel extends AbstractChannel implements Serializable { 931 932 private static final long serialVersionUID = 1L; 933 934 public ServerChannel(Gravity gravity, String channelId, ChannelFactory<ServerChannel> factory, String clientType) { 935 super(gravity, channelId, factory, clientType); 936 } 937 938 @Override 939 public Gravity getGravity() { 940 return gravity; 941 } 942 943 public void close() { 944 } 945 946 @Override 947 public void receive(AsyncMessage message) throws MessageReceivingException { 948 } 949 950 @Override 951 protected boolean hasAsyncHttpContext() { 952 return false; 953 } 954 955 @Override 956 protected AsyncHttpContext acquireAsyncHttpContext() { 957 return null; 958 } 959 960 @Override 961 protected void releaseAsyncHttpContext(AsyncHttpContext context) { 962 } 963 } 964}