Atlassian

How to Add Active Objects to Your Confluence Add-on

In this article you are going to add Active Objects to you Confluence Add-on. Active Objects is the ORM layer of Atlassian. This will enable your plug-in to persist data.

1. Requirement

We will require the following:

  1. Atlassian SDK
  2. Mars Eclipse
  3. Have read and tried out
    How to Add a Servlet Module to Your Confluence Add-on
  4. You will build upon the source code of How to Add a Servlet Module to Your Confluence Add-on article.
    Download the source: space-admin-servlet.tar.gz

Upon extracting space-admin-servlet.tar.gz, your space-admin directory will contain the following files:

  • pom.xml
  • README
  • LICENSE
  • Under “src/main/resources” – atlassian-plugin.xml, space-admin.properties, css folder,
    images folder, js folder, and META-INF folder (all folders contained their default files)
  • Under “src/main/resources/templates” – space-admin-action.vm
  • Under “src/main/java” – com.javacodegeeks.example package and under it MyAction.java, QuoteServlet.java, Quote.java

The Quote.java doesn’t exist yet. You will create it later.

2. Modify the pom.xml

Add the Active Objects library and the Atlassian Components to be imported as highlighted below.

pom.xml

<?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">

    <!-- other contents omitted -->
    <dependencies>
      <dependency>
        <groupId>com.atlassian.activeobjects</groupId>
    	<artifactId>activeobjects-plugin</artifactId>
    	<version>1.2.3</version>
    	<scope>provided</scope>
      </dependency>

    <!-- other contents omitted -->

    <instructions>
        <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>

        <!-- Add package to export here -->
        <Export-Package>
                            
        </Export-Package>

        <!-- Add package import here -->
        <Import-Package>
            com.atlassian.activeobjects.external,
            com.atlassian.confluence.plugin.descriptor.web.conditions,
            org.springframework.osgi.*;resolution:="optional",
            org.eclipse.gemini.blueprint.*;resolution:="optional",
            *
        </Import-Package>

        <!-- Ensure plugin is spring powered -->
        <Spring-Context>*</Spring-Context>
    </instructions>
    
    <!-- other contents omitted -->
</project>

Once done, execute atlas-mvn eclipse:eclipse to create .classpath and .project so that you can import this Maven project into Eclipse.

3. Add the Active Objects Module in the Add-on Descriptor

Add the Active Objects Module in atlassian-plugin.xml just before the </atlassian-plugin> closing tag and after the Servlet Module entry. Your add-on descriptor will look like the one below:

atlassian-plugin.xml

<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">

    <!-- other contents omitted -->
    </servlet>
    
    <ao key="ao-module">
    	<description>The module configuring the Active Objects service used by this plugin</description>
    	<entity>com.javacodegeeks.example.Quote</entity>
    </ao>
    
</atlassian-plugin>

You defined your entity here named Quote.

4. Create the Quote(Entity) Interface

Next up is to create your entity like so:

Quote.java

package com.javacodegeeks.example;

import net.java.ao.Entity;
import net.java.ao.Preload;

@Preload
public interface Quote extends Entity {
	String getQuote();
	void setQuote(String quote);
}

By default, Active Objects use lazy loading. The @Preload annotation tells Active Objects to use eager loading.

5. Modify MyAction Class

Modify the MyAction class like so:

MyAction.java

package com.javacodegeeks.example;

import java.util.List;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.confluence.spaces.actions.SpaceAdminAction;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;

public class MyAction extends SpaceAdminAction {

	private static final long serialVersionUID = 1L;
	
	private ActiveObjects ao;

	@Override
    public String doDefault()
    {
        return INPUT;
    }
	
	public void setActiveObjects(@ComponentImport ActiveObjects ao) {
		this.ao = checkNotNull(ao);
	}
	
	public List<Quote> getQuotes() {
		return newArrayList(ao.find(Quote.class));
	}

}

You are able to inject ActiveObjects in the setter method because you included this component in the import package in your pom.xml. Do you remember? The getter method will make available a list of quotes in the Velocity template.

6. Modify QuoteServlet Class

Modify the doPost method of your servlet like so:

QuoteServlet.java

package com.javacodegeeks.example;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.sal.api.transaction.TransactionCallback;

import static com.google.common.base.Preconditions.checkNotNull;

@Scanned
public class QuoteServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private static final Logger log = LoggerFactory.getLogger(QuoteServlet.class);
	
	private final ActiveObjects ao;
	
	@Autowired
	public QuoteServlet(@ComponentImport ActiveObjects ao) {
		this.ao = checkNotNull(ao);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		final String quote = req.getParameter("quote");
		final String spaceKey = req.getParameter("spaceKey");
		
		log.warn("====== \n Quote: {} \n ======", quote);
		
		ao.executeInTransaction(new TransactionCallback<Quote>() {
			@Override
			public Quote doInTransaction() {
				final Quote q = ao.create(Quote.class);
				q.setQuote(quote);
				q.save();

				return q;
			}
		});
		
		res.sendRedirect(req.getContextPath() + "/plugins/space-admin/sas.action?key=" + spaceKey);
	}
}

All Active Objects operations must happen in a transaction. Inside the transaction, you created a Quote entity, then set its values and persisted it to the database. You auto wire the ActiveObjects object in the constructor just like the MyAction class by using @ComponentImport.

7. Modify the Velocity Template

Modify the body section of the template like so:

space-admin-action.vm

<html>
    <head>
        <title>$action.getText("space.admin.item.label")</title>
        <meta name="decorator" content="main"/>
    </head>
    #applyDecorator("root")
        #decoratorParam("helper" $action.helper)
        ## Name of the tab to highlight: space-operations is also valid.
        #decoratorParam("context" "space-administration") 

        #applyDecorator ("root")
            ## The .vmd to use - This one displays both in Space Admin and Space Tools.
            #decoratorParam ("context" "spaceadminpanel")  
            ## Key of the web-item to highlight in Space Tools
            #decoratorParam ("selectedSpaceToolsWebItem" "space-admin-item") 
            #decoratorParam ("helper" $action.helper)
            <body>
              <form class="aui top-label" action="$req.contextPath/plugins/servlet/quotes" method="post">
                <div class ="field-group top-label">
                  <label for="quote">Quote<span class="aui-icon icon-required">required</span></label>
                  <input class="text long-field" id="quote" title="Quote field" type="text" name="quote" placeholder="enter quote here">
                  <div class="description">Your quote.</div>
                  <input type="hidden" name="spaceKey" value="$helper.spaceKey">
                  <input class="button submit" type="submit" value="Add">
                </div>
              </form>

              <table class="aui">
                <thead>
                  <tr>
                  <th id="quote-header">Quote</th>
                  </tr>
                </thead>
                <tbody>
                #foreach( $quote in $quotes )
                  <tr>
                  <td headers="quote-header">$quote.quote</td>
                  </tr>
                #end
                </tbody>
              </table>
            </body>
        #end
    #end
</html>

As you can see, you have added some styling to your template. You are using the Atlassion User Interface front-end library to conform to the Atlassian Design Guidelines.
The values of the $quotes variable are provided by the getQuotes() method of your MyAction class. Check out Apache Velocity for more details.

8. Run the Plug-in

Execute atlas-run to run the plug-in. Your Confluence Server is accessible at http://localhost:1990/confluence. Your username and password are the same. It’s admin. Click on Demonstration Space at the lower left corner of the dashboard page. At the lower left corner of the Demonstration Space page, click on Space Tools and then Add-ons. Your plug-in will be displayed in the Your Plug-in tab. Try adding some quotes. The final output looks like this:

how to add active objects to your confluence add-on
Final Output

9. How to Add Active Objects Summary

In this example, you were able to add Active Objects to your add-on by following these steps:

  1. Fulfilling the Requirements
  2. Modifying the pom.xml
  3. Adding the Active Objects Module to the Add-on Descriptor
  4. Creating the Quote/Entity Interface
  5. Modifying the MyAction Class
  6. Modifying the QuoteServlet Class
  7. Modifying the Velocity Template
  8. Running the Plug-in

10. Download the Source Code

This is an example of how to add Active Obejcts to your Confluence Add-on.

Download
You can download the source code of this example here: space-admin-ao.tar.gz.

Joel Patrick Llosa

I graduated from Silliman University in Dumaguete City with a degree in Bachelor of Science in Business Computer Application. I have contributed to many Java related projects at Neural Technologies Ltd., University of Southampton (iSolutions), Predictive Technologies, LLC., Confluence Service, North Concepts, Inc., NEC Telecom Software Philippines, Inc., and NEC Technologies Philippines, Inc. You can also find me in Upwork freelancing as a Java Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
nani
nani
6 years ago

Joel,
This article is great, I am trying to develop a similar model but my macro should GET and POST to an external database using the REST Apis exposed on my Db. I have tried a few ways but no luck. Can you please help me with some inputs ?

Jörg
Jörg
5 years ago

Hi Joel,
I like your tutorial sooooo much!

Now i have a problem, maybe you can help me?
I downloaded this tutorial and run it. But when i call http://localhost:1990/confluence/ i get the message:
HTTP Status 404 – Not Found
Type Status Report
Message /confluence/
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.

I unzipped the files. Executed in the root directory atlas-mvn package and then atlas-run.

What did i wrong?

Back to top button