From 66015ccd6d410641aee86da03a5ebaf946112278 Mon Sep 17 00:00:00 2001
From: Eike Cochu <eike@cochu.com>
Date: Wed, 9 Dec 2015 22:23:45 +0100
Subject: [PATCH] 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
---
 tmbs-processor-backend/.classpath             |   5 -
 tmbs-rest-backend/pom.xml                     |  19 ++-
 .../de/cochu/backend/rest/Application.java    |   7 +-
 .../java/de/cochu/backend/rest/Messages.java  |   8 ++
 .../backend/rest/MongoDBContextListener.java  |  44 +++++++
 .../backend/rest/{domain => dao}/Article.java |  29 +++--
 .../rest/{domain/Domain.java => dao/Dao.java} |  13 +-
 .../rest/resource/ArticleResource.java        | 120 ++++++++++++++++++
 .../cochu/backend/rest/resource/Articles.java |  97 --------------
 .../cochu/backend/rest/resource/Messages.java |   8 --
 .../backend/rest/service/ArticleService.java  |  67 ++++++++++
 .../src/main/resources/log4j2.xml             |   6 +-
 .../src/main/webapp/WEB-INF/web.xml           |  54 +++++---
 ...clesTest.java => ArticleResourceTest.java} |   4 +-
 .../src/test/resources/log4j2.xml             |  13 --
 tmbs-rest-backend/target/classes/log4j2.xml   |  13 --
 16 files changed, 335 insertions(+), 172 deletions(-)
 create mode 100644 tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Messages.java
 create mode 100644 tmbs-rest-backend/src/main/java/de/cochu/backend/rest/MongoDBContextListener.java
 rename tmbs-rest-backend/src/main/java/de/cochu/backend/rest/{domain => dao}/Article.java (71%)
 rename tmbs-rest-backend/src/main/java/de/cochu/backend/rest/{domain/Domain.java => dao/Dao.java} (70%)
 create mode 100644 tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/ArticleResource.java
 delete mode 100644 tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Articles.java
 delete mode 100644 tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Messages.java
 create mode 100644 tmbs-rest-backend/src/main/java/de/cochu/backend/rest/service/ArticleService.java
 rename tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/{ArticlesTest.java => ArticleResourceTest.java} (82%)
 delete mode 100644 tmbs-rest-backend/src/test/resources/log4j2.xml
 delete mode 100644 tmbs-rest-backend/target/classes/log4j2.xml

diff --git a/tmbs-processor-backend/.classpath b/tmbs-processor-backend/.classpath
index 0d99fca2..31aaf996 100644
--- a/tmbs-processor-backend/.classpath
+++ b/tmbs-processor-backend/.classpath
@@ -1,11 +1,6 @@
 <?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>
diff --git a/tmbs-rest-backend/pom.xml b/tmbs-rest-backend/pom.xml
index c8178215..51084ac8 100644
--- a/tmbs-rest-backend/pom.xml
+++ b/tmbs-rest-backend/pom.xml
@@ -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>
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Application.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Application.java
index 44869d6a..a761f085 100644
--- a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Application.java
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Application.java
@@ -1,11 +1,16 @@
 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");
 	}
-	
+
 }
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Messages.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Messages.java
new file mode 100644
index 00000000..862d37b4
--- /dev/null
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/Messages.java
@@ -0,0 +1,8 @@
+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";
+
+}
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/MongoDBContextListener.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/MongoDBContextListener.java
new file mode 100644
index 00000000..bf00a33c
--- /dev/null
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/MongoDBContextListener.java
@@ -0,0 +1,44 @@
+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");
+	}
+
+}
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/domain/Article.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/dao/Article.java
similarity index 71%
rename from tmbs-rest-backend/src/main/java/de/cochu/backend/rest/domain/Article.java
rename to tmbs-rest-backend/src/main/java/de/cochu/backend/rest/dao/Article.java
index f7cb9dc4..07066868 100644
--- a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/domain/Article.java
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/dao/Article.java
@@ -1,4 +1,4 @@
-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;
 	}
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/domain/Domain.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/dao/Dao.java
similarity index 70%
rename from tmbs-rest-backend/src/main/java/de/cochu/backend/rest/domain/Domain.java
rename to tmbs-rest-backend/src/main/java/de/cochu/backend/rest/dao/Dao.java
index 931db095..bc3ccec9 100644
--- a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/domain/Domain.java
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/dao/Dao.java
@@ -1,4 +1,4 @@
-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);
+
 }
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/ArticleResource.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/ArticleResource.java
new file mode 100644
index 00000000..ed8cdba2
--- /dev/null
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/ArticleResource.java
@@ -0,0 +1,120 @@
+package de.cochu.backend.rest.resource;
+
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+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;
+import javax.ws.rs.QueryParam;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.mongodb.client.MongoDatabase;
+
+import javax.ws.rs.core.GenericEntity;
+
+import de.cochu.backend.rest.Messages;
+import de.cochu.backend.rest.dao.Article;
+import de.cochu.backend.rest.service.ArticleService;
+
+@Path("articles")
+public class ArticleResource {
+
+	public static final Logger log = LoggerFactory.getLogger(ArticleResource.class);
+
+	@Context
+	UriInfo uri;
+
+	final ArticleService service;
+
+	public ArticleResource(@Context ServletContext context) {
+		MongoDatabase db = (MongoDatabase) context.getAttribute("MONGODB_DATABASE");
+		service = new ArticleService(db);
+	}
+
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getArticles(@QueryParam("skip") @DefaultValue("0") int skip,
+			@QueryParam("limit") @DefaultValue("0") int limit,
+			@QueryParam("sortby") @DefaultValue("date") String sortBy,
+			@QueryParam("order") @DefaultValue("desc") String order) {
+		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();
+		}
+		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();
+		}
+	}
+
+	@POST
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response createArticle(Article article) throws MalformedURLException, URISyntaxException {
+		article = service.createArticle(article);
+		return Response.created(article.getURI(uri.getAbsolutePath())).entity(article).build();
+	}
+
+	@DELETE
+	@Path("{id}")
+	public Response deleteArticle(@PathParam("id") String id) {
+		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();
+		}
+	}
+
+}
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Articles.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Articles.java
deleted file mode 100644
index 2c76d5c5..00000000
--- a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Articles.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package de.cochu.backend.rest.resource;
-
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
-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.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-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 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 de.cochu.backend.rest.domain.Article;
-
-@Path("articles")
-public class Articles {
-
-	static Logger log = LogManager.getLogger(Articles.class);
-
-	@Context
-	UriInfo uri;
-
-	MongoClient mongo = new MongoClient();
-	MongoDatabase db = mongo.getDatabase("test");
-	MongoCollection<Document> articles = db.getCollection("articles");
-
-	@GET
-	@Produces(MediaType.APPLICATION_JSON)
-	public Response getArticles(@QueryParam("skip") @DefaultValue("0") int skip,
-			@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);
-		return Response.ok(new GenericEntity<List<Article>>(articles) {
-		}).build();
-	}
-
-	@GET
-	@Produces(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));
-			return Response.ok(article).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);
-		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();
-	}
-
-}
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Messages.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Messages.java
deleted file mode 100644
index cce27737..00000000
--- a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/resource/Messages.java
+++ /dev/null
@@ -1,8 +0,0 @@
-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";
-
-}
diff --git a/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/service/ArticleService.java b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/service/ArticleService.java
new file mode 100644
index 00000000..49f09fb3
--- /dev/null
+++ b/tmbs-rest-backend/src/main/java/de/cochu/backend/rest/service/ArticleService.java
@@ -0,0 +1,67 @@
+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();
+	}
+
+}
diff --git a/tmbs-rest-backend/src/main/resources/log4j2.xml b/tmbs-rest-backend/src/main/resources/log4j2.xml
index fe4f3e8a..8abf1b19 100644
--- a/tmbs-rest-backend/src/main/resources/log4j2.xml
+++ b/tmbs-rest-backend/src/main/resources/log4j2.xml
@@ -1,12 +1,12 @@
 <?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>
diff --git a/tmbs-rest-backend/src/main/webapp/WEB-INF/web.xml b/tmbs-rest-backend/src/main/webapp/WEB-INF/web.xml
index 6bef82bf..529eb522 100644
--- a/tmbs-rest-backend/src/main/webapp/WEB-INF/web.xml
+++ b/tmbs-rest-backend/src/main/webapp/WEB-INF/web.xml
@@ -1,18 +1,42 @@
 <?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
diff --git a/tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/ArticlesTest.java b/tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/ArticleResourceTest.java
similarity index 82%
rename from tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/ArticlesTest.java
rename to tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/ArticleResourceTest.java
index 924b1308..3a1a7607 100644
--- a/tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/ArticlesTest.java
+++ b/tmbs-rest-backend/src/test/java/de/cochu/backend/rest/resource/ArticleResourceTest.java
@@ -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
diff --git a/tmbs-rest-backend/src/test/resources/log4j2.xml b/tmbs-rest-backend/src/test/resources/log4j2.xml
deleted file mode 100644
index e43e1b99..00000000
--- a/tmbs-rest-backend/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?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
diff --git a/tmbs-rest-backend/target/classes/log4j2.xml b/tmbs-rest-backend/target/classes/log4j2.xml
deleted file mode 100644
index fe4f3e8a..00000000
--- a/tmbs-rest-backend/target/classes/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?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
-- 
GitLab