/** * @author Cameron INGRAM * @author Venkat DANDA */ public class SeamServiceInvoker extends ServiceInvoker<SeamServiceFactory> { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(SeamServiceInvoker.class); public static final String CAPITALIZED_DESTINATION_ID = "{capitalized.destination.id}"; public static final String DESTINATION_ID = "{destination.id}"; public SeamServiceInvoker(Destination destination, SeamServiceFactory factory, Object instance) throws ServiceException { super(destination, factory); this.invokee = instance; } @Override protected void beforeInvocation(ServiceInvocationContext context) { log.debug("Before Invocation"); } @Override protected Object afterInvocation(ServiceInvocationContext context, Object result) { log.debug("After Invocation"); return result; } }
/** * @author William DRAI * @author Franck WOLFF */ public class DefaultGravity implements Gravity, DefaultGravityMBean { /////////////////////////////////////////////////////////////////////////// // Fields. private static final Logger log = Logger.getLogger(Gravity.class); private final Map<String, Object> applicationMap = new HashMap<String, Object>(); private final ConcurrentHashMap<String, TimeChannel<?>> channels = new ConcurrentHashMap<String, TimeChannel<?>>(); private GravityConfig gravityConfig = null; private ServicesConfig servicesConfig = null; private GraniteConfig graniteConfig = null; private SharedContext sharedContext = null; private Channel serverChannel = null; private AdapterFactory adapterFactory = null; private GravityPool gravityPool = null; private UdpReceiverFactory udpReceiverFactory = null; private Timer channelsTimer; private boolean started; /////////////////////////////////////////////////////////////////////////// // Constructor. public DefaultGravity( GravityConfig gravityConfig, ServicesConfig servicesConfig, GraniteConfig graniteConfig, SharedContext sharedContext) { if (gravityConfig == null || servicesConfig == null || graniteConfig == null) throw new NullPointerException("All arguments must be non null."); this.gravityConfig = gravityConfig; this.servicesConfig = servicesConfig; this.graniteConfig = graniteConfig; this.sharedContext = sharedContext; } /////////////////////////////////////////////////////////////////////////// // Properties. public GravityConfig getGravityConfig() { return gravityConfig; } public ServicesConfig getServicesConfig() { return servicesConfig; } public GraniteConfig getGraniteConfig() { return graniteConfig; } public SharedContext getSharedContext() { return sharedContext; } public boolean isStarted() { return started; } public ServiceAdapter getServiceAdapter(String messageType, String destinationId) { return adapterFactory.getServiceAdapter(messageType, destinationId); } /////////////////////////////////////////////////////////////////////////// // Starting/stopping. public void start() throws Exception { log.info("Starting Gravity..."); synchronized (this) { if (!started) { adapterFactory = new AdapterFactory(this); internalStart(); serverChannel = new ServerChannel(this, ServerChannel.class.getName(), null, null); if (gravityConfig.isUseUdp()) { ServiceLoader<UdpReceiverFactory> loader = ServiceLoader.load(UdpReceiverFactory.class); Iterator<UdpReceiverFactory> factories = loader.iterator(); if (factories.hasNext()) { udpReceiverFactory = factories.next(); udpReceiverFactory.setPort(gravityConfig.getUdpPort()); udpReceiverFactory.setNio(gravityConfig.isUdpNio()); udpReceiverFactory.setConnected(gravityConfig.isUdpConnected()); udpReceiverFactory.setSendBufferSize(gravityConfig.getUdpSendBufferSize()); udpReceiverFactory.start(); } else log.warn("UDP receiver factory not found"); } started = true; } } log.info("Gravity successfully started."); } protected void internalStart() { gravityPool = new GravityPool(gravityConfig); channelsTimer = new Timer(); if (graniteConfig.isRegisterMBeans()) { try { ObjectName name = new ObjectName( "org.graniteds:type=Gravity,context=" + graniteConfig.getMBeanContextName()); log.info("Registering MBean: %s", name); OpenMBean mBean = OpenMBean.createMBean(this); MBeanServerLocator.getInstance().register(mBean, name, true); } catch (Exception e) { log.error( e, "Could not register Gravity MBean for context: %s", graniteConfig.getMBeanContextName()); } } } public void restart() throws Exception { synchronized (this) { stop(); start(); } } public void reconfigure(GravityConfig gravityConfig, GraniteConfig graniteConfig) { this.gravityConfig = gravityConfig; this.graniteConfig = graniteConfig; if (gravityPool != null) gravityPool.reconfigure(gravityConfig); } public void stop() throws Exception { stop(true); } public void stop(boolean now) throws Exception { log.info("Stopping Gravity (now=%s)...", now); synchronized (this) { if (adapterFactory != null) { try { adapterFactory.stopAll(); } catch (Exception e) { log.error(e, "Error while stopping adapter factory"); } adapterFactory = null; } if (serverChannel != null) { try { removeChannel(serverChannel.getId(), false); } catch (Exception e) { log.error(e, "Error while removing server channel: %s", serverChannel); } serverChannel = null; } if (channelsTimer != null) { try { channelsTimer.cancel(); } catch (Exception e) { log.error(e, "Error while cancelling channels timer"); } channelsTimer = null; } if (gravityPool != null) { try { if (now) gravityPool.shutdownNow(); else gravityPool.shutdown(); } catch (Exception e) { log.error(e, "Error while stopping thread pool"); } gravityPool = null; } if (udpReceiverFactory != null) { try { udpReceiverFactory.stop(); } catch (Exception e) { log.error(e, "Error while stopping udp receiver factory"); } udpReceiverFactory = null; } started = false; } log.info("Gravity sucessfully stopped."); } /////////////////////////////////////////////////////////////////////////// // GravityMBean attributes implementation. public String getGravityFactoryName() { return gravityConfig.getGravityFactory(); } public long getChannelIdleTimeoutMillis() { return gravityConfig.getChannelIdleTimeoutMillis(); } public void setChannelIdleTimeoutMillis(long channelIdleTimeoutMillis) { gravityConfig.setChannelIdleTimeoutMillis(channelIdleTimeoutMillis); } public boolean isRetryOnError() { return gravityConfig.isRetryOnError(); } public void setRetryOnError(boolean retryOnError) { gravityConfig.setRetryOnError(retryOnError); } public long getLongPollingTimeoutMillis() { return gravityConfig.getLongPollingTimeoutMillis(); } public void setLongPollingTimeoutMillis(long longPollingTimeoutMillis) { gravityConfig.setLongPollingTimeoutMillis(longPollingTimeoutMillis); } public int getMaxMessagesQueuedPerChannel() { return gravityConfig.getMaxMessagesQueuedPerChannel(); } public void setMaxMessagesQueuedPerChannel(int maxMessagesQueuedPerChannel) { gravityConfig.setMaxMessagesQueuedPerChannel(maxMessagesQueuedPerChannel); } public long getReconnectIntervalMillis() { return gravityConfig.getReconnectIntervalMillis(); } public int getReconnectMaxAttempts() { return gravityConfig.getReconnectMaxAttempts(); } public int getCorePoolSize() { if (gravityPool != null) return gravityPool.getCorePoolSize(); return gravityConfig.getCorePoolSize(); } public void setCorePoolSize(int corePoolSize) { gravityConfig.setCorePoolSize(corePoolSize); if (gravityPool != null) gravityPool.setCorePoolSize(corePoolSize); } public long getKeepAliveTimeMillis() { if (gravityPool != null) return gravityPool.getKeepAliveTimeMillis(); return gravityConfig.getKeepAliveTimeMillis(); } public void setKeepAliveTimeMillis(long keepAliveTimeMillis) { gravityConfig.setKeepAliveTimeMillis(keepAliveTimeMillis); if (gravityPool != null) gravityPool.setKeepAliveTimeMillis(keepAliveTimeMillis); } public int getMaximumPoolSize() { if (gravityPool != null) return gravityPool.getMaximumPoolSize(); return gravityConfig.getMaximumPoolSize(); } public void setMaximumPoolSize(int maximumPoolSize) { gravityConfig.setMaximumPoolSize(maximumPoolSize); if (gravityPool != null) gravityPool.setMaximumPoolSize(maximumPoolSize); } public int getQueueCapacity() { if (gravityPool != null) return gravityPool.getQueueCapacity(); return gravityConfig.getQueueCapacity(); } public int getQueueRemainingCapacity() { if (gravityPool != null) return gravityPool.getQueueRemainingCapacity(); return gravityConfig.getQueueCapacity(); } public int getQueueSize() { if (gravityPool != null) return gravityPool.getQueueSize(); return 0; } public boolean hasUdpReceiverFactory() { return udpReceiverFactory != null; } public UdpReceiverFactory getUdpReceiverFactory() { return udpReceiverFactory; } /////////////////////////////////////////////////////////////////////////// // Channel's operations. protected <C extends Channel> C createChannel(ChannelFactory<C> channelFactory, String clientId) { C channel = null; if (clientId != null) { channel = getChannel(channelFactory, clientId); if (channel != null) return channel; } String clientType = GraniteContext.getCurrentInstance().getClientType(); channel = channelFactory.newChannel(UUIDUtil.randomUUID(), clientType); TimeChannel<C> timeChannel = new TimeChannel<C>(channel); for (int i = 0; channels.putIfAbsent(channel.getId(), timeChannel) != null; i++) { if (i >= 10) throw new RuntimeException("Could not find random new clientId after 10 iterations"); channel.destroy(false); channel = channelFactory.newChannel(UUIDUtil.randomUUID(), clientType); timeChannel = new TimeChannel<C>(channel); } String channelId = channel.getId(); // Save channel id in distributed data (clustering). try { DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); if (gdd != null) { log.debug("Saving channel id in distributed data: %s", channelId); gdd.addChannelId(channelId, channelFactory.getClass().getName(), clientType); } } catch (Exception e) { log.error(e, "Could not add channel id in distributed data: %s", channelId); } // Initialize timer task. access(channelId); return channel; } @SuppressWarnings("unchecked") public <C extends Channel> C getChannel(ChannelFactory<C> channelFactory, String clientId) { if (clientId == null) return null; TimeChannel<C> timeChannel = (TimeChannel<C>) channels.get(clientId); if (timeChannel == null) { // Look for existing channel id/subscriptions in distributed data (clustering). log.debug("Lookup channel %s in distributed data", clientId); try { DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); if (gdd != null && gdd.hasChannelId(clientId)) { log.debug("Found channel id in distributed data: %s", clientId); String channelFactoryClassName = gdd.getChannelFactoryClassName(clientId); String clientType = gdd.getChannelClientType(clientId); channelFactory = (ChannelFactory<C>) TypeUtil.newInstance( channelFactoryClassName, new Class<?>[] {Gravity.class}, new Object[] {this}); C channel = channelFactory.newChannel(clientId, clientType); timeChannel = new TimeChannel<C>(channel); if (channels.putIfAbsent(clientId, timeChannel) == null) { for (CommandMessage subscription : gdd.getSubscriptions(clientId)) { log.debug("Resubscribing channel: %s - %s", clientId, subscription); handleSubscribeMessage(channelFactory, subscription, false); } access(clientId); } } } catch (Exception e) { log.error( e, "Could not recreate channel/subscriptions from distributed data: %s", clientId); } } return (timeChannel != null ? timeChannel.getChannel() : null); } public Channel removeChannel(String channelId, boolean timeout) { if (channelId == null) return null; // Remove existing channel id/subscriptions in distributed data (clustering). try { DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); if (gdd != null) { log.debug("Removing channel id from distributed data: %s", channelId); gdd.removeChannelId(channelId); } } catch (Exception e) { log.error(e, "Could not remove channel id from distributed data: %s", channelId); } TimeChannel<?> timeChannel = channels.get(channelId); Channel channel = null; if (timeChannel != null) { try { if (timeChannel.getTimerTask() != null) timeChannel.getTimerTask().cancel(); } catch (Exception e) { // Should never happen... } channel = timeChannel.getChannel(); try { for (Subscription subscription : channel.getSubscriptions()) { try { Message message = subscription.getUnsubscribeMessage(); handleMessage(channel.getFactory(), message, true); } catch (Exception e) { log.error( e, "Error while unsubscribing channel: %s from subscription: %s", channel, subscription); } } } finally { channels.remove(channelId); channel.destroy(timeout); } } return channel; } public boolean access(String channelId) { if (channelId != null) { TimeChannel<?> timeChannel = channels.get(channelId); if (timeChannel != null) { synchronized (timeChannel) { TimerTask timerTask = timeChannel.getTimerTask(); if (timerTask != null) { log.debug("Canceling TimerTask: %s", timerTask); timerTask.cancel(); timeChannel.setTimerTask(null); } timerTask = new ChannelTimerTask(this, channelId); timeChannel.setTimerTask(timerTask); long timeout = gravityConfig.getChannelIdleTimeoutMillis(); log.debug("Scheduling TimerTask: %s for %s ms.", timerTask, timeout); channelsTimer.schedule(timerTask, timeout); return true; } } } return false; } public void execute(AsyncChannelRunner runner) { if (gravityPool == null) { runner.reset(); throw new NullPointerException("Gravity not started or pool disabled"); } gravityPool.execute(runner); } public boolean cancel(AsyncChannelRunner runner) { if (gravityPool == null) { runner.reset(); throw new NullPointerException("Gravity not started or pool disabled"); } return gravityPool.remove(runner); } /////////////////////////////////////////////////////////////////////////// // Incoming message handling. public Message handleMessage(final ChannelFactory<?> channelFactory, Message message) { return handleMessage(channelFactory, message, false); } public Message handleMessage( final ChannelFactory<?> channelFactory, final Message message, boolean skipInterceptor) { AMF3MessageInterceptor interceptor = null; if (!skipInterceptor) interceptor = GraniteContext.getCurrentInstance().getGraniteConfig().getAmf3MessageInterceptor(); Message reply = null; boolean publish = false; try { if (interceptor != null) interceptor.before(message); if (message instanceof CommandMessage) { CommandMessage command = (CommandMessage) message; switch (command.getOperation()) { case CommandMessage.LOGIN_OPERATION: case CommandMessage.LOGOUT_OPERATION: return handleSecurityMessage(command); case CommandMessage.CLIENT_PING_OPERATION: return handlePingMessage(channelFactory, command); case CommandMessage.CONNECT_OPERATION: return handleConnectMessage(channelFactory, command); case CommandMessage.DISCONNECT_OPERATION: return handleDisconnectMessage(channelFactory, command); case CommandMessage.SUBSCRIBE_OPERATION: return handleSubscribeMessage(channelFactory, command); case CommandMessage.UNSUBSCRIBE_OPERATION: return handleUnsubscribeMessage(channelFactory, command); default: throw new UnsupportedOperationException("Unsupported command operation: " + command); } } reply = handlePublishMessage(channelFactory, (AsyncMessage) message); publish = true; } finally { if (interceptor != null) interceptor.after(message, reply); } if (reply != null) { GraniteContext context = GraniteContext.getCurrentInstance(); if (context.getSessionId() != null) { reply.setHeader("org.granite.sessionId", context.getSessionId()); if (publish && context instanceof ServletGraniteContext && ((ServletGraniteContext) context).getSession(false) != null) { long serverTime = new Date().getTime(); ((ServletGraniteContext) context) .getSession() .setAttribute(GraniteContext.SESSION_LAST_ACCESSED_TIME_KEY, serverTime); reply.setHeader("org.granite.time", serverTime); reply.setHeader( "org.granite.sessionExp", ((ServletGraniteContext) context).getSession().getMaxInactiveInterval()); } } } return reply; } /////////////////////////////////////////////////////////////////////////// // Other Public API methods. public GraniteContext initThread(String sessionId, String clientType) { GraniteContext context = GraniteContext.getCurrentInstance(); if (context == null) context = SimpleGraniteContext.createThreadInstance( graniteConfig, servicesConfig, sessionId, applicationMap, clientType); return context; } public void releaseThread() { GraniteContext.release(); } public Message publishMessage(AsyncMessage message) { return publishMessage(serverChannel, message); } public Message publishMessage(Channel fromChannel, AsyncMessage message) { initThread( null, fromChannel != null ? fromChannel.getClientType() : serverChannel.getClientType()); return handlePublishMessage(null, message, fromChannel != null ? fromChannel : serverChannel); } private Message handlePingMessage(ChannelFactory<?> channelFactory, CommandMessage message) { Channel channel = createChannel(channelFactory, (String) message.getClientId()); AsyncMessage reply = new AcknowledgeMessage(message); reply.setClientId(channel.getId()); Map<String, Object> advice = new HashMap<String, Object>(); advice.put(RECONNECT_INTERVAL_MS_KEY, Long.valueOf(gravityConfig.getReconnectIntervalMillis())); advice.put(RECONNECT_MAX_ATTEMPTS_KEY, Long.valueOf(gravityConfig.getReconnectMaxAttempts())); advice.put(ENCODE_MESSAGE_BODY_KEY, Boolean.valueOf(gravityConfig.isEncodeMessageBody())); reply.setBody(advice); reply.setDestination(message.getDestination()); log.debug("handshake.handle: reply=%s", reply); return reply; } private Message handleSecurityMessage(CommandMessage message) { GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig(); Message response = null; if (!config.hasSecurityService()) log.warn( "Ignored security operation (no security settings in granite-config.xml): %s", message); else if (!config.getSecurityService().acceptsContext()) log.info( "Ignored security operation (security service does not handle this kind of granite context)", message); else { SecurityService securityService = config.getSecurityService(); try { if (message.isLoginOperation()) securityService.login( message.getBody(), (String) message.getHeader(Message.CREDENTIALS_CHARSET_HEADER)); else securityService.logout(); } catch (Exception e) { if (e instanceof SecurityServiceException) log.debug(e, "Could not process security operation: %s", message); else log.error(e, "Could not process security operation: %s", message); response = new ErrorMessage(message, e, true); } } if (response == null) { response = new AcknowledgeMessage(message, true); // For SDK 2.0.1_Hotfix2. if (message.isSecurityOperation()) response.setBody("success"); } return response; } private Message handleConnectMessage( final ChannelFactory<?> channelFactory, CommandMessage message) { Channel client = getChannel(channelFactory, (String) message.getClientId()); if (client == null) return handleUnknownClientMessage(message); return null; } private Message handleDisconnectMessage( final ChannelFactory<?> channelFactory, CommandMessage message) { Channel client = getChannel(channelFactory, (String) message.getClientId()); if (client == null) return handleUnknownClientMessage(message); removeChannel(client.getId(), false); AcknowledgeMessage reply = new AcknowledgeMessage(message); reply.setDestination(message.getDestination()); reply.setClientId(client.getId()); return reply; } private Message handleSubscribeMessage( final ChannelFactory<?> channelFactory, final CommandMessage message) { return handleSubscribeMessage(channelFactory, message, true); } private Message handleSubscribeMessage( final ChannelFactory<?> channelFactory, final CommandMessage message, final boolean saveMessageInSession) { final GraniteContext context = GraniteContext.getCurrentInstance(); // Get and check destination. final Destination destination = context .getServicesConfig() .findDestinationById(message.getMessageRefType(), message.getDestination()); if (destination == null) return getInvalidDestinationError(message); GravityInvocationContext invocationContext = new GravityInvocationContext(message, destination) { @Override public Object invoke() throws Exception { // Subscribe... Channel channel = getChannel(channelFactory, (String) message.getClientId()); if (channel == null) return handleUnknownClientMessage(message); String subscriptionId = (String) message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); if (subscriptionId == null) { subscriptionId = UUIDUtil.randomUUID(); message.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId); } DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); if (gdd != null) { if (!gdd.hasChannelId(channel.getId())) { gdd.addChannelId( channel.getId(), channel.getFactory().getClass().getName(), context.getClientType()); log.debug("Stored channel %s in distributed data", channel.getId()); } if (Boolean.TRUE .toString() .equals(destination.getProperties().get("session-selector"))) { String selector = gdd.getDestinationSelector(destination.getId()); log.debug("Session selector found: %s", selector); if (selector != null) message.setHeader(CommandMessage.SELECTOR_HEADER, selector); } } ServiceAdapter adapter = adapterFactory.getServiceAdapter(message); AsyncMessage reply = (AsyncMessage) adapter.manage(channel, message); postManage(channel); if (saveMessageInSession && !(reply instanceof ErrorMessage)) { // Save subscription message in distributed data (clustering). try { if (gdd != null) { log.debug( "Saving new subscription message for channel: %s - %s", channel.getId(), message); gdd.addSubcription(channel.getId(), message); } } catch (Exception e) { log.error( e, "Could not add subscription in distributed data: %s - %s", channel.getId(), subscriptionId); } } reply.setDestination(message.getDestination()); reply.setClientId(channel.getId()); reply.getHeaders().putAll(message.getHeaders()); if (gdd != null && message.getDestination() != null) { gdd.setDestinationClientId(message.getDestination(), channel.getId()); gdd.setDestinationSubscriptionId(message.getDestination(), subscriptionId); } return reply; } }; // Check security 1 (destination). if (destination.getSecurizer() instanceof GravityDestinationSecurizer) { try { ((GravityDestinationSecurizer) destination.getSecurizer()).canSubscribe(invocationContext); } catch (Exception e) { return new ErrorMessage(message, e); } } // Check security 2 (security service). GraniteConfig config = context.getGraniteConfig(); try { if (config.hasSecurityService() && config.getSecurityService().acceptsContext()) return (Message) config.getSecurityService().authorize(invocationContext); return (Message) invocationContext.invoke(); } catch (Exception e) { return new ErrorMessage(message, e, true); } } private Message handleUnsubscribeMessage( final ChannelFactory<?> channelFactory, CommandMessage message) { Channel channel = getChannel(channelFactory, (String) message.getClientId()); if (channel == null) return handleUnknownClientMessage(message); AsyncMessage reply = null; ServiceAdapter adapter = adapterFactory.getServiceAdapter(message); reply = (AcknowledgeMessage) adapter.manage(channel, message); postManage(channel); if (!(reply instanceof ErrorMessage)) { // Remove subscription message in distributed data (clustering). try { DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance(); if (gdd != null) { String subscriptionId = (String) message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); log.debug( "Removing subscription message from channel info: %s - %s", channel.getId(), subscriptionId); gdd.removeSubcription(channel.getId(), subscriptionId); } } catch (Exception e) { log.error( e, "Could not remove subscription from distributed data: %s - %s", channel.getId(), message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER)); } } reply.setDestination(message.getDestination()); reply.setClientId(channel.getId()); reply.getHeaders().putAll(message.getHeaders()); return reply; } protected void postManage(Channel channel) {} private Message handlePublishMessage( final ChannelFactory<?> channelFactory, final AsyncMessage message) { return handlePublishMessage(channelFactory, message, null); } private Message handlePublishMessage( final ChannelFactory<?> channelFactory, final AsyncMessage message, final Channel channel) { GraniteContext context = GraniteContext.getCurrentInstance(); // Get and check destination. Destination destination = context .getServicesConfig() .findDestinationById(message.getClass().getName(), message.getDestination()); if (destination == null) return getInvalidDestinationError(message); if (message.getMessageId() == null) message.setMessageId(UUIDUtil.randomUUID()); message.setTimestamp(System.currentTimeMillis()); if (channel != null) message.setClientId(channel.getId()); GravityInvocationContext invocationContext = new GravityInvocationContext(message, destination) { @Override public Object invoke() throws Exception { // Publish... Channel fromChannel = channel; if (fromChannel == null) fromChannel = getChannel(channelFactory, (String) message.getClientId()); if (fromChannel == null) return handleUnknownClientMessage(message); ServiceAdapter adapter = adapterFactory.getServiceAdapter(message); AsyncMessage reply = (AsyncMessage) adapter.invoke(fromChannel, message); reply.setDestination(message.getDestination()); reply.setClientId(fromChannel.getId()); return reply; } }; // Check security 1 (destination). if (destination.getSecurizer() instanceof GravityDestinationSecurizer) { try { ((GravityDestinationSecurizer) destination.getSecurizer()).canPublish(invocationContext); } catch (Exception e) { return new ErrorMessage(message, e, true); } } // Check security 2 (security service). GraniteConfig config = context.getGraniteConfig(); try { if (config.hasSecurityService() && config.getSecurityService().acceptsContext()) return (Message) config.getSecurityService().authorize(invocationContext); return (Message) invocationContext.invoke(); } catch (Exception e) { return new ErrorMessage(message, e, true); } } private Message handleUnknownClientMessage(Message message) { ErrorMessage reply = new ErrorMessage(message, true); reply.setFaultCode("Server.Call.UnknownClient"); reply.setFaultString("Unknown client"); return reply; } /////////////////////////////////////////////////////////////////////////// // Utilities. private ErrorMessage getInvalidDestinationError(Message message) { String messageType = message.getClass().getName(); if (message instanceof CommandMessage) messageType += '[' + ((CommandMessage) message).getMessageRefType() + ']'; ErrorMessage reply = new ErrorMessage(message, true); reply.setFaultCode("Server.Messaging.InvalidDestination"); reply.setFaultString( "No configured destination for id: " + message.getDestination() + " and message type: " + messageType); return reply; } private static class ServerChannel extends AbstractChannel implements Serializable { private static final long serialVersionUID = 1L; public ServerChannel( Gravity gravity, String channelId, ChannelFactory<ServerChannel> factory, String clientType) { super(gravity, channelId, factory, clientType); } @Override public Gravity getGravity() { return gravity; } public void close() {} @Override public void receive(AsyncMessage message) throws MessageReceivingException {} @Override protected boolean hasAsyncHttpContext() { return false; } @Override protected AsyncHttpContext acquireAsyncHttpContext() { return null; } @Override protected void releaseAsyncHttpContext(AsyncHttpContext context) {} } }
/** * @author Bouiaw * @author wdrai */ public class SpringSecurityService extends AbstractSecurityService { private static final Logger log = Logger.getLogger(SpringSecurityService.class); private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied"; private AbstractSpringSecurityInterceptor securityInterceptor = null; public SpringSecurityService() { log.debug("Starting Spring Security Service!"); } public void configure(Map<String, String> params) { log.debug("Configuring with parameters (NOOP) %s: ", params); } public void setSecurityInterceptor(AbstractSpringSecurityInterceptor securityInterceptor) { this.securityInterceptor = securityInterceptor; } public Principal login(Object credentials, String charset) { List<String> decodedCredentials = Arrays.asList(decodeBase64Credentials(credentials, charset)); HttpGraniteContext context = (HttpGraniteContext) GraniteContext.getCurrentInstance(); HttpServletRequest httpRequest = context.getRequest(); String user = decodedCredentials.get(0); String password = decodedCredentials.get(1); Authentication auth = new UsernamePasswordAuthenticationToken(user, password); Principal principal = null; ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( httpRequest.getSession().getServletContext()); if (ctx != null) { AbstractAuthenticationManager authenticationManager = BeanFactoryUtils.beanOfTypeIncludingAncestors(ctx, AbstractAuthenticationManager.class); try { Authentication authentication = authenticationManager.authenticate(auth); SecurityContext securityContext = SecurityContextHolder.getContext(); securityContext.setAuthentication(authentication); principal = authentication; SecurityContextHolder.setContext(securityContext); saveSecurityContextInSession(securityContext, 0); endLogin(credentials, charset); } catch (AuthenticationException e) { handleAuthenticationExceptions(e); } } log.debug("User %s logged in", user); return principal; } protected void handleAuthenticationExceptions(AuthenticationException e) { if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException) throw SecurityServiceException.newInvalidCredentialsException(e.getMessage()); throw SecurityServiceException.newAuthenticationFailedException(e.getMessage()); } public Object authorize(AbstractSecurityContext context) throws Exception { log.debug("Authorize: %s", context); log.debug( "Is %s secured? %b", context.getDestination().getId(), context.getDestination().isSecured()); startAuthorization(context); HttpGraniteContext graniteContext = (HttpGraniteContext) GraniteContext.getCurrentInstance(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); SecurityContext securityContextBefore = null; int securityContextHashBefore = 0; if (graniteContext.getRequest().getAttribute(FILTER_APPLIED) == null) { securityContextBefore = loadSecurityContextFromSession(); if (securityContextBefore == null) securityContextBefore = SecurityContextHolder.getContext(); else securityContextHashBefore = securityContextBefore.hashCode(); SecurityContextHolder.setContext(securityContextBefore); authentication = securityContextBefore.getAuthentication(); } if (context.getDestination().isSecured()) { if (!isAuthenticated(authentication) || authentication instanceof AnonymousAuthenticationToken) { log.debug("Is not authenticated!"); throw SecurityServiceException.newNotLoggedInException("User not logged in"); } if (!userCanAccessService(context, authentication)) { log.debug("Access denied for: %s", authentication.getName()); throw SecurityServiceException.newAccessDeniedException("User not in required role"); } } try { Object returnedObject = securityInterceptor != null ? securityInterceptor.invoke(context) : endAuthorization(context); return returnedObject; } catch (AccessDeniedException e) { throw SecurityServiceException.newAccessDeniedException(e.getMessage()); } catch (InvocationTargetException e) { handleAuthorizationExceptions(e); throw e; } finally { if (graniteContext.getRequest().getAttribute(FILTER_APPLIED) == null) { // Do this only when not already filtered by Spring Security SecurityContext securityContextAfter = SecurityContextHolder.getContext(); SecurityContextHolder.clearContext(); saveSecurityContextInSession(securityContextAfter, securityContextHashBefore); } } } public void logout() { HttpGraniteContext context = (HttpGraniteContext) GraniteContext.getCurrentInstance(); HttpSession session = context.getSession(false); if (session != null && session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY) != null) session.invalidate(); SecurityContextHolder.clearContext(); } protected boolean isUserInRole(Authentication authentication, String role) { for (GrantedAuthority ga : authentication.getAuthorities()) { if (ga.getAuthority().matches(role)) return true; } return false; } protected boolean isAuthenticated(Authentication authentication) { return authentication != null && authentication.isAuthenticated(); } protected boolean userCanAccessService( AbstractSecurityContext context, Authentication authentication) { log.debug("Is authenticated as: %s", authentication.getName()); for (String role : context.getDestination().getRoles()) { if (isUserInRole(authentication, role)) { log.debug("Allowed access to %s in role %s", authentication.getName(), role); return true; } log.debug("Access denied for %s not in role %s", authentication.getName(), role); } return false; } protected SecurityContext loadSecurityContextFromSession() { HttpGraniteContext context = (HttpGraniteContext) GraniteContext.getCurrentInstance(); HttpServletRequest request = context.getRequest(); return (SecurityContext) request .getSession() .getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY); } protected void saveSecurityContextInSession( SecurityContext securityContext, int securityContextHashBefore) { if (securityContext.hashCode() != securityContextHashBefore && !(securityContext.getAuthentication() instanceof AnonymousAuthenticationToken)) { HttpGraniteContext context = (HttpGraniteContext) GraniteContext.getCurrentInstance(); HttpServletRequest request = context.getRequest(); request .getSession() .setAttribute( HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY, securityContext); } } protected void handleAuthorizationExceptions(InvocationTargetException e) { for (Throwable t = e; t != null; t = t.getCause()) { // Don't create a dependency to javax.ejb in SecurityService... if (t instanceof SecurityException || t instanceof AccessDeniedException || "javax.ejb.EJBAccessException".equals(t.getClass().getName())) throw SecurityServiceException.newAccessDeniedException(t.getMessage()); } } }
/** @author Franck WOLFF */ public abstract class AbstractChannel implements Channel { /////////////////////////////////////////////////////////////////////////// // Fields. private static final Logger log = Logger.getLogger(AbstractChannel.class); protected final String id; protected final String sessionId; protected final String clientType; protected final GravityInternal gravity; protected final ChannelFactory<? extends Channel> factory; // protected final ServletConfig servletConfig; protected final ConcurrentMap<String, Subscription> subscriptions = new ConcurrentHashMap<String, Subscription>(); protected Principal userPrincipal; protected LinkedList<AsyncPublishedMessage> publishedQueue = new LinkedList<AsyncPublishedMessage>(); protected final Lock publishedQueueLock = new ReentrantLock(); protected LinkedList<AsyncMessage> receivedQueue = new LinkedList<AsyncMessage>(); protected final Lock receivedQueueLock = new ReentrantLock(); protected final AsyncPublisher publisher; protected final AsyncReceiver receiver; protected UdpReceiver udpReceiver = null; /////////////////////////////////////////////////////////////////////////// // Constructor. protected AbstractChannel( GravityInternal gravity, String id, ChannelFactory<? extends Channel> factory, String clientType) { if (id == null) throw new NullPointerException("id cannot be null"); this.id = id; GraniteContext graniteContext = GraniteContext.getCurrentInstance(); this.clientType = clientType; this.sessionId = graniteContext != null ? graniteContext.getSessionId() : null; this.gravity = gravity; this.factory = factory; this.publisher = new AsyncPublisher(this); this.receiver = new AsyncReceiver(this); } /////////////////////////////////////////////////////////////////////////// // Abstract protected method. protected abstract boolean hasAsyncHttpContext(); protected abstract AsyncHttpContext acquireAsyncHttpContext(); protected abstract void releaseAsyncHttpContext(AsyncHttpContext context); /////////////////////////////////////////////////////////////////////////// // Channel interface implementation. public String getId() { return id; } public String getClientType() { return clientType; } public ChannelFactory<? extends Channel> getFactory() { return factory; } public GravityInternal getGravity() { return gravity; } public Subscription addSubscription( String destination, String subTopicId, String subscriptionId, boolean noLocal) { Subscription subscription = new Subscription(this, destination, subTopicId, subscriptionId, noLocal); Subscription present = subscriptions.putIfAbsent(subscriptionId, subscription); return (present != null ? present : subscription); } public Collection<Subscription> getSubscriptions() { return subscriptions.values(); } public Subscription removeSubscription(String subscriptionId) { return subscriptions.remove(subscriptionId); } public boolean isConnected() { return true; } public boolean isAuthenticated() { return userPrincipal != null; } public Principal getUserPrincipal() { return userPrincipal; } public void setUserPrincipal(Principal principal) { this.userPrincipal = principal; gravity.notifyAuthenticated(this, principal); } public void publish(AsyncPublishedMessage message) throws MessagePublishingException { if (message == null) throw new NullPointerException("message cannot be null"); publishedQueueLock.lock(); try { publishedQueue.add(message); } finally { publishedQueueLock.unlock(); } publisher.queue(getGravity()); } public boolean hasPublishedMessage() { publishedQueueLock.lock(); try { return !publishedQueue.isEmpty(); } finally { publishedQueueLock.unlock(); } } public boolean runPublish() { LinkedList<AsyncPublishedMessage> publishedCopy = null; publishedQueueLock.lock(); try { if (publishedQueue.isEmpty()) return false; publishedCopy = publishedQueue; publishedQueue = new LinkedList<AsyncPublishedMessage>(); } finally { publishedQueueLock.unlock(); } for (AsyncPublishedMessage message : publishedCopy) { try { message.publish(this); } catch (Exception e) { log.error(e, "Error while trying to publish message: %s", message); } } return true; } public void receive(AsyncMessage message) throws MessageReceivingException { if (message == null) throw new NullPointerException("message cannot be null"); GravityInternal gravity = getGravity(); if (udpReceiver != null) { if (udpReceiver.isClosed()) return; try { udpReceiver.receive(message); } catch (MessageReceivingException e) { if (e.getCause() instanceof SocketException) { log.debug(e, "Closing unreachable UDP channel %s", getId()); udpReceiver.close(false); } else log.error(e, "Cannot access UDP channel %s", getId()); } return; } receivedQueueLock.lock(); try { if (receivedQueue.size() + 1 > gravity.getGravityConfig().getMaxMessagesQueuedPerChannel()) throw new MessageReceivingException( message, "Could not queue message (channel's queue is full) for channel: " + this); log.debug( "Channel %s queue message %s for client %s", getId(), message.getMessageId(), message.getClientId()); receivedQueue.add(message); } finally { receivedQueueLock.unlock(); } if (hasAsyncHttpContext()) receiver.queue(gravity); } public boolean hasReceivedMessage() { receivedQueueLock.lock(); try { return !receivedQueue.isEmpty(); } finally { receivedQueueLock.unlock(); } } public boolean runReceive() { return runReceived(null); } protected void createUdpReceiver(UdpReceiverFactory factory, AsyncHttpContext asyncHttpContext) { OutputStream os = null; try { Message connectMessage = asyncHttpContext.getConnectMessage(); if (udpReceiver == null || udpReceiver.isClosed()) udpReceiver = factory.newReceiver(this, asyncHttpContext.getRequest(), connectMessage); AsyncMessage reply = udpReceiver.acknowledge(connectMessage); HttpServletRequest request = asyncHttpContext.getRequest(); HttpServletResponse response = asyncHttpContext.getResponse(); GraniteContext context = HttpGraniteContext.createThreadIntance( gravity.getGraniteConfig(), gravity.getServicesConfig(), null, request, response); ((AMFContextImpl) context.getAMFContext()) .setCurrentAmf3Message(asyncHttpContext.getConnectMessage()); GravityServletUtil.serialize( gravity, response, new AsyncMessage[] {reply}, ContentType.forMimeType(request.getContentType())); } catch (ServletException e) { log.error(e, "Could not send UDP connect acknowledgement to channel: %s", this); } catch (IOException e) { log.error(e, "Could not send UDP connect acknowledgement to channel: %s", this); } finally { try { GraniteContext.release(); } catch (Exception e) { // should never happen... } // Close output stream. try { if (os != null) { try { os.close(); } catch (IOException e) { log.warn(e, "Could not close output stream (ignored)"); } } } finally { releaseAsyncHttpContext(asyncHttpContext); } } } public boolean runReceived(AsyncHttpContext asyncHttpContext) { GravityInternal gravity = getGravity(); if (asyncHttpContext != null && gravity.hasUdpReceiverFactory()) { UdpReceiverFactory factory = gravity.getUdpReceiverFactory(); if (factory.isUdpConnectRequest(asyncHttpContext.getConnectMessage())) { createUdpReceiver(factory, asyncHttpContext); return true; } if (udpReceiver != null) { if (!udpReceiver.isClosed()) udpReceiver.close(false); udpReceiver = null; } } boolean httpAsParam = (asyncHttpContext != null); LinkedList<AsyncMessage> messages = null; OutputStream os = null; try { receivedQueueLock.lock(); try { // Do we have any pending messages? if (receivedQueue.isEmpty()) return false; // Do we have a valid http context? if (asyncHttpContext == null) { asyncHttpContext = acquireAsyncHttpContext(); if (asyncHttpContext == null) return false; } // Both conditions are ok, get all pending messages. messages = receivedQueue; receivedQueue = new LinkedList<AsyncMessage>(); } finally { receivedQueueLock.unlock(); } HttpServletRequest request = asyncHttpContext.getRequest(); HttpServletResponse response = asyncHttpContext.getResponse(); // Set response messages correlation ids to connect request message id. String correlationId = asyncHttpContext.getConnectMessage().getMessageId(); AsyncMessage[] messagesArray = new AsyncMessage[messages.size()]; int i = 0; for (AsyncMessage message : messages) { message.setCorrelationId(correlationId); messagesArray[i++] = message; } // Setup serialization context (thread local) GraniteContext context = HttpGraniteContext.createThreadIntance( gravity.getGraniteConfig(), gravity.getServicesConfig(), null, request, response); ((AMFContextImpl) context.getAMFContext()) .setCurrentAmf3Message(asyncHttpContext.getConnectMessage()); // Write messages to response output stream. GravityServletUtil.serialize( gravity, response, messagesArray, ContentType.forMimeType(request.getContentType())); return true; // Messages were delivered, http context isn't valid anymore. } catch (ServletException e) { log.error(e, "Configuration error for channel: %s", getId()); return true; } catch (IOException e) { log.warn(e, "Could not send messages to channel: %s (retrying later)", getId()); GravityConfig gravityConfig = getGravity().getGravityConfig(); if (gravityConfig.isRetryOnError()) { receivedQueueLock.lock(); try { if (receivedQueue.size() + messages.size() > gravityConfig.getMaxMessagesQueuedPerChannel()) { log.warn( "Channel %s has reached its maximum queue capacity %s (throwing %s messages)", getId(), gravityConfig.getMaxMessagesQueuedPerChannel(), messages.size()); } else receivedQueue.addAll(0, messages); } finally { receivedQueueLock.unlock(); } } return true; // Messages weren't delivered, but http context isn't valid anymore. } finally { // Cleanup serialization context (thread local) try { GraniteContext.release(); } catch (Exception e) { // should never happen... } // Close output stream. try { if (os != null) { try { os.close(); } catch (IOException e) { log.warn(e, "Could not close output stream (ignored)"); } } } finally { // Cleanup http context (only if this method wasn't explicitly called with a non null // AsyncHttpContext from the servlet). if (!httpAsParam) releaseAsyncHttpContext(asyncHttpContext); } } } public final void destroy() { destroy(false); } public void destroy(boolean timeout) { try { GravityInternal gravity = getGravity(); gravity.cancel(publisher); gravity.cancel(receiver); subscriptions.clear(); } finally { if (udpReceiver != null) { if (!udpReceiver.isClosed()) udpReceiver.close(timeout); udpReceiver = null; } } } /////////////////////////////////////////////////////////////////////////// // Protected utilities. protected boolean queueReceiver() { if (hasReceivedMessage()) { receiver.queue(getGravity()); return true; } return false; } /////////////////////////////////////////////////////////////////////////// // Object overwritten methods. @Override public boolean equals(Object obj) { return (obj instanceof Channel && id.equals(((Channel) obj).getId())); } @Override public int hashCode() { return id.hashCode(); } @Override public String toString() { return getClass().getName() + " {id=" + id + ", subscriptions=" + subscriptions.values() + "}"; } }
/** * @author <a href="mailto:[email protected]">[email protected]</a> * @since 1.1.0 */ public class AMFServiceAdaptor extends HttpServlet { private static final long serialVersionUID = 4777538296260511097L; private static final Logger log = Logger.getLogger(AMFServiceAdaptor.class); private GraniteConfig graniteConfig = null; private ServicesConfig servicesConfig = null; BundleContext context; public AMFServiceAdaptor(BundleContext context) { this.context = context; } /* * (non-Javadoc) * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig) */ @Override public void init(ServletConfig config) { try { super.init(config); Configuration configuration = Activator.getConfigurationService(); getServletContext() .setAttribute(ServletGraniteConfig.GRANITE_CONFIG_CONFIGURATION_KEY, configuration); graniteConfig = ServletGraniteConfig.loadConfig(getServletContext()); servicesConfig = ServletServicesConfig.loadConfig(getServletContext()); // register EventHandler ServicesConfig handle Add or Remove dataservice Dictionary<String, Object> properties = new Hashtable<String, Object>(); String[] topics = new String[] { OSGIConstants.TOPIC_GDS_ADD_SERVICE, OSGIConstants.TOPIC_GDS_REMOVE_SERVICE }; properties.put(EventConstants.EVENT_TOPIC, topics); context.registerService( EventHandler.class.getName(), new ServiceEventHandler(servicesConfig), properties); } catch (ServletException e) { log.error(e, "Could initialize OSGi service adaptor"); } } public ServicesConfig getServicesConfig() { return servicesConfig; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (log.isInfoEnabled()) try { GraniteContext context = HttpGraniteContext.createThreadIntance( graniteConfig, servicesConfig, getServletContext(), request, response); if (context == null) throw new ServletException("GraniteContext not Initialized!!"); // AMFContextImpl amf = (AMFContextImpl) context.getAMFContext(); // Phase1 Deserializing AMF0 request if (log.isInfoEnabled()) log.info(">>>>> Deserializing AMF0 request from..." + request.getRequestURI()); AMF0Deserializer deserializer = new AMF0Deserializer(new DataInputStream(request.getInputStream())); AMF0Message amf0Request = deserializer.getAMFMessage(); // Phase2 Processing AMF0 request if (log.isInfoEnabled()) log.info(">>>>> Processing AMF0 request: " + amf0Request); AMF0Message amf0Response = AMF0MessageProcessor.process(amf0Request); if (log.isInfoEnabled()) log.info("<<<<< Returning AMF0 response: " + amf0Response); // Phase3 Send back response to the client response.setContentType(AMF0Message.CONTENT_TYPE); AMF0Serializer serializer = new AMF0Serializer(new DataOutputStream(response.getOutputStream())); serializer.serializeMessage(amf0Response); if (log.isInfoEnabled()) log.info("...End of Processing AMF Request......"); } catch (Exception e) { log.error(e, "Could not handle AMF request"); throw new ServletException(e); } } }
/** @author Franck WOLFF */ public class StandardRemoteAliasScanner implements RemoteAliasScanner { private static final Logger log = Logger.getLogger(StandardRemoteAliasScanner.class); @Override public Set<Class<?>> scan(Set<String> packageNames) { Set<Class<?>> classes = new HashSet<Class<?>>(); Scanner scanner = ScannerFactory.createScanner(new MessagingScannedItemHandler(packageNames, classes), null); try { scanner.scan(); } catch (Exception e) { log.error(e, "Could not scan classpath for @RemoteAlias"); } return classes; } class MessagingScannedItemHandler implements ScannedItemHandler { final String[] packageNames; final Set<Class<?>> classes; MessagingScannedItemHandler(Set<String> packageNames, Set<Class<?>> classes) { this.packageNames = new String[packageNames.size()]; int i = 0; for (String packageName : packageNames) this.packageNames[i++] = packageName.replace('.', '/') + '/'; this.classes = classes; } @Override public boolean handleMarkerItem(ScannedItem item) { return false; } @Override public void handleScannedItem(ScannedItem item) { if ("class".equals(item.getExtension())) { boolean scan = false; String path = item.getRelativePath(); for (String packageName : packageNames) { if (path.startsWith(packageName)) { scan = true; break; } } if (scan) { try { Class<?> cls = item.loadAsClass(); RemoteAlias alias = cls.getAnnotation(RemoteAlias.class); if (alias != null) classes.add(cls); } catch (ClassFormatError e) { } catch (ClassNotFoundException e) { } catch (IOException e) { log.error(e, "Could not load class: %s", item); } } } } } }
/** * @author Venkat DANDA * @author Cameron INGRAM * <p>Update services-config.xml to use the seam service exception handler <factory * id="tideSeamFactory" class="org.granite.tide.seam.SeamServiceFactory" > <properties> * <service-exception-handler>org.granite.tide.seam.SeamServiceExceptionHandler</service-exception-handler> * </properties> </factory> */ public class ExtendedServiceExceptionHandler extends DefaultServiceExceptionHandler { private static final long serialVersionUID = -1L; private static final Logger log = Logger.getLogger(ExtendedServiceExceptionHandler.class); public static final Class<?> JAVAX_EJB_EXCEPTION; static { Class<?> exception = null; try { exception = Thread.currentThread().getContextClassLoader().loadClass("javax.ejb.EJBException"); } catch (Exception e) { } JAVAX_EJB_EXCEPTION = exception; } public ExtendedServiceExceptionHandler() { this(true); } public ExtendedServiceExceptionHandler(boolean logException) { super(logException); } @Override protected ServiceException getServiceException( Message request, Destination destination, String method, Throwable t) { if (t == null) throw new NullPointerException("Parameter t cannot be null"); Map<String, Object> extendedData = new HashMap<String, Object>(); if (t instanceof ServiceException) { ((ServiceException) t).getExtendedData().putAll(extendedData); return (ServiceException) t; } List<Throwable> causes = new ArrayList<Throwable>(); for (Throwable cause = t; cause != null; cause = getCause(cause)) causes.add(cause); String detail = "\n" + "- destination: " + (destination != null ? destination.getId() : "") + "\n" + "- method: " + method + "\n" + "- exception: " + t.toString() + "\n"; for (int i = causes.size() - 1; i >= 0; i--) { Throwable cause = causes.get(i); for (ExceptionConverter ec : GraniteContext.getCurrentInstance().getGraniteConfig().getExceptionConverters()) { if (ec.accepts(cause, t)) return ec.convert(cause, detail, extendedData); } } if (getLogException()) log.error(t, "Could not process remoting message: %s", request); // Default exception handler ServiceException se = new ServiceException( t.getClass().getSimpleName() + ".Call.Failed", t.getMessage(), detail, t); se.getExtendedData().putAll(extendedData); return se; } public static Throwable getCause(Throwable t) { Throwable cause = null; try { if (JAVAX_EJB_EXCEPTION != null && JAVAX_EJB_EXCEPTION.isInstance(t)) { Method m = JAVAX_EJB_EXCEPTION.getMethod("getCausedByException"); cause = (Throwable) m.invoke(t); } else if (t instanceof ServletException) cause = ((ServletException) t).getRootCause(); else cause = t.getCause(); } catch (Exception x) { return null; } return cause == t ? null : (Throwable) cause; } }
/** @author William DRAI */ public class EjbServiceContext extends TideServiceContext { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(EjbServiceContext.class); public static final String CAPITALIZED_DESTINATION_ID = "{capitalized.component.name}"; public static final String DESTINATION_ID = "{component.name}"; private final transient Map<String, EjbComponent> ejbLookupCache = new ConcurrentHashMap<String, EjbComponent>(); private final Set<String> remoteObservers = new HashSet<String>(); private final String lookup; private final EjbIdentity identity; private String entityManagerFactoryJndiName = null; private String entityManagerJndiName = null; public EjbServiceContext() throws ServiceException { super(); lookup = ""; identity = new EjbIdentity(); } public EjbServiceContext(String lookup) throws ServiceException { super(); this.lookup = lookup; identity = new EjbIdentity(); } @Override protected AsyncPublisher getAsyncPublisher() { return null; } public void setEntityManagerFactoryJndiName(String entityManagerFactoryJndiName) { this.entityManagerFactoryJndiName = entityManagerFactoryJndiName; } public void setEntityManagerJndiName(String entityManagerJndiName) { this.entityManagerJndiName = entityManagerJndiName; } /** * Create a TidePersistenceManager * * @param create create if not existent (can be false for use in entity merge) * @return a TidePersistenceManager */ @Override protected TidePersistenceManager getTidePersistenceManager(boolean create) { if (!create) return null; EntityManager em = getEntityManager(); if (em == null) return null; return new JPAPersistenceManager(em); } /** * Find the entity manager using the jndi names stored in the bean. * * @return The found entity manager */ private EntityManager getEntityManager() { try { InitialContext jndiContext = new InitialContext(); if (entityManagerFactoryJndiName != null) { EntityManagerFactory factory = (EntityManagerFactory) jndiContext.lookup(entityManagerFactoryJndiName); return factory.createEntityManager(); } else if (entityManagerJndiName != null) { return (EntityManager) jndiContext.lookup(entityManagerJndiName); } } catch (NamingException e) { if (entityManagerFactoryJndiName != null) throw new RuntimeException( "Unable to find a EntityManagerFactory for jndiName " + entityManagerFactoryJndiName); else if (entityManagerJndiName != null) throw new RuntimeException( "Unable to find a EntityManager for jndiName " + entityManagerJndiName); } return null; } public Object callComponent(Method method, Object... args) throws Exception { String name = method.getDeclaringClass().getSimpleName(); name = name.substring(0, 1).toLowerCase() + name.substring(1); if (name.endsWith("Bean")) name = name.substring(0, name.length() - "Bean".length()); Object invokee = findComponent(name, null); method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes()); return method.invoke(invokee, args); } public Set<String> getRemoteObservers() { return remoteObservers; } /* (non-Javadoc) * @see org.granite.tide.ejb.EJBServiceContextIntf#findComponent(java.lang.String) */ @Override public Object findComponent(String componentName, Class<?> componentClass) { if ("identity".equals(componentName)) return identity; EjbComponent component = ejbLookupCache.get(componentName); if (component != null) return component.ejbInstance; // Compute EJB JNDI binding. String name = componentName; if (lookup != null) { name = lookup; if (lookup.contains(CAPITALIZED_DESTINATION_ID)) name = lookup.replace(CAPITALIZED_DESTINATION_ID, capitalize(componentName)); if (lookup.contains(DESTINATION_ID)) name = lookup.replace(DESTINATION_ID, componentName); } InitialContext ic = null; try { ic = new InitialContext(); } catch (Exception e) { throw new ServiceException("Could not get InitialContext", e); } log.debug(">> New EjbServiceInvoker looking up: %s", name); try { component = new EjbComponent(); component.ejbInstance = ic.lookup(name); component.ejbClasses = new HashSet<Class<?>>(); Class<?> scannedClass = null; EjbScannedItemHandler itemHandler = EjbScannedItemHandler.instance(); for (Class<?> i : component.ejbInstance.getClass().getInterfaces()) { if (itemHandler.getScannedClasses().containsKey(i)) { scannedClass = itemHandler.getScannedClasses().get(i); break; } } if (scannedClass == null) scannedClass = itemHandler.getScannedClasses().get(component.ejbInstance.getClass()); // GDS-768: handle of proxied no-interface EJBs in GlassFish v3 if (scannedClass == null && component.ejbInstance.getClass().getSuperclass() != null) scannedClass = itemHandler.getScannedClasses().get(component.ejbInstance.getClass().getSuperclass()); if (scannedClass != null) { component.ejbClasses.add(scannedClass); for (Map.Entry<Class<?>, Class<?>> me : itemHandler.getScannedClasses().entrySet()) { if (me.getValue().equals(scannedClass)) component.ejbClasses.add(me.getKey()); } component.ejbMetadata = new EjbServiceMetadata(scannedClass, component.ejbInstance.getClass()); } else log.warn( "Ejb " + componentName + " was not scanned: remove method will not be called if it is a Stateful bean. Add META-INF/services-config.properties if needed."); ejbLookupCache.put(componentName, component); return component.ejbInstance; } catch (NamingException e) { log.error("EJB not found " + name + ": " + e.getMessage()); throw new ServiceException("Could not lookup for: " + name, e); } } /* (non-Javadoc) * @see org.granite.tide.ejb.EJBServiceContextIntf#findComponentClass(java.lang.String) */ @Override public Set<Class<?>> findComponentClasses(String componentName, Class<?> componentClass) { if ("identity".equals(componentName)) { Set<Class<?>> classes = new HashSet<Class<?>>(1); classes.add(EjbIdentity.class); return classes; } EjbComponent component = ejbLookupCache.get(componentName); if (component == null) findComponent(componentName, componentClass); return ejbLookupCache.get(componentName).ejbClasses; } private String capitalize(String s) { if (s == null || s.length() == 0) return s; if (s.length() == 1) return s.toUpperCase(); return s.substring(0, 1).toUpperCase() + s.substring(1); } /* (non-Javadoc) * @see org.granite.tide.ejb.EJBServiceContextIntf#prepareCall(org.granite.messaging.service.ServiceInvocationContext, org.granite.tide.IInvocationCall, java.lang.String) */ @Override public void prepareCall( ServiceInvocationContext context, IInvocationCall c, String componentName, Class<?> componentClass) { if ((c instanceof InvocationCall) && ((InvocationCall) c).getListeners() != null) remoteObservers.addAll(((InvocationCall) c).getListeners()); Context.create(this); } private static class EjbComponent { public Object ejbInstance; public Set<Class<?>> ejbClasses; public EjbServiceMetadata ejbMetadata; } /* (non-Javadoc) * @see org.granite.tide.ejb.EJBServiceContextIntf#postCall(org.granite.messaging.service.ServiceInvocationContext, java.lang.Object, java.lang.String) */ @Override public IInvocationResult postCall( ServiceInvocationContext context, Object result, String componentName, Class<?> componentClass) { try { AbstractContext threadContext = AbstractContext.instance(); List<ContextUpdate> results = new ArrayList<ContextUpdate>(threadContext.size()); DataContext dataContext = DataContext.get(); Set<Object[]> dataUpdates = dataContext != null ? dataContext.getDataUpdates() : null; Object[][] updates = null; if (dataUpdates != null && !dataUpdates.isEmpty()) updates = dataUpdates.toArray(new Object[dataUpdates.size()][]); for (Map.Entry<String, Object> entry : threadContext.entrySet()) results.add(new ContextUpdate(entry.getKey(), null, entry.getValue(), 3, false)); InvocationResult ires = new InvocationResult(result, results); if (context.getBean() != null) { if (context.getBean().getClass().isAnnotationPresent(BypassTideMerge.class)) ires.setMerge(false); else { try { Method m = context .getBean() .getClass() .getMethod( context.getMethod().getName(), context.getMethod().getParameterTypes()); if (m.isAnnotationPresent(BypassTideMerge.class)) ires.setMerge(false); } catch (Exception e) { log.warn("Could not find bean method", e); } } } ires.setUpdates(updates); ires.setEvents(new ArrayList<ContextEvent>(threadContext.getRemoteEvents())); if (componentName != null) { EjbComponent component = ejbLookupCache.get(componentName); if (component != null && component.ejbMetadata != null && component.ejbMetadata.isStateful() && component.ejbMetadata.isRemoveMethod(context.getMethod())) ejbLookupCache.remove(componentName); } return ires; } finally { AbstractContext.remove(); } } /* (non-Javadoc) * @see org.granite.tide.ejb.EJBServiceContextIntf#postCallFault(org.granite.messaging.service.ServiceInvocationContext, java.lang.Throwable, java.lang.String) */ @Override public void postCallFault( ServiceInvocationContext context, Throwable t, String componentName, Class<?> componentClass) { try { if (componentName != null) { EjbComponent component = ejbLookupCache.get(componentName); if (t instanceof NoSuchEJBException || (component != null && component.ejbMetadata != null && (component.ejbMetadata.isStateful() && component.ejbMetadata.isRemoveMethod(context.getMethod()) && !component.ejbMetadata.getRetainIfException(context.getMethod())))) { ejbLookupCache.remove(componentName); } } } finally { AbstractContext.remove(); } } }
/** @author Franck WOLFF */ public class EntityCodec implements ExtendedObjectCodec { private static final Logger log = Logger.getLogger(EntityCodec.class); private final ConcurrentMap<Class<?>, SerializableProxyAdapter> serializableProxyAdapters = new ConcurrentHashMap<Class<?>, SerializableProxyAdapter>(); static class SerializableProxyAdapter { private final Object serializableProxy; private final Field idField; private final Method readResolveMethod; public SerializableProxyAdapter(HibernateProxy proxy) throws NoSuchMethodException, SecurityException, NoSuchFieldException { this.serializableProxy = proxy.writeReplace(); this.idField = serializableProxy.getClass().getDeclaredField("id"); this.idField.setAccessible(true); this.readResolveMethod = serializableProxy.getClass().getDeclaredMethod("readResolve"); this.readResolveMethod.setAccessible(true); } public HibernateProxy newProxy(Serializable id) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { synchronized (serializableProxy) { idField.set(serializableProxy, id); return (HibernateProxy) readResolveMethod.invoke(serializableProxy); } } } public boolean canEncode(ExtendedObjectOutput out, Object v) { Class<?> cls = getClass(out, v); return (cls.isAnnotationPresent(Entity.class) || cls.isAnnotationPresent(MappedSuperclass.class)); } public String getEncodedClassName(ExtendedObjectOutput out, Object v) { return getClass(out, v).getName(); } public void encode(ExtendedObjectOutput out, Object v) throws IOException, IllegalAccessException, InvocationTargetException { String detachedState = null; if (v instanceof HibernateProxy) { HibernateProxy proxy = (HibernateProxy) v; // Only write initialized flag, detachedState & id if v is an uninitialized proxy. if (proxy.getHibernateLazyInitializer().isUninitialized()) { Class<?> persistentClass = proxy.getHibernateLazyInitializer().getPersistentClass(); if (!serializableProxyAdapters.containsKey(persistentClass)) { try { SerializableProxyAdapter proxyAdapter = new SerializableProxyAdapter(proxy); serializableProxyAdapters.putIfAbsent(persistentClass, proxyAdapter); } catch (Exception e) { throw new IOException("Could not create SerializableProxyAdapter for: " + proxy); } } Serializable id = proxy.getHibernateLazyInitializer().getIdentifier(); log.debug("Writing uninitialized HibernateProxy %s with id %s", detachedState, id); out.writeBoolean(false); out.writeUTF(null); out.writeObject(id); return; } // Proxy is initialized, get the underlying persistent object. log.debug("Writing initialized HibernateProxy..."); v = proxy.getHibernateLazyInitializer().getImplementation(); } // Write initialized flag & detachedState. out.writeBoolean(true); out.writeUTF(null); // Write all properties in lexical order. List<Property> properties = out.getReflection().findSerializableProperties(v.getClass()); for (Property property : properties) out.getAndWriteProperty(v, property); } public boolean canDecode(ExtendedObjectInput in, String className) throws ClassNotFoundException { Class<?> cls = in.getReflection().loadClass(className); return (cls.isAnnotationPresent(Entity.class) || cls.isAnnotationPresent(MappedSuperclass.class)); } public String getDecodedClassName(ExtendedObjectInput in, String className) { return in.getAlias(className); } public Object newInstance(ExtendedObjectInput in, String className) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, IOException { Class<?> cls = in.getReflection().loadClass(className); // Read initialized flag & detachedState. boolean initialized = in.readBoolean(); in.readUTF(); if (initialized) return in.getReflection().newInstance(cls); // Create an HibernateProxy. SerializableProxyAdapter proxyAdapter = serializableProxyAdapters.get(cls); if (proxyAdapter == null) throw new IOException("Could not find SerializableProxyAdapter for: " + cls); Serializable id = (Serializable) in.readObject(); return proxyAdapter.newProxy(id); } public void decode(ExtendedObjectInput in, Object v) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException { if (!(v instanceof HibernateProxy)) { List<Property> properties = in.getReflection().findSerializableProperties(v.getClass()); for (Property property : properties) in.readAndSetProperty(v, property); } } protected Class<?> getClass(ExtendedObjectOutput out, Object v) { Class<?> cls = v.getClass(); if (v instanceof HibernateProxy) { LazyInitializer initializer = ((HibernateProxy) v).getHibernateLazyInitializer(); String className = (initializer.isUninitialized() ? initializer.getEntityName() : initializer.getImplementation().getClass().getName()); if (className != null && className.length() > 0) { try { cls = out.getReflection().loadClass(className); } catch (ClassNotFoundException e) { cls = initializer.getPersistentClass(); } } } return cls; } }
@Component @Provides public class OSGiServiceSimple extends SimpleService { private static final Logger log = Logger.getLogger(OSGiServiceSimple.class); @ServiceProperty(name = "ID") private String ID; @Requires(proxy = false) private ServicesConfig servicesConfig; // private boolean started = false; // public OSGiServiceSimple() { super( null, null, null, null, new HashMap<String, Adapter>(), new HashMap<String, Destination>()); } @Property(name = "id", mandatory = true) private void setId(String id) { this.id = id; this.ID = id; } @Property(name = "messageTypes", mandatory = true) private void setMessageTypes(String messageTypes) { this.messageTypes = messageTypes; } @Property(name = "class", mandatory = false, value = "flex.messaging.services.RemotingService") private void setClass(String className) { this.className = className; } @Validate public void start() { log.debug("Start OSGiServiceSimple: " + toString()); if (servicesConfig.findServiceById(id) == null) { // Clear destinations destinations.clear(); servicesConfig.addService(this); started = true; } else { log.error("Service \"" + id + "\" already registered"); } } @Invalidate public void stop() { log.debug("Stop OSGiServiceSimple: " + toString()); if (servicesConfig != null) { servicesConfig.removeService(id); started = false; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OSGiServiceSimple that = (OSGiServiceSimple) o; if (this != that) return false; return true; } @Override public String toString() { return "Service{" + "id=" + id + ", class=" + className + ", messageTypes=" + messageTypes + '}'; } }
/** @author Franck WOLFF */ public class JMFServletContextListener implements ServletContextListener { private static final Logger log = Logger.getLogger(GraniteConfigListener.class); private static final String[] CODEC_EXTENSION_FACTORY_NAMES = { "org.granite.hibernate.jmf.Hibernate3CodecExtensionFactory" }; public static final String EXTENDED_OBJECT_CODECS_PARAM = "jmf-extended-object-codecs"; public static final String DEFAULT_STORED_STRINGS_PARAM = "jmf-default-stored-strings"; public static final String SHARED_CONTEXT_KEY = SharedContext.class.getName(); public static final String DUMP_SHARED_CONTEXT_KEY = SharedContext.class.getName() + ":DUMP"; public static SharedContext getSharedContext(ServletContext servletContext) { return (SharedContext) servletContext.getAttribute(SHARED_CONTEXT_KEY); } public static SharedContext getDumpSharedContext(ServletContext servletContext) { return (SharedContext) servletContext.getAttribute(DUMP_SHARED_CONTEXT_KEY); } public static ServletException newSharedContextNotInitializedException() { return new ServletException( "JMF shared context not initialized (configure " + JMFServletContextListener.class.getName() + " in your web.xml)"); } public void contextInitialized(ServletContextEvent event) { log.info("Loading JMF shared context"); ServletContext servletContext = event.getServletContext(); List<ExtendedObjectCodec> extendedObjectCodecs = loadExtendedObjectCodecs(servletContext); List<String> defaultStoredStrings = loadDefaultStoredStrings(servletContext); SharedContext sharedContext = new DefaultSharedContext( new DefaultCodecRegistry(extendedObjectCodecs), null, defaultStoredStrings); servletContext.setAttribute(SHARED_CONTEXT_KEY, sharedContext); SharedContext dumpSharedContext = new DefaultSharedContext(new DefaultCodecRegistry(), null, defaultStoredStrings); servletContext.setAttribute(DUMP_SHARED_CONTEXT_KEY, dumpSharedContext); log.info("JMF shared context loaded"); } protected List<ExtendedObjectCodec> loadExtendedObjectCodecs(ServletContext servletContext) { String extendedObjectCodecsParam = servletContext.getInitParameter(EXTENDED_OBJECT_CODECS_PARAM); if (extendedObjectCodecsParam == null) return detectExtendedObjectCodecs(); ClassLoader classLoader = getClass().getClassLoader(); List<ExtendedObjectCodec> extendedObjectCodecs = new ArrayList<ExtendedObjectCodec>(); for (String className : extendedObjectCodecsParam.trim().split("\\s*\\,\\s*")) { if (className.length() == 0) continue; log.info("Loading JMF extended object codec: %s", className); try { @SuppressWarnings("unchecked") Class<? extends ExtendedObjectCodec> cls = (Class<? extends ExtendedObjectCodec>) classLoader.loadClass(className); extendedObjectCodecs.add(cls.getDeclaredConstructor().newInstance()); } catch (Throwable t) { log.warn(t, "Could not load JMF codec: %s", className); } } return extendedObjectCodecs; } protected List<ExtendedObjectCodec> detectExtendedObjectCodecs() { log.info("Auto detecting extended object codec..."); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); for (String factoryName : CODEC_EXTENSION_FACTORY_NAMES) { try { CodecExtensionFactory factory = (CodecExtensionFactory) classLoader.loadClass(factoryName).newInstance(); List<ExtendedObjectCodec> extendedObjectCodecs = factory.getCodecs(); log.info("Using %s: %s", factoryName, extendedObjectCodecs); return extendedObjectCodecs; } catch (Throwable t) { log.debug(t, "Could not load factory: %s", factoryName); } } log.info("No extended object codec detected"); return Collections.emptyList(); } protected List<String> loadDefaultStoredStrings(ServletContext servletContext) { List<String> defaultStoredStrings = new ArrayList<String>(JMFAMFUtil.AMF_DEFAULT_STORED_STRINGS); String defaultStoredStringsParam = servletContext.getInitParameter(DEFAULT_STORED_STRINGS_PARAM); if (defaultStoredStringsParam != null) { for (String value : defaultStoredStringsParam.trim().split("\\s*\\,\\s*")) { if (value.length() > 0) defaultStoredStrings.add(value); } } return defaultStoredStrings; } public void contextDestroyed(ServletContextEvent event) { ServletContext servletContext = event.getServletContext(); servletContext.removeAttribute(SHARED_CONTEXT_KEY); } }
/** @author Franck WOLFF */ public class DumpFilter implements Filter { private static final Logger log = Logger.getLogger(DumpFilter.class); private static final String HEXS = "0123456789ABCDEF"; private static final String DUMP_DIR = "dumpDir"; File dumpDir = null; public void init(FilterConfig config) throws ServletException { String dumpDirString = ServletParams.get(config, DUMP_DIR, String.class, null); if (dumpDirString != null) { File dumpDir = new File(dumpDirString); if (!dumpDir.exists() || !dumpDir.isDirectory() || !dumpDir.canWrite()) log.warn("Ignoring dump directory (is it a writable directory?): %s", dumpDir); else this.dumpDir = dumpDir; } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { DumpRequestWrapper requestWrapper = new DumpRequestWrapper((HttpServletRequest) request); DumpResponseWrapper responseWrapper = new DumpResponseWrapper((HttpServletResponse) response); dumpBytes("request", requestWrapper.getBytes()); chain.doFilter(requestWrapper, responseWrapper); dumpBytes("response", responseWrapper.getBytes()); } public void destroy() { dumpDir = null; } private void dumpBytes(String label, byte[] bytes) { StringBuilder hexSb = new StringBuilder(); StringBuilder charSb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { int b = bytes[i] & 0xFF; if (hexSb.length() > 0) { hexSb.append(' '); charSb.append(' '); } hexSb.append(HEXS.charAt(b >> 4)).append(HEXS.charAt(b & 0x0F)); if (b >= 0x20 && b <= 0x7e) charSb.append(' ').append((char) b); else charSb.append("##"); } log.info("[RAW %s] {\n%s\n%s\n}", label.toUpperCase(), hexSb.toString(), charSb.toString()); if (dumpDir != null) { File file = new File( dumpDir.getPath() + File.separator + label + "_" + System.currentTimeMillis() + ".amf"); for (int i = 1; i < 100 && file.exists(); i++) file = new File(file.getAbsolutePath() + "." + i); OutputStream os = null; try { os = new FileOutputStream(file); os.write(bytes); } catch (Exception e) { log.error(e, "Could not write dump file: %s", file); } finally { if (os != null) try { os.close(); } catch (Exception e) { } } } } class DumpRequestWrapper extends HttpServletRequestWrapper { private byte[] bytes = null; public DumpRequestWrapper(HttpServletRequest request) throws IOException { super(request); setCharacterEncoding("UTF-8"); InputStream is = null; try { is = request.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(128); for (int b = is.read(); b != -1; b = is.read()) out.write(b); this.bytes = out.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(bytes); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } }; } public byte[] getBytes() { return bytes; } } class DumpResponseWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream baos = new ByteArrayOutputStream(256); private ServletOutputStream out = null; public DumpResponseWrapper(HttpServletResponse response) throws IOException { super(response); this.out = response.getOutputStream(); } @Override public ServletOutputStream getOutputStream() throws IOException { return new ServletOutputStream() { @Override public void write(int b) throws IOException { baos.write(b); out.write(b); } }; } public byte[] getBytes() { return baos.toByteArray(); } } }
/** @author Franck WOLFF */ public class GraniteConfig implements ConvertersConfig, AliasRegistryConfig, AMF3Config, ExternalizersConfig, ScannedItemHandler { public enum JMF_EXTENSIONS_MODE { PREPPEND, APPEND, REPLACE } /////////////////////////////////////////////////////////////////////////// // Static fields. private static final Logger log = Logger.getLogger(GraniteConfig.class); private static final String GRANITE_CONFIG_PUBLIC_ID = "-//Granite Data Services//DTD granite-config internal//EN"; private static final String GRANITE_CONFIG_PROPERTIES = "META-INF/granite-config.properties"; final ExternalizerFactory EXTERNALIZER_FACTORY = new ExternalizerFactory(); private static final Externalizer LONG_EXTERNALIZER = new LongExternalizer(); private static final Externalizer BIGINTEGER_EXTERNALIZER = new BigIntegerExternalizer(); private static final Externalizer BIGDECIMAL_EXTERNALIZER = new BigDecimalExternalizer(); private static final Externalizer MAP_EXTERNALIZER = new MapExternalizer(); final ActionScriptClassDescriptorFactory ASC_DESCRIPTOR_FACTORY = new ActionScriptClassDescriptorFactory(); final JavaClassDescriptorFactory JC_DESCRIPTOR_FACTORY = new JavaClassDescriptorFactory(); final TideComponentMatcherFactory TIDE_COMPONENT_MATCHER_FACTORY = new TideComponentMatcherFactory(); /////////////////////////////////////////////////////////////////////////// // Instance fields. // Should we scan classpath for auto-configured services/externalizers? private boolean scan = false; private AliasRegistry aliasRegistry = new DefaultAliasRegistry(); private String MBeanContextName = null; // Custom AMF3 (De)Serializer configuration. private Constructor<AMF3Serializer> amf3SerializerConstructor = null; private Constructor<AMF3Deserializer> amf3DeserializerConstructor = null; private AMF3DeserializerSecurizer amf3DeserializerSecurizer = null; // Custom AMF3 message interceptor configuration. private AMF3MessageInterceptor amf3MessageInterceptor = null; // Converters configuration. private List<Class<? extends Converter>> converterClasses = new ArrayList<Class<? extends Converter>>(); private Converters converters = null; // MethodMatcher configuration. private MethodMatcher methodMatcher = new DefaultMethodMatcher(); // Invocation listener configuration. private ServiceInvocationListener invocationListener = null; // Instantiators configuration. private final Map<String, String> instantiators = new HashMap<String, String>(); // Class getter configuration. private ClassGetter classGetter = new DefaultClassGetter(); private boolean classGetterSet = false; // Externalizers configuration. private XMap externalizersConfiguration = null; private final List<Externalizer> scannedExternalizers = new ArrayList<Externalizer>(); private final ConcurrentHashMap<String, Externalizer> externalizersByType = new ConcurrentHashMap<String, Externalizer>(); private final Map<String, String> externalizersByInstanceOf = new HashMap<String, String>(); private final Map<String, String> externalizersByAnnotatedWith = new HashMap<String, String>(); // JMF extended codecs. private JMF_EXTENSIONS_MODE jmfExtendedCodecsMode = JMF_EXTENSIONS_MODE.APPEND; private final List<ExtendedObjectCodec> jmfExtendedCodecs = new ArrayList<ExtendedObjectCodec>(); // JMF default stored strings. private JMF_EXTENSIONS_MODE jmfDefaultStoredStringsMode = JMF_EXTENSIONS_MODE.APPEND; private final List<String> jmfDefaultStoredStrings = new ArrayList<String>(); // JMF reflection. private Reflection jmfReflection = null; // JMF private SharedContext sharedContext; // Java descriptors configuration. private final ConcurrentHashMap<String, JavaClassDescriptor> javaDescriptorsCache = new ConcurrentHashMap<String, JavaClassDescriptor>(); private final ConcurrentHashMap<String, Class<? extends JavaClassDescriptor>> javaDescriptorsByType = new ConcurrentHashMap<String, Class<? extends JavaClassDescriptor>>(); private final Map<String, String> javaDescriptorsByInstanceOf = new HashMap<String, String>(); // AS3 descriptors configuration. private final ConcurrentHashMap<String, Class<? extends ActionScriptClassDescriptor>> as3DescriptorsByType = new ConcurrentHashMap<String, Class<? extends ActionScriptClassDescriptor>>(); private final Map<String, String> as3DescriptorsByInstanceOf = new HashMap<String, String>(); // Exception converters private final List<ExceptionConverter> exceptionConverters = new ArrayList<ExceptionConverter>(); // Tide-enabled Components configuration. private final ConcurrentHashMap<String, Object[]> enabledTideComponentsByName = new ConcurrentHashMap<String, Object[]>(); private final ConcurrentHashMap<String, Object[]> disabledTideComponentsByName = new ConcurrentHashMap<String, Object[]>(); private final List<TideComponentMatcher> tideComponentMatchers = new ArrayList<TideComponentMatcher>(); // Security service configuration. private SecurityService securityService = null; // MessageSelector configuration. private Constructor<?> messageSelectorConstructor; // Gravity configuration. private XMap gravityConfig; // Clustering private DistributedDataFactory distributedDataFactory; /////////////////////////////////////////////////////////////////////////// // Constructor. public GraniteConfig( String stdConfig, InputStream customConfigIs, Configuration configuration, String MBeanContextName) throws IOException, SAXException { try { amf3SerializerConstructor = TypeUtil.getConstructor(AMF3Serializer.class, new Class<?>[] {OutputStream.class}); amf3DeserializerConstructor = TypeUtil.getConstructor(AMF3Deserializer.class, new Class<?>[] {InputStream.class}); } catch (Exception e) { throw new GraniteConfigException("Could not get constructor for AMF3 (de)serializers", e); } this.MBeanContextName = MBeanContextName; ClassLoader loader = GraniteConfig.class.getClassLoader(); final ByteArrayInputStream dtd = StreamUtil.getResourceAsStream("org/granite/config/granite-config.dtd", loader); final EntityResolver resolver = new EntityResolver() { public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { if (GRANITE_CONFIG_PUBLIC_ID.equals(publicId)) { dtd.reset(); InputSource source = new InputSource(dtd); source.setPublicId(publicId); return source; } return null; } }; // Load standard config. InputStream is = null; try { is = StreamUtil.getResourceAsStream("org/granite/config/granite-config.xml", loader); XMap doc = new XMap(is, resolver); forElement(doc, false, null); } finally { if (is != null) is.close(); } if (stdConfig != null) { try { is = StreamUtil.getResourceAsStream(stdConfig, loader); XMap doc = new XMap(is, resolver); forElement(doc, false, null); } finally { if (is != null) is.close(); } } // Load custom config (override). if (customConfigIs != null) { XMap doc = new XMap(customConfigIs, resolver); forElement( doc, true, configuration != null ? configuration.getGraniteConfigProperties() : null); } if (amf3DeserializerSecurizer == null) log.warn( "You should configure a deserializer securizer in your granite-config.xml file in order to prevent potential security exploits!"); } /////////////////////////////////////////////////////////////////////////// // Classpath scan initialization. private void scanConfig(String graniteConfigProperties) { // if config overriding exists Scanner scanner = ScannerFactory.createScanner( this, graniteConfigProperties != null ? graniteConfigProperties : GRANITE_CONFIG_PROPERTIES); try { scanner.scan(); } catch (Exception e) { log.error(e, "Could not scan classpath for configuration"); } } public boolean handleMarkerItem(ScannedItem item) { try { return handleProperties(item.loadAsProperties()); } catch (Exception e) { log.error(e, "Could not load properties: %s", item); } return true; } public void handleScannedItem(ScannedItem item) { if ("class".equals(item.getExtension()) && item.getName().indexOf('$') == -1) { try { handleClass(item.loadAsClass()); } catch (NoClassDefFoundError e) { // Ignore errors with Tide classes depending on Gravity } catch (LinkageError e) { // Ignore errors with GraniteDS/Hibernate classes depending on Hibernate 3 when using // Hibernate 4 } catch (Throwable t) { log.error(t, "Could not load class: %s", item); } } } private boolean handleProperties(Properties properties) { if (properties.getProperty("dependsOn") != null) { String dependsOn = properties.getProperty("dependsOn"); try { TypeUtil.forName(dependsOn); } catch (ClassNotFoundException e) { // Class not found, skip scan for this package return true; } } String classGetterName = properties.getProperty("classGetter"); if (!classGetterSet && classGetterName != null) { try { classGetter = TypeUtil.newInstance(classGetterName, ClassGetter.class); } catch (Throwable t) { log.error(t, "Could not create instance of: %s", classGetterName); } } String amf3MessageInterceptorName = properties.getProperty("amf3MessageInterceptor"); if (amf3MessageInterceptor == null && amf3MessageInterceptorName != null) { try { amf3MessageInterceptor = TypeUtil.newInstance(amf3MessageInterceptorName, AMF3MessageInterceptor.class); } catch (Throwable t) { log.error(t, "Could not create instance of: %s", amf3MessageInterceptorName); } } for (Map.Entry<?, ?> me : properties.entrySet()) { if (me.getKey().toString().startsWith("converter.")) { String converterName = me.getValue().toString(); try { converterClasses.add(TypeUtil.forName(converterName, Converter.class)); } catch (Exception e) { throw new GraniteConfigException( "Could not get converter class for: " + converterName, e); } } } return false; } private void handleClass(Class<?> clazz) { if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) return; if (Externalizer.class.isAssignableFrom(clazz)) { try { scannedExternalizers.add(TypeUtil.newInstance(clazz, Externalizer.class)); } catch (Exception e) { log.error(e, "Could not create new instance of: %s", clazz); } } if (ExceptionConverter.class.isAssignableFrom(clazz)) { try { exceptionConverters.add(TypeUtil.newInstance(clazz, ExceptionConverter.class)); } catch (Exception e) { if (!clazz .getName() .equals("org.granite.tide.hibernate.HibernateValidatorExceptionConverter")) // GDS-582 log.error(e, "Could not create new instance of: %s", clazz); } } } /////////////////////////////////////////////////////////////////////////// // Property getters. public boolean getScan() { return scan; } public boolean isRegisterMBeans() { return MBeanContextName != null; } public String getMBeanContextName() { return MBeanContextName; } public ObjectOutput newAMF3Serializer(OutputStream out) { try { return amf3SerializerConstructor.newInstance(new Object[] {out}); } catch (Exception e) { throw new GraniteConfigException( "Could not create serializer instance with: " + amf3SerializerConstructor, e); } } public Constructor<?> getAmf3SerializerConstructor() { return amf3SerializerConstructor; } public ObjectInput newAMF3Deserializer(InputStream in) { try { return amf3DeserializerConstructor.newInstance(new Object[] {in}); } catch (Exception e) { throw new GraniteConfigException( "Could not create deserializer instance with: " + amf3DeserializerConstructor, e); } } public Constructor<?> getAmf3DeserializerConstructor() { return amf3DeserializerConstructor; } public AMF3DeserializerSecurizer getAmf3DeserializerSecurizer() { return amf3DeserializerSecurizer; } public void setAmf3DeserializerSecurizer(AMF3DeserializerSecurizer amf3DeserializerSecurizer) { this.amf3DeserializerSecurizer = amf3DeserializerSecurizer; } public AMF3MessageInterceptor getAmf3MessageInterceptor() { return amf3MessageInterceptor; } public void setAmf3MessageInterceptor(AMF3MessageInterceptor amf3MessageInterceptor) { this.amf3MessageInterceptor = amf3MessageInterceptor; } public Map<String, String> getInstantiators() { return instantiators; } public Converters getConverters() { return converters; } public MethodMatcher getMethodMatcher() { return methodMatcher; } public ServiceInvocationListener getInvocationListener() { return invocationListener; } public String getInstantiator(String type) { return instantiators.get(type); } public ClassGetter getClassGetter() { return classGetter; } public XMap getExternalizersConfiguration() { return externalizersConfiguration; } public void setExternalizersConfiguration(XMap externalizersConfiguration) { this.externalizersConfiguration = externalizersConfiguration; } public Externalizer getExternalizer(String type) { Externalizer externalizer = getElementByType( type, EXTERNALIZER_FACTORY, externalizersByType, externalizersByInstanceOf, externalizersByAnnotatedWith, scannedExternalizers); if (externalizer != null) return externalizer; if ("java".equals(GraniteContext.getCurrentInstance().getClientType())) { // Force use of number externalizers when serializing from/to a Java client if (Long.class.getName().equals(type)) return LONG_EXTERNALIZER; else if (BigInteger.class.getName().equals(type)) return BIGINTEGER_EXTERNALIZER; else if (BigDecimal.class.getName().equals(type)) return BIGDECIMAL_EXTERNALIZER; else { try { Class<?> clazz = TypeUtil.forName(type); if (Map.class.isAssignableFrom(clazz) && !Externalizable.class.isAssignableFrom(clazz)) return MAP_EXTERNALIZER; } catch (Exception e) { } } } return null; } public void registerExternalizer(Externalizer externalizer) { scannedExternalizers.add(externalizer); } public Map<String, Externalizer> getExternalizersByType() { return externalizersByType; } public Map<String, String> getExternalizersByInstanceOf() { return externalizersByInstanceOf; } public Map<String, String> getExternalizersByAnnotatedWith() { return externalizersByAnnotatedWith; } public List<Externalizer> getScannedExternalizers() { return scannedExternalizers; } public JMF_EXTENSIONS_MODE getJmfExtendedCodecsMode() { return jmfExtendedCodecsMode; } public List<ExtendedObjectCodec> getJmfExtendedCodecs() { return jmfExtendedCodecs; } public JMF_EXTENSIONS_MODE getJmfDefaultStoredStringsMode() { return jmfDefaultStoredStringsMode; } public Reflection getJmfReflection() { return jmfReflection; } public List<String> getJmfDefaultStoredStrings() { return jmfDefaultStoredStrings; } public Class<? extends ActionScriptClassDescriptor> getActionScriptDescriptor(String type) { return getElementByType( type, ASC_DESCRIPTOR_FACTORY, as3DescriptorsByType, as3DescriptorsByInstanceOf, null, null); } public Map<String, Class<? extends ActionScriptClassDescriptor>> getAs3DescriptorsByType() { return as3DescriptorsByType; } public Map<String, String> getAs3DescriptorsByInstanceOf() { return as3DescriptorsByInstanceOf; } public ConcurrentMap<String, JavaClassDescriptor> getJavaDescriptorsCache() { return javaDescriptorsCache; } public Class<? extends JavaClassDescriptor> getJavaDescriptor(String type) { return getElementByType( type, JC_DESCRIPTOR_FACTORY, javaDescriptorsByType, javaDescriptorsByInstanceOf, null, null); } public Map<String, Class<? extends JavaClassDescriptor>> getJavaDescriptorsByType() { return javaDescriptorsByType; } public Map<String, String> getJavaDescriptorsByInstanceOf() { return javaDescriptorsByInstanceOf; } public boolean isComponentTideEnabled( String componentName, Set<Class<?>> componentClasses, Object instance) { return TideComponentMatcherFactory.isComponentTideEnabled( enabledTideComponentsByName, tideComponentMatchers, componentName, componentClasses, instance); } public boolean isComponentTideDisabled( String componentName, Set<Class<?>> componentClasses, Object instance) { return TideComponentMatcherFactory.isComponentTideDisabled( disabledTideComponentsByName, tideComponentMatchers, componentName, componentClasses, instance); } public List<ExceptionConverter> getExceptionConverters() { return exceptionConverters; } public void registerExceptionConverter( Class<? extends ExceptionConverter> exceptionConverterClass) { registerExceptionConverter(exceptionConverterClass, false); } public void registerExceptionConverter( Class<? extends ExceptionConverter> exceptionConverterClass, boolean first) { for (ExceptionConverter ec : exceptionConverters) { if (ec.getClass() == exceptionConverterClass) return; } try { ExceptionConverter exceptionConverter = TypeUtil.newInstance(exceptionConverterClass, ExceptionConverter.class); if (first) exceptionConverters.add(0, exceptionConverter); else exceptionConverters.add(exceptionConverter); } catch (Exception e) { log.error(e, "Could not instantiate exception converter: %s", exceptionConverterClass); } } public void registerExceptionConverter(ExceptionConverter exceptionConverter, boolean first) { for (ExceptionConverter ec : exceptionConverters) { if (ec.getClass() == exceptionConverter.getClass()) return; } if (first) exceptionConverters.add(0, exceptionConverter); else exceptionConverters.add(exceptionConverter); } public boolean hasSecurityService() { return securityService != null; } public SecurityService getSecurityService() { return securityService; } public List<TideComponentMatcher> getTideComponentMatchers() { return tideComponentMatchers; } public Map<String, Object[]> getEnabledTideComponentsByName() { return enabledTideComponentsByName; } public Map<String, Object[]> getDisabledTideComponentsByName() { return disabledTideComponentsByName; } public XMap getGravityConfig() { return gravityConfig; } public DistributedDataFactory getDistributedDataFactory() { return distributedDataFactory; } public Constructor<?> getMessageSelectorConstructor() { return messageSelectorConstructor; } public Externalizer setExternalizersByType(String type, String externalizerType) { return externalizersByType.put(type, EXTERNALIZER_FACTORY.getInstance(externalizerType, this)); } public String putExternalizersByInstanceOf(String instanceOf, String externalizerType) { return externalizersByInstanceOf.put(instanceOf, externalizerType); } public String putExternalizersByAnnotatedWith(String annotatedWith, String externalizerType) { return externalizersByAnnotatedWith.put(annotatedWith, externalizerType); } /////////////////////////////////////////////////////////////////////////// // Static GraniteConfig loading helpers. private void forElement(XMap element, boolean custom, String graniteConfigProperties) { String scan = element.get("@scan"); this.scan = Boolean.TRUE.toString().equals(scan); loadCustomAMF3Serializer(element, custom); loadCustomAMF3DeserializerSecurizer(element, custom); loadCustomAMF3MessageInterceptor(element, custom); loadCustomConverters(element, custom); loadCustomMethodMatcher(element, custom); loadCustomInvocationListener(element, custom); loadCustomInstantiators(element, custom); loadCustomClassGetter(element, custom); loadCustomExternalizers(element, custom); loadCustomJMFExtendedCodecs(element, custom); loadCustomJMFDefaultStoredStrings(element, custom); loadCustomJMFReflection(element, custom); loadCustomDescriptors(element, custom); loadCustomExceptionConverters(element, custom); loadCustomTideComponents(element, custom); loadCustomSecurity(element, custom); loadCustomMessageSelector(element, custom); loadCustomGravity(element, custom); loadCustomDistributedDataFactory(element, custom); if (this.scan) scanConfig(graniteConfigProperties); finishCustomConverters(custom); } private void loadCustomAMF3Serializer(XMap element, boolean custom) { XMap amf3Serializer = element.getOne("amf3-serializer"); if (amf3Serializer != null) { String type = amf3Serializer.get("@type"); try { Class<AMF3Serializer> amf3SerializerClass = TypeUtil.forName(type, AMF3Serializer.class); amf3SerializerConstructor = TypeUtil.getConstructor(amf3SerializerClass, new Class<?>[] {OutputStream.class}); } catch (Exception e) { throw new GraniteConfigException( "Could not get constructor for AMF3 serializer: " + type, e); } } XMap amf3Deserializer = element.getOne("amf3-deserializer"); if (amf3Deserializer != null) { String type = amf3Deserializer.get("@type"); try { Class<AMF3Deserializer> amf3DeserializerClass = TypeUtil.forName(type, AMF3Deserializer.class); amf3DeserializerConstructor = TypeUtil.getConstructor(amf3DeserializerClass, new Class<?>[] {InputStream.class}); } catch (Exception e) { throw new GraniteConfigException( "Could not get constructor for AMF3 deserializer: " + type, e); } } } private void loadCustomAMF3DeserializerSecurizer(XMap element, boolean custom) { XMap securizer = element.getOne("amf3-deserializer-securizer"); if (securizer != null) { String type = securizer.get("@type"); try { amf3DeserializerSecurizer = (AMF3DeserializerSecurizer) TypeUtil.newInstance(type); } catch (Exception e) { throw new GraniteConfigException( "Could not construct amf3 deserializer securizer: " + type, e); } String param = securizer.get("@param"); try { amf3DeserializerSecurizer.setParam(param); } catch (Exception e) { throw new GraniteConfigException( "Could not set param of amf3 deserializer securizer: " + type + ", param: " + param, e); } } } private void loadCustomAMF3MessageInterceptor(XMap element, boolean custom) { XMap interceptor = element.getOne("amf3-message-interceptor"); if (interceptor != null) { String type = interceptor.get("@type"); try { amf3MessageInterceptor = (AMF3MessageInterceptor) TypeUtil.newInstance(type); } catch (Exception e) { throw new GraniteConfigException( "Could not construct amf3 message interceptor: " + type, e); } } } private void loadCustomDistributedDataFactory(XMap element, boolean custom) { XMap distributedDataFactory = element.getOne("distributed-data-factory"); if (distributedDataFactory != null) { String type = distributedDataFactory.get("@type"); try { this.distributedDataFactory = (DistributedDataFactory) TypeUtil.newInstance(type); } catch (Exception e) { throw new GraniteConfigException( "Could not construct build distributed data factory: " + type, e); } } } private void loadCustomConverters(XMap element, boolean custom) { XMap converters = element.getOne("converters"); if (converters != null) { // Should we override standard config converters? String override = converters.get("@override"); if (Boolean.TRUE.toString().equals(override)) converterClasses.clear(); int i = 0; for (XMap converter : converters.getAll("converter")) { String type = converter.get("@type"); try { // For custom config, shifts any standard converters to the end of the list... converterClasses.add(i++, TypeUtil.forName(type, Converter.class)); } catch (Exception e) { throw new GraniteConfigException("Could not get converter class for: " + type, e); } } } } private void finishCustomConverters(boolean custom) { try { converters = new Converters(converterClasses); } catch (Exception e) { throw new GraniteConfigException("Could not construct new Converters instance", e); } // Cleanup... if (custom) converterClasses = null; } private void loadCustomMethodMatcher(XMap element, boolean custom) { XMap methodMatcher = element.getOne("method-matcher"); if (methodMatcher != null) { String type = methodMatcher.get("@type"); try { this.methodMatcher = (MethodMatcher) TypeUtil.newInstance(type); } catch (Exception e) { throw new GraniteConfigException("Could not construct method matcher: " + type, e); } } } private void loadCustomInvocationListener(XMap element, boolean custom) { XMap invocationListener = element.getOne("invocation-listener"); if (invocationListener != null) { String type = invocationListener.get("@type"); try { this.invocationListener = (ServiceInvocationListener) TypeUtil.newInstance(type); } catch (Exception e) { throw new GraniteConfigException( "Could not instantiate ServiceInvocationListener: " + type, e); } } } private void loadCustomInstantiators(XMap element, boolean custom) { XMap instantiators = element.getOne("instantiators"); if (instantiators != null) { for (XMap instantiator : instantiators.getAll("instantiator")) this.instantiators.put(instantiator.get("@type"), instantiator.get(".")); } } private void loadCustomClassGetter(XMap element, boolean custom) { XMap classGetter = element.getOne("class-getter"); if (classGetter != null) { String type = classGetter.get("@type"); try { this.classGetter = (ClassGetter) TypeUtil.newInstance(type); classGetterSet = true; } catch (Exception e) { throw new GraniteConfigException("Could not instantiate ClassGetter: " + type, e); } } } private void loadCustomExternalizers(XMap element, boolean custom) { externalizersConfiguration = element.getOne("externalizers/configuration"); for (XMap externalizer : element.getAll("externalizers/externalizer")) { String externalizerType = externalizer.get("@type"); for (XMap include : externalizer.getAll("include")) { String type = include.get("@type"); if (type != null) externalizersByType.put(type, EXTERNALIZER_FACTORY.getInstance(externalizerType, this)); else { String instanceOf = include.get("@instance-of"); if (instanceOf != null) externalizersByInstanceOf.put(instanceOf, externalizerType); else { String annotatedWith = include.get("@annotated-with"); if (annotatedWith == null) throw new GraniteConfigException( "Element 'include' has no attribute 'type', 'instance-of' or 'annotated-with'"); externalizersByAnnotatedWith.put(annotatedWith, externalizerType); } } } } } private void loadCustomJMFExtendedCodecs(XMap element, boolean custom) { String jmfExtendedCodecsMode = element.get("jmf-extended-codecs/@mode"); if (jmfExtendedCodecsMode != null) { try { this.jmfExtendedCodecsMode = JMF_EXTENSIONS_MODE.valueOf(jmfExtendedCodecsMode.toLowerCase()); } catch (Exception e) { throw new GraniteConfigException( "Illegal JMF extended codecs mode: " + jmfExtendedCodecsMode, e); } } for (XMap codec : element.getAll("jmf-extended-codecs/jmf-extended-codec")) { String codecType = codec.get("@type"); try { jmfExtendedCodecs.add((ExtendedObjectCodec) TypeUtil.newInstance(codecType)); } catch (Exception e) { throw new GraniteConfigException( "Could not instantiate JMF extended codec: " + codecType, e); } } } private void loadCustomJMFDefaultStoredStrings(XMap element, boolean custom) { String jmfDefaultStoredStringsMode = element.get("jmf-default-stored-strings/@mode"); if (jmfDefaultStoredStringsMode != null) { try { this.jmfDefaultStoredStringsMode = JMF_EXTENSIONS_MODE.valueOf(jmfDefaultStoredStringsMode.toLowerCase()); } catch (Exception e) { throw new GraniteConfigException( "Illegal JMF default stored strings mode: " + jmfDefaultStoredStringsMode, e); } } for (XMap codec : element.getAll("jmf-default-stored-strings/jmf-default-stored-string")) jmfDefaultStoredStrings.add(codec.get("@value")); } private void loadCustomJMFReflection(XMap element, boolean custom) { String jmfReflection = element.get("jmf-reflection/@type"); if (jmfReflection == null) this.jmfReflection = new Reflection(Thread.currentThread().getContextClassLoader()); else { try { this.jmfReflection = (Reflection) TypeUtil.newInstance(jmfReflection); } catch (Exception e) { throw new GraniteConfigException( "Could not instantiate JMF reflection: " + jmfReflection, e); } } } /** * Read custom class descriptors. Descriptor must have 'type' or 'instanceof' attribute and one of * 'java' or 'as3' attributes specified. */ private void loadCustomDescriptors(XMap element, boolean custom) { for (XMap descriptor : element.getAll("descriptors/descriptor")) { String type = descriptor.get("@type"); if (type != null) { String java = descriptor.get("@java"); String as3 = descriptor.get("@as3"); if (java == null && as3 == null) throw new GraniteConfigException( "Element 'descriptor' has no attributes 'java' or 'as3'\n" + descriptor); if (java != null) javaDescriptorsByType.put(type, JC_DESCRIPTOR_FACTORY.getInstance(java, this)); if (as3 != null) as3DescriptorsByType.put(type, ASC_DESCRIPTOR_FACTORY.getInstance(as3, this)); } else { String instanceOf = descriptor.get("@instance-of"); if (instanceOf == null) throw new GraniteConfigException( "Element 'descriptor' has no attribute 'type' or 'instance-of'\n" + descriptor); String java = descriptor.get("@java"); String as3 = descriptor.get("@as3"); if (java == null && as3 == null) { throw new GraniteConfigException( "Element 'descriptor' has no attributes 'java' or 'as3' in:\n" + descriptor); } if (java != null) javaDescriptorsByInstanceOf.put(instanceOf, java); if (as3 != null) as3DescriptorsByInstanceOf.put(instanceOf, as3); } } } public void setAliasRegistry(AliasRegistry aliasRegistry) { this.aliasRegistry = aliasRegistry; } public AliasRegistry getAliasRegistry() { return aliasRegistry; } public void setSharedContext(SharedContext sharedContext) { this.sharedContext = sharedContext; } public SharedContext getSharedContext() { return sharedContext; } /** Read custom class exception converters Converter must have 'type' attribute */ private void loadCustomExceptionConverters(XMap element, boolean custom) { for (XMap exceptionConverter : element.getAll("exception-converters/exception-converter")) { String type = exceptionConverter.get("@type"); ExceptionConverter converter = null; try { converter = (ExceptionConverter) TypeUtil.newInstance(type); exceptionConverters.add(converter); } catch (Exception e) { throw new GraniteConfigException("Could not construct exception converter: " + type, e); } } } private void loadCustomTideComponents(XMap element, boolean custom) { for (XMap component : element.getAll("tide-components/tide-component")) { boolean disabled = Boolean.TRUE.toString().equals(component.get("@disabled")); String type = component.get("@type"); if (type != null) tideComponentMatchers.add(TIDE_COMPONENT_MATCHER_FACTORY.getTypeMatcher(type, disabled)); else { String name = component.get("@name"); if (name != null) tideComponentMatchers.add(TIDE_COMPONENT_MATCHER_FACTORY.getNameMatcher(name, disabled)); else { String instanceOf = component.get("@instance-of"); if (instanceOf != null) tideComponentMatchers.add( TIDE_COMPONENT_MATCHER_FACTORY.getInstanceOfMatcher(instanceOf, disabled)); else { String annotatedWith = component.get("@annotated-with"); if (annotatedWith == null) throw new GraniteConfigException( "Element 'component' has no attribute 'type', 'name', 'instance-of' or 'annotated-with'"); tideComponentMatchers.add( TIDE_COMPONENT_MATCHER_FACTORY.getAnnotatedWithMatcher(annotatedWith, disabled)); } } } } } private void loadCustomSecurity(XMap element, boolean custom) { XMap security = element.getOne("security"); if (security != null) { String type = security.get("@type"); try { securityService = (SecurityService) TypeUtil.newInstance(type); } catch (Exception e) { throw new GraniteConfigException("Could not instantiate SecurityService: " + type, e); } Map<String, String> params = new HashMap<String, String>(); for (XMap param : security.getAll("param")) { String name = param.get("@name"); String value = param.get("@value"); params.put(name, value); } try { securityService.configure(params); } catch (Exception e) { throw new GraniteConfigException( "Could not configure SecurityService " + type + " with: " + params, e); } } } public void setSecurityService(SecurityService securityService) { this.securityService = securityService; } private void loadCustomMessageSelector(XMap element, boolean custom) { XMap selector = element.getOne("message-selector"); if (selector != null) { String type = selector.get("@type"); try { messageSelectorConstructor = TypeUtil.getConstructor(type, new Class<?>[] {String.class}); } catch (Exception e) { throw new GraniteConfigException("Could not construct message selector: " + type, e); } } } private void loadCustomGravity(XMap element, boolean custom) { gravityConfig = element.getOne("gravity"); } /////////////////////////////////////////////////////////////////////////// // Other helpers. @SuppressWarnings("unchecked") private <T, C extends Config> T getElementByType( String type, ConfigurableFactory<T, C> factory, ConcurrentHashMap<String, T> elementsByType, Map<String, String> elementsByInstanceOf, Map<String, String> elementsByAnnotatedWith, List<T> scannedConfigurables) { // This NULL object is a Java null placeholder: ConcurrentHashMap doesn't allow // null values... final T NULL = factory.getNullInstance(); T element = elementsByType.get(type); if (element != null) return (NULL == element ? null : element); element = NULL; Class<?> typeClass = null; try { typeClass = TypeUtil.forName(type); } catch (Exception e) { throw new GraniteConfigException("Could not load class: " + type, e); } if (elementsByAnnotatedWith != null && NULL == element) { for (Map.Entry<String, String> entry : elementsByAnnotatedWith.entrySet()) { String annotation = entry.getKey(); try { Class<Annotation> annotationClass = TypeUtil.forName(annotation, Annotation.class); if (typeClass.isAnnotationPresent(annotationClass)) { element = factory.getInstance(entry.getValue(), (C) this); break; } } catch (Exception e) { throw new GraniteConfigException("Could not load class: " + annotation, e); } } } if (elementsByInstanceOf != null && NULL == element) { for (Map.Entry<String, String> entry : elementsByInstanceOf.entrySet()) { String instanceOf = entry.getKey(); try { Class<?> instanceOfClass = TypeUtil.forName(instanceOf); if (instanceOfClass.isAssignableFrom(typeClass)) { element = factory.getInstance(entry.getValue(), (C) this); break; } } catch (Exception e) { throw new GraniteConfigException("Could not load class: " + instanceOf, e); } } } if (NULL == element) element = factory.getInstanceForBean(scannedConfigurables, typeClass, (C) this); T previous = elementsByType.putIfAbsent(type, element); if (previous != null) element = previous; return (NULL == element ? null : element); } }
/** @author Franck WOLFF */ public abstract class AbstractChannel implements Channel { /////////////////////////////////////////////////////////////////////////// // Fields. private static final Logger log = Logger.getLogger(AbstractChannel.class); protected final String id; protected final ServletConfig servletConfig; protected final ConcurrentMap<String, Subscription> subscriptions = new ConcurrentHashMap<String, Subscription>(); protected LinkedList<AsyncPublishedMessage> publishedQueue = new LinkedList<AsyncPublishedMessage>(); protected final Lock publishedQueueLock = new ReentrantLock(); protected LinkedList<AsyncMessage> receivedQueue = new LinkedList<AsyncMessage>(); protected final Lock receivedQueueLock = new ReentrantLock(); protected final AsyncPublisher publisher; protected final AsyncReceiver receiver; /////////////////////////////////////////////////////////////////////////// // Constructor. protected AbstractChannel(ServletConfig servletConfig, GravityConfig gravityConfig, String id) { if (id == null) throw new NullPointerException("id cannot be null"); this.id = id; this.servletConfig = servletConfig; this.publisher = new AsyncPublisher(this); this.receiver = new AsyncReceiver(this); } /////////////////////////////////////////////////////////////////////////// // Abstract protected method. protected abstract boolean hasAsyncHttpContext(); protected abstract AsyncHttpContext acquireAsyncHttpContext(); protected abstract void releaseAsyncHttpContext(AsyncHttpContext context); /////////////////////////////////////////////////////////////////////////// // Channel interface implementation. public String getId() { return id; } public Gravity getGravity() { return GravityManager.getGravity(getServletContext()); } public Subscription addSubscription( String destination, String subTopicId, String subscriptionId, boolean noLocal) { Subscription subscription = new Subscription(this, destination, subTopicId, subscriptionId, noLocal); Subscription present = subscriptions.putIfAbsent(subscriptionId, subscription); return (present != null ? present : subscription); } public Collection<Subscription> getSubscriptions() { return subscriptions.values(); } public Subscription removeSubscription(String subscriptionId) { return subscriptions.remove(subscriptionId); } public void publish(AsyncPublishedMessage message) throws MessagePublishingException { if (message == null) throw new NullPointerException("message cannot be null"); publishedQueueLock.lock(); try { publishedQueue.add(message); } finally { publishedQueueLock.unlock(); } publisher.queue(getGravity()); } public boolean hasPublishedMessage() { publishedQueueLock.lock(); try { return !publishedQueue.isEmpty(); } finally { publishedQueueLock.unlock(); } } public boolean runPublish() { LinkedList<AsyncPublishedMessage> publishedCopy = null; publishedQueueLock.lock(); try { if (publishedQueue.isEmpty()) return false; publishedCopy = publishedQueue; publishedQueue = new LinkedList<AsyncPublishedMessage>(); } finally { publishedQueueLock.unlock(); } for (AsyncPublishedMessage message : publishedCopy) { try { message.publish(this); } catch (Exception e) { log.error(e, "Error while trying to publish message: %s", message); } } return true; } public void receive(AsyncMessage message) throws MessageReceivingException { if (message == null) throw new NullPointerException("message cannot be null"); Gravity gravity = getGravity(); receivedQueueLock.lock(); try { if (receivedQueue.size() + 1 > gravity.getGravityConfig().getMaxMessagesQueuedPerChannel()) throw new MessageReceivingException( message, "Could not queue message (channel's queue is full) for channel: " + this); receivedQueue.add(message); } finally { receivedQueueLock.unlock(); } if (hasAsyncHttpContext()) receiver.queue(gravity); } public boolean hasReceivedMessage() { receivedQueueLock.lock(); try { return !receivedQueue.isEmpty(); } finally { receivedQueueLock.unlock(); } } public boolean runReceive() { return runReceived(null); } public boolean runReceived(AsyncHttpContext asyncHttpContext) { boolean httpAsParam = (asyncHttpContext != null); LinkedList<AsyncMessage> messages = null; OutputStream os = null; try { receivedQueueLock.lock(); try { // Do we have any pending messages? if (receivedQueue.isEmpty()) return false; // Do we have a valid http context? if (asyncHttpContext == null) { asyncHttpContext = acquireAsyncHttpContext(); if (asyncHttpContext == null) return false; } // Both conditions are ok, get all pending messages. messages = receivedQueue; receivedQueue = new LinkedList<AsyncMessage>(); } finally { receivedQueueLock.unlock(); } HttpServletRequest request = asyncHttpContext.getRequest(); HttpServletResponse response = asyncHttpContext.getResponse(); // Set response messages correlation ids to connect request message id. String correlationId = asyncHttpContext.getConnectMessage().getMessageId(); AsyncMessage[] messagesArray = new AsyncMessage[messages.size()]; int i = 0; for (AsyncMessage message : messages) { message.setCorrelationId(correlationId); messagesArray[i++] = message; } // Setup serialization context (thread local) Gravity gravity = getGravity(); GraniteContext context = HttpGraniteContext.createThreadIntance( gravity.getGraniteConfig(), gravity.getServicesConfig(), null, request, response); ((AMFContextImpl) context.getAMFContext()) .setCurrentAmf3Message(asyncHttpContext.getConnectMessage()); // Write messages to response output stream. response.setStatus(HttpServletResponse.SC_OK); response.setContentType(AMF0Message.CONTENT_TYPE); response.setDateHeader("Expire", 0L); response.setHeader("Cache-Control", "no-store"); os = response.getOutputStream(); ObjectOutput amf3Serializer = context.getGraniteConfig().newAMF3Serializer(os); log.debug("<< [MESSAGES for channel=%s] %s", this, messagesArray); amf3Serializer.writeObject(messagesArray); os.flush(); response.flushBuffer(); return true; // Messages were delivered, http context isn't valid anymore. } catch (IOException e) { log.warn(e, "Could not send messages to channel: %s (retrying later)", this); GravityConfig gravityConfig = getGravity().getGravityConfig(); if (messages != null && gravityConfig.isRetryOnError()) { receivedQueueLock.lock(); try { if (receivedQueue.size() + messages.size() > gravityConfig.getMaxMessagesQueuedPerChannel()) { log.warn( "Channel %s has reached its maximum queue capacity %s (throwing %s messages)", this, gravityConfig.getMaxMessagesQueuedPerChannel(), messages.size()); } else receivedQueue.addAll(0, messages); } finally { receivedQueueLock.unlock(); } } return true; // Messages weren't delivered, but http context isn't valid anymore. } finally { // Cleanup serialization context (thread local) try { GraniteContext.release(); } catch (Exception e) { // should never happen... } // Close output stream. try { if (os != null) { try { os.close(); } catch (IOException e) { log.warn(e, "Could not close output stream (ignored)"); } } } finally { // Cleanup http context (only if this method wasn't explicitly called with a non null // AsyncHttpContext from the servlet). if (!httpAsParam) releaseAsyncHttpContext(asyncHttpContext); } } } public void destroy() { Gravity gravity = getGravity(); gravity.cancel(publisher); gravity.cancel(receiver); subscriptions.clear(); } /////////////////////////////////////////////////////////////////////////// // Protected utilities. protected boolean queueReceiver() { if (hasReceivedMessage()) { receiver.queue(getGravity()); return true; } return false; } protected ServletConfig getServletConfig() { return servletConfig; } protected ServletContext getServletContext() { return servletConfig.getServletContext(); } /////////////////////////////////////////////////////////////////////////// // Object overwritten methods. @Override public boolean equals(Object obj) { return (obj instanceof Channel && id.equals(((Channel) obj).getId())); } @Override public int hashCode() { return id.hashCode(); } @Override public String toString() { return getClass().getName() + " {id=" + id + ", subscriptions=" + subscriptions.values() + "}"; } }