Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
matt_steiner
Active Contributor

This blog post is the first of a series of tutorials provided as part of the SAP TechEd session Tapping into Open Source with SAP NetWeaver Cloud created by lars.karg from SAP Research and myself. It was our goal to provide something more sustainable than just a slide deck talking about the benefits of Open Source and hence we decided to create a session supplements: https://cd208.netweaver.ondemand.com

The  session consists of six individual chapters building on top of each other and together they comprise a step-by-step tutorial  demonstrating how-to use popular Open Source frameworks with SAP NetWeaver Cloud. We provide you with all the source code and detailed documentation about how we developed the supplements application itself, so that you can try it yourself!

This very first chapter talks about - who would have guessed so - getting started!

Note:  Content-wise this blog post also links back to a previous series called The Rise of Enterprise APIs (Part 2, Part 3) and as such it kind-of also represents the promised fourth and final post of that series. :wink:

Enough of the small talk, let's tackle it!

As stated in the introduction of chapter 1 in the supplements web application we'll clone a github repo(sitory) and then add some Maven dependencies to finally expose a simple service according to REST principles. For that matter, you need a few additional Eclipse plugins to work with Git and Maven respectively. I have described all you need to know in another blog post: Essentials - Working with Git & Maven in Eclipse

Once you have installed and configured these plug-ins we are ready to go...

1. Cloning the 'basecamp' repo

First thing we do is to clone the 'basecamp' github repo to our local workspace. I explained this in detail in the above mentioned blog, so no need to re-write it here.

2. Importing the project

Once, we have successfully cloned the repository, we have to import it into our Eclipse workspace. So let's switch to the "Java EE" perspective and use the "File > Import..." menu and then the "Existing Maven projects" option. Provide the link to the root directory of our cloned repo and click on Finish. Now, the project sources should be imported into your workspace. You should now see the project structure in the 'Project Explorer' view.

3. Updating the pom.xml

The most important file in a Maven-based project is the pom.xml file, which contains the so called "Project Object Model". In simple terms think of it as a "recipe" listing all the required "ingredients" (referenced Open Source projects) as well as the "cooking instructions" (build process). Please take a moment to look at the content of this file before we continue...

We'll reference a couple of Open Source projects such as Apache CXF and the famous Spring framework. In order to easily update these dependencies when new versions will be released we'll start by defining some version constants, so that we can update them with ease in a central place. This is done in the <properties /> section of the POM. So, let's add these two properties now:

<org.apache.cxf.version>2.6.1</org.apache.cxf.version>
<org.springframework.version>3.1.2.RELEASE</org.springframework.version>

As stated before, the POM is like a "ingredients" list. Sticking to this metaphor, we would need to specify the "stores" where one can get these ingredients. This is done in the <repositories> section of the POM. The standard "store" is called Maven Central and it is already contained in the POM we downloaded.

We'll get most of the commonly used projects here, but the Spring framework has it's own repository, which we'll now add:

 <repository>
<id>springsource-repo</id>
<name>SpringSource Repository</name>
<url>http://repo.springsource.org/release</url>
</repository>

The last remaining step to make our ingerdients list complete is to specify all the components(dependencies) we need. Please find the complete list to add below:

        <!-- Apache CXF -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-extension-providers</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-bundle-jaxrs</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <!--  Spring framework -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <!-- Jettison -->
        <dependency>
            <groupId>org.codehaus.jettison</groupId>
            <artifactId>jettison</artifactId>
            <version>1.3.2</version>
            <scope>runtime</scope>
            <exclusions>
                <exclusion>
                    <groupId>stax</groupId>
                    <artifactId>stax-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Once you save the POM file you'll notice that Maven will now get all the stated dependencies and download them into your local Maven repository (so that you don't have to download them over and over again!) If all went fine, your POM file should now look like this file (well, except for the artifactID!)

4. Let's code

Now that the project setup is completed let's create a simple class - UserService:

package com.sap.netweaver.cloud.samples.springrest.srv;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.springframework.stereotype.Service;
@Service("userService")
@Path("/users")
@Produces({ "application/json" })
public class UserService
{
    @Path("/user")
    @Produces("text/plain")
    @GET
    /**
     * Returns the name of the currently logged-on user.
     *
     * @param request The {@link HttpServletRequest} that is processed
     * @return the name of the currently logged-on user
     */
    public String getUserName(@Context HttpServletRequest request)
    {
        String retVal = null;
        retVal = (request.getUserPrincipal() != null) ? request.getUserPrincipal().getName() : "Guest";
        return retVal;
    }
}

As you can see it's pretty much straight-forward: we have a simple Java class (aka POJO) that contains one method, which returns the currently logged on username as a String. We see a few JAX-RS annotations. In principal we define the URL path pointing to this service via the @Papth annoations, while the annotation at method-level is added to the one on class level, hence making the complete path to access this service: "/users/user". The @GET annotation indicates that this RESTful service will be mapped to a HTTP GET operation. Please also note, that we did override the @Produces annotation at method-level, to switch from "application/json" to "text/plain" for that particular service.

There are two more things to point out here: a) we also use a Spring annotation called @Service to indicate that this is a Spring-baked Service. By giving ot a name we can make use of of a feature called component-scanning, which frees us from declaring all used beans explicitly in the Spring configuration but instead just use the stated bean name instead. (That will get more clear in just a few minutes - hang on!)

Last thing to note is that we inject the HttpServletRequest as a method parameter. This is automatically done via Spring and JAX-RS and hence we can retrieve the currently logged on user via the standard servlet API.

5. Updating the web.xml

Now, with the coding in place let's have a look at the web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
   <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/springrest-context.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
  </listener>
  <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/api/*</url-pattern>
  </servlet-mapping>
<login-config>
  <auth-method>FORM</auth-method>
</login-config>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Protected Area</web-resource-name>
    <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>Everyone</role-name>
    </auth-constraint>
</security-constraint>
<security-role>
  <description>All SAP NetWeaver Cloud users</description>
  <role-name>Everyone</role-name>
</security-role>
</web-app>

Let's quickly go through this, shall we? So lines 8-16 simply make  Spring to automatically start once the web application is started via the ContextLoaderListener based on the spring configuration declared at line no 10.

Lines 17-27 define the CFX Servlet and map it to every incoming request via the "*" asterisk (catch-all) url-pattern.

Lines 29-44 comprise the security configuration, which enforces that all users need to authenticate prior to being able to access any resource of this web application (we protected the whole app via the "*" asterisk in line 35.) All of this is well explained in the corresponding chapter (User Authentication) of our online help.

6. Spring Configuration

The last missing piece is the spring configuration file we talked about when we created the UserService class and which we referenced in the web.xml. So, let's check it out. [springrest-context.xml]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<context:component-scan base-package="com.sap.netweaver.cloud.samples.springrest.srv" />
<jaxrs:server id="restContainer" address="/v1">
<jaxrs:serviceBeans>
    <ref bean="userService" />
</jaxrs:serviceBeans>
<jaxrs:providers>
            <ref bean="jsonProvider"/>
        </jaxrs:providers>
</jaxrs:server>
<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
        <property name="ignoreNamespaces" value="true"/>
        <property name="serializeAsArray" value="true"/>
</bean>
</beans>

Line 13 shows the context component scan we briefly touched upon in step 4. Simply put, Spring scans all defined packages here and searches for a variety of annotations such as the @Service annotation we used in our UserService class. As we provided a simple name "userService" as a value to this annotation we can now reference this service (bean) within the configuration w/o explicitly defining it.

Lines 15-22 define a logical JAX-RS based server. Please note that we specify the address here: "/api/v1". This is the base url path this logical server is bound to. Hence, the url path to access the getUserName() would be "/<application-context>/api/v1/users/user".

Line 16-18 define the services exposed via this logical server; which in our examples is only the UserService.

Line 19-21 define the so-called providers. For this example we only specify a standard JSONProvider, which is responsible for converting the output of a particular operation to the specified MIME type of the method.

In line 24-27 this JSONProvider is defined and a few properties are set, which influence the rendering of JSON content. For further info please consult the excellent documentation.

7. Run the application

With that our project is ready to be deployed. Well, almost! There's one last step to be done, we need to activate the 'Project Facets' in order to be able to treat this Maven-based project just like any other 'Dynamic Web Module' in Eclipse (WTP).

For that purpose, please open up the project properties by using the context-menu of the project in the "Project Explorer" view. Select 'Properties' and then "Project Facets". Here, you need to convert the project to faceted form (This is an Eclipse-specific setting and hence - by design - not part of the Maven project nor the project repo.) Once this is done you should see a "Dynamic Web Module" Facet being active. Select it. On the right hand side you should see a "Details/Runtimes" toggle. Switch to the "Runtimes" option and select "SAP NetWeaver Cloud" as the designated runtime.

Now, you can build your project by selecting "Run as > Maven install" from the context menu of the project. Once the build is complete you can just add the web project to either the local server or a cloud server. Publish the project and start the server (if applicable.)

Note: If you use a local server, make sure you have defined at least one user by following this documentation. If you deploy to the cloud you have to use your SCN credentials to log on.

Once you're logged on, you should be able to access the 'service' via the following URL: http://<server>:<port>/<application-context>/api/v1/users/user

Wrap-up

So, that's it! Let's recap... so, in this tutorial we learned how-to clone a repo(sitory) from Github and import it into our Eclipse IDE. We learned the very basics about Maven and how to use Spring + Apache CXF to expose business functionality as RESTful services. We also touched upon the basics of securing a web application... not bad for a day, isn't it?

So, whether or not you liked this tutorial, you may want to check out chapter 2, where lars.karg explains how to build a responsive UI with Twitter Bootstrap, Backbone.JS, Handlebars.JS and some other great open source frameworks...

PS: The result of this tutorial can be downloaded here.

3 Comments