diff --git a/.gitignore b/.gitignore
index f7500c5f50d673c70bb1d825f2b98ea231803103..0aecefcd70688787a253e45d05604ea23366e8c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
+*.log
 .vagrant/
-vm/webapps/
\ No newline at end of file
+vm/webapps/
diff --git a/ma-impl.sublime-workspace b/ma-impl.sublime-workspace
index 9e358ffc760a20886c0c39b38f68707e5e3eded2..400b2e04842c26c7404cd13cef0c20702e9d4b3d 100644
--- a/ma-impl.sublime-workspace
+++ b/ma-impl.sublime-workspace
@@ -275,14 +275,6 @@
 	},
 	"buffers":
 	[
-		{
-			"file": "vm/bootstrap.sh",
-			"settings":
-			{
-				"buffer_size": 3519,
-				"line_ending": "Unix"
-			}
-		}
 	],
 	"build_system": "",
 	"build_system_choices":
@@ -462,15 +454,24 @@
 	"expanded_folders":
 	[
 		"/home/eike/repos/master/ma-impl",
-		"/home/eike/repos/master/ma-impl/vm"
+		"/home/eike/repos/master/ma-impl/vipra-cmd"
 	],
 	"file_history":
 	[
-		"/usr/share/applications/defaults.list",
+		"/home/eike/repos/master/ma-impl/vipra-cmd.sh",
+		"/home/eike/repos/master/ma-impl/vipra-cmd/build2.xml",
+		"/home/eike/repos/master/ma-impl/vipra-ui/README.md",
+		"/home/eike/repos/master/ma-impl/vipra-ui/bower.json",
+		"/home/eike/repos/testasd/bower.json",
+		"/home/eike/repos/master/ma-impl/vipra-ui2/package.json",
+		"/home/eike/repos/master/ma-impl/vipra-ui/app/components/dynamic-high-charts.js",
+		"/home/eike/repos/master/ma-impl/vipra-ui/app/routes/articles/list.js",
+		"/home/eike/repos/master/ma-impl/vipra-ui/package.json",
+		"/home/eike/repos/master/ma-impl/vipra-ui/node_modules/ember-highcharts/package.json",
+		"/home/eike/repos/master/ma-impl/vm/bootstrap.sh",
 		"/home/eike/repos/master/ma-impl/vm/config/initd-mongod",
 		"/home/eike/repos/master/ma-impl/vm/webapps/vipra-rest/WEB-INF/web.xml",
 		"/core",
-		"/home/eike/repos/master/ma-impl/vm/bootstrap.sh",
 		"/home/eike/repos/master/ma-impl/vm/config/environment",
 		"/home/eike/repos/master/ma-impl/Vagrantfile",
 		"/home/eike/repos/master/ma-impl/vm/config/initd-tomcat",
@@ -584,16 +585,7 @@
 		"/home/eike/.duplicity-exclude",
 		"/home/eike/Repositories/fu/ss15/ki/exercise-08/task2a.pl",
 		"/home/eike/Repositories/fu/ss15/ki/exercise-08/games.nkb",
-		"/home/eike/Repositories/fu/ss15/ki/exercise-08/exercise-08.tex",
-		"/home/eike/Repositories/fu/ss15/ki/exercise-08/task01.pl",
-		"/home/eike/Repositories/latex-templates/invoice.tex",
-		"/home/eike/Repositories/niels_website/Rechnungen/exercise-01.tex",
-		"/home/eike/OwnCloud/NiELS/todo-2015-06-13.txt",
-		"/home/eike/Repositories/fu/ss15/bfm/exercise-06/ex06.txt",
-		"/home/eike/Repositories/fu/ss15/bfm/exercise-04/ex04.txt",
-		"/home/eike/Repositories/fu/ss15/bfm/exercise-05/ex05.txt",
-		"/home/eike/Repositories/fu/ss15/ki/exercise-08/native.pl",
-		"/home/eike/Repositories/fu/ss15/ki/exercise-08/birds.nkb"
+		"/home/eike/Repositories/fu/ss15/ki/exercise-08/exercise-08.tex"
 	],
 	"find":
 	{
@@ -642,7 +634,6 @@
 		"case_sensitive": false,
 		"find_history":
 		[
-			"gedit.desktop",
 			"00:00Z\" },",
 			"{ \"$date\": ",
 			".000+0000",
@@ -769,7 +760,8 @@
 			"intended",
 			"redirect_intended",
 			"amount-in",
-			"'"
+			"'",
+			"!important"
 		],
 		"highlight": true,
 		"in_selection": false,
@@ -777,7 +769,6 @@
 		"regex": false,
 		"replace_history":
 		[
-			"sublime_text.desktop",
 			"00:00Z\",",
 			"",
 			"Z",
@@ -904,7 +895,8 @@
 			"@append$1",
 			"survey",
 			"SurveysController",
-			""
+			"",
+			"/assets"
 		],
 		"reverse": false,
 		"show_context": true,
@@ -915,37 +907,8 @@
 	"groups":
 	[
 		{
-			"selected": 0,
 			"sheets":
 			[
-				{
-					"buffer": 0,
-					"file": "vm/bootstrap.sh",
-					"semi_transient": false,
-					"settings":
-					{
-						"buffer_size": 3519,
-						"regions":
-						{
-						},
-						"selection":
-						[
-							[
-								933,
-								933
-							]
-						],
-						"settings":
-						{
-							"syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage"
-						},
-						"translation.x": 0.0,
-						"translation.y": 0.0,
-						"zoom_level": 1.0
-					},
-					"stack_index": 0,
-					"type": "text"
-				}
 			]
 		}
 	],
@@ -996,7 +959,7 @@
 	"project": "ma-impl.sublime-project",
 	"replace":
 	{
-		"height": 66.0
+		"height": 46.0
 	},
 	"save_all_on_build": true,
 	"select_file":
diff --git a/vipra-cmd/src/main/resources/log4j2.xml b/vipra-cmd/src/main/resources/log4j2.xml
index 2fc755c19c3d600e8e689f71fb93fc590e4178bb..f2c17f3831e8186b7cb4e19ab64efec6a43ca294 100644
--- a/vipra-cmd/src/main/resources/log4j2.xml
+++ b/vipra-cmd/src/main/resources/log4j2.xml
@@ -4,10 +4,19 @@
 		<Console name="Console" target="SYSTEM_OUT">
 			<PatternLayout pattern="%highlight{%-5level - %msg%n}{FATAL=red,ERROR=red,WARN=red,INFO=normal,DEBUG=normal,TRACE=normal}" />
 		</Console>
+		<File name="File" fileName="vipra-cmd.log" append="false">
+			<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n" />
+			<Filters>
+				<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
+				<ThresholdFilter level="DEBUG" onMatch="DENY" onMismatch="NEUTRAL"/>
+				<ThresholdFilter level="TRACE" onMatch="DENY" onMismatch="NEUTRAL"/>
+			</Filters>
+		</File>
 	</Appenders>
 	<Loggers>
 		<Root level="ERROR">
 			<AppenderRef ref="Console" />
+			<AppenderRef ref="File" />
 		</Root>
 		<Logger name="shellout" level="INFO" />
 	</Loggers>
diff --git a/vipra-rest/.classpath b/vipra-rest/.classpath
index 9045b6827282a56ced68ac1642cb9030843d918a..3d2bb26617600d59704c87bd3ac5f184450c98b0 100644
--- a/vipra-rest/.classpath
+++ b/vipra-rest/.classpath
@@ -22,5 +22,11 @@
 			<attribute name="maven.pomderived" value="true"/>
 		</attributes>
 	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
 	<classpathentry kind="output" path="target/classes"/>
 </classpath>
diff --git a/vipra-rest/.settings/org.eclipse.wst.common.component b/vipra-rest/.settings/org.eclipse.wst.common.component
index a844e2a09c35fe74264f050cbdb15288a43c3a40..8f4686f842327678fcce653ac158229d64b411e4 100644
--- a/vipra-rest/.settings/org.eclipse.wst.common.component
+++ b/vipra-rest/.settings/org.eclipse.wst.common.component
@@ -8,6 +8,6 @@
             <dependency-type>uses</dependency-type>
         </dependent-module>
         <property name="java-output-path" value="/vipra-rest/target/classes"/>
-        <property name="context-root" value="rest"/>
+        <property name="context-root" value="vipra-rest"/>
     </wb-module>
 </project-modules>
diff --git a/vipra-rest/pom.xml b/vipra-rest/pom.xml
index bb7194310298b9ec118271c8196e5a859d6ed268..97251852e16040e9e9c1abfbc37f59ee7ad5f0ca 100644
--- a/vipra-rest/pom.xml
+++ b/vipra-rest/pom.xml
@@ -20,6 +20,10 @@
 		<log4jVersion>2.4.1</log4jVersion>
 	</properties>
 
+	<build>
+		<finalName>vipra-rest</finalName>
+	</build>
+
 	<dependencies>
 		<!-- Jersey REST -->
 		<dependency>
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Article.java b/vipra-rest/src/main/java/de/vipra/rest/model/Article.java
index 15e9003eee3bd6479e401b4d99f871a8c7c00eeb..c14c71a640b6be25b5e794f5b86880783abc5e21 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Article.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/Article.java
@@ -4,7 +4,7 @@ import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 
-public class Article extends de.vipra.util.model.Article {
+public class Article extends de.vipra.util.model.Article implements Linked {
 
 	private Map<String, String> links;
 
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Linked.java b/vipra-rest/src/main/java/de/vipra/rest/model/Linked.java
index b9f157ba2e172476e7673f6fecd93d675f8e22b3..b3033930223dc97940ce5b09c6bc6fef42effada 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Linked.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/Linked.java
@@ -3,9 +3,7 @@ package de.vipra.rest.model;
 import java.net.URI;
 import java.util.Map;
 
-import de.vipra.util.model.Model;
-
-public abstract class Linked extends Model {
+public interface Linked {
 
 	public abstract Map<String, String> getLinks();
 
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Topic.java b/vipra-rest/src/main/java/de/vipra/rest/model/TopicDefinition.java
similarity index 85%
rename from vipra-rest/src/main/java/de/vipra/rest/model/Topic.java
rename to vipra-rest/src/main/java/de/vipra/rest/model/TopicDefinition.java
index d99d427335272ef88310d4bdb0dd64c7fe1dab97..a514a4cd1248a8c00773f73818192ab490115435 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Topic.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/TopicDefinition.java
@@ -4,7 +4,7 @@ import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 
-public class Topic extends de.vipra.util.model.TopicDefinition {
+public class TopicDefinition extends de.vipra.util.model.TopicDefinition implements Linked {
 
 	private Map<String, String> links;
 
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
index 6862695136636d10edf6c12c00cb973749e5a038..edd78cc9bcecb77d0f6f1dbcd34df8702383454c 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
@@ -1,7 +1,7 @@
 package de.vipra.rest.resource;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.util.List;
 
 import javax.servlet.ServletContext;
 import javax.ws.rs.Consumes;
@@ -48,8 +48,8 @@ public class ArticleResource {
 	public Response getArticles(@QueryParam("skip") @DefaultValue("0") int skip,
 			@QueryParam("limit") @DefaultValue("0") int limit,
 			@QueryParam("sort") @DefaultValue("date") String sortBy) {
-		ArrayList<Article> articles = service.getArticles(uri.getAbsolutePath(), skip, limit, sortBy);
-		ResponseWrapper<ArrayList<Article>> res = new ResponseWrapper<>(articles);
+		List<Article> articles = service.getMultiple(uri.getAbsolutePath(), skip, limit, sortBy);
+		ResponseWrapper<List<Article>> res = new ResponseWrapper<>(articles);
 		res.addLink("self", uri.getAbsolutePath().toString());
 		return Response.ok().entity(res).build();
 	}
@@ -65,7 +65,7 @@ public class ArticleResource {
 					String.format(Messages.BAD_REQUEST, "id cannot be empty")));
 			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
 		}
-		Article article = service.getArticle(uri.getAbsolutePath(), id);
+		Article article = service.getSingle(uri.getAbsolutePath(), id);
 		if (article != null) {
 			res.setData(article);
 			return Response.ok().entity(res).build();
@@ -82,7 +82,7 @@ public class ArticleResource {
 	public Response createArticle(Article article) {
 		ResponseWrapper<Article> res;
 		try {
-			article = service.createArticle(uri.getAbsolutePath(), article);
+			article = service.createSingle(uri.getAbsolutePath(), article);
 			res = new ResponseWrapper<>(article);
 			return Response.created(article.uri(uri.getAbsolutePath())).entity(res).build();
 		} catch (DatabaseException e) {
@@ -98,7 +98,7 @@ public class ArticleResource {
 		ResponseWrapper<Article> res = new ResponseWrapper<>();
 		long deleted;
 		try {
-			deleted = service.deleteArticle(id);
+			deleted = service.deleteSingle(id);
 		} catch (DatabaseException e) {
 			res = new ResponseWrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be deleted",
 					"item could not be created due to an internal server error"));
@@ -124,7 +124,7 @@ public class ArticleResource {
 	public Response updateArticle(@PathParam("id") String id, Article article) {
 		ResponseWrapper<Article> res = new ResponseWrapper<>();
 		try {
-			long updated = service.updateArticle(uri.getAbsolutePath(), article);
+			long updated = service.updateSingle(uri.getAbsolutePath(), article);
 			int updatedInt = updated > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) updated;
 			switch (updatedInt) {
 			case 0:
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java
index f0b5d571ad7aaad45b680f087c6632bec8be1567..f2f94cb023fcd37574c9875a536af06017dc06ad 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java
@@ -1,7 +1,7 @@
 package de.vipra.rest.resource;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.util.List;
 
 import javax.servlet.ServletContext;
 import javax.ws.rs.Consumes;
@@ -20,12 +20,12 @@ import de.vipra.rest.APIMediaType;
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
 import de.vipra.rest.model.ResponseWrapper;
-import de.vipra.rest.model.Topic;
+import de.vipra.rest.model.TopicDefinition;
+import de.vipra.rest.service.TopicService;
 import de.vipra.util.Config;
-import de.vipra.util.Constants;
+import de.vipra.util.Mongo;
 import de.vipra.util.ex.ConfigException;
 import de.vipra.util.ex.DatabaseException;
-import de.vipra.util.service.DatabaseService;
 
 @Path("topics")
 public class TopicResource {
@@ -33,19 +33,20 @@ public class TopicResource {
 	@Context
 	UriInfo uri;
 
-	DatabaseService<Topic> service;
+	TopicService service;
 
 	public TopicResource(@Context ServletContext servletContext) throws ConfigException, IOException {
 		Config config = Config.getConfig();
-		service = DatabaseService.getDatabaseService(config, Constants.Collection.TOPICS, Topic.class);
+		Mongo mongo = Mongo.getInstance(config);
+		service = new TopicService(mongo);
 	}
 
 	@GET
 	@Produces(APIMediaType.APPLICATION_JSONAPI)
 	public Response getTopics(@QueryParam("skip") @DefaultValue("0") int skip,
 			@QueryParam("limit") @DefaultValue("0") int limit) {
-		ArrayList<Topic> topics = service.getMultiple(skip, limit, null);
-		ResponseWrapper<ArrayList<Topic>> res = new ResponseWrapper<>(topics);
+		List<TopicDefinition> topics = service.getMultiple(uri.getAbsolutePath(), skip, limit, null);
+		ResponseWrapper<List<TopicDefinition>> res = new ResponseWrapper<>(topics);
 		res.addLink("self", uri.getAbsolutePath().toString());
 		return Response.ok().entity(res).build();
 	}
@@ -55,15 +56,14 @@ public class TopicResource {
 	@Consumes(APIMediaType.APPLICATION_JSONAPI)
 	@Path("{id}")
 	public Response getTopic(@PathParam("id") String id) {
-		ResponseWrapper<Topic> res = new ResponseWrapper<>();
+		ResponseWrapper<TopicDefinition> res = new ResponseWrapper<>();
 		if (id == null || id.trim().length() == 0) {
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty",
 					String.format(Messages.BAD_REQUEST, "id cannot be empty")));
 			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
 		}
-		Topic topic = service.getSingle(id);
+		TopicDefinition topic = service.getSingle(uri.getAbsolutePath(), id);
 		if (topic != null) {
-			topic.setBase(uri.getAbsolutePath());
 			res.setData(topic);
 			return Response.ok().entity(res).build();
 		} else {
@@ -77,8 +77,8 @@ public class TopicResource {
 	@Consumes(APIMediaType.APPLICATION_JSONAPI)
 	@Produces(APIMediaType.APPLICATION_JSONAPI)
 	@Path("{id}")
-	public Response updateTopic(@PathParam("id") String id, Topic topic) {
-		ResponseWrapper<Topic> res = new ResponseWrapper<>();
+	public Response updateTopic(@PathParam("id") String id, TopicDefinition topic) {
+		ResponseWrapper<TopicDefinition> res = new ResponseWrapper<>();
 		try {
 			long updated = service.updateSingle(topic);
 			int updatedInt = updated > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) updated;
diff --git a/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java b/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java
index 139413e1dbb225cd9ee59f01ae01052a1921fde9..099194ea9e1057ca7b0bec3e851907b6336804b1 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java
@@ -1,55 +1,26 @@
 package de.vipra.rest.service;
 
 import java.net.URI;
-import java.util.ArrayList;
+import java.util.List;
 
 import de.vipra.rest.model.Article;
 import de.vipra.util.Constants;
 import de.vipra.util.Mongo;
-import de.vipra.util.ex.DatabaseException;
-import de.vipra.util.service.DatabaseService;
 
-public class ArticleService extends DatabaseService<Article> {
+public class ArticleService extends Service<Article> {
 
 	public ArticleService(Mongo mongo) {
 		super(mongo, Constants.Collection.ARTICLES, Article.class);
 	}
 
-	public Article getArticle(URI base, String id) {
-		Article article = super.getSingle(id);
-		if (article != null) {
-			article.setBase(base);
-		}
-		return article;
-	}
-
-	public ArrayList<Article> getArticles(URI base, int skip, int limit, String sortBy) {
-		ArrayList<Article> articles = super.getMultiple(skip, limit, sortBy);
+	public List<Article> getMultiple(URI base, int skip, int limit, String sortBy) {
+		List<Article> articles = super.getMultiple(skip, limit, sortBy);
 		for (Article article : articles) {
 			// delete data for listing
 			article.setText(null);
 			article.setStats(null);
-			article.setBase(base);
 		}
 		return articles;
 	}
 
-	public Article createArticle(URI base, Article article) throws DatabaseException {
-		article = super.createSingle(article);
-		if (article != null) {
-			article.setBase(base);
-		}
-		return article;
-	}
-
-	public long deleteArticle(String id) throws DatabaseException {
-		return super.deleteSingle(id);
-	}
-
-	public long updateArticle(URI base, Article article) throws DatabaseException {
-		long updated = super.updateSingle(article);
-		article.setBase(base);
-		return updated;
-	}
-
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/service/Service.java b/vipra-rest/src/main/java/de/vipra/rest/service/Service.java
new file mode 100644
index 0000000000000000000000000000000000000000..01107f8d963b00ef46ef9de57bc633cccabfaf9b
--- /dev/null
+++ b/vipra-rest/src/main/java/de/vipra/rest/service/Service.java
@@ -0,0 +1,53 @@
+package de.vipra.rest.service;
+
+import java.net.URI;
+import java.util.List;
+
+import de.vipra.rest.model.Linked;
+import de.vipra.util.Constants.Collection;
+import de.vipra.util.ex.DatabaseException;
+import de.vipra.util.Mongo;
+import de.vipra.util.model.Model;
+import de.vipra.util.service.DatabaseService;
+
+public class Service<T extends Model & Linked> extends DatabaseService<T> {
+
+	public Service(Mongo mongo, Collection collection, Class<T> clazz) {
+		super(mongo, collection, clazz);
+	}
+
+	public T getSingle(URI base, String id) {
+		T t = super.getSingle(id);
+		if (t != null) {
+			t.setBase(base);
+		}
+		return t;
+	}
+
+	public List<T> getMultiple(URI base, int skip, int limit, String sortBy) {
+		List<T> ts = super.getMultiple(skip, limit, sortBy);
+		for (T t : ts) {
+			t.setBase(base);
+		}
+		return ts;
+	}
+
+	public T createSingle(URI base, T t) throws DatabaseException {
+		t = super.createSingle(t);
+		if (t != null) {
+			t.setBase(base);
+		}
+		return t;
+	}
+
+	public long deleteSingle(String id) throws DatabaseException {
+		return super.deleteSingle(id);
+	}
+
+	public long updateSingle(URI base, T t) throws DatabaseException {
+		long updated = super.updateSingle(t);
+		t.setBase(base);
+		return updated;
+	}
+
+}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java b/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java
new file mode 100644
index 0000000000000000000000000000000000000000..56a4897a2509f782acb4939b1e6584770351331b
--- /dev/null
+++ b/vipra-rest/src/main/java/de/vipra/rest/service/TopicService.java
@@ -0,0 +1,24 @@
+package de.vipra.rest.service;
+
+import java.net.URI;
+import java.util.List;
+
+import de.vipra.rest.model.TopicDefinition;
+import de.vipra.util.Constants;
+import de.vipra.util.Mongo;
+
+public class TopicService extends Service<TopicDefinition> {
+
+	public TopicService(Mongo mongo) {
+		super(mongo, Constants.Collection.TOPICS, TopicDefinition.class);
+	}
+
+	public List<TopicDefinition> getMultiple(URI base, int skip, int limit, String sortBy) {
+		List<TopicDefinition> topics = super.getMultiple(skip, limit, sortBy);
+		for (TopicDefinition topic : topics) {
+			topic.setWords(null);
+		}
+		return topics;
+	}
+
+}
diff --git a/vipra-ui/app/components/dynamic-high-charts.js b/vipra-ui/app/components/dynamic-high-charts.js
index 730778b19069c2e2471929011d0ae606ad879dec..746faf9d4f4af4590cc72257e260f895ac9e4470 100644
--- a/vipra-ui/app/components/dynamic-high-charts.js
+++ b/vipra-ui/app/components/dynamic-high-charts.js
@@ -1,4 +1,3 @@
-import Ember from 'ember';
 import EmberHighChartsComponent from 'ember-highcharts/components/high-charts';
 
 export default EmberHighChartsComponent.extend({
diff --git a/vipra-ui/app/models/topic.js b/vipra-ui/app/models/topic.js
new file mode 100644
index 0000000000000000000000000000000000000000..af2db9b44b47931b9b77cab2e68a284afdc2e57a
--- /dev/null
+++ b/vipra-ui/app/models/topic.js
@@ -0,0 +1,5 @@
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+  
+});
diff --git a/vipra-ui/app/router.js b/vipra-ui/app/router.js
index 0a0fb7055747ae093b2b52b9eef98df5277c9fb5..8ba6e2d47afaff7a3a03dcfcfed41fcabbdc8b5d 100644
--- a/vipra-ui/app/router.js
+++ b/vipra-ui/app/router.js
@@ -10,6 +10,10 @@ Router.map(function() {
     this.route('list', { path: '/' });
     this.route('show', { path: '/:article_id' });
   });
+  this.route('topics', function() {
+  	this.route('list', { path: '/' });
+  	this.route('show', { path: '/:topic_id' });
+  });
   this.route('not-found', { path: '/*:' });
 });
 
diff --git a/vipra-ui/app/routes/topics/list.js b/vipra-ui/app/routes/topics/list.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef14ad4bccdd995300f8c0ec7ec1f248a840a00c
--- /dev/null
+++ b/vipra-ui/app/routes/topics/list.js
@@ -0,0 +1,9 @@
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model() {
+    return Ember.RSVP.hash({
+      topics: this.store.findAll('topic')
+    });
+  }
+});
\ No newline at end of file
diff --git a/vipra-ui/app/templates/articles/new.hbs b/vipra-ui/app/templates/articles/new.hbs
deleted file mode 100644
index c1318fddb4debfa78918fc66c95283760fc470cd..0000000000000000000000000000000000000000
--- a/vipra-ui/app/templates/articles/new.hbs
+++ /dev/null
@@ -1 +0,0 @@
-<h2>New article</h2>
\ No newline at end of file
diff --git a/vipra-ui/app/templates/index.hbs b/vipra-ui/app/templates/index.hbs
index 51b28c7ae97459acfa02b24e3afbe8260ea55896..3d941a81a2bfea16957c6bd216087f15965630ed 100644
--- a/vipra-ui/app/templates/index.hbs
+++ b/vipra-ui/app/templates/index.hbs
@@ -1 +1,5 @@
-<h1>Vipra</h1>
\ No newline at end of file
+<h1>Vipra</h1>
+
+{{#link-to 'articles.list'}}Articles{{/link-to}}
+
+{{#link-to 'topics.list'}}Topics{{/link-to}}
\ No newline at end of file
diff --git a/vipra-ui/app/templates/topics.hbs b/vipra-ui/app/templates/topics.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..c24cd68950a9a5c52c59d7020c3a30a00adb094d
--- /dev/null
+++ b/vipra-ui/app/templates/topics.hbs
@@ -0,0 +1 @@
+{{outlet}}
diff --git a/vipra-ui/app/templates/topics/list.hbs b/vipra-ui/app/templates/topics/list.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..5db134dd36dc5d29533065860e48cd8bd34fe030
--- /dev/null
+++ b/vipra-ui/app/templates/topics/list.hbs
@@ -0,0 +1 @@
+<h2>Found topics</h2>
\ No newline at end of file
diff --git a/vipra-ui/app/templates/topics/show.hbs b/vipra-ui/app/templates/topics/show.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/vipra-ui/tests/unit/models/topic-test.js b/vipra-ui/tests/unit/models/topic-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d71d76e0ea46c2b4c5ad1e3a3596c63fad6d213
--- /dev/null
+++ b/vipra-ui/tests/unit/models/topic-test.js
@@ -0,0 +1,12 @@
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('topic', 'Unit | Model | topic', {
+  // Specify the other units that are required for this test.
+  needs: []
+});
+
+test('it exists', function(assert) {
+  let model = this.subject();
+  // let store = this.store();
+  assert.ok(!!model);
+});
diff --git a/vipra-ui/tests/unit/routes/topics-test.js b/vipra-ui/tests/unit/routes/topics-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0218b7379c24b5a84ab827245558be174cb2b20
--- /dev/null
+++ b/vipra-ui/tests/unit/routes/topics-test.js
@@ -0,0 +1,11 @@
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:topics', 'Unit | Route | topics', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('it exists', function(assert) {
+  let route = this.subject();
+  assert.ok(route);
+});
diff --git a/vipra-util/src/main/java/de/vipra/util/model/Topic.java b/vipra-util/src/main/java/de/vipra/util/model/Topic.java
deleted file mode 100644
index a693f41e6ca60af3cb45818900d91b0a58da32a1..0000000000000000000000000000000000000000
--- a/vipra-util/src/main/java/de/vipra/util/model/Topic.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package de.vipra.util.model;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.bson.Document;
-
-public class Topic extends Model {
-
-	@Override
-	public void fromDocument(Document document) {
-		// TODO Auto-generated method stub
-		
-	}
-
-	@Override
-	public Document toDocument() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public void fromFile(File file) throws IOException {
-		// TODO Auto-generated method stub
-		
-	}
-
-	@Override
-	public String toFileString() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-}
diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicDefinition.java b/vipra-util/src/main/java/de/vipra/util/model/TopicDefinition.java
index 921b17557135c7128ea076da4d6e9325ea1bb3b7..dbaaecd6632133fcf3e983cc3abe87f1717eb99a 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/TopicDefinition.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/TopicDefinition.java
@@ -12,7 +12,7 @@ import de.vipra.util.ex.NotImplementedException;
 public class TopicDefinition extends Model {
 
 	private int index;
-	private List<String> names;
+	private String name;
 	private List<TopicWord> words;
 
 	public TopicDefinition() {}
@@ -29,12 +29,12 @@ public class TopicDefinition extends Model {
 		this.index = index;
 	}
 
-	public List<String> getNames() {
-		return names;
+	public String getName() {
+		return name;
 	}
 
-	public void setNames(List<String> names) {
-		this.names = names;
+	public void setName(String name) {
+		this.name = name;
 	}
 
 	public List<TopicWord> getWords() {
@@ -48,6 +48,7 @@ public class TopicDefinition extends Model {
 	@SuppressWarnings("unchecked")
 	@Override
 	public void fromDocument(Document document) {
+		setName(document.getString("name"));
 		setIndex(document.getInteger("index", 0));
 		if (document.containsKey("words")) {
 			List<Document> topicWords = (List<Document>) document.get("words");
@@ -61,12 +62,15 @@ public class TopicDefinition extends Model {
 	@Override
 	public Document toDocument() {
 		Document document = new Document();
+		document.append("name", getName());
 		document.append("index", getIndex());
-		List<Document> topicWords = new ArrayList<>(words.size());
-		for (TopicWord word : words) {
-			topicWords.add(word.toDocument());
+		if (getWords() != null) {
+			List<Document> topicWords = new ArrayList<>(words.size());
+			for (TopicWord word : words) {
+				topicWords.add(word.toDocument());
+			}
+			document.put("words", topicWords);
 		}
-		document.put("words", topicWords);
 		return document;
 	}
 
diff --git a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java
index b8fe81eddfebdd64dd5a022626025b428865c99d..54adc442d6c6c3f6d2582473110ffc269ba81331 100644
--- a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java
+++ b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java
@@ -4,6 +4,7 @@ import static de.vipra.util.MongoUtils.getSorts;
 import static de.vipra.util.MongoUtils.objectId;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import org.bson.Document;
 import org.slf4j.Logger;
@@ -54,10 +55,10 @@ public class DatabaseService<T extends Model> implements Service<T, DatabaseExce
 		}
 	}
 
-	public ArrayList<T> getMultiple(int skip, int limit, String sortBy) {
-		ArrayList<Document> documents = collection.find().skip(skip).limit(limit).sort(getSorts(sortBy))
+	public List<T> getMultiple(int skip, int limit, String sortBy) {
+		List<Document> documents = collection.find().skip(skip).limit(limit).sort(getSorts(sortBy))
 				.into(new ArrayList<Document>());
-		ArrayList<T> items = new ArrayList<>(documents.size());
+		List<T> items = new ArrayList<>(documents.size());
 
 		for (Document document : documents) {
 			items.add(newT(document));
diff --git a/vm/bootstrap.sh b/vm/bootstrap.sh
index bfaa0b1cf382b8eacac8b9b809608b5a5d2904a2..78e21dd33ea79ffc3d81c062f6ef8cdabe6cb93f 100644
--- a/vm/bootstrap.sh
+++ b/vm/bootstrap.sh
@@ -95,6 +95,7 @@ ufw disable
 # set environment
 
 cat $CONFIG/environment >> /etc/environment
+cat $CONFIG/motd > /etc/motd
 
 # -----------------------------------------------------------------------------
 # cleanup
diff --git a/vm/config/mongod.conf b/vm/config/mongod.conf
deleted file mode 100644
index 6ca9d657224d6271c5d07453cfbcbbde82db49bc..0000000000000000000000000000000000000000
--- a/vm/config/mongod.conf
+++ /dev/null
@@ -1,41 +0,0 @@
-# mongod.conf
-
-# for documentation of all options, see:
-#   http://docs.mongodb.org/manual/reference/configuration-options/
-
-# Where and how to store data.
-storage:
-  dbPath: /var/lib/mongodb
-  journal:
-    enabled: true
-#  engine:
-#  mmapv1:
-#  wiredTiger:
-
-# where to write logging data.
-systemLog:
-  destination: file
-  logAppend: true
-  path: /var/log/mongodb/mongod.log
-
-# network interfaces
-net:
-  port: 27017
-  bindIp: 0.0.0.0
-
-
-#processManagement:
-
-#security:
-
-#operationProfiling:
-
-#replication:
-
-#sharding:
-
-## Enterprise-Only Options:
-
-#auditLog:
-
-#snmp:
diff --git a/vm/config/motd b/vm/config/motd
new file mode 100644
index 0000000000000000000000000000000000000000..194c716be5d85c82de72c01f0bc748533cc14427
--- /dev/null
+++ b/vm/config/motd
@@ -0,0 +1,8 @@
+====================
+Vipra Development VM
+====================
+
+Start database and web server manually:
+ > service mongod restart
+ > service tomcat restart
+