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 }