001/* 002 GRANITE DATA SERVICES 003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S. 004 005 This file is part of Granite Data Services. 006 007 Granite Data Services is free software; you can redistribute it and/or modify 008 it under the terms of the GNU Library General Public License as published by 009 the Free Software Foundation; either version 2 of the License, or (at your 010 option) any later version. 011 012 Granite Data Services is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License 015 for more details. 016 017 You should have received a copy of the GNU Library General Public License 018 along with this library; if not, see <http://www.gnu.org/licenses/>. 019 */ 020 021package org.granite.tide.spring; 022 023import java.util.Map; 024 025import javax.servlet.ServletContext; 026 027import org.granite.config.flex.Destination; 028import org.granite.context.GraniteContext; 029import org.granite.logging.Logger; 030import org.granite.messaging.service.ExtendedServiceExceptionHandler; 031import org.granite.messaging.service.ServiceException; 032import org.granite.messaging.service.ServiceFactory; 033import org.granite.messaging.service.ServiceInvoker; 034import org.granite.messaging.webapp.HttpGraniteContext; 035import org.granite.tide.TideServiceInvoker; 036import org.granite.tide.data.PersistenceExceptionConverter; 037import org.granite.util.TypeUtil; 038import org.granite.util.XMap; 039import org.springframework.context.ApplicationContext; 040import org.springframework.web.context.support.WebApplicationContextUtils; 041 042import flex.messaging.messages.RemotingMessage; 043 044 045/** 046 * @author Sebastien Deleuze 047 */ 048public class SpringServiceFactory extends ServiceFactory { 049 050 private static final Logger log = Logger.getLogger(SpringServiceFactory.class); 051 052 public static final String PERSISTENCE_MANAGER_BEAN_NAME = "persistence-manager-bean-name"; 053 public static final String ENTITY_MANAGER_FACTORY_BEAN_NAME = "entity-manager-factory-bean-name"; 054 055 private ApplicationContext springContext = null; 056 057 public void setApplicationContext(ApplicationContext applicationContext) { 058 this.springContext = applicationContext; 059 } 060 061 062 @Override 063 public void configure(XMap properties) throws ServiceException { 064 String sServiceExceptionHandler = properties.get("service-exception-handler"); 065 if (sServiceExceptionHandler == null) { 066 XMap props = new XMap(properties); 067 props.put("service-exception-handler", ExtendedServiceExceptionHandler.class.getName()); 068 super.configure(props); 069 } 070 else 071 super.configure(properties); 072 073 GraniteContext graniteContext = GraniteContext.getCurrentInstance(); 074 try { 075 graniteContext.getGraniteConfig().registerExceptionConverter(PersistenceExceptionConverter.class); 076 } 077 catch (Throwable t) { 078 log.info(t, "JPA exception converter not registered (JPA not found on classpath)"); 079 } 080 } 081 082 083 @Override 084 public ServiceInvoker<?> getServiceInstance(RemotingMessage request) throws ServiceException { 085 086 String messageType = request.getClass().getName(); 087 String destinationId = request.getDestination(); 088 089 GraniteContext context = GraniteContext.getCurrentInstance(); 090 Map<String, Object> cache = context.getSessionMap(false); 091 if (cache == null) 092 cache = context.getRequestMap(); 093 Destination destination = context.getServicesConfig().findDestinationById(messageType, destinationId); 094 if (destination == null) 095 throw new ServiceException("No matching destination: " + destinationId); 096 097 String key = TideServiceInvoker.class.getName() + '.' + destinationId; 098 099 return getServiceInvoker(cache, destination, key); 100 101 } 102 103 private ServiceInvoker<?> getServiceInvoker(Map<String, Object> cache, Destination destination, String key) { 104 GraniteContext context = GraniteContext.getCurrentInstance(); 105 if (context.getSessionMap(false) == null) 106 return internalGetServiceInvoker(cache, destination, key); 107 108 synchronized (context.getSessionLock()) { 109 return internalGetServiceInvoker(cache, destination, key); 110 } 111 } 112 113 private ServiceInvoker<?> internalGetServiceInvoker(Map<String, Object> cache, Destination destination, String key) { 114 ServiceInvoker<?> invoker = (ServiceInvoker<?>)cache.get(key); 115 if (invoker == null) { 116 SpringServiceContext tideContext = null; 117 ServletContext sc = ((HttpGraniteContext)GraniteContext.getCurrentInstance()).getServletContext(); 118 ApplicationContext springContext = this.springContext != null ? this.springContext : WebApplicationContextUtils.getRequiredWebApplicationContext(sc); 119 Map<String, ?> beans = springContext.getBeansOfType(SpringServiceContext.class); 120 if (beans.size() > 1) 121 throw new RuntimeException("More than one SpringServiceContext bean found"); 122 else if (beans.size() == 1) 123 tideContext = (SpringServiceContext)beans.values().iterator().next(); 124 else { 125 // Try to create Spring MVC context when Spring MVC available 126 String className = "org.granite.tide.spring.SpringMVCServiceContext"; 127 try { 128 Class<SpringServiceContext> clazz = TypeUtil.forName(className, SpringServiceContext.class); 129 tideContext = clazz.getConstructor(ApplicationContext.class).newInstance(springContext); 130 } 131 catch (Exception e) { 132 tideContext = new SpringServiceContext(springContext); 133 } 134 } 135 136 String persistenceManagerBeanName = destination.getProperties().get(PERSISTENCE_MANAGER_BEAN_NAME); 137 tideContext.setPersistenceManagerBeanName(persistenceManagerBeanName); 138 139 String entityManagerFactoryBeanName = destination.getProperties().get(ENTITY_MANAGER_FACTORY_BEAN_NAME); 140 tideContext.setEntityManagerFactoryBeanName(entityManagerFactoryBeanName); 141 142 invoker = new TideServiceInvoker<SpringServiceFactory>(destination, this, tideContext); 143 cache.put(key, invoker); 144 } 145 return invoker; 146 } 147}