Monday, June 2, 2014

REST API development with NodeJS and Mongo DB

Brief description about NodeJS:
Node.js is a platform built on Chrome's JavaScript runtime for easily building fast,
scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.


So lets start with our main topic, without discussing more about other things.
Developing Rest APIs with node is also having one or more node module available in the market like expressJS and restify.js. Lots of pros and cons are also available on net.

For me everything has its own pros & cons, so i am using Restify.js here. As i find it easy.
So install NodeJS & MongoDb in your operating system and register it globally then install the required module.
1: MongoDB.
2: RestifyJS.
3: MongoJS.

MongoDB can be downloaded from http://www.mongodb.org/ and after installing nodeJS which can downloaded from http://nodejs.org/, you can instal restify and mongojs easily.

Commands to install both one by one.
1: npm install restify
2:npm install mongojs

Now create a directory and go inside that directory, within that directory create a package.json file.
This package.json file will be used when you package your project for distribution.
To auto generate package.json type in terminal "npm init" (Windows user first register node and npm in environment variable to access it globally.)

Command and its uses:
"npm init"

My Demo App Details: $ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sane defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install --save` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. name: (app) HelloRestAPI version: (0.0.0) 1.0.0 description: This is my first rest API project entry point: (index.js) app.js test command: node app.js git repository: https://github.com/ashishnigam/Team_Dev_Work/tree/master/Servers/nodeJS/myapp keywords: Rest,REST, REST APIs, API, APIs, Rest API, Node API, NPM. author: Ashish Nigam license: (BSD-2-Clause) Copyright (c) 2014, Ashish Nigam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. About to write to /Users/ashish.nigam/Documents/NodeHTTP_Server/app/package.json: {   "name": "HelloRestAPI",   "version": "1.0.0",   "description": "This is my first rest API project",   "main": "app.js",   "scripts": {     "test": "node app.js"   },   "repository": {     "type": "git",     "url": "https://github.com/ashishnigam/Team_Dev_Work/tree/master/Servers/nodeJS/myapp"   },   "keywords": [     "Rest",     "REST",     "REST",     "APIs",     "API",     "APIs",     "Rest",     "API",     "Node",     "API",     "NPM."   ],   "author": "Ashish Nigam",   "license": "Copyright (c) 2014, Ashish Nigam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." } Is this ok? (yes) yes

After this, your package.json is generated and you can check the content of package.json by opening in any text editor.

After that to install the dependencies and add it to package.json, execute the below mentioned command in the terminal (within same directory in terminal).


$ npm install restify --save
$ npm install mongojs --save

Similarly you can install any dependency and add it package.json. a dependency key will be added automatically once you use the above mentioned command.

My package.json file generated:

{
  "name": "HelloRestAPI",
  "version": "1.0.0",
  "description": "This is my first rest API project",
  "main": "app.js",
  "scripts": {
    "test": "node app.js"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/ashishnigam/Team_Dev_Work/tree/master/Servers/nodeJS/myapp"
  },
  "keywords": [
    "Rest",
    "REST",
    "REST",
    "APIs",
    "API",
    "APIs",
    "Rest",
    "API",
    "Node",
    "API",
    "NPM."
  ],
  "author": "Ashish Nigam",
  "license": "Copyright (c) 2014, Ashish Nigam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
  "dependencies": {
    "mongojs": "~0.13.0",
    "restify": "~2.8.1"
  }
}
See the dependency section, which i made it bold to highlight.

Now create a app.js file in the same directory and open it in your favourite text editor.

Require both the dependency in your app.js at the top. copy paste to write it at the top of your app.js

var restify = require('restify'); 
var mongojs = require("mongojs"); 
The two lines show above load the restify and mongojs modules using the require function and assign them to variables.
Now create a new server using restify API:
var ip_addr = '127.0.0.1';
var port    =  '8080';
 
var server = restify.createServer({
    name : "myapp"
});
 
server.listen(port ,ip_addr, function(){
    console.log('%s listening at %s ', server.name , server.url);
});
The code shown above creates a new server. The createServer() function takes an options object. We passed myapp as the name of the server in options object. You can view the full list of options in the documentation. After create the server instance, we call the listen function passing port, ip address, and a callback function.

Run the application by typing the following command.

$ node app.js
You will see following on the command line terminal.
myapp listening at http://127.0.0.1:8080

Configure Plugins

The restify module has a lot of built in plugins which we can use. Copy and paste the following in app.js. These should be added before the server.listen() function. Refer to documentation to learn about all the supported plugins.
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(restify.CORS());

The three lines shown above :
  1. The restify.queryParser() plugin is used to parse the HTTP query string (i.e., /jobs?skills=java,mysql). The parsed content will always be available in req.query.
  2. The restify.bodyParser() takes care of turning your request data into a JavaScript object on the server automatically.
  3. The restify.CORS() configures CORS support in the application.

Configure MongoDB

Before adding the routes, let's add code to connect to myapp to the MongoDB database.
var connection_string = '127.0.0.1:27017/myapp';
var db = mongojs(connection_string, ['myapp']);
var jobs = db.collection("jobs");
In the code shown above, we connect to local MongoDB instance. Next, we get the jobs collection using database object.

Writing CRUD API

Now, we have the server and database part ready. We still need routes to define the behaviour of the API. Copy and paste the following code to app.js.
var PATH = '/jobs'
server.get({path : PATH , version : '0.0.1'} , findAllJobs);
server.get({path : PATH +'/:jobId' , version : '0.0.1'} , findJob);
server.post({path : PATH , version: '0.0.1'} ,postNewJob);
server.del({path : PATH +'/:jobId' , version: '0.0.1'} ,deleteJob);
The code shown above does the following:
  1. When a user makes a GET request to '/jobs', then findAllJobs callback will be invoked. The another interesting part is the use of versioned routes. A client can specify the version using Accept-Version header.
  2. When a user makes a GET request to '/jobs/123', then findJob callback will be invoked.
  3. When a user makes POST request to '/jobs', then postNewJob callback will be invoked.
  4. When a user makes DELETE request to '/jobs/123', then postNewJob callback will be invoked.
Now we will write the callbacks. Copy and paste the following to app.js.
function findAllJobs(req, res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    jobs.find().limit(20).sort({postedOn : -1} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(200 , success);
            return next();
        }else{
            return next(err);
        }
 
    });
 
}
 
function findJob(req, res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    jobs.findOne({_id:mongojs.ObjectId(req.params.jobId)} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(200 , success);
            return next();
        }
        return next(err);
    })
}
 
function postNewJob(req , res , next){
    var job = {};
    job.title = req.params.title;
    job.description = req.params.description;
    job.location = req.params.location;
    job.postedOn = new Date();
 
    res.setHeader('Access-Control-Allow-Origin','*');
 
    jobs.save(job , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(201 , job);
            return next();
        }else{
            return next(err);
        }
    });
}
 
function deleteJob(req , res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    jobs.remove({_id:mongojs.ObjectId(req.params.jobId)} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(204);
            return next();      
        } else{
            return next(err);
        }
    })
 
}
The code shown above is self explanatory. We are using mongojs API to perform CRUD operations.
We can test the web services using curl. To create a new job, type the command shown below.
$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"NodeJS Developer Required" , "description":"NodeJS Developer Required" , "location":"Sector 30, Gurgaon, India"}' http://127.0.0.1:8080/jobs
To find all the jobs
$ curl -is http://127.0.0.1:8080/jobs
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Content-Length: 187
Date: Sun, 24 Nov 2013 16:17:27 GMT
Connection: keep-alive
 
[{"title":"NodeJS Developer Required","description":"NodeJS Developer Required","location":"Sector 30, Gurgaon, India","postedOn":"2013-11-24T16:16:16.688Z","_id":"52922650aab6107320000001"}]
Complete app.js 
// Below shared app.js is prepared for uploading same project in open shift (RedHat directory).
// Will be sharing details of how to upload on open shift in my next blog.
#!/bin/env node
//  OpenShift sample Node application
var restify = require('restify');
var mongojs = require("mongojs");

var ip_addr = process.env.OPENSHIFT_NODEJS_IP   || '127.0.0.1';
var port    = process.env.OPENSHIFT_NODEJS_PORT || '8080';

var db_name = process.env.OPENSHIFT_APP_NAME || "localjobs";

var connection_string = '127.0.0.1:27017/' + db_name;
// if OPENSHIFT env variables are present, use the available connection info:
if(process.env.OPENSHIFT_MONGODB_DB_PASSWORD){
  connection_string = process.env.OPENSHIFT_MONGODB_DB_USERNAME + ":" +
  process.env.OPENSHIFT_MONGODB_DB_PASSWORD + "@" +
  process.env.OPENSHIFT_MONGODB_DB_HOST + ':' +
  process.env.OPENSHIFT_MONGODB_DB_PORT + '/' +
  process.env.OPENSHIFT_APP_NAME;
}

var db = mongojs(connection_string, [db_name]);
var jobs = db.collection("jobs");

var items = db.collection("items");

var server = restify.createServer({
    name : "localjobs"
});

server.pre(restify.pre.userAgentConnection());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(restify.CORS());

function findAllJobs(req, res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    jobs.find().limit(20).sort({postedOn : -1} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(200 , success);
            return next();
        }else{
            return next(err);
        }
        
    });
    
}

function findJob(req, res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    jobs.findOne({_id:mongojs.ObjectId(req.params.jobId)} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(200 , success);
            return next();
        }
        return next(err);
    })
}

function findItem(req, res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    items.findOne({_id:mongojs.ObjectId(req.params.itemId)} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(200 , success);
            return next();
        }
        return next(err);
    })
}

function postNewItem(req , res , next){
    var item = {};
    item.title = req.params.title;
    item.description = req.params.description;
    item.value = req.params.value;
    item.postedOn = new Date();

    res.setHeader('Access-Control-Allow-Origin','*');
    
    items.save(item , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(201 , item);
            return next();
        }else{
            return next(err);
        }
    });
}

function postNewJob(req , res , next){
    var job = {};
    job.title = req.params.title;
    job.description = req.params.description;
    job.location = req.params.location;
    job.postedOn = new Date();

    res.setHeader('Access-Control-Allow-Origin','*');
    
    jobs.save(job , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(201 , job);
            return next();
        }else{
            return next(err);
        }
    });
}

function deleteJob(req , res , next){
    res.setHeader('Access-Control-Allow-Origin','*');
    jobs.remove({_id:mongojs.ObjectId(req.params.jobId)} , function(err , success){
        console.log('Response success '+success);
        console.log('Response error '+err);
        if(success){
            res.send(204);
            return next();      
        } else{
            return next(err);
        }
    })
    
}

var PATH2 = '/items';

server.get({path : PATH2 +'/:itemId' , version : '0.0.1'} , findItem);
server.post({path : PATH2 , version: '0.0.1'} ,postNewItem);

var PATH = '/jobs';

server.get({path : PATH , version : '0.0.1'} , findAllJobs);
server.get({path : PATH +'/:jobId' , version : '0.0.1'} , findJob);
server.post({path : PATH , version: '0.0.1'} ,postNewJob);
server.del({path : PATH +'/:jobId' , version: '0.0.1'} ,deleteJob);


server.listen(port ,ip_addr, function(){
    console.log('%s listening at %s ', server.name , server.url);
});
Test the application with POST and get request.
Read steps below to start both DB and node server first.
Start MongoDB database server first, for this you have to go to your MongoDB bin directory and start(execute) mongod executable.
on MAC machine, command will work like this.
ashish-mac:bin ashish.nigam$ sudo ./mongod
// I used sudo to provide admin access, it depends upon your directory on which mongod resides.

Now you have to start the node server which we created just now, open another terminal window
and from there type in following command (move to project directory).

$ node app.js 
Now you are ready to make post and get request. so open another window and use below mentioned post request.
POST Request:
$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"NodeJS Developer Required" , "description":"NodeJS Developer Required ashish nigam" , "location":"Sector 30, Gurgaon, India"}' http://127.0.0.1:8080/jobs
GET Request: (Open your browser and use below mentioned IP and get request for all jobs)
http://127.0.0.1:8080/jobs
My Chrome browser image with get request:




Get a specific JOB POST:
http://127.0.0.1:8080/jobs/538c42d9a24b3e1b094e1aa6
Part which is common : http://127.0.0.1:8080/jobs while job id added after jobs.
538c42d9a24b3e1b094e1aa6        is JOB id in my sample request.
To find the get & post request path refer the following code section in app.js
var PATH2 = '/items';

server.get({path : PATH2 +'/:itemId' , version : '0.0.1'} , findItem);

server.post({path : PATH2 , version: '0.0.1'} ,postNewItem);



var PATH = '/jobs';

server.get({path : PATH , version : '0.0.1'} , findAllJobs);

server.get({path : PATH +'/:jobId' , version : '0.0.1'} , findJob);

server.post({path : PATH , version: '0.0.1'} ,postNewJob);

server.del({path : PATH +'/:jobId' , version: '0.0.1'} ,deleteJob);
Thank you for reading my BLOG.
Please share your feedback and any query regarding this blog are most welcome.

Reference Link:

Friday, May 30, 2014

REST API development with JAVA

As we all know java is a powerful programming language. It offers almost everything for web development and REST API development.

There are many ways through which you can develop REST API, but here i am going to share knowledge about most common and easy to use jersey web service APIs.

Jersey is a framework specially designed to develop REST APIs by Oracle.
In this tutorial i am using jersey 2.0 API, which is the latest release of jersey. While there are some API level changes from jersey 1.0 to 2.0 which offers some more functionality in 2.0 but a major change which blocks all the beginner to start their 1st REST API project is the web.xml changes.

Basically jersey servlet container class has been changed in 2.0. In 1.0 jersey jars the servlet container
class was "com.sun.jersey.spi.container.servlet.ServletContainer" while in 2.0 it has been changed to
"org.glassfish.jersey.servlet.ServletContainer".

Many tutorial available online are old enough and generally uses 1.0 jersey version which creates confusion for the new comer.

so if you are using jersey 1.0 jars then your web.xml should look like this(Basic configuration):
"web.xml if using jersey jars version 1.0"
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">
  <display-name>com.ashish.nigam.lovechat</display-name>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.ashish.nigam.lovechat</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app> 
and if you are using jersey 2.0 jars then your web.xml should look like below(Basic configuration):
"Web.xml" when using jersey jars version 2.0"
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">
  <display-name>lovechat</display-name>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>  
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>com.nigam.ashish.lovechat</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet> 
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app> 

So lets start with step by step guide to write your first REST API:

1: Create a Dynamic Web Project.
2: Copy all the downloaded jersey jars in WEB-INF folder inside WebContent folder.
3: Edit web.xml file as described above.
4: Go inside your package directory and open .java file, edit it with most common code shared below.
package com.nigam.ashish.lovechat; // replace it with your package name import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; // Plain old Java Object it does not extend as class or implements // an interface // The class registers its methods for the HTTP GET request using the @GET annotation. // Using the @Produces annotation, it defines that it can deliver several MIME types, // text, XML and HTML. // The browser requests per default the HTML MIME type. //Sets the path to base URL + /hello @Path("/hello") public class Hello { // This method is called if TEXT_PLAIN is request @GET @Produces(MediaType.TEXT_PLAIN) public String sayPlainTextHello() { return "Hello Jersey, this is plan text"; } // This method is called if XML is request @GET @Produces(MediaType.TEXT_XML) public String sayXMLHello() { return "" + " Hello Jersey, good to have xlm reply" + ""; } //This method is called if XML is request @GET @Produces(MediaType.APPLICATION_JSON) public String sayJSONHello() { return "Hello Jersey, good to have json reply"; } }


5: Now configure your eclipse with tomcat 6 or 7. (preferred is 7).
6: right click on your project and go to RUN AS--> then RUN ON SERVER.

When it launches on eclipse browser or external browser, at the end add /rest/hello to get string returned
as per API request.
/rest/hello is basically two component. one mentioned in web.xml under "url-pattern" and another as class path in your .java class "@Path("/hello")".


Project configuration image:


Sample Screen for REST API testing:



Reference Links:

(Jersey Java Related)

  1. https://jersey.java.net/documentation/latest/deployment.html
  2. http://stackoverflow.com/questions/22022114/org-glassfish-jersey-servlet-servletcontainer-classnotfoundexception
  3. https://jersey.java.net/apidocs/2.0/jersey/org/glassfish/jersey/servlet/ServletContainer.html
  4. https://blogs.oracle.com/japod/entry/jersey_2_0_integrated_into
  5. https://jersey.java.net/download.html
  6. https://jersey.java.net/
  7. http://www.vogella.com/tutorials/REST/article.html
  8. http://www.9lessons.info/2012/09/restful-web-services-api-using-java-and.html
Complete Source Code used in Blog:
  1. https://app.box.com/s/e2szc0crsbayffejbg2a // source code link
Feel free to ask any question regarding this, and subscribe to my blogs for more new coming blogs and deep dive for existing one.


Thank you

Wednesday, May 28, 2014

Building your Own Titanium SDK with Java Script Core

Building your Own Titanium SDK with Java Script Core


Pre-requisite: You are aware of iOS basics and familiar with iOS app development process.

Q.- How does Appcelerator Titanium Work?
A.- Basically is has modified Java Script Core open source framework and created ticore library,
      Which gets added to your build Xcode project when you build your Titanium App for iOS.

So you can use Java Script Core framework directly and create your own SDK, which will work as your want.
Appcelerator has customised this to make a cross platform SDK, because similar JS functionality is provided in android side by V8 JS engine.
So we can use Java Script Core in iOS and V8 in android app development and provide common functionality to create Titanium Like SDK.

Let me show with an example:

Create a Xcode Project, as we create in Xcode. for example say single window application.

My single window Xcode Project:



In the above picture, i have added ANMakePoint and MyButton classes along with all the .js files.

Now the open the project in Xcode:

Add Java Script Core framework

Steps:

  1. After adding the Java Script Core framework, go to your view controller and import the necessary header: "#import ".
  2. After than you need to add one Java Script file, like i did as test.js.
  3. Write your java script function there and save the file.
  4. now come to viewController.m and in viewdidload() function, initialise your JS context and evaluate your Java Script code.
  5. Then you need to get the function form evalauted script using context.
  6. After that you need to call the JS function from Objc code.
  7. Sample Code:

              NSString *myJSPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"js"];
              NSString *Script = [NSString stringWithContentsOfFile:myJSPath encoding:NSUTF8StringEncoding error:nil];

              JSContext *context = [[JSContext alloc] init];
              JSValue *value = [context evaluateScript:Script];
             // OBJC-->JS calling example
             JSValue *result = [function callWithArguments:@[@5]];


            This is very basic example code for Objc to JS calling.

Now Deep diving a little:

What is bonding between JS and Objc:
Actually Java Script Core framework is designed in  such a way to take care the differences required by two different languages to execute their code.

For Example Objc is a compiled language and code is compiled before execution, while JS is interpreted language and to run JS with Objc we require a VM(virtual machine) to provided interpreted 
environment.

Now This VM and context to execute code is provided by Java Script Core.
Note: VM:- Provide environment.
          Context:- is kind of global java script object for your js file. In web world its Window object.
          JSValue: is java script object or function or any value on which we work upon.

We can have multiple virtual machine and within one virtual machine we can have multiple context as well.
Image:


We can transfer information (object and value exchange) between two context of same virtual machine.

But we can not transfer information (object and value exchange) between two context of two different virtual machine.

Allowed:


Not Allowed: (No information exchange allowed between two context or different VM).


Part1:
Now Objc to Java Script communication happen with the following sequence:
(calling java script function from objective c)
  1. We create a Context. (until required one VM is created by default)
  2. We get our java script code from js file.
  3. Evaluate java script code. (this will return the value of any self executing function -- java script notation.)
  4. From context we get our JS value.(any JSfunction or JSObject).
  5. Call the JS function from Objc using java script core function calling mechanism.
  6. Get the return value from JS function as JSvalue.
Part2:
Now Java Script to Objc communication: (calling Objc method from Java Script)
  1. There are two possible way.
  • Blocks (Objc block can work as a medium to provide functionality of OBJC code to JS).
  • JSExport protocol. (A protocol will work as a class to JS code).
Blocks:
context[@"makeNSColor"] = ^(NSDictionary *rgb){
        float red = [rgb[@"red"] floatValue];
        float green = [rgb[@"green"] floatValue];
        float blue = [rgb[@"blue"] floatValue];
        float alpha = 1.0f;
        
        UIColor *colorReturn = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha];
        return colorReturn;
    };

Here makeNSColor  will be directly available to java script code as i have added to context(global object).
can be directly called from java script.

var rgb = {
        red:255.0,
        green:67.0,
        blue:70.0
        };
var colorVal = makeNSColor(rgb);

JSExport Protocol:
JSExport protocol works just like normal protocol but provide easy access to Objc code from Java Script.
Sample:

#import <UIKit/UIKit.h>

#import <JavaScriptCore/JavaScriptCore.h>

@protocol MyButtonExports <JSExport>

@property (readwrite,nonatomic) NSString* name;
@property double top;
@property double left;
@property double bottom;
@property double right;

-(id)onClick:(JSValue*)handler;
+(double)makeMyPointWithX:(double)x;
@end

@interface MyButton : UIButton <MyButtonExports>

-(void)clickHappened:(id)args;
@end

Using this approach, property values will be used as a java script getter and setter, while instance method will be used with class instance object and class method will be accessed using class object contained within global context object.

Things to take care: Threading and Memory Management is trivial task in this as java script is a garbage collected language and Objc uses ARC.

Apple has also shared one video in WWDC2013 regarding this communication:
(You need to have Safari and developer account.)

My JSCore sample link: https://app.box.com/s/5d7lfqh7l9o8j23pgt57 (Old code, check new code)
(Note: Its quite unstructured yet, but working. Will update soon with a nice well behaved code.)

Online view: https://app.box.com/s/sgbg3xzrj2idtw5svw1g

(New Code Sample)
Updated Code (Much structured and understandable)
Link: https://app.box.com/s/trr1glmd0dfx9y5hoa4a 

A working well behaved code is available in github under my github repo.
Link: https://github.com/ashishnigam/Team_Dev_Work

(Code is available now, as promised)

Direct Code Link: https://github.com/ashishnigam/Team_Dev_Work/tree/master/iOS_Native/JScoreDemo

JS functions exposed to java script in my sample.

1: createButton();
2: makeNSColor();
3: alert("Happy");
4: button.onClick(callback) {};  
is the instance method exposed. used for binding call backs to JS button object.
5: var callback = function(args){
        alert(args);
        button.tintColor = colorVal;
    };
this the call back exposed which is called on button click.
6: Button.createButton({}); is the Class method exposed, used in "testGeometry.js" which can be tested by commenting JSCoreExport feature and thus commenting the "JSCoreFundamental" method call and uncommenting the "JSCoreExports" method call as below viewdidload call in ANViewController.
- (void)viewDidLoad
{
    [super viewDidLoad];
    [self JSCoreFundamental:nil];
    //  [self JSCoreExports:nil];   
}
7: button.removeButton();
Much more functionality is available, best understood with the help of updated code sample. Checkout from github and test on your own system. Let me know for any clarification.
In sample i have created colours in JS and button object with its click event binding callback function.
Overloaded JS alert function as titanium sdk did.
Changed the label text colour using JS code with its background colour.
The only thing you may need to do it shuffling of commented and uncommented code in viewdidload().

Feel free to ask any query or any suggestions are most welcome. 
This code structure and sample shared is just for conceptual understanding.