Sunday, March 22, 2015

Integrating RESTful API in web layer with jQuery Mobile



Series:







We will create a simple web client using jQuery Mobile and use its search functionality.

Steps:

1.      Download jquery and jquery mobile.

2.      Create necessary folders under webcontent

3.      Create a /mobile/1.4.5/<jquery mobile files>

4.      Create a /jquery/<jquery file>

5.      Create a html file

a.      Include correct character set

b.      Make it mobile ready

c.      Include the correct js and css files

d.      Create a jquery page

e.      Create an empty

f.       Place a AJAX call to the service layer and Collect Json

g.      Parse JSON

h.      Add li to ul

6.      Test:

a.      Browse to the file

b.      Check the n/w calls (I am using firebug, use alternatives such as Fiddler in IE)

                                                    i.     Check that json request/response

                                                   ii.     (optionally) check the json parsed data

c.      Check the search functionality

Step 1: Download the jquery and jquery mobile files


Obvious.

Step 2, 3, 4: create necessary folders



Step 5: Create a html file


a.      Include correct character set

<meta charset="utf-8">

b.      Make it mobile ready

<meta name="viewport" content="width=device-width, initial-scale=1">

c.      Include the correct js and css files

 

<link rel="stylesheet"

       href="mobile/1.4.5/jquery.mobile.theme-1.4.5.min.css" />

<link rel="stylesheet"

       href="mobile/1.4.5/jquery.mobile.structure-1.4.5.min.css">

<link rel="stylesheet" href="mobile/1.4.5/jquery.mobile-1.4.5.min.css">

<script src="jquery/jquery-2.1.3.min.js"></script>

<script src="mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>

 

d.      Create a jquery page

 

       <div id="index" data-role="page" class="ui-page-theme-b">

             <header data-role="header" data-position="fixed">

                    <h3>My App Title</h3>

             </header>

             <div id="homebody" data-role="content">

 

e.      Create an empty ul

 

                    <ul id="locationid" data-role="listview" data-inset="true"

                           data-filter-placeholder="Locations..." data-filter="true"

                           data-split-theme="b" data-split-icon="plus" data-theme="b">

                    </ul>

 

f.       Place a AJAX call to the service layer and Collect Json

 

       jQuery(document).ready(function() {

             $.ajax({

                    url : "ws/xxx/",

                    dataType : "html",

                    type : 'GET',

                    data : "",

                    success : function(data) {

                           objData = jQuery.parseJSON(data);

                           for (var count=0; count < objData.data.length; count++) {

                                 $("#homebody ul").append("<li class=\"ui-li-static ui-body-inherit ui-last-child\">" +  objData.data[count].locationName + "</li>");

                           }

 

                    }

             });

       });

 

g.      Parse JSON

Check the above code snippter.

h.      Add li to ul

Check the above code snippet.

 

Step 6: Test


a.      Browse to the file

b.      Check the n/w calls (I am using firebug, use alternatives such as Fiddler in IE)

d.      Check that json request/response


e.      (optionally) check the json parsed data


c.      Check the search functionality


 

Appendix:


HTML code:

<body>

       <div id="index" data-role="page" class="ui-page-theme-b">

             <header data-role="header" data-position="fixed">

                    <h3>My App Title</h3>

             </header>

             <div id="homebody" data-role="content">

                    <ul id="locationid" data-role="listview" data-inset="true"

                           data-filter-placeholder="Locations..." data-filter="true"

                           data-split-theme="b" data-split-icon="plus" data-theme="b">

                    </ul>

             </div>

             <footer data-role="footer" data-position="fixed">

                    <h3>It's a xxx Application!!!</h3>

             </footer>

       </div>

       <!-- /page -->

</body>

Friday, March 20, 2015

Expose Service Layer via RESTful services in Spring + Unit Test with TDD


Series:






Goal:

·        We are going to build on top of Service Layer (that we covered in the previous post.)

·        We are going to expose Service Layer as RESTful APIs.

·        We are going to use TDD to develop the RESTful service functionality.

·        We are going to use Jackson which returns JSON object in the RESTful response.

 

Steps:

1.      add dependencies to pom.xml

a.      web, mvc, Jackson, selenium, etc.

2.      Amend context.xml to include MVC

3.      Amend web.xml to include RESTful/Servlet mapping, etc.

4.      Create a POJO which exposes the service layer.

a.      Accepts HTTP requests (standard HTTP verbs, POST, PUT, GET, etc.)

b.      Invokes service layer

c.      Returns response as JSON object.

5.      Create a Test class which uses Selenium to fire HTTP request with desired RESTful service with proper Verb (we will demonstrate a GET call.)

6.      Run the Test Suite

Step 1: Add dependencies


For Spring MVC + JSON related stuff + Selenium (the exact packages are mentioned below in Appendex.)

Step 2: Amend context.xml


Ensure that all relevant namespace and xsd references are included:

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd

             http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd

             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd

             http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd

             http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd

             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">

 

Scan the RESTFul web layer package:

       <context:component-scan base-package="com.mycos.dpweb.ws" />

 

Add JSON handler:

       <!-- Configures view for returning JSON to the client -->

       <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">

             <property name="contentType" value="text/plain"/>

       </bean>

       <!-- 

            maps handler methods based on HTTP paths

      -->

       <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">

             <property name="messageConverters">

                    <util:list id="beanList">

                           <ref bean="jsonMessageConverter"/>

                    </util:list>

             </property>

       </bean>

       <!-- 

            Converts JSON to POJO and vice versa 

      -->

       <bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>

       <bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver">

             <property name="prefix" value="/WEB-INF/"/>

       </bean>

Step 3: Amend web.xml


       <!-- Main configuration file for this Spring web application. -->

       <context-param>

             <param-name>contextConfigLocation</param-name>

             <param-value>/WEB-INF/classes/context.xml</param-value>

       </context-param>

       <!-- Loads the Spring web application context using the config file defined

             above. -->

       <listener>

             <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

       </listener>

       <!-- Define the Spring Dispatcher Servlet for the REST services. The 'contextConfiguration'

             param with an empty value means that the Spring Context won't try to load

             a default file called restservices-servlet.xml -->

       <servlet>

             <servlet-name>restservices</servlet-name>

             <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

             <init-param>

                    <param-name>contextConfigLocation</param-name>

                    <param-value></param-value>

             </init-param>

             <load-on-startup>1</load-on-startup>

       </servlet>

       <!-- This Servlet mapping means that this Servlet will handle all incoming

              requests -->

       <servlet-mapping>

             <servlet-name>restservices</servlet-name>

             <url-pattern>/ws/*</url-pattern>

       </servlet-mapping>

Step 4: Create POJO for RESTFul service


1.      Create a package and a Java class

2.      Annotate with @Controller

3.      Inject Service Layer Object

4.      Inject “View”

5.      Inject @RequestMapping wherever is needed.

Please note that for simplicity I have eliminated the detail steps to get to the code. But essentially you start with a blank template method, implement unit test cases, and then start adding flesh to the template.

Code:

@Controller

public class XxxConroller {

       @Autowired

       private XxxService xxxService;

       @Autowired

       private View jsonView_i;

 

       @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)

       public ModelAndView getUserID(@PathVariable("userId") String userId) {

             /* validate user Id parameter */

             if (isEmpty(userId)) {

                    String sMessage = "Error invoking getUserById - Invalid user Id parameter";

                    return createErrorResponse(sMessage);

             }

             try {

                    return new ModelAndView(jsonView_i, DATA_FIELD, this.xxxService.getUser(userId));

             } catch (Exception e) {

                    String sMessage = "Error invoking getAllLocations. [%1$s]";

                    return createErrorResponse(String.format(sMessage, e.toString()));

             }

       }

 

Step 5: Create Test Class.


1.      Create a package and a class.

public class XxxRESTAPITest extends XxxDBUnitTest{ //extend for the DBUnitTest classes created in the previous post

Create a properties file with expected value. Test case is going to use this value to compare with the json object returned by the RESTful API call. (       ClassPathResource locationTblContent = new ClassPathResource("/webtest/webtest.properties");

2.      Handle JSON object.

·        You can store the entire json object (john_expected_data={"data"\:{"userId"\:"john","isxxx"\:"Y"}})

or

·        You can store only the relevant value and parse the JSON object from the invoked RESTful call to assert a success or unsuccessful call.

 

       @Test

       public void testgetUserByIDRESTAPI() throws Exception {

             init();

             this.testHTML("user/john/", this.prop.getProperty("john_expected_data"));

       }

             StringBuilder strBldr = new StringBuilder();

             strBldr.append(prop.get("url"));

             strBldr.append(uri);

        WebDriver driver = new FirefoxDriver();

        driver.get(strBldr.toString());

        log.debug(driver.findElement(By.tagName("body")).getText());

        assertEquals(expectedValue, driver.findElement(By.tagName("body")).getText());

        driver.close();

        driver.quit();

 

Appendix:


Pom.xml (relevant jar   files)

             <dependency>

                    <groupId>org.springframework</groupId>

                    <artifactId>spring-aop</artifactId>

                    <version>${spring.version}</version>

             </dependency>

             <dependency>

                    <groupId>org.springframework</groupId>

                    <artifactId>spring-aspects</artifactId>

                    <version>${spring.version}</version>

             </dependency>

             <dependency>

                    <groupId>org.springframework</groupId>

                    <artifactId>spring-beans</artifactId>

                    <version>${spring.version}</version>

             </dependency>

             <dependency>

                    <groupId>org.springframework</groupId>

                    <artifactId>spring-expression</artifactId>

                    <version>${spring.version}</version>

             </dependency>

             <dependency>

                    <groupId>org.springframework</groupId>

                    <artifactId>spring-web</artifactId>

                    <version>${spring.version}</version>

             </dependency>

             <dependency>

                    <groupId>org.springframework</groupId>

                    <artifactId>spring-webmvc</artifactId>

                    <version>${spring.version}</version>

             </dependency>

 

             <!-- for Spring WEB/VMC/REST -->

             <dependency>

                    <groupId>org.codehaus.jackson</groupId>

                    <artifactId>jackson-mapper-asl</artifactId>

                    <version>${jackson.mapper.version}</version>

             </dependency>

             <dependency>

                    <groupId>com.fasterxml.jackson.core</groupId>

                    <artifactId>jackson-core</artifactId>

                    <version>${jackson.fasterxml.version}</version>

             </dependency>

             <dependency>

                    <groupId>com.fasterxml.jackson.core</groupId>

                    <artifactId>jackson-databind</artifactId>

                    <version>${jackson.fasterxml.version}</version>

             </dependency>

 

             <!-- for Selenium; testing HTTP request/response -->

             <dependency>

                    <groupId>org.seleniumhq.selenium</groupId>

                    <artifactId>selenium-java</artifactId>

                    <version>${selecnium.version}</version>

             </dependency>

             <dependency>

                    <groupId>xml-apis</groupId>

                    <artifactId>xml-apis</artifactId>

                    <version>${xml-api.version}</version>

             </dependency>