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.spring;
022
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import javax.servlet.ServletContext;
029import javax.servlet.http.HttpServletRequest;
030import javax.servlet.http.HttpServletResponse;
031
032import org.granite.config.GraniteConfig;
033import org.granite.config.flex.Channel;
034import org.granite.config.flex.Destination;
035import org.granite.config.flex.EndPoint;
036import org.granite.config.flex.Factory;
037import org.granite.config.flex.Service;
038import org.granite.config.flex.ServicesConfig;
039import org.granite.logging.Logger;
040import org.granite.messaging.amf.io.convert.Converter;
041import org.granite.messaging.amf.process.AMF3MessageInterceptor;
042import org.granite.messaging.service.ExceptionConverter;
043import org.granite.messaging.service.security.RemotingDestinationSecurizer;
044import org.granite.messaging.service.security.SecurityService;
045import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
046import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
047import org.granite.messaging.service.tide.TideComponentNameMatcher;
048import org.granite.messaging.service.tide.TideComponentTypeMatcher;
049import org.granite.messaging.webapp.AMFEndpoint;
050import org.granite.util.TypeUtil;
051import org.granite.util.XMap;
052import org.springframework.beans.BeansException;
053import org.springframework.beans.factory.DisposableBean;
054import org.springframework.beans.factory.InitializingBean;
055import org.springframework.beans.factory.annotation.Autowired;
056import org.springframework.context.ApplicationContext;
057import org.springframework.context.ApplicationContextAware;
058import org.springframework.web.context.ServletContextAware;
059import org.springframework.web.servlet.HandlerAdapter;
060import org.springframework.web.servlet.ModelAndView;
061
062
063public class ServerFilter implements InitializingBean, DisposableBean, ApplicationContextAware, ServletContextAware, HandlerAdapter {
064        
065    private static final Logger log = Logger.getLogger(ServerFilter.class);
066        
067    @Autowired(required=false)
068    private ServletContext servletContext = null;
069    private ApplicationContext context = null;
070    
071    private GraniteConfig graniteConfig = null;
072    private ServicesConfig servicesConfig = null;
073    
074    private List<String> tideRoles = null;
075    private List<String> tideAnnotations = null;
076    private List<String> tideInterfaces = null;
077    private List<String> tideNames = null;
078    private List<String> tideTypes = null;
079    private List<Class<? extends ExceptionConverter>> exceptionConverters = null;
080    private AMF3MessageInterceptor amf3MessageInterceptor;
081    private boolean tide = false;
082    private String type = "server";
083    
084    private AMFEndpoint amfEndpoint = null;
085
086        public void setApplicationContext(ApplicationContext context) throws BeansException {
087                this.context = context;
088        }
089    
090        public void setServletContext(ServletContext servletContext) throws BeansException {
091                this.servletContext = servletContext;
092        }
093    
094        public void afterPropertiesSet() {
095                SpringGraniteConfig springGraniteConfig = context.getBeansOfType(SpringGraniteConfig.class).values().iterator().next();
096                
097        this.graniteConfig = springGraniteConfig.getGraniteConfig();
098        
099        Map<String, SecurityService> securityServices = context.getBeansOfType(SecurityService.class);
100        if (securityServices.size() > 1)
101                log.error("More than one Security Service bean defined");
102        else if (!securityServices.isEmpty()) {
103                log.debug("Security Service bean " + securityServices.keySet().iterator().next() + " selected");
104                SecurityService securityService = securityServices.values().iterator().next();
105                this.graniteConfig.setSecurityService(securityService);
106        }
107        
108        if (tideAnnotations != null) {
109                for (String ta : tideAnnotations) {
110                        try {
111                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentAnnotatedWithMatcher(ta, false));
112                                log.debug("Enabled components annotated with %s for Tide remoting", ta);
113                        }
114                        catch (Exception e) {
115                                log.error(e, "Could not add tide-component annotation %s", ta);
116                        }
117                }
118        }
119        if (tideInterfaces != null) {
120                for (String ti : tideInterfaces) {
121                        try {
122                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentInstanceOfMatcher(ti, false));
123                                log.debug("Enabled components extending %s for Tide remoting", ti);
124                        }
125                        catch (Exception e) {
126                                log.error(e, "Could not add tide-component interface %s", ti);
127                        }
128                }
129        }
130        if (tideNames != null) {
131                for (String tn : tideNames) {
132                        try {
133                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentNameMatcher(tn, false));
134                                log.debug("Enabled components named like %s for Tide remoting", tn);
135                        }
136                        catch (Exception e) {
137                                log.error(e, "Could not add tide-component name %s", tn);
138                        }
139                }
140        }
141        if (tideTypes != null) {
142                for (String tt : tideTypes) {
143                        try {
144                                this.graniteConfig.getTideComponentMatchers().add(new TideComponentTypeMatcher(tt, false));
145                                log.debug("Enabled components with type %s for Tide remoting", tt);
146                        }
147                        catch (Exception e) {
148                                log.error(e, "Could not add tide-component type %s", tt);
149                        }
150                }
151        }
152        if (exceptionConverters != null) {
153                for (Class<? extends ExceptionConverter> ec : exceptionConverters) {
154                        this.graniteConfig.registerExceptionConverter(ec);
155                        log.debug("Registered exception converter %s", ec);
156                }
157        }
158        if (amf3MessageInterceptor != null)
159                this.graniteConfig.setAmf3MessageInterceptor(amf3MessageInterceptor);
160        
161        // If Spring Data available, automatically enable Page/Pageable converter
162        try {
163                TypeUtil.forName("org.springframework.data.domain.Page");
164                Class<Converter> converterClass = TypeUtil.forName("org.granite.spring.data.PageableConverter", Converter.class);
165                this.graniteConfig.getConverters().addConverter(converterClass);
166        }
167        catch (Exception e) {
168                // Spring Data not present, ignore
169        }
170        
171        servicesConfig = springGraniteConfig.getServicesConfig();
172        
173        Channel channel = servicesConfig.findChannelById("graniteamf");
174        if (channel == null) {
175                channel = new Channel("graniteamf", "mx.messaging.channels.AMFChannel", 
176                        new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint"), 
177                        new XMap());
178                servicesConfig.addChannel(channel);
179        }
180        
181        if (tide) {
182                Factory factory = servicesConfig.findFactoryById("tide-spring-factory");
183                if (factory == null) {
184                        factory = new Factory("tide-spring-factory", "org.granite.tide.spring.SpringServiceFactory", new XMap());
185                servicesConfig.addFactory(factory);
186                }
187                
188                Service service = servicesConfig.findServiceById("granite-service");
189                if (service == null) {
190                        service = new Service("granite-service", "flex.messaging.services.RemotingService", 
191                                "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
192                }
193                Destination destination = servicesConfig.findDestinationById("flex.messaging.messages.RemotingMessage", type);
194                if (destination == null) {
195                        List<String> channelIds = new ArrayList<String>();
196                        channelIds.add("graniteamf");
197                        destination = new Destination(type, channelIds, new XMap(), tideRoles, null, null);
198                        destination.getProperties().put("factory", factory.getId());
199                        destination.getProperties().put("validator-name", "tideValidator");
200                        service.getDestinations().put(destination.getId(), destination);
201                        servicesConfig.addService(service);
202                }
203                
204                if (destination.getSecurizer() == null) {
205                Map<String, RemotingDestinationSecurizer> securizers = context.getBeansOfType(RemotingDestinationSecurizer.class);
206                if (securizers.size() > 1)
207                        log.error("More than one Remoting Destination Securizer bean defined");
208                else if (!securizers.isEmpty()) {
209                        log.debug("Remoting Destination Securizer bean " + securizers.keySet().iterator().next() + " selected");
210                        RemotingDestinationSecurizer securizer = securizers.values().iterator().next();
211                        destination.setSecurizer(securizer);
212                }
213                }
214                
215                log.info("Registered Tide/Spring service factory and destination %s", type);
216        }
217        else {
218                Factory factory = servicesConfig.findFactoryById("spring-factory");
219                if (factory == null) {
220                        factory = new Factory("spring-factory", "org.granite.spring.SpringServiceFactory", new XMap());
221                        servicesConfig.addFactory(factory);
222                }
223                
224                Service service = servicesConfig.findServiceById("granite-service");
225                if (service == null) {
226                        service = new Service("granite-service", "flex.messaging.services.RemotingService", 
227                                "flex.messaging.messages.RemotingMessage", null, null, new HashMap<String, Destination>());
228                        servicesConfig.addService(service);
229                }
230            
231            servicesConfig.scan(null);
232                
233                log.info("Registered Spring service factory");
234        }
235        
236        amfEndpoint = new AMFEndpoint();
237        amfEndpoint.init(servletContext);
238        }
239        
240        public void destroy() throws Exception {
241                amfEndpoint.destroy();
242                amfEndpoint = null;
243        }
244
245        public void setTideRoles(List<String> tideRoles) {
246                this.tideRoles = tideRoles;
247        }
248        
249        public void setTideAnnotations(List<String> tideAnnotations) {
250                this.tideAnnotations = tideAnnotations;
251        }
252        
253        public void setTideInterfaces(List<String> tideInterfaces) {
254                this.tideInterfaces = tideInterfaces;
255        }
256        
257        public void setTideNames(List<String> tideNames) {
258                this.tideNames = tideNames;
259        }
260        
261        public void setTideTypes(List<String> tideTypes) {
262                this.tideTypes = tideTypes;
263        }
264        
265        public void setExceptionConverters(List<Class<? extends ExceptionConverter>> exceptionConverters) {
266                this.exceptionConverters = exceptionConverters;
267        }
268        
269        public void setAmf3MessageInterceptor(AMF3MessageInterceptor amf3MessageInterceptor) {
270                this.amf3MessageInterceptor = amf3MessageInterceptor;
271        }
272        
273        public void setTide(boolean tide) {
274                this.tide = tide;
275        }
276        
277        public void setType(String type) {
278                this.type = type;
279        }
280        
281        
282        public long getLastModified(HttpServletRequest request, Object handler) {
283                return -1;
284        }
285        
286    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
287        amfEndpoint.service(graniteConfig, servicesConfig, servletContext, request, response);
288                return null;
289    }
290
291        public boolean supports(Object handler) {
292                return handler instanceof ServerFilter;
293        }
294}