Commits

Rich Manalang [Atlassian] committed cbff767

completed tutorial, minus tests ;)

Comments (0)

Files changed (12)

-<?xml version="1.0" encoding="UTF-8"?>
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>com.example.plugins.tutorial</groupId>
-    <artifactId>IssueCRUDAndSearch</artifactId>
-    <version>1.0</version>
-
-    <organization>
-        <name>Example Company</name>
-        <url>http://www.example.com/</url>
-    </organization>
-
-    <name>IssueCRUDAndSearch</name>
-    <description>This is the com.example.plugins.tutorial:IssueCRUDAndSearch plugin for Atlassian JIRA.</description>
-    <packaging>atlassian-plugin</packaging>
-
-    <dependencies>
-        <dependency>
-            <groupId>com.atlassian.jira</groupId>
-            <artifactId>jira-api</artifactId>
-            <version>${jira.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <!-- Add dependency on jira-core if you want access to JIRA implementation classes as well as the sanctioned API. -->
-        <!-- This is not normally recommended, but may be required eg when migrating a plugin originally developed against JIRA 4.x -->
-        <!--
-        <dependency>
-            <groupId>com.atlassian.jira</groupId>
-            <artifactId>jira-core</artifactId>
-            <version>${jira.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        -->
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.8.1</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.atlassian.jira</groupId>
-            <artifactId>jira-tests</artifactId>
-            <version>${jira.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.atlassian.jira</groupId>
-            <artifactId>jira-func-tests</artifactId>
-            <version>${jira.version}</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>com.atlassian.maven.plugins</groupId>
-                <artifactId>maven-jira-plugin</artifactId>
-                <version>3.7</version>
-                <extensions>true</extensions>
-                <configuration>
-                    <productVersion>${jira.version}</productVersion>
-                    <productDataVersion>${jira.version}</productDataVersion>
-                </configuration>
-            </plugin>
-            <plugin>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <configuration>
-                    <source>1.6</source>
-                    <target>1.6</target>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <properties>
-        <!-- TODO: Update to 5.0 after release of 5.0 final -->
-        <jira.version>5.0-beta1</jira.version>
-        <amps.version>3.7</amps.version>
-    </properties>
-
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.example.plugins.tutorial</groupId>
+    <artifactId>IssueCRUDAndSearch</artifactId>
+    <packaging>atlassian-plugin</packaging>
+    <name>IssueCRUDAndSearch</name>
+    <version>1.0</version>
+    <description>This is the com.example.plugins.tutorial:IssueCRUDAndSearch plugin for Atlassian JIRA.</description>
+    <organization>
+        <name>Example Company</name>
+        <url>http://www.example.com/</url>
+    </organization>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.atlassian.maven.plugins</groupId>
+                <artifactId>maven-jira-plugin</artifactId>
+                <version>3.7</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <productVersion>${jira.version}</productVersion>
+                    <productDataVersion>${jira.version}</productDataVersion>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>com.atlassian.jira</groupId>
+            <artifactId>jira-api</artifactId>
+            <version>${jira.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.templaterenderer</groupId>
+            <artifactId>atlassian-template-renderer-api</artifactId>
+            <version>${template.renderer.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.jira</groupId>
+            <artifactId>jira-tests</artifactId>
+            <version>${jira.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.jira</groupId>
+            <artifactId>jira-func-tests</artifactId>
+            <version>${jira.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.1.1</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+    <properties>
+        <amps.version>3.7</amps.version>
+        <jira.version>5.0-beta3</jira.version>
+        <template.renderer.version>1.3.1</template.renderer.version>
+    </properties>
+</project>
+

src/main/java/com/example/plugins/tutorial/MyPlugin.java

-package com.example.plugins.tutorial;
-
-public class MyPlugin
-{
-}

src/main/java/com/example/plugins/tutorial/servlet/IssueCRUD.java

+package com.example.plugins.tutorial.servlet;
+
+import com.atlassian.crowd.embedded.api.User;
+import com.atlassian.jira.bc.issue.IssueService;
+import com.atlassian.jira.bc.issue.search.SearchService;
+import com.atlassian.jira.bc.project.ProjectService;
+import com.atlassian.jira.issue.Issue;
+import com.atlassian.jira.issue.IssueInputParameters;
+import com.atlassian.jira.issue.MutableIssue;
+import com.atlassian.jira.issue.search.SearchException;
+import com.atlassian.jira.jql.builder.JqlClauseBuilder;
+import com.atlassian.jira.jql.builder.JqlQueryBuilder;
+import com.atlassian.jira.project.Project;
+import com.atlassian.jira.web.bean.PagerFilter;
+import com.atlassian.sal.api.user.UserManager;
+import com.atlassian.templaterenderer.TemplateRenderer;
+import com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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.util.List;
+import java.util.Map;
+
+public class IssueCRUD extends HttpServlet {
+    private static final Logger log = LoggerFactory.getLogger(IssueCRUD.class);
+    private IssueService issueService;
+    private ProjectService projectService;
+    private SearchService searchService;
+    private UserManager userManager;
+    private TemplateRenderer renderer;
+    private static final String LIST_BROWSER_TEMPLATE = "/templates/list.vm";
+    private static final String NEW_BROWSER_TEMPLATE = "/templates/new.vm";
+    private static final String EDIT_BROWSER_TEMPLATE = "/templates/edit.vm";
+    private com.atlassian.jira.user.util.UserManager jiraUserManager;
+
+    /**
+     * Servlet constructor that is auto-wired by Spring to include the following services registered
+     * in the params.
+     *
+     * @param issueService
+     * @param projectService
+     * @param searchService
+     * @param userManager
+     * @param jiraUserManager
+     * @param templateRenderer
+     */
+    public IssueCRUD(IssueService issueService, ProjectService projectService, SearchService searchService,
+                     UserManager userManager, com.atlassian.jira.user.util.UserManager jiraUserManager,
+                     TemplateRenderer templateRenderer) {
+        this.issueService = issueService;
+        this.projectService = projectService;
+        this.searchService = searchService;
+        this.userManager = userManager;
+        this.renderer = templateRenderer;
+        this.jiraUserManager = jiraUserManager;
+    }
+
+    /**
+     * GET method for servlet handles both showing a list of issues, the new issue page, and the edit issue page
+     *
+     * @param req
+     * @param resp
+     * @throws ServletException
+     * @throws IOException
+     */
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+        if ("y".equals(req.getParameter("new"))) {
+            // Renders new.vm template if the "new" parameter is passed
+
+            // Create an empty context map to pass into the render method
+            Map<String, Object> context = Maps.newHashMap();
+            // Make sure to set the contentType otherwise bad things happen
+            resp.setContentType("text/html;charset=utf-8");
+            // Render the velocity template (new.vm). Since the new.vm template doesn't need to render
+            // any in dynamic content, we just pass it an empty context
+            renderer.render(NEW_BROWSER_TEMPLATE, context, resp.getWriter());
+        } else if ("y".equals(req.getParameter("edit"))) {
+            // Renders edit.vm template if the "edit" parameter is passed
+
+            // Retrieve issue with the specified key
+            IssueService.IssueResult issue = issueService.getIssue(getCurrentUser(req), req.getParameter("key"));
+            Map<String, Object> context = Maps.newHashMap();
+            context.put("issue", issue.getIssue());
+            resp.setContentType("text/html;charset=utf-8");
+            // Render the template with the issue inside the context
+            renderer.render(EDIT_BROWSER_TEMPLATE, context, resp.getWriter());
+        } else {
+            // Render the list of issues (list.vm) if no params are passed in
+            List<Issue> issues = getIssues(req);
+            Map<String, Object> context = Maps.newHashMap();
+            context.put("issues", issues);
+            resp.setContentType("text/html;charset=utf-8");
+            // Pass in the list of issues as the context
+            renderer.render(LIST_BROWSER_TEMPLATE, context, resp.getWriter());
+        }
+    }
+
+    /**
+     * Returns a list of issues used to populate the list.vm template in the GET request
+     *
+     * @param req
+     * @return
+     */
+    private List<Issue> getIssues(HttpServletRequest req) {
+        // User is required to carry out a search
+        User user = getCurrentUser(req);
+
+        // search issues
+
+        // The search interface requires JQL clause... so let's build one
+        JqlClauseBuilder jqlClauseBuilder = JqlQueryBuilder.newClauseBuilder();
+        // Our JQL clause is simple project="TUTORIAL"
+        com.atlassian.query.Query query = jqlClauseBuilder.project("TUTORIAL").buildQuery();
+        // A page filter is used to provide pagination. Let's use an unlimited filter to
+        // to bypass pagination.
+        PagerFilter pagerFilter = PagerFilter.getUnlimitedFilter();
+        com.atlassian.jira.issue.search.SearchResults searchResults = null;
+        try {
+            // Perform search results
+            searchResults = searchService.search(user, query, pagerFilter);
+        } catch (SearchException e) {
+            e.printStackTrace();
+        }
+        // return the results
+        return searchResults.getIssues();
+    }
+
+    /**
+     * Helper method for getting the current user
+     *
+     * @param req
+     * @return
+     */
+    private User getCurrentUser(HttpServletRequest req) {
+        // To get the current user, we first get the username from the session.
+        // Then we pass that over to the jiraUserManager in order to get an
+        // actual User object.
+        return jiraUserManager.getUser(userManager.getRemoteUsername(req));
+    }
+
+    /**
+     * POST method for servlet handles creating new issues and updating existing issues
+     *
+     * @param req
+     * @param resp
+     * @throws ServletException
+     * @throws IOException
+     */
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        Map params = req.getParameterMap();
+
+        User user = getCurrentUser(req);
+
+        if ("y".equals(req.getParameter("edit"))) {
+            // Perform update if the "edit" param is passed in
+            // First get the issue from the key that's passed in
+            IssueService.IssueResult issueResult = issueService.getIssue(user, req.getParameter("key"));
+            MutableIssue issue = issueResult.getIssue();
+            // Next we need to validate the updated issue
+            IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
+            issueInputParameters.setSummary(req.getParameter("summary"));
+            issueInputParameters.setDescription(req.getParameter("description"));
+            IssueService.UpdateValidationResult result = issueService.validateUpdate(user, issue.getId(),
+                    issueInputParameters);
+
+            if (result.getErrorCollection().hasAnyErrors()) {
+                // If the validation fails, we re-render the edit page with the errors in the context
+                Map<String, Object> context = Maps.newHashMap();
+                context.put("issue", issue);
+                context.put("errors", result.getErrorCollection().getErrors());
+                resp.setContentType("text/html;charset=utf-8");
+                renderer.render(EDIT_BROWSER_TEMPLATE, context, resp.getWriter());
+            } else {
+                // If the validation passes, we perform the update then redirect the user back to the
+                // page with the list of issues
+                issueService.update(user, result);
+                resp.sendRedirect("issuecrud");
+            }
+
+        } else {
+            // Perform creation if the "new" param is passed in
+            // First we need to validate the new issue being created
+            IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
+            // We're only going to set the summary and description. The rest are hard-coded to
+            // simplify this tutorial.
+            issueInputParameters.setSummary(req.getParameter("summary"));
+            issueInputParameters.setDescription(req.getParameter("description"));
+            // We need to set the assignee, reporter, project, and issueType...
+            // For assignee and reporter, we'll just use the currentUser
+            issueInputParameters.setAssigneeId(user.getName());
+            issueInputParameters.setReporterId(user.getName());
+            // We hard-code the project name to be the project with the TUTORIAL key
+            Project project = projectService.getProjectByKey(user, "TUTORIAL").getProject();
+            issueInputParameters.setProjectId(project.getId());
+            // We also hard-code the issueType to be a "bug" == 1
+            issueInputParameters.setIssueTypeId("1");
+            // Perform the validation
+            IssueService.CreateValidationResult result = issueService.validateCreate(user, issueInputParameters);
+
+            if (result.getErrorCollection().hasAnyErrors()) {
+                // If the validation fails, render the list of issues with the error in a flash message
+                List<Issue> issues = getIssues(req);
+                Map<String, Object> context = Maps.newHashMap();
+                context.put("issues", issues);
+                context.put("errors", result.getErrorCollection().getErrors());
+                resp.setContentType("text/html;charset=utf-8");
+                renderer.render(LIST_BROWSER_TEMPLATE, context, resp.getWriter());
+            } else {
+                // If the validation passes, redirect the user to the main issue list page
+                issueService.create(user, result);
+                resp.sendRedirect("issuecrud");
+            }
+        }
+    }
+
+    /**
+     * DELETE method handles AJAX based deletion of issues. Returns JSON status.
+     *
+     * @param req
+     * @param resp
+     * @throws ServletException
+     * @throws IOException
+     */
+    @Override
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        User user = getCurrentUser(req);
+
+        // This will be the output string that we will put the JSON in
+        String respStr = "";
+
+        // Retrieve the issue with the specified key
+        IssueService.IssueResult issue = issueService.getIssue(user, req.getParameter("key"));
+
+        if (issue.isValid()) {
+            // If the issue is found, let's delete it...
+
+            // ... but first, we must validate that user can delete issue
+            IssueService.DeleteValidationResult result = issueService.validateDelete(user, issue.getIssue().getId());
+            if (result.getErrorCollection().hasAnyErrors()) {
+                // If the validation fails, we send the error back to the user in a JSON payload
+                respStr = "{ \"success\": \"false\", error: \"" + result.getErrorCollection().getErrors().get(0) + "\" }";
+            } else {
+                // If the validation passes, we perform the delete, then return a success msg back to the user
+                issueService.delete(user, result);
+                respStr = "{ \"success\" : \"true\" }";
+            }
+        } else {
+            // The issue can't be found... so we send an error to the user
+            respStr = "{ \"success\" : \"false\", error: \"Couldn't find issue\"}";
+        }
+        // We set the content-type to application/json here so that the AJAX client knows how to deal with it
+        resp.setContentType("application/json;charset=utf-8");
+        // Send the raw output string we put together
+        resp.getWriter().write(respStr);
+    }
+}

src/main/resources/atlassian-plugin.properties

+#
+#Tue Nov 08 16:51:56 PST 2011
+issue-crud.name=Issue CRUD
+issue-crud.description=The Issue CRUD Plugin
+my-class.name=My Class
+my-class.description=The My Class Plugin

src/main/resources/atlassian-plugin.xml

+<?xml version="1.0" encoding="UTF-8"?>
+
 <atlassian-plugin key="${project.groupId}.${project.artifactId}" 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>
-</atlassian-plugin>
+  <plugin-info>
+    <description>${project.description}</description>
+    <version>${project.version}</version>
+    <vendor name="${project.organization.name}" url="${project.organization.url}"/>
+  </plugin-info>
+  <resource type="i18n" name="i18n" location="atlassian-plugin"/>
+  <servlet name="Issue CRUD" i18n-name-key="issue-crud.name" key="issue-crud" class="com.example.plugins.tutorial.servlet.IssueCRUD">
+    <description key="issue-crud.description">The Issue CRUD Plugin</description>
+    <url-pattern>/issuecrud</url-pattern>
+  </servlet>
+  <component-import key="velocityTemplateRenderer" interface="com.atlassian.templaterenderer.velocity.one.six.VelocityTemplateRenderer"/>
+  <component-import key="userManager" interface="com.atlassian.sal.api.user.UserManager" />
+</atlassian-plugin>

src/main/resources/templates/edit.vm

+<html>
+<head>
+    <title>Edit Issue &mdash; Issue CRUD Tutorial</title>
+    <meta name="decorator" content="atl.general">
+</head>
+<body class="page-type-admin">
+<div class="content-container">
+
+    <div class="content-body">
+        <h1>Edit issue $issue.getKey()</h1>
+
+        #if ($errors.size()>0)
+            <div class="aui-message error shadowed">
+                #foreach($error in $errors)
+                    <p class="title">
+                        <span class="aui-icon icon-error"></span>
+                        <strong>$error</strong>
+                    </p>
+                #end
+            </div>
+            <!-- .aui-message -->
+        #end
+
+        <div class="create-issue-panel">
+
+            <form method="post" id="h" action="issuecrud" class="aui">
+                <input type="hidden" name="edit" value="y">
+                <input type="hidden" name="key" value="$issue.getKey()">
+                <div class="field-group">
+                    <label for="h-fsummary">
+                        Summary
+                        <span class="aui-icon icon-required"></span>
+                        <span class="content">required</span>
+                    </label>
+                    <input id="h-fsummary" class="text long-field" type="text" name="summary" value="$issue.getSummary()">
+                </div>
+                <div class="field-group">
+                    <label for="h-fdescription">
+                        Description
+                        <span class="aui-icon icon-required"></span>
+                        <span class="content">required</span>
+                    </label>
+                    <textarea id="h-fdescription" name="description">$issue.getDescription()</textarea>
+                </div>
+                <div class="buttons">
+                    <input class="button" type="submit" value="Update">&nbsp;
+                    <a href="issuecrud">Cancel</a>
+                </div>
+            </form>
+        </div>
+    </div>
+
+</div>
+</body>
+</html>

src/main/resources/templates/list.vm

+<html>
+<head>
+    <title>All Tutorial Issues &mdash; Issue CRUD Tutorial</title>
+    <meta name="decorator" content="atl.general">
+    <script>
+        AJS.$(document).ready(function() {
+            jQuery('.delete-issue').click(function() {
+                console.log('deleting');
+                var self = jQuery(this);
+                jQuery.ajax({
+                    type: "delete",
+                    url: "issuecrud?key=" + self.data("key"),
+                    success: function(data) {
+                        console.log('dom', self, data);
+                        self.parent().parent().remove();
+                    },
+                    error: function() {
+                        console.log('error', arguments);
+                    }
+                });
+                return false;
+            });
+        });
+    </script>
+</head>
+<body class="page-type-admin">
+<div class="content-container">
+
+    <div class="content-body">
+        <h1>You've Got #if($issues.size()==0)<span style="color:red">NO</span>#end Issues!</h1>
+
+        #if ($errors.size()>0)
+            <div class="aui-message error shadowed">
+                #foreach($error in $errors)
+                    <p class="title">
+                        <span class="aui-icon icon-error"></span>
+                        <strong>$error</strong>
+                    </p>
+                #end
+            </div>
+            <!-- .aui-message -->
+        #end
+
+        #if ($issues.size() > 0)
+            <div class="issues">
+                <table class="aui">
+                    <thead>
+                    <tr>
+                        <th>Key</th>
+                        <th>Summary</th>
+                        <th>Description</th>
+                        <th>Assignee</th>
+                        <th>Reporter</th>
+                        <th></th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                        #foreach( $issue in $issues )
+                        <tr>
+                            <td>$issue.getKey()</td>
+                            <td>$issue.getSummary()</td>
+                            <td>
+                                #if($issue.getDescription())
+                            $issue.getDescription()
+                        #end
+                            </td>
+                            <td>
+                                $issue.getAssignee().getName()
+                            </td>
+                            <td>
+                                $issue.getReporter().getName()
+                            </td>
+                            <td>
+                                <a href="issuecrud?edit=y&key=$issue.getKey()">Edit</a> &nbsp;
+                                <a href="#" class="delete-issue" data-key="$issue.getKey()">Delete</a>
+                            </td>
+                        </tr>
+                        #end
+                    </tbody>
+                </table>
+            </div>
+        #end
+        <form method="get" action="issuecrud" class="aui">
+            <input type="hidden" name="new" value="y">
+            <input type="submit" class="button" value="Create new issue">
+        </form>
+    </div>
+</div>
+</body>
+</html>

src/main/resources/templates/new.vm

+<html>
+<head>
+    <title>Create Issue &mdash; Issue CRUD Tutorial</title>
+    <meta name="decorator" content="atl.general">
+</head>
+<body class="page-type-admin">
+<div class="content-container">
+
+    <div class="content-body">
+        <h1>Create issue</h1>
+        <div class="create-issue-panel">
+
+            <form method="post" id="h" action="issuecrud" class="aui">
+                <div class="field-group">
+                    <label for="h-fsummary">
+                        Summary
+                        <span class="aui-icon icon-required"></span>
+                        <span class="content">required</span>
+                    </label>
+                    <input id="h-fsummary" class="text long-field" type="text" name="summary">
+                </div>
+                <div class="field-group">
+                    <label for="h-fdescription">
+                        Description
+                        <span class="aui-icon icon-required"></span>
+                        <span class="content">required</span>
+                    </label>
+                    <textarea id="h-fdescription" name="description"></textarea>
+                </div>
+                <div class="buttons">
+                    <input class="button" type="submit" value="Create">&nbsp;
+                    <a href="issuecrud">Cancel</a>
+                </div>
+            </form>
+        </div>
+    </div>
+
+</div>
+</body>
+</html>

src/test/java/com/example/plugins/tutorial/MyPluginTest.java

-package com.example.plugins.tutorial;
-
-import org.junit.Test;
-
-public class MyPluginTest
-{
-    @Test
-    public void testSomething()
-    {
-    }
-}

src/test/java/com/example/plugins/tutorial/servlet/IssueCRUDTest.java

+package com.example.plugins.tutorial.servlet;
+
+import org.junit.Test;
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.Mockito;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class IssueCRUDTest {
+
+    HttpServletRequest mockRequest;
+    HttpServletResponse mockResponse;
+
+    @Before
+    public void setup() {
+        mockRequest = mock(HttpServletRequest.class);
+        mockResponse = mock(HttpServletResponse.class);
+    }
+
+    @After
+    public void tearDown() {
+
+    }
+
+    @Test
+    public void testSomething() {
+        String expected = "test";
+        when(mockRequest.getParameter(Mockito.anyString())).thenReturn(expected);
+        assertEquals(expected,mockRequest.getParameter("some string"));
+
+    }
+}

src/test/java/it/MyPluginTest.java

-package it;
-
-import org.junit.Test;
-
-public class MyPluginTest
-{
-    @Test
-    public void integrationTest()
-    {
-    }
-}

src/test/java/it/com/example/plugins/tutorial/servlet/IssueCRUDFuncTest.java

+package it.com.example.plugins.tutorial.servlet;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.junit.Test;
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+
+public class IssueCRUDFuncTest {
+
+    HttpClient httpClient;
+    String baseUrl;
+    String servletUrl;
+
+    @Before
+    public void setup() {
+        httpClient = new DefaultHttpClient();
+        baseUrl = System.getProperty("baseurl");
+        servletUrl = baseUrl + "/plugins/servlet/issuecrud";
+    }
+
+    @After
+    public void tearDown() {
+        httpClient.getConnectionManager().shutdown();
+    }
+
+    @Test
+    public void testSomething() throws IOException {
+        HttpGet httpget = new HttpGet(servletUrl);
+
+        // Create a response handler
+        ResponseHandler<String> responseHandler = new BasicResponseHandler();
+        String responseBody = httpClient.execute(httpget, responseHandler);
+        assertTrue(null != responseBody && !"".equals(responseBody));
+    }
+}