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    
021    package org.granite.spring;
022    
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    
028    import javax.servlet.ServletContext;
029    import javax.servlet.http.HttpServletRequest;
030    import javax.servlet.http.HttpServletResponse;
031    
032    import org.granite.config.GraniteConfig;
033    import org.granite.config.flex.Channel;
034    import org.granite.config.flex.Destination;
035    import org.granite.config.flex.EndPoint;
036    import org.granite.config.flex.Factory;
037    import org.granite.config.flex.Service;
038    import org.granite.config.flex.ServicesConfig;
039    import org.granite.logging.Logger;
040    import org.granite.messaging.amf.io.convert.Converter;
041    import org.granite.messaging.amf.process.AMF3MessageInterceptor;
042    import org.granite.messaging.service.ExceptionConverter;
043    import org.granite.messaging.service.security.RemotingDestinationSecurizer;
044    import org.granite.messaging.service.security.SecurityService;
045    import org.granite.messaging.service.tide.TideComponentAnnotatedWithMatcher;
046    import org.granite.messaging.service.tide.TideComponentInstanceOfMatcher;
047    import org.granite.messaging.service.tide.TideComponentNameMatcher;
048    import org.granite.messaging.service.tide.TideComponentTypeMatcher;
049    import org.granite.messaging.webapp.AMFEndpoint;
050    import org.granite.util.TypeUtil;
051    import org.granite.util.XMap;
052    import org.springframework.beans.BeansException;
053    import org.springframework.beans.factory.DisposableBean;
054    import org.springframework.beans.factory.InitializingBean;
055    import org.springframework.beans.factory.annotation.Autowired;
056    import org.springframework.context.ApplicationContext;
057    import org.springframework.context.ApplicationContextAware;
058    import org.springframework.web.context.ServletContextAware;
059    import org.springframework.web.servlet.HandlerAdapter;
060    import org.springframework.web.servlet.ModelAndView;
061    
062    
063    public 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    }