|
||||||
Flex remoting without configuring the clientFor a while I am experimenting with flex. I wrote quite some posts about flex and security and I started writing about the Springframework BlazeDS Integration project. One thing that I do not really like about the configuration is the way to configure the remoting. All the hassle with the service-config.xml that needs to be available on the client as well as on the server. Not really nice. Using the maven way of creating a jar with these config files and unzipping this jar into the web server project as well as the client flex project is a way. Still, not ideal when developing in your ide when you want to add a new remote service. What is the arternative? On the serverside, the mentioned spring project is doing a good job. You do not have to configure all endpoints. But you still need something on the client. This post talks about a mechanism to enable you to loose all this configuration on the client
An overviewSo what am I planning to do. Using the Does that sound complicated? Well, read on and I’ll explain it step by step. The server sideAs mentioned in the introduction, we use the special spring BlazeDS integration project on the server. I am not going to discuss the configuration anymore. Read my previous post Wow springframework enters the actionscript and flex domain to learn how to do that. For this post I’ll focus on the differences. So what do we need? We need the server to respond to the url:
with the result: host=localhost port=8080 context-root=books-web And of course the result should be different when running on another server. web.xmlWe add a servlet mapping for a *.properties
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>*.properties</url-pattern>
</servlet-mapping>
spring-web.xmlWe need to add a url mapping, and we have to add a mapping for the
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/config.properties=configPropertyController
/*=mySpringManagedMessageBroker
</value>
</property>
</bean>
Next step is to configure the controller and a default view resolver. The view resolver uses a resource bundle to connect the views and the classes that generate content.
<bean id="configPropertyController" class="nl.gridshore.samples.books.web.controller.ConfigPropertyController"/>
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
<property name="order" value="1"/>
</bean>
The last thing to mention for the spring configuration took me a moment to figure out. Usually when creating a spring application, by default a <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> PropertyControllerThe classes I added are pretty straightforward. I created a super class called
public abstract class PropertyController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, String> exposedParams = createExposedParamsMap(request);
return new ModelAndView("propertiesView", "exposedParams", exposedParams);
}
abstract protected Map<String, String> createExposedParamsMap(HttpServletRequest request);
}
ConfigPropertyControllerFor our case, we mapped the config.properties to the class
public class ConfigPropertyController extends PropertyController {
protected Map<String, String> createExposedParamsMap(HttpServletRequest request) {
Map<String, String> exposedParams = new HashMap<String, String>();
exposedParams.put("host", request.getServerName());
exposedParams.put("port", String.valueOf(request.getServerPort()));
// The context root path contains a prefix '/', we have to take that of.
String contextRoot = request.getContextPath();
contextRoot = contextRoot.substring(1);
exposedParams.put("context-root", contextRoot);
return exposedParams;
}
}
The last piece of the puzzle is the
public class PropertyView extends AbstractView {
public PropertyView() {
setContentType("text/plain");
}
protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String,String> exposedParams = (Map<String,String>)model.get("exposedParams");
Set<Map.Entry<String,String>> entries = exposedParams.entrySet();
response.setContentType(getContentType());
for(Map.Entry<String,String> entry : entries) {
StringBuilder lineBuilder = new StringBuilder();
lineBuilder.append(entry.getKey());
lineBuilder.append("=");
lineBuilder.append(entry.getValue());
response.getWriter().println(lineBuilder.toString());
}
}
}
Now you can start up the server and test the implementation. Than we continue with the flex client The ClientOn the client side I use Mate, I do not think this changes the implementation of this strategy a lot. Another thing you’ll need is a copy of the spring-ActionScript project. Sorry no maven yet. Get a copy from the site and build it. http://forum.springsource.org/showthread.php?t=66193 You’ll need this library for easy access to a nice way of loading property files from the server. The next block of code shows some functions that reside in you main flex application file.
[Bindable]
public var properties:Config = new Config();
private var loadedProps:Properties = new Properties();
public function initApp():void {
loadedProps.addEventListener(Event.COMPLETE, onLoaderComplete);
loadedProps.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
loadedProps.load("config.properties");
}
private function onLoaderComplete(event:Event):void {
properties.host = loadedProps.getProperty("host");
properties.port = loadedProps.getProperty("port");
properties.webcontext = loadedProps.getProperty("context-root");
}
First we initiate a load of the file we used in the server side part called config.properties. Responding to the complete event, we set the properties of the
In the EventMap we have to add the property called props of type Config. Using this object we can configure the remote obejcts like this.
<mx:RemoteObject id="booksService"
endpoint="http://{props.host}:{props.port}/{props.webcontext}/messagebroker/amf"
destination="remoteBookManager"/>
That is about it, now the client knows how to obtain the server config data and with this information it can access the endpoints. If you want to have a look at the code or try it yourself, have a look at the following project in google code. http://code.google.com/p/gridshore/source/browse/#svn/trunk/books-overview Look for the books-flex-mate module to check the current progress of the new implementation. It is by far from finished. As of now you can only login using user/user and logout again. Hope this can help you program your flex better, or at least have some inspiration to improve your (or mine) code. Comments are welcome 5 comments to Flex remoting without configuring the client |
||||||
|
Copyright © 2010 Gridshore - All Rights Reserved |
||||||
Hi again,
in some cases the context path might be not available. This causes an exception in the ConfigPropertyController. To prevent this use following code:
—
public class ConfigPropertyController extends PropertyController {
protected Map createExposedParamsMap(HttpServletRequest request) {
Map exposedParams = new HashMap();
exposedParams.put(“host”, request.getServerName());
exposedParams.put(“port”, String.valueOf(request.getServerPort()));
// The context root path contains a prefix ‘/’, we have to take that of.
String contextRoot = request.getContextPath();
if (contextRoot == null || contextRoot.isEmpty()) {
contextRoot = “”;
} else {
contextRoot = contextRoot.substring(1);
}
exposedParams.put(“context-root”, contextRoot);
return exposedParams;
}
}
I am sorry, this exception (though the missing file name is called config.properties) has NOTHING to do with your example…unfortunately there was another (missing) config.properties file in the project
Hi jettro,
I think I did the same like you, but when starting the server following exception occurs:
–
org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [config.properties] cannot be opened because it does not exist
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:78)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:553)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:527)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:362)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3827)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4334)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:825)
at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:714)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:490)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:566)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by: java.io.FileNotFoundException: class path resource [config.properties] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:143)
at org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:182)
at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:161)
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:69)
… 30 more
Any idea? Thanks!
Thanks for the code. I was looking for a way to load server-side properties from Mate. I have to ask this though : What happens if the remote object is called before the properties file has finished loading ? Actually is there a risk of that happening ? If yes, how would you prevent that ? Thank you.
Of course that is a risk, I make sure that I only start my application when I have the properties. Using events this is not really hard.