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