/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.flex.core.io;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.flex.config.MessageBrokerConfigProcessor;
import flex.messaging.MessageBroker;
import flex.messaging.io.PropertyProxy;
import flex.messaging.io.PropertyProxyRegistry;
/**
* Base {@link MessageBrokerConfigProcessor} implementation that installs an alternative {@link PropertyProxy} implementation for
* enhanced serialization and deserialization of specific types. The alternative implementation uses Sping's {@link ConversionService}
* to allow specialized conversion of certain property types and is more flexible in the types it can handle.
*
* <p>
* The installed {@code SpringPropertyProxy} instances may be configured to use field-based property access (instead of requiring getters and setters) by
* setting the {@link AbstractAmfConversionServiceConfigProcessor#setUseDirectFieldAccess(boolean) useDirectFieldAccess} property to {@code true}. Additionally,
* types that do not have a default no-arg constructur can be handled, as long as they provide a constructor annotated with {@link AmfCreator}.
*
* <p>
* Subclasses are expected to supply their own mechanisms for determining the list of types to register and may apply additional configuration of the
* {@code ConversionService}, such as provisioning additional custom {@link Converter Converters}.
*
* @see SpringPropertyProxy
*
* @author Jeremy Grelle
*/
public abstract class AbstractAmfConversionServiceConfigProcessor implements MessageBrokerConfigProcessor, InitializingBean {
protected final Log log = LogFactory.getLog(getClass());
private ConversionService conversionService;
private boolean useDirectFieldAccess = false;
/**
*
* {@inheritDoc}
*/
public void afterPropertiesSet() throws Exception {
this.conversionService = conversionService != null ? conversionService : getDefaultConversionService();
}
/**
*
* {@inheritDoc}
*/
public final MessageBroker processAfterStartup(MessageBroker broker) {
registerAmfProxies(this.conversionService, this.useDirectFieldAccess);
return broker;
}
/**
*
* {@inheritDoc}
*/
public final MessageBroker processBeforeStartup(MessageBroker broker) {
return broker;
}
/**
* Sets the {@link ConversionService} implementation to be used by all registered {@link SpringPropertyProxy} instances.
* @param conversionService the conversion service to be used
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* When true, configures the registered {@link SpringPropertyProxy} instances to access fields directly, rather than
* requiring strict JavaBean compliance. Defaults to false.
*
* @param useDirectFieldAccess determines whether fields should be accessed directly.
*/
public void setUseDirectFieldAccess(boolean useDirectFieldAccess) {
this.useDirectFieldAccess = useDirectFieldAccess;
}
/**
* Called during initialization, the default implementation configures and registers a {@link SpringPropertyProxy} instance
* for each type returned by {@link AbstractAmfConversionServiceConfigProcessor#findTypesToRegister() findTypesToRegister}.
* @param conversionService the conversion service to be used for property conversion
* @param useDirectFieldAccess determines whether fields should be accessed directly
*/
protected void registerAmfProxies(ConversionService conversionService, boolean useDirectFieldAccess) {
Set<Class<?>> typesToRegister = findTypesToRegister();
if (log.isInfoEnabled()) {
log.info("Types detected for AMF serialization support: "+typesToRegister.toString());
}
for (Class<?> type : typesToRegister) {
registerPropertyProxy(SpringPropertyProxy.proxyFor(type, useDirectFieldAccess, conversionService));
}
}
/**
* Registers the given {@link SpringPropertyProxy} with the BlazeDS {@link PropertyProxyRegistry}.
* @param proxy the property proxy to register
*/
protected void registerPropertyProxy(SpringPropertyProxy proxy) {
PropertyProxyRegistry.getRegistry().register(proxy.getBeanType(), proxy);
}
/**
* Returns the set of types for which {@link SpringPropertyProxy} instances should be registered.
* @return the set of types to register
*/
protected abstract Set<Class<?>> findTypesToRegister();
/**
* Template method to allow subclasses to configure their own set of {@link Converter} instances. This is a
* convenient alternative to supplying a completely custom-configured {@link ConversionService} instance.
*
* <p>
* The default implementation does not register any additional {@link Converter Converters}, thus subclasses do not need to delegate to it.
*
* @param registry - the converter registry used by the {@link ConversionService}
*/
protected void configureConverters(ConverterRegistry registry) {
//default no-op
}
private ConversionService getDefaultConversionService() {
GenericConversionService conversionService = new GenericConversionService();
configureConverters(conversionService);
conversionService.addConverter(new NumberConverter());
return conversionService;
}
}