Review
In the previous section, we have updated our app's configuration, so that our repositories are exposed as RESTful endpoints. We've also applied the HATEOAS methodology which "serves to decouple client and server in a way that allows the server to evolve functionality independently" (Wikipedia). In this section, we will build and run the application using Maven, demonstrate how to import the project in Eclipse, and run a series of manual tests to examine the RESTful endpoints.Table of Contents
Part 1: Configuration- Update the pom.xml
- Update the web.xml
- Update Spring configurations
- Update the repositories
What is HATEOAS?
HATEOAS, an abbreviation for Hypermedia as the Engine of Application State, is a constraint of the REST application architecture that distinguishes it from most other network application architectures. The principle is that a client interacts with a network application entirely through hypermedia provided dynamically by application servers. A REST client needs no prior knowledge about how to interact with any particular application or server beyond a generic understanding of hypermedia. Contrast this with e.g. a service-oriented architecture (SOA), where clients and servers interact through a fixed interface shared through documentation or an interface description language (IDL).
The HATEOAS constraint serves to decouple client and server in a way that allows the server to evolve functionality independently.
Source: http://en.wikipedia.org/wiki/HATEOAS
Running the Application
Access the source code
To download the source code, please visit the project's Github repository (click here)Preparing the data source
- Run MySQL (install one if you don't have one yet)
- Create a new database:
spring_data_rest_tutorial
- Import the following file which is included in the source code under the src/main/resources folder:
spring_data_rest_tutorial.sql
Building with Maven
- Ensure Maven is installed
- Open a command window (Windows) or a terminal (Linux/Mac)
- Run the following command:
mvn tomcat:run
- You should see the following output:
[INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'tomcat'. [INFO] [INFO] -------------------------------------------------------------- [INFO] Building spring-data-rest-tutorial Maven Webapp 0.0.1-SNAPSHOT [INFO] -------------------------------------------------------------- [INFO] [INFO] Preparing tomcat:run [INFO] [apt:process {execution: default}] [INFO] [resources:resources {execution: default-resources}] [INFO] [tomcat:run {execution: default-cli}] [INFO] Running war on http://localhost:8080/spring-data-rest-tutorial Nov 20, 2012 8:01:45 PM org.apache.catalina.startup.Embedded start INFO: Starting tomcat server Nov 20, 2012 8:01:45 PM org.apache.catalina.core.StandardEngine start INFO: Starting Servlet Engine: Apache Tomcat/6.0.29 Nov 20, 2012 8:01:46 PM org.apache.catalina.core.ApplicationContext log INFO: Initializing Spring root WebApplicationContext Nov 20, 2012 8:02:01 PM org.apache.catalina.core.ApplicationContext log INFO: Initializing Spring FrameworkServlet 'rest-exporter' Nov 20, 2012 8:02:03 PM org.apache.coyote.http11.Http11Protocol init INFO: Initializing Coyote HTTP/1.1 on http-8080 Nov 20, 2012 8:02:03 PM org.apache.coyote.http11.Http11Protocol start INFO: Starting Coyote HTTP/1.1 on http-8080
Note: If the project will not build due to missing repositories, please enable the repositories section in the pom.xml!
Access the grid page
This displays a grid that allows us to experiment with the data visually.- Follow the steps with Building with Maven
- Open a browser
- Enter the following URL (8080 is the default port for Tomcat):
http://localhost:8080/spring-data-rest-tutorial/
Access the RESTful entry endpoint
We've declared entry endpoint in spring-data-rest.xml and web.xml files.- Follow the steps with Building with Maven
- Open a browser
- Enter the following URL:
http://localhost:8080/spring-data-rest-tutorial/api/
This gives the following result:
{ "links" : [ { "rel" : "role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/role" }, { "rel" : "user", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user" } ], "content" : [ ] }
Access the User endpoint
- Enter the following URL:
http://localhost:8080/spring-data-rest-tutorial/api/user
This gives the following result:
{ "links" : [ { "rel" : "user.search", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search" } ], "content" : [ { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1/role" } ], "lastName" : "Smith", "username" : "john", "firstName" : "John", "password" : "21232f297a57a5a743894a0e4a801fc3" }, { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2/role" } ], "lastName" : "Adams", "username" : "jane", "firstName" : "Jane", "password" : "ee11cbb19052e40b07aac0ca060c23ee" }, ... (TRUNCATED) ... (TRUNCATED) ... (TRUNCATED) { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/13" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/13/role" } ], "lastName" : "Zeigler", "username" : "edward", "firstName" : "Edward", "password" : "mncmksk" } ], "page" : { "size" : 20, "totalElements" : 13, "totalPages" : 1, "number" : 1 } }
Access the User endpoint with a limit
- Enter the following URL:
http://localhost:8080/spring-data-rest-tutorial/api/user?limit=2
This gives the following result:
{ "links" : [ { "rel" : "user.next", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user?page=2&limit=2" }, { "rel" : "user.search", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search" } ], "content" : [ { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1/role" } ], "lastName" : "Smith", "username" : "john", "firstName" : "John", "password" : "21232f297a57a5a743894a0e4a801fc3" }, { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/2/role" } ], "lastName" : "Adams", "username" : "jane", "firstName" : "Jane", "password" : "ee11cbb19052e40b07aac0ca060c23ee" } ], "page" : { "size" : 2, "totalElements" : 12, "totalPages" : 6, "number" : 1 } }
Notice the result also provided the endpoint for search and each person's record.
Let's do a search
- Open a browser
- Enter the following URL:
http://localhost:8080/spring-data-rest-tutorial/api/user/search
This gives the following result:
{ "links" : [ { "rel" : "user.findByFirstNameLike", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByFirstNameLike" }, { "rel" : "user.findByUsernameLike", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByUsernameLike" }, { "rel" : "user.findByFirstNameLikeAndLastNameLike", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByFirstNameLikeAndLastNameLike" }, { "rel" : "user.findByUsername", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByUsername" }, { "rel" : "user.findByLastNameLike", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByLastNameLike" }, { "rel" : "user.findByRole", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByRole" } ], "content" : [ ] }
This exposes all the queries we declared in the UserRepository interface.
- Let's try searching by username.
- Enter the following URL:
http://localhost:8080/spring-data-rest-tutorial/api/user/search/findByUsername?username=john
This gives the following result:
{ "links" : [ ], "content" : [ { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/1/role" } ], "lastName" : "Smith", "username" : "john", "firstName" : "John", "password" : "21232f297a57a5a743894a0e4a801fc3" } ] }
Delete a record
You are required to have curl installed.- Open a command line.
- Enter the following command:
curl -v -X DELETE http://localhost:8080/spring-data-rest-tutorial/api/user/3
This gives the following output:
If you check the records, notice that User with id 3 has been deleted.* About to connect() to localhost port 8080 (#0) * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 8080 (#0) > DELETE /spring-data-rest-tutorial/api/user/3 HTTP/1.1 > User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3 > Host: localhost:8080 > Accept: */* > < HTTP/1.1 204 No Content < Server: Apache-Coyote/1.1 < Date: Mon, 19 Nov 2012 14:32:05 GMT < * Connection #0 to host localhost left intact * Closing connection #0
Add a record
You are required to have curl installed.- Open a command line.
- Enter the following command:
curl -v -d '{"username":"homer", "firstName":"Homer", "lastName":"Simpson", "password":"12345678"}' -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/user
Under Windows use the following command instead:
curl -v -d "{\"username\":\"homer\", \"firstName\":\"Homer\", \"lastName\":\"Simpson\", \"password\":\"12345678\"}" -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/user
This adds a User record and gives the following output:
* About to connect() to localhost port 8080 (#0) * Trying 127.0.0.1... connected > POST /spring-data-rest-tutorial/api/user HTTP/1.1 > User-Agent: curl/7.22.0 (i386-pc-win32) libcurl/7.22.0 OpenSSL/1.0.0e zlib/1.2 .5 > Host: localhost:8080 > Accept: */* > Content-Type: application/json > Content-Length: 86 > * upload completely sent off: 86out of 86 bytes < HTTP/1.1 201 Created < Server: Apache-Coyote/1.1 < Location: http://localhost:8080/spring-data-rest-tutorial/api/user/14 < Content-Type: application/octet-stream < Content-Length: 0 < Date: Tue, 20 Nov 2012 04:58:56 GMT < * Connection #0 to host localhost left intact * Closing connection #0
- However, a Role has not been associated with this User record. We have to manually create and associate one. Open a command line.
- Enter the following command:
curl -v -d '{"role":"1","user": {"rel": "user","href": "http://localhost:8080/spring-data-rest-tutorial/api/user/14"}}' -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/role
Under Windows use the following command instead:
curl -v -d "{\"role\":\"1\",\"user\": {\"rel\": \"user\",\"href\": \"http://localhost:8080/spring-data-rest-tutorial/api/user/14\"}}" -H "Content-Type: application/json" http://localhost:8080/spring-data-rest-tutorial/api/role
This adds a Role record and gives the following output:
* About to connect() to localhost port 8080 (#0) * Trying 127.0.0.1... connected > POST /spring-data-rest-tutorial/api/role HTTP/1.1 > User-Agent: curl/7.22.0 (i386-pc-win32) libcurl/7.22.0 OpenSSL/1.0.0e zlib/1.2 .5 > Host: localhost:8080 > Accept: */* > Content-Type: application/json > Content-Length: 106 > * upload completely sent off: 106out of 106 bytes < HTTP/1.1 201 Created < Server: Apache-Coyote/1.1 < Location: http://localhost:8080/spring-data-rest-tutorial/api/role/14 < Content-Type: application/octet-stream < Content-Length: 0 < Date: Tue, 20 Nov 2012 04:59:57 GMT < * Connection #0 to host localhost left intact * Closing connection #0
- Examine the output and we should see the new record:
http://localhost:8080/spring-data-rest-tutorial/api/user/14 { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/14" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/14/role" } ], "lastName" : "Simpson", "username" : "homer", "firstName" : "Homer", "password" : "12345678" } http://localhost:8080/spring-data-rest-tutorial/api/user/14/role { "links" : [ { "rel" : "self", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/role/14" }, { "rel" : "role.Role.user", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/role/14/user" }, { "rel" : "user.User.role", "href" : "http://localhost:8080/spring-data-rest-tutorial/api/user/14/role" } ], "role" : 1 }
- Examine the grid and we shall see the new record as well:
Import the project in Eclipse
- Ensure Maven is installed
- Open a command window (Windows) or a terminal (Linux/Mac)
- Run the following command:
mvn eclipse:eclipse -Dwtpversion=2.0
- You should see the following output:
[INFO] Scanning for projects... [INFO] [INFO] -------------------------------------------------- [INFO] Building spring-data-rest-tutorial Maven Webapp 0.0.1-SNAPSHOT [INFO] --------------------------------------------------- [INFO] [INFO] Adding support for WTP version 2.0. [INFO] [INFO] -------------------------------------------------- [INFO] BUILD SUCCESS [INFO] --------------------------------------------------
This command will add the following files to your project:
.classpath .project .settings target
You may have to enable "show hidden files" in your file explorer to view them
- Open Eclipse and import the project
Conclusion
That's it! We've have successfully updated our Spring MVC application and exposed our repositories as RESTful endpoints using Spring Data Rest. We've also demonstrated how to access our application using pure hyperlinks with HATEOAS methodology.
I hope you've enjoyed this tutorial. Don't forget to check my other tutorials at the Tutorials section.
Revision History
Revision | Date | Description |
---|---|---|
1 | Nov 20 2012 | Uploaded tutorial and Github repository |
Share the joy:
|
Subscribe by reader Subscribe by email Share
Very nice tutorial, I will give a try, since I just discovered such new feature of the Spring Framework and I was thinking to use it!
ReplyDeleteHi,
ReplyDeleteWhen I run this example in eclipse I got this error :
Apr 5, 2013 9:03:02 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
java.lang.NoClassDefFoundError: org/springframework/beans/factory/NoUniqueBeanDefinitionException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2436)
at java.lang.Class.getDeclaredMethods(Class.java:1793)
at org.springframework.core.type.StandardAnnotationMetadata.hasAnnotatedMethods(StandardAnnotationMetadata.java:159)
at org.springframework.context.annotation.ConfigurationClassUtils.isLiteConfigurationCandidate(ConfigurationClassUtils.java:104)
at org.springframework.context.annotation.ConfigurationClassUtils.checkConfigurationClassCandidate(ConfigurationClassUtils.java:87)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:253)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:223)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:630)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:461)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:383)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4791)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:618)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:650)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1582)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.ClassNotFoundException: org.springframework.beans.factory.NoUniqueBeanDefinitionException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
... 27 more
Fantastic as always ...
ReplyDeleteExcellent tutorial - User from India.
ReplyDeleteThanks so much!. It works well on MySQL but when I switch to MsSQL i get the following errors:
ReplyDeleteRequest processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: Incorrect syntax near 'limit'.; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: Incorrect syntax near 'limit'.
Please how can I run this on MsSQL.
thanks
Figured it out! I only needed to change the dialect to SQLServer and it worked!!!
ReplyDeleteI was very pleased to find this site.I wanted to thank you for this great read!! I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you post. This is the un-official subreddit for Instagram viewer- Share your posts, ask questions and get feedback on your account. Come join our great page.
ReplyDeletejordan 11
ReplyDeletegoyard
supreme new york
hermes birkin bag
supreme
jordan shoes
off white shoes outlet
off white nike
yeezy 500
yeezy