Skip to content
Snippets Groups Projects
Commit 66015ccd authored by Eike Cochu's avatar Eike Cochu
Browse files

updated rest service, added context listener for db connection

renamed domain package to dao
added servlet context listener to create/destroy database connection
junit tests currently broken because of listener
added database configuration to web.xml
fixed logging with log4j2 and slf4j for mongodb, ignoring tomcat logging
extracted article service calls into a service class
parent c21739b3
No related branches found
No related tags found
No related merge requests found
Showing
with 335 additions and 75 deletions
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/scala"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
<attributes>
......
......@@ -11,6 +11,7 @@
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<!-- Jersey REST -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
......@@ -31,19 +32,23 @@
<artifactId>jersey-test-framework-provider-simple</artifactId>
<version>2.22.1</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<artifactId>log4j-api</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<artifactId>log4j-core</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
......@@ -51,11 +56,21 @@
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.4.1</version>
<scope>runtime</scope>
</dependency>
<!-- MongoDB Database Adapter -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.0.4</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
......
package de.cochu.backend.rest;
import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Application extends ResourceConfig {
public static final Logger log = LoggerFactory.getLogger(Application.class);
public Application() {
packages("de.cochu.backend.rest");
log.info("Application started");
}
}
package de.cochu.backend.rest;
public class Messages {
public static final String NOT_FOUND = "%1 with id %2 was not found";
public static final String BAD_REQUEST = "bad request: %1";
}
package de.cochu.backend.rest;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
@WebListener
public class MongoDBContextListener implements ServletContextListener {
public static final Logger log = LoggerFactory.getLogger(MongoDBContextListener.class);
@Override
public void contextDestroyed(ServletContextEvent sce) {
MongoClient mongo = (MongoClient) sce.getServletContext().getAttribute("MONGODB_CLIENT");
mongo.close();
log.debug("mongodb connection closed successfully");
}
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
String host = ctx.getInitParameter("MONGODB_HOST");
Integer port = Integer.parseInt(ctx.getInitParameter("MONGODB_PORT"));
String databaseName = ctx.getInitParameter("MONGODB_DATABASE");
MongoClient mongo = new MongoClient(host, port);
MongoDatabase db = mongo.getDatabase(databaseName);
ctx.setAttribute("MONGODB_CLIENT", mongo);
ctx.setAttribute("MONGODB_DATABASE", db);
log.debug("mongodb connection created successfully");
}
}
package de.cochu.backend.rest.domain;
package de.cochu.backend.rest.dao;
import java.util.ArrayList;
import java.util.Date;
......@@ -9,13 +9,21 @@ import org.bson.Document;
import org.bson.types.ObjectId;
@XmlRootElement
public class Article extends Domain {
public class Article extends Dao {
private String title;
private String text;
private String url;
private Date date;
public Article() {
super();
}
public Article(Document document) {
super(document);
}
public String getTitle() {
return title;
}
......@@ -57,20 +65,19 @@ public class Article extends Domain {
return doc;
}
public static Article fromDocument(final Document doc) {
Article article = new Article();
article.id = doc.getObjectId("_id").toString();
article.title = doc.getString("title");
article.text = doc.getString("text");
article.url = doc.getString("url");
article.date = doc.getDate("date");
return article;
@Override
public void fromDocument(Document document) {
id = document.getObjectId("_id").toString();
title = document.getString("title");
text = document.getString("text");
url = document.getString("url");
date = document.getDate("date");
}
public static ArrayList<Article> fromDocuments(final ArrayList<Document> docs) {
ArrayList<Article> articles = new ArrayList<Article>(docs.size());
for (Document doc : docs) {
articles.add(fromDocument(doc));
articles.add(new Article(doc));
}
return articles;
}
......
package de.cochu.backend.rest.domain;
package de.cochu.backend.rest.dao;
import java.net.MalformedURLException;
import java.net.URI;
......@@ -9,10 +9,17 @@ import javax.xml.bind.annotation.XmlRootElement;
import org.bson.Document;
@XmlRootElement
public abstract class Domain {
public abstract class Dao {
protected String id;
public Dao() {
}
public Dao(Document document) {
fromDocument(document);
}
public String getId() {
return id;
}
......@@ -27,4 +34,6 @@ public abstract class Domain {
public abstract Document toDocument();
public abstract void fromDocument(Document document);
}
......@@ -2,14 +2,15 @@ package de.cochu.backend.rest.resource;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
......@@ -18,33 +19,32 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.GenericEntity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import static com.mongodb.client.model.Sorts.*;
import javax.ws.rs.core.GenericEntity;
import de.cochu.backend.rest.domain.Article;
import de.cochu.backend.rest.Messages;
import de.cochu.backend.rest.dao.Article;
import de.cochu.backend.rest.service.ArticleService;
@Path("articles")
public class Articles {
public class ArticleResource {
static Logger log = LogManager.getLogger(Articles.class);
public static final Logger log = LoggerFactory.getLogger(ArticleResource.class);
@Context
UriInfo uri;
MongoClient mongo = new MongoClient();
MongoDatabase db = mongo.getDatabase("test");
MongoCollection<Document> articles = db.getCollection("articles");
final ArticleService service;
public ArticleResource(@Context ServletContext context) {
MongoDatabase db = (MongoDatabase) context.getAttribute("MONGODB_DATABASE");
service = new ArticleService(db);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
......@@ -52,46 +52,69 @@ public class Articles {
@QueryParam("limit") @DefaultValue("0") int limit,
@QueryParam("sortby") @DefaultValue("date") String sortBy,
@QueryParam("order") @DefaultValue("desc") String order) {
ArrayList<Document> result = articles.find().skip(skip).limit(limit)
.sort(order.equals("desc") ? descending(sortBy) : ascending(sortBy)).into(new ArrayList<Document>());
ArrayList<Article> articles = Article.fromDocuments(result);
List<Article> articles = service.getArticles(skip, limit, sortBy, order);
return Response.ok(new GenericEntity<List<Article>>(articles) {
}).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("{id}")
public Response getArticle(@PathParam("id") String id) {
if (id == null || id.trim().length() == 0) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(String.format(Messages.BAD_REQUEST, "id cannot be empty")).build();
}
ObjectId objectId = new ObjectId(id);
ArrayList<Document> result = articles.find(Filters.eq("_id", objectId)).into(new ArrayList<Document>());
if (result.size() == 1) {
Article article = Article.fromDocument(result.get(0));
Article article = service.getArticle(id);
if (article != null) {
return Response.ok(article).build();
} else {
return Response.status(Response.Status.NOT_FOUND).type(MediaType.TEXT_PLAIN)
.entity(String.format(Messages.NOT_FOUND, "article", id)).build();
}
return Response.status(Response.Status.NOT_FOUND).entity(String.format(Messages.NOT_FOUND, id)).build();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createArticle(Article article) throws MalformedURLException, URISyntaxException {
Document doc = new Document(article.toDocument());
articles.insertOne(doc);
article = Article.fromDocument(doc);
article = service.createArticle(article);
return Response.created(article.getURI(uri.getAbsolutePath())).entity(article).build();
}
@DELETE
@Path("{id}")
public Response deleteArticle(@PathParam("id") String id) {
ObjectId objectId = new ObjectId(id);
articles.deleteOne(Filters.eq("_id", objectId));
return Response.noContent().build();
long deleted = service.deleteArticle(id);
switch (Math.toIntExact(deleted)) {
case 0:
return Response.status(Response.Status.NOT_FOUND).entity(String.format(Messages.NOT_FOUND, "article", id))
.build();
case 1:
return Response.noContent().build();
default:
log.error(String.format("deleted count while deleting article with id %1 was %2", id, deleted));
return Response.serverError().build();
}
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
public Response updateArticle(@PathParam("id") String id, Article article) {
long updated = service.updateArticle(article);
switch (Math.toIntExact(updated)) {
case 0:
return Response.status(Response.Status.NOT_FOUND).type(MediaType.TEXT_PLAIN)
.entity(String.format(Messages.NOT_FOUND, "article", id)).build();
case 1:
return Response.ok().entity(article).build();
default:
log.error(String.format("updated count while updating article with id %1 was %2", id, updated));
return Response.serverError().build();
}
}
}
package de.cochu.backend.rest.resource;
public class Messages {
public static final String NOT_FOUND = "Entity not found for id %1";
public static final String BAD_REQUEST = "Bad request: %1";
}
package de.cochu.backend.rest.service;
import static com.mongodb.client.model.Sorts.ascending;
import static com.mongodb.client.model.Sorts.descending;
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import org.bson.types.ObjectId;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import de.cochu.backend.rest.dao.Article;
public class ArticleService {
final MongoCollection<Document> articles;
public ArticleService(MongoDatabase db) {
articles = db.getCollection("articles");
}
public Article getArticle(String id) {
ObjectId objectId = new ObjectId(id);
ArrayList<Document> result = articles.find(Filters.eq("_id", objectId)).into(new ArrayList<Document>());
if (result.size() == 1) {
return new Article(result.get(0));
} else {
return null;
}
}
public List<Article> getArticles(int skip, int limit, String sortBy, String order) {
ArrayList<Document> docs = articles.find().skip(skip).limit(limit)
.sort(order.equals("desc") ? descending(sortBy) : ascending(sortBy)).into(new ArrayList<Document>());
ArrayList<Article> result = Article.fromDocuments(docs);
for (Article article : result) {
article.setText(null);
}
return result;
}
public Article createArticle(Article article) {
Document doc = new Document(article.toDocument());
articles.insertOne(doc);
return new Article(doc);
}
public long deleteArticle(String id) {
ObjectId objectId = new ObjectId(id);
DeleteResult result = articles.deleteOne(Filters.eq("_id", objectId));
return result.getDeletedCount();
}
public long updateArticle(Article article) {
Document docOld = new Document("_id", new ObjectId(article.getId()));
Document docNew = article.toDocument();
UpdateResult result = articles.replaceOne(docOld, docNew);
return result.getModifiedCount();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF">
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="error">
<Root level="all">
<AppenderRef ref="Console" />
</Root>
</Loggers>
......
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:web="http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee">
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>de.cochu.backend.rest.Application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee" xmlns:web="http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee">
<context-param>
<param-name>MONGODB_HOST</param-name>
<param-value>localhost</param-value>
</context-param>
<context-param>
<param-name>MONGODB_PORT</param-name>
<param-value>27017</param-value>
</context-param>
<context-param>
<param-name>MONGODB_DATABASE</param-name>
<param-value>test</param-value>
</context-param>
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>de.cochu.backend.rest.Application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
de.cochu.backend.rest.MongoDBContextListener
</listener-class>
</listener>
</web-app>
\ No newline at end of file
......@@ -9,11 +9,11 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
public class ArticlesTest extends JerseyTest {
public class ArticleResourceTest extends JerseyTest {
@Override
protected Application configure() {
return new ResourceConfig(Articles.class);
return new ResourceConfig(ArticleResource.class);
}
@Test
......
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment