What is JSON Service?
JSON Service is a JSON-RPC server implementation. It supports version 2 of the specification and additionally it provides an implementation of the Service Mapping Description (SMD) specification for providing service metadata to service consumers.
Overview
JSON-RPC is a lightweight Remote Procedure Call protocol that utilises JSON for its messaging envelopes. In a typical situation you will simply need to:
- Instantiate the server object
- Register one or more classes/objects and their methods
- Produce SMD or handle() the request
JSON Service performs reflection on any attached classes and uses that information to build both the SMD and enforce method call signatures.
Features
TODO
Dependencies
Maven dependencies that need to be included in the POM file:
<!-- SLF4J --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- Jackson --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-lgpl</artifactId> <version>1.9.9</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-lgpl</artifactId> <version>1.9.9</version> </dependency> <!-- Spring Framework (optional) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.2.RELEASE</version> </dependency>
Examples
Example 1: Java Servlet
Download json-service-example1-sources.jar
CalculatorService.javapublic class CalculatorService { @JsonService public Integer add(Integer v1, Integer v2) { return v1 + v2; } @JsonService public Integer subtract(Integer v1, Integer v2) { return v1 - v2; } @JsonService public Integer multiple(Integer v1, Integer v2) { return v1 * v2; } @JsonService public Integer divide(Integer v1, Integer v2) { return v1 / v2; } }JsonRpcController.java
public class JsonRpcController extends HttpServlet { private static JsonServiceRegistry service = new JsonServiceRegistry(); static { service.register(CalculatorService.class); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); service.getServiceMap(CalculatorService.class, response); response.setStatus(200); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) { String date = (new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z")).format(Calendar.getInstance().getTime()); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); response.setHeader("Expires", "Mon, 01 Jan 2000 00:00:00 GMT"); response.setHeader("Last-Modified", date); response.setHeader("Cache-Control", "no-cache"); service.handle(request, response, CalculatorService.class); response.setStatus(200); } }web.xml
<servlet> <display-name>servlet</display-name> <servlet-name>servlet</servlet-name> <servlet-class>org.stefaniuk.json.service.example1.JsonRpcController</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet</servlet-name> <url-pattern>/jsonrpc</url-pattern> </servlet-mapping>index.jsp
require([ 'dojo/ready', 'dojox/rpc/Service', 'dojox/rpc/JsonRPC' // JsonRPC has to be loaded too ], function(ready, Service) { ready(function() { // create JSON-RPC service service = new Service('jsonrpc'); // call JSON-RPC methods service.add(12345, 54321).then(function(result) { console.log('(add)', result); }); service.subtract(12345, 54321).then(function(result) { console.log('(subtract)', result); }); service.multiple(12345, 54321).then(function(result) { console.log('(multiple)', result); }); service.divide(12345, 54321).then(function(result) { console.log('(divide)', result); }); }); });
Example 2: Simple Spring Framework integration
Download json-service-example2-sources.jar
CalculatorService.javapublic class CalculatorService { @JsonService public Integer add(Integer v1, Integer v2) { return v1 + v2; } @JsonService public Integer subtract(Integer v1, Integer v2) { return v1 - v2; } @JsonService public Integer multiple(Integer v1, Integer v2) { return v1 * v2; } @JsonService public Integer divide(Integer v1, Integer v2) { return v1 / v2; } }JsonRpcController.java
@Controller @RequestMapping(value = "/jsonrpc/*") public class JsonRpcController { @Autowired private JsonServiceRegistry jsonService; @RequestMapping(value = "*") public ResponseEntity<String> service(HttpServletRequest request, HttpServletResponse response) throws Exception { return JsonServiceUtil.handle(jsonService, request, response, CalculatorService.class); } }service.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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- JSON Service (JSON-RPC) --> <bean id="jsonService" class="org.stefaniuk.json.service.JsonServiceRegistry"> <property name="registry"> <list> <value>org.stefaniuk.json.service.example2.CalculatorService</value> </list> </property> </bean> </beans>index.jsp
require([ 'dojo/ready', 'dojox/rpc/Service', 'dojox/rpc/JsonRPC' // JsonRPC has to be loaded too ], function(ready, Service) { ready(function() { // create JSON-RPC service service = new Service('controller/jsonrpc/'); // call JSON-RPC methods service.add(12345, 54321).then(function(result) { console.log('(add)', result); }); service.subtract(12345, 54321).then(function(result) { console.log('(subtract)', result); }); service.multiple(12345, 54321).then(function(result) { console.log('(multiple)', result); }); service.divide(12345, 54321).then(function(result) { console.log('(divide)', result); }); }); });
Example 3: Advanced Spring Framework integration
Download json-service-example3-sources.jar
Album.java@ModelTable(name = "Album") public class Album extends AbstractModel { @ModelColumn(name = "AlbumId") private Integer id; @ModelColumn(name = "Title") private String title; @ModelColumn(name = "ArtistId") private Integer artistId; public Album(Integer id, String title, Integer artistId) { this.id = id; this.title = title; this.artistId = artistId; } public Album(Map<String, Object> row) { setId((Integer) row.get(getColumnName(this.getClass(), "id"))); setTitle((String) row.get(getColumnName(this.getClass(), "title"))); setArtistId((Integer) row.get(getColumnName(this.getClass(), "artistId"))); } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Integer getArtistId() { return artistId; } public void setArtistId(Integer artistId) { this.artistId = artistId; } }AlbumDao.java
public class AlbumDao extends AbstractModelDao<Album> { @Override public Integer create(Album model) { return null; } @Override public Integer update(Album model) { return null; } @Override public Integer update(Album model, Album changed) { return null; } @Override public Integer remove(Album model) { return null; } @Override public List<Album> findAll() { return null; } public List<Album> findByArtistId(Integer artistId) { List<Album> list = new ArrayList<Album>(); String sql = String.format("select * from %1$s where ArtistId = " + artistId, TABLE_NAME); List<Map<String, Object>> rows = getJdbcTemplate().queryForList(sql); for(Map<String, Object> row: rows) { list.add(new Album(row)); } return list; } @Override public Album findById(Integer id) { return null; } @Override public Integer findIdByModel(Album model) { return null; } @Override public Integer countAll() { return null; } }AlbumService.java
public class AlbumService extends AbstractService<AlbumDao> { @JsonService public List<Album> list(Integer artistId) { return dao.findByArtistId(artistId); } }ChinookController.java
@Controller @RequestMapping(value = "/chinook/*") public class ChinookController extends AbstractController { @Override @RequestMapping("service/{service}") public ResponseEntity<String> service(HttpServletRequest request, HttpServletResponse response, @PathVariable String service) throws Exception { ResponseEntity<String> re = null; if(service.equals("artist")) { re = handleJsonRpc(request, response, ArtistService.class); } else if(service.equals("album")) { re = handleJsonRpc(request, response, AlbumService.class); } else if(service.equals("track")) { re = handleJsonRpc(request, response, TrackService.class); } return re; } }service.xml
<!-- JSON Service (JSON-RPC) --> <bean id="jsonService" class="org.stefaniuk.json.service.JsonServiceRegistry" /> <!-- artistService --> <bean id="artistService" class="org.stefaniuk.json.service.example3.service.ArtistService"> <property name="service" ref="jsonService" /> </bean> <!-- albumService --> <bean id="albumService" class="org.stefaniuk.json.service.example3.service.AlbumService"> <property name="service" ref="jsonService" /> </bean> <!-- trackService --> <bean id="trackService" class="org.stefaniuk.json.service.example3.service.TrackService"> <property name="service" ref="jsonService" /> </bean>persistence.xml
<!-- chinook.sqlite --> <bean id="sqliteFileDataSource" class="org.stefaniuk.json.service.example3.datasource.SQLiteFileDataSource" scope="globalSession"> <aop:scoped-proxy proxy-target-class="false" /> </bean> <!-- artistDao --> <bean id="artistDao" class="org.stefaniuk.json.service.example3.model.ArtistDao"> <property name="dataSource" ref="sqliteFileDataSource" /> </bean> <!-- albumDao --> <bean id="albumDao" class="org.stefaniuk.json.service.example3.model.AlbumDao"> <property name="dataSource" ref="sqliteFileDataSource" /> </bean> <!-- trackDao --> <bean id="trackDao" class="org.stefaniuk.json.service.example3.model.TrackDao"> <property name="dataSource" ref="sqliteFileDataSource" /> </bean>index.jsp
require([ 'dojo/ready', 'dojox/rpc/Service', 'dojox/rpc/JsonRPC' // JsonRPC has to be loaded too ], function(ready, Service) { ready(function() { // create JSON-RPC services var artistService = new Service('chinook/service/artist'); var albumService = new Service('chinook/service/album'); var trackService = new Service('chinook/service/track'); var count = 1; var artistId = 87; artistService.list().then(function(artists) { albumService.list(artists[artistId].id).then(function(albums) { for(var i in albums) { var album = albums[i]; (function(album) { // this closure is requried to save a reference to the current album trackService.list(album.id).then(function(tracks) { for(var j in tracks) { var track = tracks[j]; console.log( count++, ' - ', artists[artistId].name, ' - ', album.title, ' - ', track.name ); } }); })(album); // end: closure } }); }); }); });
Example 4: Spring Framework, Hibernate and Dojo Store
TODO
Download
json-service-1.1.0.jarjson-service-1.1.0-sources.jar
json-service-1.1.0-javadoc.jar
json-service-example1-sources.jar
json-service-example2-sources.jar
json-service-example3-sources.jar