Commits

Samuel Le Berrigaud committed 99b4813

Added stage 6:
- Introducing the user object,
- Adding a second upgrade task

Comments (0)

Files changed (15)

ao-tutorial-stage6/pom.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.atlassian.tutorial.ao.todo</groupId>
+        <artifactId>ao-tutorial</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>ao-tutorial-stage6</artifactId>
+    <packaging>atlassian-plugin</packaging>
+
+    <name>ActiveObjects - Tutorial - Stage 6</name>
+    <description>This is the stage 6 of the Active Objects tutorial</description>
+
+    <properties>
+        <amps.version>3.8</amps.version>
+        <refapp.version>2.12.0</refapp.version>
+        <ao.version>0.19.7</ao.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.atlassian.activeobjects</groupId>
+            <artifactId>activeobjects-plugin</artifactId>
+            <version>${ao.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.sal</groupId>
+            <artifactId>sal-api</artifactId>
+            <version>2.4.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.collections</groupId>
+            <artifactId>google-collections</artifactId>
+            <version>1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!--
+            test dependencies
+        -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.8</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.activeobjects</groupId>
+            <artifactId>activeobjects-test</artifactId>
+            <version>${ao.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <version>1.8.0.10</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.6.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.5</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.atlassian.maven.plugins</groupId>
+                <artifactId>maven-refapp-plugin</artifactId>
+                <version>${amps.version}</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <pluginArtifacts>
+                        <pluginArtifact>
+                            <groupId>com.atlassian.activeobjects</groupId>
+                            <artifactId>activeobjects-plugin</artifactId>
+                            <version>${ao.version}</version>
+                        </pluginArtifact>
+                        <pluginArtifact>
+                            <groupId>com.atlassian.activeobjects</groupId>
+                            <artifactId>activeobjects-refapp-spi</artifactId>
+                            <version>${ao.version}</version>
+                        </pluginArtifact>
+                    </pluginArtifacts>
+                    <productVersion>${refapp.version}</productVersion>
+                    <log4jProperties>src/test/resources/log4j.properties</log4jProperties>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/Todo.java

+package com.atlassian.tutorial.ao.todo;
+
+import net.java.ao.Entity;
+import net.java.ao.Preload;
+import net.java.ao.schema.NotNull;
+
+@Preload
+public interface Todo extends Entity
+{
+    @NotNull
+    void setUser(User user);
+
+    @NotNull
+    User getUser();
+
+    String getDescription();
+
+    void setDescription(String description);
+
+    boolean isComplete();
+
+    void setComplete(boolean complete);
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/TodoService.java

+package com.atlassian.tutorial.ao.todo;
+
+import com.atlassian.activeobjects.tx.Transactional;
+
+import java.util.List;
+
+@Transactional
+public interface TodoService
+{
+    Todo add(String description);
+
+    List<Todo> all();
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/TodoServiceImpl.java

+package com.atlassian.tutorial.ao.todo;
+
+import com.atlassian.activeobjects.external.ActiveObjects;
+import com.atlassian.sal.api.user.UserManager;
+import com.google.common.collect.ImmutableMap;
+import net.java.ao.Query;
+
+import java.util.List;
+
+import static com.google.common.base.Preconditions.*;
+import static com.google.common.collect.Lists.*;
+
+public final class TodoServiceImpl implements TodoService
+{
+    private final ActiveObjects ao;
+    private final UserManager userManager;
+
+    public TodoServiceImpl(ActiveObjects ao, UserManager userManager)
+    {
+        this.ao = checkNotNull(ao);
+        this.userManager = checkNotNull(userManager);
+    }
+
+    @Override
+    public Todo add(String description)
+    {
+        final Todo todo = ao.create(Todo.class, ImmutableMap.<String, Object>of("USER_ID", currentUser()));
+        todo.setDescription(description);
+        todo.setComplete(false);
+        todo.save();
+        return todo;
+    }
+
+    @Override
+    public List<Todo> all()
+    {
+        return newArrayList(ao.find(Todo.class, Query.select().where("USER_ID = ?", currentUser())));
+    }
+
+    private User currentUser()
+    {
+        return getOrCreateUser(userManager.getRemoteUsername());
+    }
+
+    private User getOrCreateUser(String userName)
+    {
+        User[] users = ao.find(User.class, Query.select().where("NAME = ?", userName));
+        if (users.length == 0)
+        {
+            return createUser(userName);
+        }
+        else if (users.length == 1)
+        {
+            return users[0];
+        }
+        else
+        {
+            throw new IllegalStateException("Found multiple users for username: " + userName);
+        }
+    }
+
+    private User createUser(String userName)
+    {
+        return ao.create(User.class, ImmutableMap.<String, Object>of("NAME", userName));
+    }
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/TodoServlet.java

+package com.atlassian.tutorial.ao.todo;
+
+import com.atlassian.sal.api.user.UserManager;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import static com.google.common.base.Preconditions.*;
+
+public final class TodoServlet extends HttpServlet
+{
+    private final TodoService todoService;
+    private final UserManager userManager;
+
+    public TodoServlet(TodoService todoService, UserManager userManager)
+    {
+        this.todoService = checkNotNull(todoService);
+        this.userManager = checkNotNull(userManager);
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
+    {
+        // is the user is not connected, redirect to the login page
+        if (userManager.getRemoteUsername() == null)
+        {
+            res.sendRedirect(req.getContextPath() + "/plugins/servlet/login");
+        }
+
+        final PrintWriter w = res.getWriter();
+        w.printf("<h1>Todos (%s)</h1>", userManager.getRemoteUsername());
+
+        // the form to post more TODOs
+        w.write("<form method=\"post\">");
+        w.write("<input type=\"text\" name=\"task\" size=\"25\"/>");
+        w.write("&nbsp;&nbsp;");
+        w.write("<input type=\"submit\" name=\"submit\" value=\"Add\"/>");
+        w.write("</form>");
+
+        w.write("<ol>");
+
+        for (Todo todo : todoService.all()) // (2)
+        {
+            w.printf("<li><%2$s> %s </%2$s></li>", todo.getDescription(), todo.isComplete() ? "strike" : "strong");
+        }
+
+        w.write("</ol>");
+        w.write("<script language='javascript'>document.forms[0].elements[0].focus();</script>");
+
+        w.close();
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
+    {
+        final String description = req.getParameter("task");
+        todoService.add(description);
+        res.sendRedirect(req.getContextPath() + "/plugins/servlet/todo/list");
+    }
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/User.java

+package com.atlassian.tutorial.ao.todo;
+
+import net.java.ao.Entity;
+
+public interface User extends Entity
+{
+    String getName();
+    void setName(String name);
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/upgrade/v1/Todo.java

+package com.atlassian.tutorial.ao.todo.upgrade.v1;
+
+import net.java.ao.Entity;
+import net.java.ao.Preload;
+
+@Preload
+public interface Todo extends Entity
+{
+    void setUserName(String userName);
+
+    String getUserName();
+
+    String getDescription();
+
+    void setDescription(String description);
+
+    boolean isComplete();
+
+    void setComplete(boolean complete);
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/upgrade/v1/TodoUpgradeTask001.java

+package com.atlassian.tutorial.ao.todo.upgrade.v1;
+
+import com.atlassian.activeobjects.external.ActiveObjects;
+import com.atlassian.activeobjects.external.ActiveObjectsUpgradeTask;
+import com.atlassian.activeobjects.external.ModelVersion;
+
+public final class TodoUpgradeTask001 implements ActiveObjectsUpgradeTask
+{
+    @Override
+    public ModelVersion getModelVersion()
+    {
+        return ModelVersion.valueOf("1");
+    }
+
+    @Override
+    public void upgrade(ModelVersion currentVersion, ActiveObjects ao)
+    {
+        ao.migrate(Todo.class);
+
+        for (Todo todo : ao.find(Todo.class))
+        {
+            todo.setUserName("admin");
+            todo.save();
+        }
+    }
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/upgrade/v2/Todo.java

+package com.atlassian.tutorial.ao.todo.upgrade.v2;
+
+import net.java.ao.Entity;
+import net.java.ao.Preload;
+
+@Preload
+public interface Todo extends Entity
+{
+    void setUserName(String userName);
+
+    String getUserName();
+
+    void setUser(User user);
+
+    User getUser();
+
+    String getDescription();
+
+    void setDescription(String description);
+
+    boolean isComplete();
+
+    void setComplete(boolean complete);
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/upgrade/v2/TodoUpgradeTask002.java

+package com.atlassian.tutorial.ao.todo.upgrade.v2;
+
+import com.atlassian.activeobjects.external.ActiveObjects;
+import com.atlassian.activeobjects.external.ActiveObjectsUpgradeTask;
+import com.atlassian.activeobjects.external.ModelVersion;
+import com.google.common.collect.ImmutableMap;
+import net.java.ao.Query;
+
+public final class TodoUpgradeTask002 implements ActiveObjectsUpgradeTask
+{
+    @Override
+    public ModelVersion getModelVersion()
+    {
+        return ModelVersion.valueOf("2");
+    }
+
+    @Override
+    public void upgrade(ModelVersion currentVersion, ActiveObjects ao)
+    {
+        ao.migrate(Todo.class, User.class);
+
+        Todo[] todos = ao.find(Todo.class);
+        for (Todo todo : todos)
+        {
+            todo.setUser(getOrCreateUser(ao, todo.getUserName()));
+            todo.save();
+        }
+    }
+
+    private User getOrCreateUser(ActiveObjects ao, String userName)
+    {
+        User[] users = ao.find(User.class, Query.select().where("NAME = ?", userName));
+        if (users.length == 0)
+        {
+            return createUser(ao, userName);
+        }
+        else if (users.length == 1)
+        {
+            return users[0];
+        }
+        else
+        {
+            throw new IllegalStateException("There shouldn't be 2 users with the same username! " + userName);
+        }
+    }
+
+    private User createUser(ActiveObjects ao, String userName)
+    {
+        return ao.create(User.class, ImmutableMap.<String, Object>of("NAME", userName));
+    }
+}

ao-tutorial-stage6/src/main/java/com/atlassian/tutorial/ao/todo/upgrade/v2/User.java

+package com.atlassian.tutorial.ao.todo.upgrade.v2;
+
+import net.java.ao.Entity;
+
+public interface User extends Entity
+{
+    String getName();
+    void setName(String name);
+}

ao-tutorial-stage6/src/main/resources/atlassian-plugin.xml

+<atlassian-plugin key="com.atlassian.tutorial.ao.todo" name="${project.name}" plugins-version="2">
+    <plugin-info>
+        <description>${project.description}</description>
+        <version>${project.version}</version>
+        <vendor name="${project.organization.name}" url="${project.organization.url}"/>
+    </plugin-info>
+
+    <ao key="ao-module">
+        <description>The module configuring the Active Objects service used by this plugin</description>
+        <entity>com.atlassian.tutorial.ao.todo.Todo</entity>
+        <entity>com.atlassian.tutorial.ao.todo.User</entity>
+
+        <upgradeTask>com.atlassian.tutorial.ao.todo.upgrade.v1.TodoUpgradeTask001</upgradeTask>
+        <upgradeTask>com.atlassian.tutorial.ao.todo.upgrade.v2.TodoUpgradeTask002</upgradeTask>
+    </ao>
+
+    <component-import key="ao" name="Active Objects service"
+                      interface="com.atlassian.activeobjects.external.ActiveObjects">
+        <description>Component to access Active Objects functionality from the plugin</description>
+    </component-import>
+
+    <component-import key="user-manager" name="SAL User Manager" interface="com.atlassian.sal.api.user.UserManager">
+        <description>The SAL user manager</description>
+    </component-import>
+
+    <component key="tx-processor" name="Transactional Annotation Processor"
+               class="com.atlassian.activeobjects.external.TransactionalAnnotationProcessor">
+        <decription>Processes @Transactional annotations.</decription>
+    </component>
+
+    <component key="todo-service" name="Todo Service" class="com.atlassian.tutorial.ao.todo.TodoServiceImpl"/>
+
+    <servlet name="Todo List &amp; Add Servlet" class="com.atlassian.tutorial.ao.todo.TodoServlet" key="todo-list">
+        <description>A servlet to add and list todos</description>
+        <url-pattern>/todo/list</url-pattern>
+    </servlet>
+</atlassian-plugin>

ao-tutorial-stage6/src/test/java/com/atlassian/tutorial/ao/todo/TodoServiceImplTest.java

+package com.atlassian.tutorial.ao.todo;
+
+import com.atlassian.activeobjects.external.ActiveObjects;
+import com.atlassian.activeobjects.test.TestActiveObjects;
+import com.atlassian.sal.api.user.UserManager;
+import com.google.common.collect.ImmutableMap;
+import net.java.ao.EntityManager;
+import net.java.ao.test.jdbc.Data;
+import net.java.ao.test.jdbc.DatabaseUpdater;
+import net.java.ao.test.jdbc.DynamicJdbcConfiguration;
+import net.java.ao.test.jdbc.Jdbc;
+import net.java.ao.test.junit.ActiveObjectsJUnitRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+@RunWith(ActiveObjectsJUnitRunner.class)
+@Jdbc(DynamicJdbcConfiguration.class)
+@Data(TodoServiceImplTest.TodoServiceImplTestDatabaseUpdater.class)
+public class TodoServiceImplTest
+{
+    private static final String TODO_DESC = "This is a todo";
+    private static final String USER_NAME_OTHER = "other";
+
+    private EntityManager entityManager;
+
+    private ActiveObjects ao;
+
+    private UserManager userManager;
+    private static User userAdmin;
+
+    private TodoServiceImpl todoService;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        assertNotNull(entityManager);
+        ao = new TestActiveObjects(entityManager);
+        userManager = mock(UserManager.class);
+        when(userManager.getRemoteUsername()).thenReturn(userAdmin.getName());
+        todoService = new TodoServiceImpl(ao, userManager);
+    }
+
+    @Test
+    public void testAdd() throws Exception
+    {
+        final String description = TODO_DESC + "#1";
+
+        assertEquals(2, ao.find(Todo.class).length);
+
+        final Todo add = todoService.add(description);
+        assertFalse(add.getID() == 0);
+
+        ao.flushAll(); // clear all caches
+
+        final Todo[] todos = ao.find(Todo.class);
+        assertEquals(3, todos.length);
+        final Todo addedTodo = todos[2];
+        assertEquals(add.getID(), addedTodo.getID());
+        assertEquals(description, addedTodo.getDescription());
+        assertEquals(false, addedTodo.isComplete());
+        assertEquals(userAdmin.getID(), addedTodo.getUser().getID());
+    }
+
+    @Test
+    public void testAll() throws Exception
+    {
+        assertEquals(1, todoService.all().size());
+
+        final Todo todo = ao.create(Todo.class, ImmutableMap.<String, Object>of("USER_ID", userAdmin));
+        todo.setDescription("Some todo");
+        todo.save();
+
+        ao.flushAll(); // clear all caches
+
+        final List<Todo> all = todoService.all();
+        assertEquals(2, all.size());
+        assertEquals(todo.getID(), all.get(1).getID());
+    }
+
+    public static final class TodoServiceImplTestDatabaseUpdater implements DatabaseUpdater
+    {
+        @Override
+        public void update(EntityManager em) throws Exception
+        {
+            em.migrate(Todo.class);
+
+            userAdmin = em.create(User.class, ImmutableMap.<String, Object>of("NAME", "admin"));
+
+            final Todo todo = em.create(Todo.class, ImmutableMap.<String, Object>of("USER_ID", userAdmin));
+            todo.setDescription(TODO_DESC);
+            todo.save();
+
+            final Todo todo1 = em.create(Todo.class, ImmutableMap.<String, Object>of("USER_ID", em.create(User.class, ImmutableMap.<String, Object>of("NAME", USER_NAME_OTHER))));
+            todo1.setDescription("Some other description");
+            todo1.save();
+        }
+    }
+}

ao-tutorial-stage6/src/test/resources/log4j.properties

+log4j.rootLogger = WARN, console
+
+log4j.appender.console = org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout = org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern = %5p - %60.60c - %m%n
+
+log4j.logger.com.atlassian.plugin.osgi = WARN, console
+log4j.additivity.com.atlassian.plugin.osgi = false
+
+log4j.logger.com.atlassian.activeobjects = INFO, console
+log4j.additivity.com.atlassian.activeobjects = false
+
+log4j.logger.net.java.ao = INFO, console
+log4j.additivity.net.java.ao = false
+
+log4j.logger.net.java.ao.sql = DEBUG, console
+log4j.additivity.net.java.ao.sql = false
         <module>ao-tutorial-stage3</module>
         <module>ao-tutorial-stage4</module>
         <module>ao-tutorial-stage5</module>
+        <module>ao-tutorial-stage6</module>
     </modules>
 </project>