Java Development 2.0: Implementing REST through CouchDB and Groovy restclient

Source: Internet
Author: User
Keywords nbsp can HTTP through

In the past few years, the innovative development of the open source world has elevated the productivity of Java™ developers to one level. Free tools, frameworks and solutions make up for once-scarce vacancies. The Apache CouchDB, which some people think is a WEB 2.0 database, is very promising. It's not difficult to master CouchDB, it's as simple as using a Web browser. This installment of Java Development 2.0 will introduce CouchDB and show you how to play the powerful features of Groovy's restclient.

So far, this column series has explored the cloud computing in Google and Amazon platforms. Although they are both implemented and structurally different, both platforms support rapid and scalable deployment. They can quickly and economically assemble, test, run, and maintain Java applications, which is no doubt unprecedented. However, the cloud is not the only factor affecting the speed of Java development today. Open source solutions can also help you quickly assemble software applications because you no longer need to write a lot of code. The era of manual authoring of object-relational mappings (ORM), logs, or test frameworks is gone. These problems have been resolved over time, and then again in the open source realm-faced with these problems again-but these solutions are almost always better than yours.

In the whole process of Java development, open source innovation simplifies the assembly process of the application. The new Open source database Apache CouchDB (as of 0.10.0) is no exception when you write this article. You can use it easily after you build a CouchDB environment. You only need to operate its HTTP connection, neither the JDBC driver nor the Third-party control management platform is required. In this article, I'll introduce you to CouchDB and show you how to use it to improve development speed. Given the ease of installation, you will use Amazon's EC2 platform. And you'll be communicating with it through an easy-to-use Groovy module.

Database facing documents

The relational database basically dominates the database market. But other similar databases-including object-oriented and document-oriented databases--are very different in a relationship-oriented world-and often play a pivotal role. CouchDB is a document-oriented database. It is modeless and allows you to store documents in the form of a JavaScript Object notation (JSON) string.

JSON


JSON is a lightweight data interchange format that is also an alternative format for WEB applications. It's similar to XML, but it's far less detailed than it is. Thanks to its lightweight features, it is becoming a lingua-Web language.

Imagine a parking ticket. The ticket will cover the following items:

violation of date and time Location Vehicle Description license Information violation

The format and data collected on a ticket vary by jurisdiction. Even for standard parking tickets within a single jurisdiction, their content is likely to be different. For example, a police officer may not fill out the time when issuing a ticket, or can omit the model, and only fill in the details of the license plate. The location can be a combination of two streets (such as the intersection of Fourth and Lexington) or a fixed address (for example, 19993 Main Street). But the semantics of the collected information are probably similar.

The data points of a ticket can be modeled in a relational database, but the details are a bit cumbersome. For example, how do you effectively capture an intersection in a relational database? And without a street intersection, does the database use a null field to represent the second address (assuming that the modeling method captures different street names in each column)?

In these cases, the relational database may be somewhat more abstract. The required information is already in the form of a document (ticket). Why not model data as a document? This can be done without having to cling to strict relational patterns, but rather to follow the semantics of the advanced schema in general. This is where CouchDB is. It allows you to model these domain types in a flexible way-the result is a complete document that has no schema, but uses blueprints that are roughly similar to other documents.

MapReduce


Google's original MapReduce is a conceptual framework for dealing with massive datasets. It is a highly optimized distributed problem-solving mechanism that uses a large number of computers. MapReduce contains two functions: Map and reduce. The map function accepts large amounts of input and splits them into smaller portions (while passing the data to other processes). The function of the reduce function is to consolidate all individual outputs from the map into one final output.

With CouchDB, you can search documents, document properties, and even associate documents in a relational world. The way you implement this is to use views, not SQL. Essentially, a view is a function that you write in the MapReduce style (in JavaScript), that is, you end up writing only a map function and a reduce function. These functions will collectively filter or extract document data, or effectively utilize the relationships between them. In fact, CouchDB has the flexibility to speed up the view process as long as the underlying document does not occur, and it only needs to run these functions once.

The most interesting part of CouchDB is the way it is designed. CouchDB embodies the basic (and extremely successful) concept of the Web itself. It exposes a comprehensive set of restful APIs that allow you to create, query, update, and delete documents, views, and databases. This makes the use of CouchDB very simple. You do not need to use other drivers or platforms to start development: A browser can do all the work. That is, rich libraries make the use of CouchDB very simple-but internally, they simply use the REST concept through HTTP.

Similar to the intrinsic nature of the Web, CouchDB is designed to incorporate a large number of scalability factors. It is written in Erlang using the Concurrent programming language, which supports distributed, fault tolerant, and uninterrupted applications. The language (now available for open source) is developed by Ericsson and is widely used in the telecommunications environment.

Install CouchDB, cloud style

The CouchDB installation method varies depending on the operating system. If you are using Windows®, you will need to install the Microsoft C compiler Cygwin and some other dependencies. If you are using a Mac, you need to use MacPorts. However, if you are using a linux® platform, such as Ubuntu, the installation method is not that simple. But not everyone installs the Ubuntu instance. Is that what you are?

Of course, you can easily get an example of Ubuntu! Amazon's EC2 is a relatively economical and on-demand way to use Ubuntu. So, just use a little EC2 magic, you can quickly set up a CouchDB environment; When you're done, you can turn it off (so to speak).

First, you need to find a EC2 AMI that acts as a basic instance. I finally decided to use the AMI ami-ccf615a5 (an Ubuntu 9.04 instance), which is the latest version as of this writing. (When you read this article, there is a good chance that the 9.10 version of the AMI has already appeared). Use Eclipse or the AWS Management Console to start a ami-ccf615a5 instance. Make sure that you have set up security policies that allow access through SSH. (Although CouchDB uses HTTP, you will communicate with it through an SSH channel, given the simplicity.) You also need to use a value pair. (If you need guidance, see the first two articles in this series, "You can also hire EC2" and "easy to use EC2".) )

After you start the EC2 instance of Ubuntu 9.04, you need to set up SSH for it. (Remember, this instance takes approximately 1 minutes to fully start, so be patient.) For example, I can open a terminal and use SSH to set up a newly created instance, as follows:

aglover#> ssh-i Ec2/agkey.pem root@ec2-174-129-157-167.compute-1.amazonaws.com

The DNS name of my AMI is ec2-174-129-157-167.compute-1.amazonaws.com and the name of the value pair I reference is agkey. Your DNS name and value pairs will certainly be different.

In the Cloud Ubuntu instance command prompt, enter:

apt-get Update

Then enter:

Aptitude Install Couchdb

These commands automatically install CouchDB. However, note that they do not install the latest version. If you need the latest version, you need to install CouchDB through the source code.

After the command execution is complete, you can check that the CouchDB is working properly by issuing the ps-eaf command. View the process that uses COUCHDB in the path by passing the PS output to egrep. You should see the output shown in Listing 1:


Listing 1. CouchDB is running (each row is segmented to fit the page width)

couchdb 1820 1 0 00:54? 00:00:00/bin/sh-e/usr/bin/couchdb-c/etc/couchdb/couch.ini-b-R 5-p/var/run/couchdb.pid-o/couchdb 1827 1820 0 00: 54? 00:00:00/bin/sh-e/usr/bin/couchdb-c/etc/couchdb/couch.ini-b-R 5-p/var/run/couchdb.pid-o/couchdb 1828 1827 0 00: 54? 00:00:00/USR/LIB/ERLANG/ERTS-5.6.5/BIN/BEAM-BD---root/usr/lib/erlang-progname erl---home/vcouchdb 1836 1828 0 00: 54? 00:00:00 heart-pid 1828-ht 11

Next, go back to the local machine and set up an SSH channel to allow access to CouchDB instances running on the cloud, just like on your own machine. To do this, open a new terminal session on the local machine and enter:

Ssh-i your key-l 5498:localhost:5984 root@your AMI DNS

Finally, open a browser on the local machine. In the Address bar, enter http://127.0.0.1:5498/. You should be able to see a nice JSON welcome message as follows:

{"Couchdb": "Welcome", "Version": "0.8.0-incubating"}

Now it looks like everything is working, and then you can start applying CouchDB.

Restclient using Groovy in REST style

REST


In the state Transport (REST) design style, loosely coupled Web applications will depend on the specified resources-such as the Uniform Resource Locator (URL), Uniform Resource Identifier (URI), and Uniform Resource Name (URN), rather than messages. REST wisely employs the proven and successful infrastructure-http in the Web. In other words, REST will take advantage of the various aspects of the HTTP protocol, such as Get and POST requests. These requests will well meet business application requirements, such as Create, read, update, and delete (CRUD).

Because CouchDB exposes data through the REST HTTP interface above, using CouchDB (you've seen one or two in your browser) is fairly straightforward. Almost all work can be done via HTTP.

You can select various tools to interact with HTTP. When using the REST interface, I prefer the restclient extension of Groovy Httpbuilder. httpbuilder-the HttpClient wrapper for Apache Commons Project-adds some Groovy elements to the syntax of HTTP POST, get, put, and DELETE. Because Httpbuilder is created using Groovy, scripting scripts that take advantage of restful concepts, such as communication with CouchDB, are simply not easy to write.

Further simplification of the Grape

To keep in line with the general theme of Java Development 2.0-fast, easy, and free (or cheap)-groovy handy Grape (Groovy Advanced packaging Engine or groovy adaptable Packagin G Engine) features are useful for interacting with httpbuilder. Grape is a dependency manager that allows Groovy scripts and classes to automatically configure their own specific dependencies at run time. This simplifies the use of various open source libraries because you don't need to download various JAR files to start coding. For example, with Grape, you can write a Groovy script that uses httpbuilder without the need for a httpbuilder JAR. With Grape, you can download them (via Apache) at run time or compile time.

You will use annotations and method tuning to leverage Grape. For example, you can use @Grab annotations to decorate a method or class declaration. In this note, you assign a primary dependency to some related metadata (all intermediate dependencies can be determined with the help of Ivy's magic). At run time, or when compiling (regardless of the previous), Grape downloads These dependencies and ensures that they are under your classpath. If a dependency has been downloaded (for example, from a previous run), Grape still ensures that the appropriate JAR files are included under the classpath.

Simplifies CouchDB REST style through Groovy

Before you can create any document in CouchDB, you must first create a database. To create a parking ticket database, you can use its restclient to issue an HTTP put using the Httpbuilder domain-related language (DSL), as shown in Listing 2.


Listing 2. Create a CouchDB database

import static Groovyx.net.http.ContentType.JSONimport Groovyx.net.http.restclient@grab (group= ' Org.codehaus.groovy.modules.http-builder ', module= ' Http-builder ', version= ' 0.5.0-rc2 ') def getRESTClient () {return New Restclient ("http://localhost:5498/")}def client = getrestclient () def response = client.put (path: "Parking_tickets" , Requestcontenttype:json, Contenttype:json) assert Response.data.ok = = true: Response from server wasn ' t OK


COUCHCB should return answer {"OK": true}. As shown in Listing 2, you can easily parse JSON in Httpbuilder and ensure that the value of the OK element is true.

Next, we need to create some documents to keep in line with the subject of the parking ticket. To create a parking ticket model, you need to remember some aspects related to the ticket. Also remember that because they are actual forms that officers will fill out, some fields may not be filled in or used in predefined patterns-consider intersection points and exact locations.

With Httpbuilder, you can create a document in CouchDB (just as you would create a database in Listing 2) by using HTTP put. Because CouchDB will process the JSON document, you must follow the name value format of JSON. To do this, create a map-like data structure in Groovy (Httpbuilder converts it to a valid JSON). As shown in Listing 3:


Listing 3. Create a CouchDB document from Restclient

response = client.put (path: "parking_tickets/1234334325", Contenttype:json, Requestcontenttype:json, body: [offic ER: "Kristen Ree", Location: "199 Baldwin Dr", vehicle_plate: "Maryland 77777", offense: "Parked in no Parking zone", date : "2009/01/31"]) assert Response.data.ok = = = true: "Response from server wasn ' t OK" assert response.data.id = "1234334325" : "The returned ID didn ' t match"


Listing 3 completes a number of tasks. First, when you put a put on a CouchDB document, you must assign an UUID. CouchDB can assign these values to you, or you can manage them yourself. In Listing 3, I set a value (1234334325), and then the UUID is appended to the URL. If the UUID is available, CouchDB assigns it a document that performs the put operation. In the body part of the put call, note that specifying the relevant value for each name is almost the same as a normal mapping. For example, the name of the designated police officer is Kristen Ree, and the place of the ticket is 199 Baldwin Dr.

Listing 4 uses the same technique to create another ticket in COUHDB:


Listing 4. Another parking ticket.

def ID = new Date (). Timeresponse = client.put (path: "Parking_tickets/${id}", Contenttype:json, Requestcontenttype: JSON, Body: [Officer: "Anthony Richards", Location: "Walmart Parking Lot", Vehicle_plate: "Delaware 4433-op", Offense: "Pa Rked in non-parking spaces ", Date:" 2009/02/01 "]) assert Response.data.ok = = true:" Response from server wasn ' t OK "assert r Esponse.data.id = = "${id}": "The returned ID didn ' t match"


Every time I put a put through restclient, I assert that the OK value of the JSON answer is ture and that there is an ID value. Note that in Listing 4, I didn't create the UUID, but I used the current time-not very simple technique, but I was no longer satisfied with the simple intersection.

After a new document has been successfully created in CouchDB, it returns a JSON that contains the UUID and revision IDs. For example, the answer represents the JSON I validated in Listing 4:

{"OK": true, "id": "12339892938945", "Rev": "12351463"}

Your ID and Rev values must be different. Note that I can capture the ID value by issuing a call such as response.data.id.

In CouchDB, it tracks the document through revisions, so you can return the previous version of the document (through the revision ID), which is very similar to the approach in CVS or Subversion.

Views in CouchDB

Now that I've created some parking tickets (or, in CouchDB terms, some documents), I can then create some views in CouchDB. Remember that views are the actual MapReduce functions, so you must define them. In many cases, you do not need the reduce function; The map function can help you accomplish most tasks. As its name, it is the mapping of tasks. For example, you can map any "things" or aspects that you want to filter or find.

I have defined two fines: one is opened by Officer Ree and the other is emitted by Officer Richards. For example, to find all the fines for Officer Ree, you can write a map function to filter the corresponding Officer properties. You can then pass the results to the CouchDB emit function.

Managing interfaces using CouchDB: Futon

You can define a view through the CouchDB REST API or through the CouchDB management interface Futon. Futon is just a WEB application that you can download from http://localhost:5498/_utils/. Access this location now (assuming you have followed me to create a database and some documents), you should see a simple interface for parking_tickets, as shown in Figure 1:


Figure 1. Futon interface

If you select the Parking_tickets database, you can then see a drop-down list (select View:) on the far right. By selecting Custom Query ... To define a custom view, as shown in the figure:


Figure 2. Futon View Selection Interface

The Futon interface now allows you to define the map function and the reduce function. (You may need to click the View Code link). In the Map text box, define a simple map as shown in Listing 5:


Listing 5. Simple map function in CouchDB

function (DOC) {if (Doc.officer = = "Kristen Ree") {Emit (null, doc);}

As you can see, the map function in Listing 5 is defined using JavaScript. It is used to filter documents in the CouchDB database through the officer properties of the document. In particular, this function passes a document to emit only if the name of the officer is Kristen Ree. Figure 3 shows where this function is defined in Futon:


Figure 3. Create a MapReduce function

Next, you need to specify the document name (input by_name) and the View name (enter Officer_ree). These names will be used as a way to establish a URL to call this view later (the URL is http://localhost:5498/parking_tickets/_view/by_name/officer_ree).

You can now use this view through Httpbuilder:


Listing 6. Call your new view

response = client.get (path: "Parking_tickets/_view/by_name/officer_ree", Contenttype:json, Requestcontenttype: JSON) Assert response.data.total_rows = = 1response.data.rows.each{assert it.value.officer = = "Kristen Ree"}


The view returns a JSON reply that contains only one document: Officer Ree issued a ticket on January 31. By parsing the corresponding JSON, the response object in Listing 6 hides the original HTTP reply. You can view the original JSON answer by calling the ToString method on the response Data property. The original answer will be as shown in Listing 7:


Listing 7. The original result of the view

{"Total_rows": 1, "offset": 0, "Rows": [{"id": "1234334325", "key": null, "value": {"_id": "1234334325", "_rev": " 4205717256 "," Officer ":" Kristen Ree "," Location ":" 199 Baldwin Dr "," vehicle_plate ":" Maryland 77777 "," offense ":" Parked In no Parking zone "," date ":" 2009/01/31 "}}]}


As you can see from the original JSON document returned, Httpbuilder can parse JSON very easily because it supports estimating various properties and their corresponding values through a mechanism similar to an object graph.

For demonstration purposes, I'll add some more documents to the database. To follow the example, you should use code downloads to accomplish the same task.

The CouchDB emit function acts as a variety of forms of organization. If you do not add a limit to the map function (as I did in Listing 5), the basic role of emit is to sort incoming documents. For example, if you want to get all the tickets by date (this can be considered an order by statement for SQL), you can perform emit by the document's date, as shown in Listing 8:


Listing 8. A relatively simple map function

function (DOC) {emit (Doc.date, doc);}

Listing 9 emits an HTTP get to this view (I have specified dates as the design document name, by_date as the view name.) )。


Listing 9. Another view of the call

Response = Client.get (path: "Parking_tickets/_view/dates/by_date", Contenttype:json, Requestcontenttype:json) assert Response.data.total_rows = 4

The query in the manifest returns all documents in the Parking_tickets database in the order of date. The Assert statement verifies that the Total_rows property is equal to 4. This is a key point. The view returns some results and a little metadata, such as the number of documents returned, so it will help to view the original answer before parsing begins. Listing 10 shows the original results:


Listing 10. Original JSON document sorted by date

{"Total_rows": 4, "offset": 0, "Rows": [{"id": "85D4DBF45747E45406E5695B4B5796FE", "Key": "2009/01/30", "value": {"_id": "85d4dbf45747e45406e5695b4b5796fe", "_rev": "1318766781", "Officer": "Anthony Richards", "Location": "54th and Main", " Vehicle_plate ":" Virginia FCD-4444 "," offense ":" Parked in no Parking zone "," date ":" 2009/01/30 "}}, {" id ":" 1234334325 "," Key ":" 2009/01/31 "," value ": {" _id ":" 1234334325 "," _rev ":" 4205717256 "," Officer ":" Kristen Ree "," Location ":" 199 Baldwin Dr "," vehicle_plate ":" Maryland 77777 "," offense ":" Parked in no Parking zone "," date ":" 2009/01/31 "}}, {" id ":" 12345 "," Key ":" 2009/01/31 "," value ": {" _id ":" 12345 "," _rev ":" 1479261876 "," Officer ":" Anthony Richards "," Location ":" 1893 Main St "," vehicle_plate ":" Maryland 4433-op "," offense ":" Parked in no Parking zone "," date ":" 2009/01/31 "}}, {" id ":" 12339892938945 "," Key ":" 2009/02/01 "," value ": {" _id ":" 12339892938945 "," _rev ":" 12351463 "," Officer ":" Anthony Richards "," Location ":" Walmart Parking Lot "," vehicle_plate ":" Maine 4433-op "," offense ":" ParkEd in non-parking spaces "," date ":" 2009/02/01 "}}]}

When you define such a view, you can then pass it a value-that is, the initial value of the emit function. For example, the view defined in Listing 8 is primarily used to sort by date. If you want to use a specific date, pass the date to the view query. For example, you can enter the following URL in the browser's address bar:

http://localhost:5498/parking_tickets/_view/dates/by_date?key= "2009/01/31"


This view will then return only the ticket issued on January 31. You should be able to see some JSON-style text in the browser window, similar to the contents in Listing 11. Note that using the browser as a query tool is an extremely simple way to view the original JSON response of an HTTP request.


Listing 11. Only two tickets were issued on January 31.

{"Total_rows": 4, "offset": 1, "Rows": [{"id": "1234334325", "Key": "2009/01/31", "value": {"_id": "1234334325", "_rev": " 4205717256 "," Officer ":" Kristen Ree "," Location ":" 199 Baldwin Dr "," vehicle_plate ":" Maryland 77777 "," offense ":" Parked In no Parking zone ", Date:" 2009/01/31 "}}, {" id ":" 12345 "," Key ":" 2009/01/31 "," value ": {" _id ":" 12345 "," _rev ":" 1479261876 "," Officer ":" Anthony Richards "," Location ":" 1893 Main St "," vehicle_plate ":" Maryland 4433-op "," offense ":" Parked in handicap zone without permit ", date": "2009/01/31"}}]}

You can customize the functionality of the view as needed. For example, with just a few JavaScript string operations, I can write a view that finds the ticket out of Main Street, as shown in Listing 12:


Listing 12. Another view with String Magic added

function (DOC) {if (Doc.location.toLowerCase (). IndexOf (' main ') > 0) {emit (Doc.location, doc);}}

As you can see from listing 12, if the location element of any document contains main, the document is passed to the emit function. Remember, this search is quite extensive. If the location of a document contains a string such as a Germaine street, it is returned. For the few tickets I've defined, the view returns the result shown in Listing 13:


Listing 13. The result of filtering by Main Street

{"Total_rows": 2, "offset": 0, "Rows": [{"id": "123433432asdefasdf4325", "Key": "4th and Main", "value": {"_id": " 123433432asdefasdf4325 "," _rev ":" 498239926 "," Officer ":" Chris Smith "," Location ":" 4th and Main "," vehicle_plate ":" VA Fga-jd33 "," offense ":" Parked in no Parking zone "," date ":" 2009/02/01 "}}, {" id ":" 123433432223e432325 "," Key ":" Si and Main "," value ": {" _id ":" 123433432223e432325 "," _rev ":" 841089995 "," Officer ":" Kristen Ree "," Location ":" and Main Street ", "Vehicle_plate": "Maryland 77777", "offense": "Parked in no Parking zone", "date": "2009/02/02"}}]}

Note that the JSON answer contains a key element that describes why a particular document is being built. The information in this regard is quite useful. Also note that the data in the various fines I have defined do not do well in terms of consistency: Some locations are accurate, but some are imprecise. Although this data can be stored in a relational database, it is also possible to use a document-oriented model. In addition, with Groovy and Httpbuilder's powerful ability to parse JSON efficiently, it's easy to get data (much simpler than the original JDBC).

CouchDB as a WEB database

The charm of CouchDB is that its use is very simple. Relational databases are also simple, but the advantage of the database is that you can master the APIs only if you are familiar with the Web browser. In addition, because CouchDB provides a REST-style API, you can communicate with some cool frameworks, such as Httpbuilder's restclient. You are not subject to httpbuilder; Various Java libraries are trying to simplify the use of CouchDB. One of the most promising is jcouchdb, which helps you avoid REST and JSON while allowing you to manipulate documents and views programmatically using the Java language.

Please look forward to next month's column, I will go back to discuss Google App Engine. Under the umbrella of the open spirit of innovation, new frameworks are emerging to promote the development and deployment of Google App Engine. You'll see how they can simplify Java development 2.0 on Google's cloud platform.

The Groovy source code for the article example, J-javadev2-5.zip:temp_10022710405034.zip

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.