Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

In this blog I'll share with you an alternative to the SAPUI5 development on the NetWeaver platform. SAPUI5 is an interesting and innovative effort from SAP on the front of modern UI development. But at the time of writing this post, it is still in an evaluation beta stage and it seems like there is still no decision about the licensing scheme that SAP will adopt for this technology. Since NetWeaver has been able to take advantage of Java 5 and JEE 5 and especially since it has become very easy to deploy a third-party JARs with a webapp deployed on this platform, we have a multitude of free and open source alternatives available for developing modern UI in the same way SAPUI5 has been designed. I will describe a way of using a very cool JavaScript framework, Dojo, together with my favorite MVC framework, Spring, to build a simple hello world webapp which demonstrates how these technologies can be used together on NetWeaver 7.3.

The sources for this webapp can be found in the Code Exchange project dojoui.

Note: I assume that you are already at least somewhat familiar with Spring MVC and Dojo frameworks.

Download a Dojo distribution from their site. I used the latest release, 1.8.1. You'll need to create a JAR containing these packages under the path /META-INF/resources/. The idea is that we will delegate to Spring the care of serving and caching these JavaScript files from this JAR for us, instead of bundling them as is in our WAR.

You also need to get all the JARs necessary for Spring MVC setup. There many ways to do this. I use Apache Ivy for this purpose. You can create a simple Java project in NWDS, import the single build.xml file from the Ivy's site and create a simple target which will retrieve Spring's libraries and their dependencies for us from the Maven repository. Here is the target:

<target name="resolve-and-retrieve" depends="install-ivy" description="--> resolves dependencies decalred in ivy.xml file">
    <ivy:resolve file="${basedir}/ivy.xml" transitive="true"/>
    <ivy:retrieve/>
</target>

It will resolve the dependencies declared in ivy.xml file, download them and put then in lib folder. Here are the contents of ivy.xml file:

<info organisation="your.org" module="anything" />
<configurations defaultconfmapping="default->default"></configurations>
<dependencies>
    <dependency org="org.springframework" name="spring-webmvc" rev="3.1.0.RELEASE" />
    <dependency org="cglib" name="cglib" rev="2.2" />
    <dependency org="org.codehaus.jackson" name="jackson-mapper-asl" rev="1.9.10" />
</dependencies>

You might need ivysettings.xml file, as well, here it is:

<ivysettings>
    <settings defaultResolver="chain" />
    <resolvers>
        <chain name="chain">
            <ibiblio name="central" m2compatible="true"></ibiblio>
            <!-- uncomment if you want to use Spring's milestone libraries -->
            <!--
            <ibiblio name="spring-milestone" m2compatible="true"
                root="http://repo.springsource.org/milestone"></ibiblio>
            -->
        </chain>
    </resolvers>
</ivysettings>

After retrieving the libraries you should have all the JARs (together with the dojo-1.8.1.jar created above) needed to create a simple webapp. You can see all the JARs needed in the image of the webapp structure at the end of this post.

Create a simple webapp project in NWDS (tmp~dojo~web) and assign it to an EAR (tmp~dojo~ear). Copy all the JARs into /WEB-INF/lib directory of your web project, they will be deployed to the server. Now we need to set up Spring's MVC for our webapp. This requires registering Spring's Dispatcher servlet in our web.xml file. Here it is:

<servlet>
    <servlet-name>SpringMvcDispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>ch.unil.dojo.web.config.WebConfig</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMvcDispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Spring MVC can be configured using Java annotations, this is how I have done it:

/**
* Web context configuration to be processed by {@code
* AnnotationConfigWebApplicationContext} and specified as {@code
* contextConfigLocation} parameter for {@code DispatcherServlet}, see {@code
* web.xml} file.
* <p>
* {@code EnableWebMvc} annotation configures default MVC infrastructure
* including an instance of {@code MappingJacksonHttpMessageConverter} Json to
* Java converter for request handlers. {@code ComponentScan} annotation
* specifies the base package which will be scanned for the {@code Controller}
* annotated classes.
*
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.context.support.AnnotationConfigWebApplicationContext
* @see org.springframework.web.servlet.config.annotation.EnableWebMvc
* @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
*/
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "ch.unil.dojo.web")
public class WebConfig extends WebMvcConfigurerAdapter {
    // set up the default view resolver, mapping logical view name
    // to a JSP with the same file name
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
    // add handlers for the static resources (js, css, images),
    // will look up and cache Dojo files from the jar on the classpath
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/META-INF/resources/")
            .setCachePeriod(31556926);
    }
    // set up the redirection to the main view if the root URL is accessed
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }
}

Notice how we are setting up a special ResourceHandler for all the Dojo JavaScript files which will be served from the JAR on the classpath and even cached for faster subsequent accesses.

Now we can create our JavaScript font-end UI using Dojo. Create a JSP page called index.jsp in /WEB-INF/pages/ folder. The Spring will automatically use this view for all requests to the root of the web application. Here are the contents of this file:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Testing Dojo</title>
<link rel="stylesheet" type="text/css"
    href="<%=request.getContextPath()%>/resources/dijit/themes/claro/claro.css">
<script src="<%=request.getContextPath()%>/resources/dojo/dojo.js"
    data-dojo-config="async: true,
    packages: [{name: 'js', location: '<%=request.getContextPath()%>/resources/js'}],
    gfxRenderer: 'svg'"></script>
</head>
<body class="claro">
    <script type="dojo/require">at: "dojox/mvc/at"</script>
    <div data-dojo-type="dojox/mvc/Group" data-dojo-props="target: model">
        <div data-dojo-type="dijit/form/TextBox"
            data-dojo-props="value: at('rel:', 'name'), placeHolder: 'First Name',
                properCase: true, trim: true"></div>
        <div id="submitBtn" data-dojo-type="dijit/form/Button"
            data-dojo-props="label: 'Submit'"></div>
        <div id="surfaceDiv"></div>
    </div>
    <script>
    require(["js/utils", "dojo/_base/kernel", "dojo/when", "dojo/parser", "dojo/json", "dojo/Stateful",
             "dojo/on", "dojo/mouse", "dijit/registry", "dojox/gfx", "dojox/gfx/fx", "dojo/colors"],
            function(utils, kernel, when, parser, json, Stateful,
                    on, mouse, registry, gfx, gfxAnim, Color){
        //create a model
        var model = kernel.global.model = new Stateful();
        //parce the document, then connect the submit button
        when(parser.parse(), function() {
            //create SVG surface
            var surface = gfx.createSurface("surfaceDiv", 400, 100);
            registry.byId("submitBtn").on("click", function(evt){
                //make Ajax request
                when(utils.ajaxRequest("<%=request.getContextPath()%>/greet",
                        json.stringify(model)), function(data) {
                     //clear previous shape
                    surface.clear();
                    //crete text shape
                    var text = surface
                        .createText({x: 200, y: 50, text: data.greeting, align: "middle"})
                        .setFont({family: "Arial", size: "20pt", weight: "bold"})
                        .setFill(Color.named.skyblue);
                     new gfxAnim.animateTransform({
                         duration: 1500,
                         shape: text,
                         transform: [{
                             name: "rotategAt",
                             start: [0, 200, 50],
                             end: [360, 200, 50]
                         }]
                     }).play();
                });
            });
        });
    });
    </script>
</body>
</html>

This is a simple hello world webapp but with a little Dojo twist. It collects the user's name and sends to the Spring's controller in a form of an Ajax request with the name serialized as a Json string. On the cotroller's side the Json string from the body of the request is automatically converted to a POJO of the corresponding structure by the MappingJacksonHttpMessageConverter instance registered with Spring MVC by default (it's a part of EnableWebMvc configuration). Once the greeting message is generated it is stored as a value of the POJO's property and the POJO is serialized to the body of the HTTP response in the form of a Json string again automatically by the Jackson mapper. The returned Json response is parsed by Dojo which displays the greeting message as a rotating SVG text shape.

As you can see, we don't actually use a lot of JSP's functionality, it is mostly a plain HTML page with some JavaScript. However, we need a reference to the context path of the webapp which we can obtain by using a scriplet <%=request.getContextPath()%>. We also use a custom module, "js/utils", which Dojo is instructed to look up under /resources/js location. I have put this file on the classpath (src folder) under /WEB-INF/resources/js/utils.js this way the Spring's ResourceHandler servlet can pick it up as well. Actually, this is the only way that I found to reference my custom module on NetWeaver if I wanted to have it served by the Spring's resources servlet. Here are the contents of this file, it's just a simple utility to generate an asynchronous Ajax request to the server using Dojo's handy "dojo/request" module.

define(["dojo/request", "dojo/Deferred"], function(request, Deferred){
    return {
        ajaxRequest: function(path, data, sync){
             var def = new Deferred();
            request.post(path, {
                headers: {"Content-Type": "application/json"},
                data: data,
                handleAs: "json",
                sync: (sync || false)
            }).then(function(data){
                def.resolve(data);
            },
            function(error){
                def.reject(error);
            });
            return def;
        }
    };
});

The controller class which is responsible for handling the Ajax request is given below:

/**
* Controller which will handle all incoming HTTP requests. Registered in the
* MVC infrastructure via {@code ComponentScan} annotation used in the web
* context configuration class.
*/
@Controller
public class AjaxController {
    private static final Logger logger = Logger.getLogger(AjaxController.class);
    /**
     * Request handling method applicable to any POST request with a header
     * {@code Content-type: application/json} and the path {@code /greet}.
     * Spring automatically converts the Json string from the body of the
     * request via {@code MappingJacksonHttpMessageConverter} to an instance of
     * {@code Greeting} form and serializes it back to a Json string set to the
     * body of the response.
     *
     * @param form person data
     * @return greeting data
     *
     * @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
     */
    @RequestMapping(value = "/greet", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
    public @ResponseBody
    Greeting greet(@RequestBody Greeting form) {
        logger.debug("Processing Ajax request for /greet with form: " + form);
        form.setGreeting("Hello, " + form.getName() + "!");
        return form;
    }
}

As you can see Spring makes it very easy for us to work with this kind of requests and thus it is a particularly good choice for the server-side Ajax processing.

Here is an outlay of the entire webapp project in NWDS for reference:

We can recap the things that we have used in this showcase and the advantages of this architecture.

1) Dojo front-end

Dojo is an excellent JavaScript framework, it is reach and constantly growing, uses AMD module loading, has a vibrant community and it is open source. The learning curve for Dojo is probably not much steeper than the one for learning SAPUI5 considering that you would have to learn JQuery with SAPUI5 at some point. The advantage of using Dojo is that you don't need to use the NetWeaver at all. I actually developed the entire webapp in STS with a Tomcat 6. Of course you will not get the code assist for the JavaScript files as you have with SAPUI5 plugin but at least it is free.

2) Spring MVC

Spring MVC framework allows to configure a clean separation of concern, RESTfull architecture for a webapp very easily, especially when it comes to conversion to and from Json requests. It all happens behind the scenes with Jackson mapper allowing one to implement server-side logic with simple POJOs. If you like this setup you can easily imagine using Sprin's Security to make your webapp secure or to use Spring CCI with an EJB session facade to connect to an ABAP backend.