001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.spring;
023
024import java.math.BigDecimal;
025import java.math.BigInteger;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import javax.servlet.ServletContext;
032import javax.servlet.http.HttpServletRequest;
033import javax.servlet.http.HttpServletResponse;
034
035import org.granite.config.GraniteConfig;
036import org.granite.config.flex.Channel;
037import org.granite.config.flex.Destination;
038import org.granite.config.flex.EndPoint;
039import org.granite.config.flex.Factory;
040import org.granite.config.flex.Service;
041import org.granite.config.flex.ServicesConfig;
042import org.granite.logging.Logger;
043import org.granite.messaging.amf.io.convert.Converter;
044import org.granite.messaging.amf.io.util.externalizer.BigDecimalExternalizer;
045import org.granite.messaging.amf.io.util.externalizer.BigIntegerExternalizer;
046import org.granite.messaging.amf.io.util.externalizer.Externalizer;
047import org.granite.messaging.amf.io.util.externalizer.LongExternalizer;
048import org.granite.messaging.amf.process.AMF3MessageInterceptor;
049import org.granite.messaging.service.ExceptionConverter;
050import org.granite.messaging.service.security.RemotingDestinationSecurizer;
051import org.granite.messaging.service.security.SecurityService;
052import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
053import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
054import org.granite.messaging.service.tide.TideComponentNameMatcher;
055import org.granite.messaging.service.tide.TideComponentTypeMatcher;
056import org.granite.messaging.webapp.AMFEndpoint;
057import org.granite.util.TypeUtil;
058import org.granite.util.XMap;
059import org.springframework.beans.BeansException;
060import org.springframework.beans.factory.DisposableBean;
061import org.springframework.beans.factory.InitializingBean;
062import org.springframework.beans.factory.annotation.Autowired;
063import org.springframework.context.ApplicationContext;
064import org.springframework.context.ApplicationContextAware;
065import org.springframework.web.context.ServletContextAware;
066import org.springframework.web.servlet.HandlerAdapter;
067import org.springframework.web.servlet.ModelAndView;
068
069
070public class ServerFilter implements InitializingBean, DisposableBean, ApplicationContextAware, ServletContextAware, HandlerAdapter {
071        
072    private static final Logger log = Logger.getLogger(ServerFilter.class);
073        
074    @Autowired(required=false)
075    private ServletContext servletContext = null;
076    private ApplicationContext context = null;
077    
078    private GraniteConfig graniteConfig = null;
079    private ServicesConfig servicesConfig = null;
080    
081    private List<String> tideRoles = null;
082    private List<String> tideAnnotations = null;
083    private List<String> tideInterfaces = null;
084    private List<String> tideNames = null;
085    private List<String> tideTypes = null;
086    private List<Class<? extends ExceptionConverter>> exceptionConverters = null;
087    private AMF3MessageInterceptor amf3MessageInterceptor;
088    private boolean useLong = false;
089    private boolean useBigInteger = false;
090    private boolean useBigDecimal = false;
091    private boolean tide = false;
092    private String type = "server";
093    
094    private AMFEndpoint amfEndpoint = null;
095
096        public void setApplicationContext(ApplicationContext context) throws BeansException {
097                this.context = context;
098        }
099    
100        public void setServletContext(ServletContext servletContext) throws BeansException {
101                this.servletContext = servletContext;
102        }
103    
104        public void afterPropertiesSet() {
105                SpringGraniteConfig springGraniteConfig = context.getBeansOfType(SpringGraniteConfig.class).values().iterator().next();
106                
107        this.graniteConfig = springGraniteConfig.getGraniteConfig();
108        
109        Map<String, SecurityService> securityServices = context.getBeansOfType(SecurityService.class);
110        if (securityServices.size() > 1)
111                log.error("More than one Security Service bean defined");
112        else if (!securityServices.isEmpty()) {
113                log.debug("Security Service bean " + securityServices.keySet().iterator().next() + " selected");
114                SecurityService securityService = securityServices.values().iterator().next();
115                this.graniteConfig.setSecurityService(securityService);
116        }
117
118        Map<String, Externalizer> externalizers = context.getBeansOfType(Externalizer.class);
119        for (Externalizer externalizer : externalizers.values())
120            this.graniteConfig.registerExternalizer(externalizer);
121
122        if (useBigDecimal)
123            graniteConfig.setExternalizersByType(BigDecimal.class.getName(), BigDecimalExternalizer.class.getName());
124
125        if (useBigInteger)
126            graniteConfig.setExternalizersByType(BigInteger.class.getName(), BigIntegerExternalizer.class.getName());
127
128        if (useLong)
129            graniteConfig.setExternalizersByType(Long.class.getName(), LongExternalizer.class.getName());
130
131        if (tideAnnotations != null) {
132                for (String ta : tideAnnotations) {
133                        try {
134                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta, false));
135                                log.debug("Enabled components annotated with %s for Tide remoting", ta);
136                        }
137                        catch (Exception e) {
138                                log.error(e, "Could not add tide-component annotation %s", ta);
139                        }
140                }
141        }
142        if (tideInterfaces != null) {
143                for (String ti : tideInterfaces) {
144                        try {
145                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti, false));
146                                log.debug("Enabled components extending %s for Tide remoting", ti);
147                        }
148                        catch (Exception e) {
149                                log.error(e, "Could not add tide-component interface %s", ti);
150                        }
151                }
152        }
153        if (tideNames != null) {
154                for (String tn : tideNames) {
155                        try {
156                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false));
157                                log.debug("Enabled components named like %s for Tide remoting", tn);
158                        }
159                        catch (Exception e) {
160                                log.error(e, "Could not add tide-component name %s", tn);
161                        }
162                }
163        }
164        if (tideTypes != null) {
165                for (String tt : tideTypes) {
166                        try {
167                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false));
168                                log.debug("Enabled components with type %s for Tide remoting", tt);
169                        }
170                        catch (Exception e) {
171                                log.error(e, "Could not add tide-component type %s", tt);
172                        }
173                }
174        }
175        if (exceptionConverters != null) {
176                for (Class<? extends ExceptionConverter> ec : exceptionConverters) {
177                        this.graniteConfig.registerExceptionConverter(ec);
178                        log.debug("Registered exception converter %s", ec);
179                }
180        }
181        if (amf3MessageInterceptor != null)
182                this.graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor);
183        
184        // If Spring Data available, automatically enable Page/Pageable converter
185        try {
186                TypeUtil.forName("org.springframework.data.domain.Page");
187                Class<Converter> converterClass = TypeUtil.forName("org.granite.spring.data.PageableConverter", Converter.class);
188                this.graniteConfig.getConverters().addConverter(converterClass);
189        }
190        catch (Exception e) {
191                // Spring Data not present, ignore
192        }
193        
194        servicesConfig = springGraniteConfig.getServicesConfig();
195        
196        Channel channel = servicesConfig.findChannelById("graniteamf");
197        if (channel == null) {
198                channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel", 
199                        new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"), 
200                        new XMap());
201                servicesConfig.addChannel(channel);
202        }
203        
204        if (tide) {
205                Factory factory = servicesConfig.findFactoryById("tide-spring-factory");
206                if (factory == null) {
207                        factory = new Factory("tide-spring-factory", "org.granite.tide.spring.SpringServiceFactory", new XMap());
208                servicesConfig.addFactory(factory);
209                }
210                
211                Service service = servicesConfig.findServiceById("granite-service");
212                if (service == null) {
213                        service = new Service("granite-service", "flex.messaging.services.RemotingService", 
214                                "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
215                }
216                Destination destination = servicesConfig.findDestinationById("flex.messaging.messages.RemotingMessage", type);
217                if (destination == null) {
218                        List<String> channelIds = new ArrayList<String>();
219                        channelIds.add("graniteamf");
220                        destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null);
221                        destination.getProperties().put("factory", factory.getId());
222                        destination.getProperties().put("validator-name", "tideValidator");
223                        service.getDestinations().put(destination.getId(), destination);
224                        servicesConfig.addService(service);
225                }
226                
227                if (destination.getSecurizer() == null) {
228                Map<String, RemotingDestinationSecurizer> securizers = context.getBeansOfType(RemotingDestinationSecurizer.class);
229                if (securizers.size() > 1)
230                        log.error("More than one Remoting Destination Securizer bean defined");
231                else if (!securizers.isEmpty()) {
232                        log.debug("Remoting Destination Securizer bean " + securizers.keySet().iterator().next() + " selected");
233                        RemotingDestinationSecurizer securizer = securizers.values().iterator().next();
234                        destination.setSecurizer(securizer);
235                }
236                }
237                
238                log.info("Registered Tide/Spring service factory and destination %s", type);
239        }
240        else {
241                Factory factory = servicesConfig.findFactoryById("spring-factory");
242                if (factory == null) {
243                        factory = new Factory("spring-factory", "org.granite.spring.SpringServiceFactory", new XMap());
244                        servicesConfig.addFactory(factory);
245                }
246                
247                Service service = servicesConfig.findServiceById("granite-service");
248                if (service == null) {
249                        service = new Service("granite-service", "flex.messaging.services.RemotingService", 
250                                "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
251                        servicesConfig.addService(service);
252                }
253            
254            servicesConfig.scan(null);
255                
256                log.info("Registered Spring service factory");
257        }
258        
259        amfEndpoint = new AMFEndpoint();
260        amfEndpoint.init(servletContext);
261        }
262        
263        public void destroy() throws Exception {
264                amfEndpoint.destroy();
265                amfEndpoint = null;
266        }
267
268        public void setTideRoles(List<String> tideRoles) {
269                this.tideRoles = tideRoles;
270        }
271        
272        public void setTideAnnotations(List<String> tideAnnotations) {
273                this.tideAnnotations = tideAnnotations;
274        }
275        
276        public void setTideInterfaces(List<String> tideInterfaces) {
277                this.tideInterfaces = tideInterfaces;
278        }
279        
280        public void setTideNames(List<String> tideNames) {
281                this.tideNames = tideNames;
282        }
283        
284        public void setTideTypes(List<String> tideTypes) {
285                this.tideTypes = tideTypes;
286        }
287        
288        public void setExceptionConverters(List<Class<? extends ExceptionConverter>> exceptionConverters) {
289                this.exceptionConverters = exceptionConverters;
290        }
291        
292        public void setAmf3MessageInterceptor(AMF3MessageInterceptor amf3MessageInterceptor) {
293                this.amf3MessageInterceptor = amf3MessageInterceptor;
294        }
295
296    public void setUseLong(boolean useLong) {
297        this.useLong = useLong;
298    }
299
300    public void setUseBigInteger(boolean useBigInteger) {
301        this.useBigInteger = useBigInteger;
302    }
303
304    public void setUseBigDecimal(boolean useBigDecimal) {
305        this.useBigDecimal = useBigDecimal;
306    }
307
308    public void setTide(boolean tide) {
309                this.tide = tide;
310        }
311        
312        public void setType(String type) {
313                this.type = type;
314        }
315        
316        
317        public long getLastModified(HttpServletRequest request, Object handler) {
318                return -1;
319        }
320        
321    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
322        amfEndpoint.service(graniteConfig, servicesConfig, servletContext, request, response);
323                return null;
324    }
325
326        public boolean supports(Object handler) {
327                return handler instanceof ServerFilter;
328        }
329}