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