在8年軟件開發生涯中我儼然一位Java開發者了。大多數我寫的程序,都用Spring框架或者Java EE.最近我在學Python Web開發,其中印象很深的一個是Flask框架。Flask框架是個微框架,使得寫REST後端很簡單。我今天的30天挑戰,決定找一款Java框架替代Python的Flask。一番搜索後,我發現Dropwizard框架能夠達到如Flask一樣的功效。這篇博客,咱們來學習怎樣用Dropwizard構建RESTful Java MongoDB程序。html
Dropwizard是一款開源Java框架,用於ops友好開發,高性能RESTful後端開發。它由Yammer開發並提供基於JVM後端的支持。 java
Dropwizar延續最優Java庫,嵌入到程序包,包括如下組件:linux
我學習Dropwizard的幾點緣由:git
今天的demo放在github: day13-dropwizard-mongodb-demo-app. github
安裝Eclipse很簡單,只需解壓下載的安裝包。在linux或者mac上,打開命令管理器輸入如下命令。mongodb
$ tar -xzvf eclipse-jee-kepler-R-*.tar.gz
Windows上能夠用7-zip或者其餘解壓工具解壓,解壓後,在你解壓的路徑會有一個eclipse的文件夾,能夠給可執行文件建立快捷鍵。數據庫
打開Eclipse IDE導航到項目空間,新建項目,到File > New > Maven Project,選擇maven-archetype-quichstart,輸入Ground Id和Artifact Id,最後點Finish.apache
如今更新pom.xml, 加入dropwizard-core maven依賴,再更新Maven項目用Java 1.7版本,更新pom.xml後更新Maven項目(右擊>Maven>Update Project)json
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.shekhar</groupId> <artifactId>blog</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>blog</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.yammer.dropwizard</groupId> <artifactId>dropwizard-core</artifactId> <version>0.6.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
每一個Dropwizard程序都有一個配置類,用來指定特定環境變量。後面咱們會添加MongoDB配置參數如host, port, db name.這個類繼承com.yammer.dropwizard.config.Configuration。flask
import com.yammer.dropwizard.config.Configuration; public class BlogConfiguration extends Configuration{ }
Dropwizard由一個服務類引導,這個類涵蓋全部提供基礎功能的綁定和命令,也啓用內嵌的Jetty服務,繼承com.yammer.dropwizard.Service.
import com.yammer.dropwizard.Service; import com.yammer.dropwizard.config.Bootstrap; import com.yammer.dropwizard.config.Environment; public class BlogService extends Service<BlogConfiguration> { public static void main(String[] args) throws Exception { new BlogService().run(new String[] { "server" }); } @Override public void initialize(Bootstrap<BlogConfiguration> bootstrap) { bootstrap.setName("blog"); } @Override public void run(BlogConfiguration configuration, Environment environment) throws Exception { } }
以上代碼作了如下動做:
來寫第一個當GET請求 '/' url時會被引用的資源,新建一個JAX-RS資源,它會列出全部博客。
import java.util.Arrays; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import com.yammer.metrics.annotation.Timed; @Path("/") public class IndexResource { @GET @Produces(value = MediaType.APPLICATION_JSON) @Timed public List<Blog> index() { return Arrays.asList(new Blog("Day 12: OpenCV--Face Detection for Java Developers", "https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers")); } }
以上代碼是一個標準的JAX-RS資源類,註釋了@Path,定義了index()方法。Index()返回博客的集合,這些博客會被轉換成JSON文檔,@Timed註釋肯定了Dropwizard基礎時間。
以上IndexResource用一篇博客呈現,顯示以下,博客的呈現用hibernate validator註釋來確保內容有效性。例如,用@URL註釋來確保只有有效的URL會存到MongoDB數據庫。
import java.util.Date; import java.util.UUID; import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.URL; public class Blog { private String id = UUID.randomUUID().toString(); @NotBlank private String title; @URL @NotBlank private String url; private final Date publishedOn = new Date(); public Blog() { } public Blog(String title, String url) { super(); this.title = title; this.url = url; } public String getId() { return id; } public String getTitle() { return title; } public String getUrl() { return url; } public Date getPublishedOn() { return publishedOn; } }
而後,在service類run方法裏註冊IndexResource, 用如下代碼更新BlogService的run方法。
@Override public void run(BlogConfiguration configuration, Environment environment) throws Exception { environment.addResource(new IndexResource()); }
如今咱們能夠把BlogService做爲主程序運行,右擊>Run As>Java Application. 會啓動內嵌Jetty容器而後看到程序運行在http://localhost:8080/.
$ curl http://localhost:8080 [{"id":"9bb43d53-5436-4dac-abaa-ac530c833df1","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384090975372}]
點擊Metrics能夠查看IndexResource的Metricx, 數據是JSON格式。
"com.shekhar.blog.IndexResource" : { "index" : { "type" : "timer", "duration" : { "unit" : "milliseconds", "min" : 17.764, "max" : 17.764, "mean" : 17.764, "std_dev" : 0.0, "median" : 17.764, "p75" : 17.764, "p95" : 17.764, "p98" : 17.764, "p99" : 17.764, "p999" : 17.764 }, "rate" : { "unit" : "seconds", "count" : 1, "mean" : 7.246537731991882E-4, "m1" : 2.290184897291144E-12, "m5" : 3.551918562683463E-5, "m15" : 2.445031498756583E-4 } } },
在pom.xml裏添加Mongo-jackson-mapper依賴。
<dependency> <groupId>net.vz.mongodb.jackson</groupId> <artifactId>mongo-jackson-mapper</artifactId> <version>1.4.2</version> </dependency>
更新BlogConfiguration類的MongoDB數據庫信息,如host, port, database name.
import javax.validation.constraints.Max; import javax.validation.constraints.Min; import org.codehaus.jackson.annotate.JsonProperty; import org.hibernate.validator.constraints.NotEmpty; import com.yammer.dropwizard.config.Configuration; public class BlogConfiguration extends Configuration { @JsonProperty @NotEmpty public String mongohost = "localhost"; @JsonProperty @Min(1) @Max(65535) public int mongoport = 27017; @JsonProperty @NotEmpty public String mongodb = "mydb"; }
接下來新建一個MongoManaged類,讓咱們能控制程序啓動和中止,它實現接口com.yammer.dropwizard.lifecycle.Managed.
import com.mongodb.Mongo; import com.yammer.dropwizard.lifecycle.Managed; public class MongoManaged implements Managed { private Mongo mongo; public MongoManaged(Mongo mongo) { this.mongo = mongo; } @Override public void start() throws Exception { } @Override public void stop() throws Exception { mongo.close(); } }
以上代碼,咱們在Stop方法裏關掉MongoDB鏈接。
接下來咱們建立一個MongoHealthCheck來檢查MongoDB是鏈接仍是斷開的,Health check是Dropwizard在生產環境作執行時測試檢測服務的行爲的功能。
import com.mongodb.Mongo; import com.yammer.metrics.core.HealthCheck; public class MongoHealthCheck extends HealthCheck { private Mongo mongo; protected MongoHealthCheck(Mongo mongo) { super("MongoDBHealthCheck"); this.mongo = mongo; } @Override protected Result check() throws Exception { mongo.getDatabaseNames(); return Result.healthy(); } }
如今更新BlogService類, 加入MongoDB配置。
package com.shekhar.blog; import com.mongodb.Mongo; import com.yammer.dropwizard.Service; import com.yammer.dropwizard.config.Bootstrap; import com.yammer.dropwizard.config.Environment; public class BlogService extends Service<BlogConfiguration> { public static void main(String[] args) throws Exception { new BlogService().run(new String[] { "server" }); } @Override public void initialize(Bootstrap<BlogConfiguration> bootstrap) { bootstrap.setName("blog"); } @Override public void run(BlogConfiguration configuration, Environment environment) throws Exception { Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport); MongoManaged mongoManaged = new MongoManaged(mongo); environment.manage(mongoManaged); environment.addHealthCheck(new MongoHealthCheck(mongo)); environment.addResource(new IndexResource()); } }
以上代碼:
以主程序運行應用,能夠經過http://localhost:8081/healthcheck的HealthCheck頁面檢測MongoDB是否在運行,若是沒有運行,能夠看到異常信息。
! MongoDBHealthCheck: ERROR ! can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin com.mongodb.MongoException$Network: can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:227) at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:305) at com.mongodb.DB.command(DB.java:160) at com.mongodb.DB.command(DB.java:183) at com.mongodb.Mongo.getDatabaseNames(Mongo.java:327) at com.shekhar.blog.MongoHealthCheck.check(MongoHealthCheck.java:17) at com.yammer.metrics.core.HealthCheck.execute(HealthCheck.java:195) at Caused by: java.io.IOException: couldn't connect to [Shekhars-MacBook-Pro.local/192.168.1.101:27017] bc:java.net.ConnectException: Connection refused at com.mongodb.DBPort._open(DBPort.java:228) at com.mongodb.DBPort.go(DBPort.java:112) at com.mongodb.DBPort.call(DBPort.java:79) at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:218) ... 33 more * deadlocks: OK
啓動MongoDB能夠看到以下
* MongoDBHealthCheck: OK
* deadlocks: OK
如今來寫BlogResource類,用於響應建立博客的入口。
import java.util.ArrayList; import java.util.List; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import net.vz.mongodb.jackson.DBCursor; import net.vz.mongodb.jackson.JacksonDBCollection; import com.yammer.metrics.annotation.Timed; @Path("/blogs") @Produces(value = MediaType.APPLICATION_JSON) @Consumes(value = MediaType.APPLICATION_JSON) public class BlogResource { private JacksonDBCollection<Blog, String> collection; public BlogResource(JacksonDBCollection<Blog, String> blogs) { this.collection = blogs; } @POST @Timed public Response publishNewBlog(@Valid Blog blog) { collection.insert(blog); return Response.noContent().build(); } }
用Java程序類型運行BlogService類,要測試BlogResource, 設置一個curl請求。
$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"}' http://localhost:8080/blogs HTTP/1.1 204 No Content Date: Sun, 10 Nov 2013 14:08:03 GMT Content-Type: application/json
如今更新IndexResource index()方法,從MongoDB獲取全部博客文檔。
import java.util.ArrayList; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import net.vz.mongodb.jackson.DBCursor; import net.vz.mongodb.jackson.JacksonDBCollection; import com.yammer.metrics.annotation.Timed; @Path("/") public class IndexResource { private JacksonDBCollection<Blog, String> collection; public IndexResource(JacksonDBCollection<Blog, String> blogs) { this.collection = blogs; } @GET @Produces(value = MediaType.APPLICATION_JSON) @Timed public List<Blog> index() { DBCursor<Blog> dbCursor = collection.find(); List<Blog> blogs = new ArrayList<>(); while (dbCursor.hasNext()) { Blog blog = dbCursor.next(); blogs.add(blog); } return blogs; } }
更新BlogService run方法,傳遞博客集合到IndexResource.
@Override public void run(BlogConfiguration configuration, Environment environment) throws Exception { Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport); MongoManaged mongoManaged = new MongoManaged(mongo); environment.manage(mongoManaged); environment.addHealthCheck(new MongoHealthCheck(mongo)); DB db = mongo.getDB(configuration.mongodb); JacksonDBCollection<Blog, String> blogs = JacksonDBCollection.wrap(db.getCollection("blogs"), Blog.class, String.class); environment.addResource(new IndexResource(blogs)); environment.addResource(new BlogResource(blogs)); }
用Java程序形式運行BlogService, 要測試BlogResource, 設置curl請求。
$ curl http://localhost:8080 [{"id":"527f9806300462bbd300687e","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384093702592}]
這有一個博客講咱們怎樣發佈Dropwizard程序到OpenShift, 參考博客。
這是今天的內容,繼續給反饋吧。
原文:https://www.openshift.com/blogs/day-13-dropwizard-the-awesome-java-rest-server-stack