CustomWare Greenhouse Blog

Writing an Atlassian Gadget

Purpose

This guide describes the processes and architecture of developing Atlassian Gadgets, currently this is supported in JIRA 4.0 with plans to implementing these in Confluence 3.1 release as well (the required resources to enable gadgets in Confluence was included in the 3.1 milestone 2 release of Confluence. See: CONF-16414).

Resources

Atlassian has written a detailed guide on how to write Gadgets in their guide "Writing an Atlassian Gadget" which details a large amount of the steps one needs to write your own gadget, this guide is meant to be a supplement to Atlassian's guides to help clarify some points and help people get started on Atlassian Gadgets.

Anatomy of Gadgets

Declaration

Like all plugin modules, Gadgets are required to be declared in the "atlassian-plugin.xml" descriptor file for it to be picked up by the plugin system. There is no limit to the number of Gadgets which you are include within a single plugin.

The actual content of a Gadget is contained within a separate XML file (described here)

Data Flow

Gadgets has 2 main sources of information retrieval, this includes: User Preferences and obtaining them from external sources Using REST APIs.

For instructions on how to retrieve information from these sources, please refer to the sections User Preferences and Using REST APIs respectively.

Structure

Gadgets are standalone modules, they are not affected by the context in which they are used and as such, they are implemented on the JIRA dashboard using iframes.

Writing Your Gadget

Setting up your Gadget

Setting up your Project

Gadgets are only available in the plugin 2.0 framework (which uses OSGi), legacy plugin will need to upgrade to the plugin 2.0 framework to support Gadgets.

The easiest way to check this is to check your atlassian-plugin.xml to see if it matches the following:

Setting up a new Project

The fastest way to create a new project is to use the standard archetypes, as Gadgets are currently only released officially for JIRA 4.0, I will include the details for JIRA, for all others, please refer to the Atlassian documentation on plugin archetypes.

Run the following in your command line prompt:

For Linux/Unix:

mvn archetype:create \
    -DarchetypeGroupId=com.atlassian.maven.archetypes \
    -DarchetypeArtifactId=jira-plugin-archetype \
    -DarchetypeVersion=16 \
    -DremoteRepositories=https://maven.atlassian.com/repository/public/ \
    -DgroupId=$MY_PACKAGE -DartifactId=$MY_PLUGIN

For Windows:

mvn archetype:create ^
    -DarchetypeGroupId=com.atlassian.maven.archetypes ^
    -DarchetypeArtifactId=jira-plugin-archetype ^
    -DarchetypeVersion=16 ^
    -DremoteRepositories=https://maven.atlassian.com/repository/public/ ^
    -DgroupId=%MY_PACKAGE% -DartifactId=%MY_PLUGIN%

Please note that the "DarchetypeVersion" specifies the version of the template to use, the latest version as of the time that this guide was written is version 16, please use the latest version as they are released, a list of all maven archetypes (for the various Atlassian products) can be found here: https://maven.atlassian.com/content/groups/public/com/atlassian/maven/archetypes

Setting up an Existing Project

Please update your parent in your pom.xml, if you are not using a parent in your pom.xml, then please add it, below is the code to set up the parent:

The version specifies which version of the JIRA plugin base pom to use, please update this to the most recent, version 18 is the latest at the time that this guide was written, a list of all versions can be found here: https://maven.atlassian.com/content/groups/public/com/atlassian/jira/plugins/jira-plugin-base

Creating Your Gadget Descriptor File

Create a simple XML file in your plugin resources directory (eg. /src/main/resources/net/customware/gadgets/example-gadget.xml) with the following standard format:

This sets up the basic layout of a Gadget, the above example is a very simple example of a Gadget, when run, it displays "Hello World!" within the Gadget frame.

Including Your Gadget in Your Plugin

To actually include the Gadget in your plugin, one extra step is required, and that is to include a reference to the descriptor file within your "atlassian-plugin.xml" so that the plugin system can pick it up.

Including Other Resources For Your Gadget

From the example shown in #Creating Your Gadget Descriptor File, we actually used some references to external resources (namely the thumbnail and screenshot), these resources are typically included as part of the plugin and thus need to made available. To do this, we will need to set these up as resources, to do this, you will need to add something like the following in your "atlassian-plugin.xml".

This will make the resources available for your Gadget.

Including Your Gadget in the Gadget Directory

To make your Gadget become available in the Gadget directory (currently only available in JIRA), you will need to include the "gadget-directory" feature in your Gadget, this feature is specific to the Atlassian implementation of Gadgets, so it is best to mark it as "Optional" as opposed to "Required".

In our example, we used the following:

Which makes the Gadget available in the Gadget directory under the JIRA section, there are currently a number of sections available, including:

  • JIRA
  • Confluence
  • FishEye
  • Crucible
  • Crowd
  • Clover
  • Bamboo
  • Admin
  • Charts
  • External Content
  • Other

To make it appear in more than 1 section, just list them within the <Param name="categories"> element, with each one on a new line.

Defining The Contents of Your Gadget

CDATA is required for almost all Gadgets, this is due to the some special characters which are commonly used that is not valid when used in an XML file (as they are reserved characters), these include namely the '<' and '>' characters which is treated as the start and end of an element tag.

Having CDATA around these forces everything inside it to be treated as plain text.

To use CDATA, just put "<![CDATA[" at the very start and "]]>" at the very end of your "<Content>" element.

The contents of your Gadget is specified by the "<Content>" element in your Gadget descriptor file, within our example, we used a very brief example using plain text:

There are several options available when declaring the contents of a Gadget, the main ways are described below:

Using HTML in Content

This is possible the most basic implementation of a Gadget, you can include static HTML in the "<Content>" element and by doing so, whenever the Gadget is rendered, the static HTML will be displayed, for example:

Will display Hello World! everytime the Gadget is rendered.

Using Javascript and CSS in Content

As with normal HTML content, you also have the ability to use both CSS and Javascript within your Gadget, and the way that it is done is no different from how it is done on traditional web pages, for example we can declare a Javascript method:

And then add a HTML link to call the Javascript:

Then add some CSS to the the top to style the link:

Putting it all together:

Including External Javascript and CSS files

Normally, to include external Javascript and CSS files, you would use the standard notation:

But within Gadgets, you can use the standard Atlassian notation for including these external resources, for those whom are familiar with Atlassian plugin development, this may seem familiar, and that is using the "#requireResource" function.

In order to use the #requireResource, 2 things needs to be completed, the first is to declare the resource in your atlassian-plugin.xml and the second thing is to update your Gadget descriptor file to reference the resource.

Declaring the Resources in Your Plugin

The first step is to put all of the resources in the resources directory of your plugin (eg. src/main/resources/net/customware/gadgets/example-gadget/css and src/main/resources/net/customware/gadgets/example-gadget/js), then adding references to these in your atlassian-plugin.xml (as web-resources)

This will make the resources available within your plugin.

Adding the Resource to Your Gadget

Once the resources are declared, you can make use of them in your Gadget, to do this you will need to first list all of your required resources, in this example, we will use the ones we declared in the previous step

You may include as many or as few resources as you want in this section, once you have added all of your resources, you will need to put the following line to get these resources to be embedded into the Gadget:

These should appear as the very first thing in your "<Content>" block of your descriptor.

Using Views in Content

Enabling Views

Within your "<ModulePrefs>" element of your descriptor, you will need to add a new "<Require>" element to enable the "views" functionality as it is not enabled by default.

Declaring Your View

The way to declare a view is to use the AJS.Gadget method, this method creates a Gadget object based upon the parameters passed in, the format that it accepts is in JSON.

Sample Gadget initialisation

From the above list, only the "baseUrl" and "view" parameters are required, all of the rest is optional but recommended to have.

Gadget - baseUrl (required)

This sets the base URL for the Gadget, which will be used to append to relative Ajax requests as well as made available via the Gadget object (by using gadget.getBaseUrl()).

To get JIRA to inject this value, simply use the following:

Gadget - useOauth (optional)

OAuth is a standard used to allow secure API authorization that Gadgets relies on, there are 2 valid options for this parameter:

Option

Description

always

Forces all requests to use OAuth, anonymous users will not be able to access this Gadget if this is used.

URL

Put the URL of the Oauth service you wish to use, JIRA has its own build it rest service for this located at "/rest/gadget/1.0/currentUser" it is recommended to use this one unless you have a good reason to use a different one.

Gadget - config (optional)

As this specifies a different view for the configurations, it is best to disable the default Edit screen by setting all of your "<UserPref>" elements to use "datatype="hidden"" (if all the fields are hidden, then the default Edit is not displayed, this helps avoid confusion as there will be 2 "Edit" links displayed if the fields are not set to hidden)

Specifies the configuration view of the Gadget (what is displayed when the user chooses to Edit the Gadget).

This parameter accepts a JSON object containing a "descriptor" and "args".

Gadget - config - descriptor (required)

This describes a function which returns a JSON object that contains the details of all of the fields and options for the configuration display, and accepts a series of arguments specified by the args parameter.

The values that this should return is in the following format:

With the list of fields, please refer to the Atlassian documentation for the Field Definitions for a complete list of what is available.

Gadget - config - args (optional)

This allows you to specify which values are available in the descriptor function, this requires 2 parameters and relied heavily on the Ajax APIs, the parameters includes:

Parameter

Description

key

The key to map the result of the Ajax call to, this can be accessed in the descriptor function using args.key

ajaxOptions

A set of options (JSON format) describing the Ajax call, for more information on this, please refer to using REST APIs

Gadget - view (required)

This specifies the view of the Gadget, and is called every time the Gadget is rendered, please make sure that code used in this have a consistent behavior when called more than once (it might be a good idea to reset the view using "gadget.getView().empty()" at the start to get rid of everything from the previous calls before rendering the view.

Gadget - view - template (required)

This specifies the functional to call each time the Gadget is rendered, and accepts a series of arguments specified by the args parameter.

The functional should accept a parameter called args:

Gadget - view - args (optional)

This allows you to specify which values are available in the descriptor function, this requires 2 parameters and relied heavily on the Ajax APIs, the parameters includes:

Parameter

Description

key

The key to map the result of the Ajax call to, this can be accessed in the template function using args.key

ajaxOptions

A set of options (JSON format) describing the Ajax call, for more information on this, please refer to using REST APIs

Making Your Gadget Configurable

User Preferences

User preferences is the simplest way to store user specific preferences for a Gadget, these are declared by adding "<UserPref>" elements in your descriptor file (usually right before the "<Content>" element), these describe possible user inputs which is stored into the Gadget and can be accessed within the "<Content>" using gadget.getPref("key").

The standard "<UserPref>" takes in a number of options, only one of which is required to be set explicitly (the others are not required but can be used to further customise the interface for the user).

The list of fields includes:

Field

Description

Default Value

name

REQUIRED - specifies the name of the user preference, this is the "key" that the value will be stored, to access the value of a preference, simply use gadget.getPref("name")

N/A

display_name

This is the field name for display to the user, sometimes you may not want the actual field name to be displayed to the user but something more meaningful and descriptive, this also allows for internationalisation of the interface

Whatever the "name" is set to

urlparam

String to pass as the parameter name for content type="url"

N/A

datatype

The type of data this field describes, valid options include: string, bool, enum, hidden, or list

string

required

Marks this preference as required or not, if set to true, the user MUST specify a value for this.

false

default_value

Sets the default value for this, if no value is set by the user, then this default value will be used to render the Gadget

N/A

User preferences are set every time the Gadget loads, to prevent this, a special user preference is reserved for this, and that is the "isConfigured" UserPref.

To use this, you will need to do 2 things, the first is to add the following line to your descriptor file:

Then within your "config" descriptor, add the following as one of the elements in your fields:

Advanced User Preference Interfaces

An API has been set up as part of the Atlassian Gadget framework to display specific types of fields in User Preferences, these fields are extensions of the basic fields mentioned in the User Preferences section.

Currently this is only available in JIRA but there are plans to make some of the more generic ones standardised across the various Atlassian systems (some of these are related to JIRA specific components such as filters and project categories).

Required Arguments

The following is required to be added to your config arguments:

args.projects

This will allow the Advanced User Preference Templates to access the list of Projects in the JIRA instance.

args.categories

To be completed...

Advanced User Preference Templates
AJS.gadget.fields.filterPicker

Example

Display

Description
Displays a textbox which attempts to do autocomplete (searches for matching filter names as you type into the box) your input.

AJS.gadget.fields.projectPicker

Example

Display

Description
Displays a dropdown list of all of the projects that you have access to.

AJS.gadget.fields.projectsAndCategoriesPicker

Example

Display

Description

AJS.gadget.fields.projectsOrCategoriesPicker

Example

Display

Description

To be completed...

AJS.gadget.fields.projectOrFilterPicker

To be completed...

AJS.gadget.fields.period

Example

Display

Description
Displays a dropdown box a list of options related to time, the options generated includes:

  • Hourly
  • Daily
  • Weekly
  • Monthly
  • Quarterly
  • Yearly
AJS.gadget.fields.nowConfigured

Example

Display

N/A

Description
This is a special hidden field just to set the "isConfigured" field to true, the purpose of this is to make it so that the configuration screen only appears once (when the Gadget if first configured).

Special - Refresh Interval

This is a special field that appears automatically when the "enableReload" parameter is set to "true" in your Gadget view and allows you to set how often the Gadget should refresh itself.

The options available are:

  • Every 15 Minutes
  • Every 30 Minutes
  • Every 1 Hour
  • Every 2 Hours

Injecting User Preferences

A very handy way of simplifying the accessing of user preferences is to basically specify it using the _UP_preferencename_ format, the Gadget API will actually replace these with the corresponding user preference value.

For example, if you have an user preference called "project_key", you can use this in your Gadget by putting: "_UP_project_key_".

Extending The Functionality of Your Gadget

There are a number of ways in which you can expand upon the functionalities of your Gadget beyond the standard set of features, the 2 main ways are described below:

Using REST APIs

This is one of the only ways in which your Gadget can communicate and gather information from various locations, by declaring REST APIs as resources in conjunction with User Preferences, a whole new set of possibilities is opened up, from the previous sections about the "args" parameter (in both "view" and "config"), we can include the results from REST APIs as input parameters to the various functions.

The way to declare one of these resources is described in the following example:

Within this example, we declared one data source called "data" which accesses a REST API located at "rest/example-rest/1.0/services/getdata", passing in the parameter "key" (the value that is passed is gotten from the User Preference with the name "key"), the result is then mapped to a parameter called "data".

There are a number of options for Ajax calls from the jQuery library which is not included in this guide, please refer to the documentation for Ajax in jQuery for more details.

Once this has been declared, you can access the results from this as a standard JSON object via the args parameter.

Example:
The REST API returns:

Then you can access the title by using "args.data.title".

Requiring Additional Features

There are a number of features which is not enabled by default in a Gadget, these features extends the standard functionality of the Gadget API, to enable these, you will need to explicitly declare these in "<Require>" tags within your "<ModulePrefs>" of your descriptor file.

For a complete listing of all of the available features, please visit the Gadget Feature Guide by Atlassian.

Contact

If you wish to contribute to this guide or have any questions or issues related to Atlassian Gadgets, please feel free to drop us a line in GetSatisfaction

This article is contributed by Martin Anaya.

Apache - Mod Proxy configuration

The use of Apache mod_proxy (reverse proxy) will allow external users to access internal applications. A good example when reverse proxy is implemented, is when internal users are behind a firewall and they need to use some specific ports. I will describe a scenario in which we decided to apply a new solution by using *reverse proxy.*

Reverse proxies allow several servers to be part of one single context path.

Current Network View

The Problem:

  • User wants to get access from a different location using a standard port i.e. 443. All internal access works
  • Some companies block specific ports that include ports 8443, 8444 and 8445.

Alternative solutions:

1) Redirection

This approach is simple to configure. The apache configuration file httpd.conf needs to be edited with the following lines:

The disadvantage with this approach is that once the user enters the url: https://comapnaydomain.com/comfluence, a new URL will be displayed pointing to the Confluence environment in https://companydomain.com:8443/confluence.

Useful Information

*Note: This approach will take place if the request is coming from a port 80.

2) Mod_proxy - reverse Proxy approach

Mod_proxy will allow to use one single port to access other application servers running internally. It will make external users to use standard ports such as 443 instead of ports such as 8443, 8444 and so forth.

The port 443 is a standard port and accessible from all companies. The external users will be listening to that port during all the request session, and behind the scenes the Apache web server with mod_proxy enabled will manage all the mapping and routing to the specific application servers depending on the route it was given when the request arrived to the Apache server.

The diagram below will demonstrate how the data transfers from one place to another and also how Apache processes the requests.

Configuration Process

We will configure a mod_proxy environment and connect an external user to Confluence through Apache server. This configuration is also applicable to Jira and Crowd.

Our Case study
  1. Client enters https://companydomain.com/confluence
  2. Request is received by Apache server. Apache checks its mapping config and finds that "/confluence" is set to inform https://companydomain.com:8443/confluence (Tomcat sever) that a request has been performed.
  3. Internal Confluence Tomcat takes the request and checks its configuration file server.xml in order to send the information back to the specified computer and port. In this case, we setup Confluence as proxyName="companydomain.com" and proxyPort="443".
  4. Apache receives back the response from the Confluence server and sends it back to the client.
  5. The client will see the information on the page, but he/she will not know that the data is coming from a different machine. The user will be working on the https://companydomain.com/confluence/ ..... environment.

The scenario above will allow you to have a good understanding of the changes that need to be done in Apache and Tomcat servers.

Lists of things we need for this case study:

  • Your domain computer should be running Apache as the front end and Tomcat as the internal server.
  • Apache should have the mod_ssl module installed. Check your modules folder under the "/conf/modules".
  • The following files will be edited in order to have the mod_proxy running.
    • *Apache files:

      - httpd-ssl.conf
      - httpd.conf

    • *Tomcat files:

      - server.xml.conf

Httpd.conf

(warning) Take a look at the following text lines:

ProxyRequests Off
ProxyPreserveHost Off
.....
ServerName <computername / domain name>

Https-ssl.conf

(warning) Take a look at the following text lines:

SSLProxyEngine on

Server.xml (Tomcat)

(warning) Take a look at the following text lines:

proxyName="companydomain.com" proxyPort="443"

These two connectors are available in server.xml file. If you are using SSL, you must enable the second connector.
For the purpose of this example I am running Confluence through two ports 8282 and 8443. Users that try to get access to port 8282 will be redirected to 8443 automatically.

AboutproxyName and proxyPort: (info)

These two paramters have to match with the apache domain specified in httpd.conf config file. Tomcat will use these values in order to send the information back to Apache.

References

In order to complete my research I used the following resources:

Oeyvind Eliassen, Kelvin Yap, Adam Saint-Prix and 8 others like this. Please log in to like this.

Assumptions

This guide assumes that your project is already set up properly (pom, directory structure, maven 2, correct version of java).

If you are not familiar with the process of developing Confluence Plugins, here is a list of relevant documentation to get you started:

Setting Up

Parent pom

The first a foremost thing you have to do is to include the following at the very top of your pom.xml (within the project element) :

Please note that you can change the version of the plugin base, typically try to use the newest version as they get improved upon often.

This parent pom includes a lot of dependencies for the test harness including the JWebUnit libraries, the custom Confluence abstract test cases, sample data, the webapp, tomcat, etc.

Note: by default the parent pom uses Java 1.4, you should overwrite this so you use (at least) Java 1.5 by inserting:

Setting up the Properties

You can specify a few environment details for your testing environment in your pom.xml, to do this, simply append the variables to your properties, here is a list of the available ones:

Property Name

Description

atlassian.plugin.key

The plugin key for the plugin that you are developing.

confluence.version or atlassian.product.version

The version of Confluence you are developing for.

atlassian.product.data.version

The sample data for the version of Confluence you are developing for, typically this is the same as the confluence.version property.

atlassian.product.test-lib.version

The version of the testing library to use, as a general recommendation you should at least use version 2.0 or higher as it exposes more of the page's content and provides quite a few extra helper classes to aid in your testing.

atlassian.plugin.application.version.min

The minimum version of Confluence that your plugin is compatible with.

atlassian.plugin.application.version.max

The maximum version of Confluence that your plugin is compatible with.

confluence.plugin.bundled

Bundle your plugin as part of Confluence.

confluence.plugin.install

Installs your plugin into the test instance of Confluence once it starts up.

confluence.url

The url of the testing Confluence instance, note that you do not have to change this, it is set automatically based on your other settings.

tomcat.installer.url

The url of the Tomcat installer to use, typically it is a link to a release on a maven 2 repository, by default it uses version 5.5.20 of Tomcat.

http.port

The port to run the testing Confluence instance on.

rmi.port

The port to use for RMI.

cargo.wait

false by default (shuts down server as soon as the tests are completed), I recommend not turning this to true unless you really want to see what is inside the system after the testing is completed.

jdkLevel

Sets the version of the JDK to use for compiling the plugin

Sample properties

Sample Confluence Plugin pom.xml Files

Here is a list of some sample pom files used in Atlassian Supported plugins for reference.

Writing Your Test

Setting up your Test Class

All Confluence tests should extend the com.atlassian.confluence.plugin.functest.AbstractConfluencePluginWebTestCase class, it is an extension of the base net.sourceforge.jwebunit.junit.WebTestCase class but provides a lot of helper methods that aid in the writing of your tests. This class also has some predefined setUp and tearDown sequences that are useful in all test cases
Some features that the setUp provides include:

  • Creating the confluence web tester class
  • Setting the base URL
  • Logging into confluence as an administrator
  • Installing your plugin
  • Installing the license
  • Putting in sample data
    Some features that the tearDown provides are:
  • Logging out

Because of these features, it is HIGHLY recommended that you invoke these parent methods using super.xxx() if you decide to overwrite them to extend their functionality.

Writing the Test Cases

As the AbstractConfluencePluginWebTestCase class extends the WebTestCase class, you have full access to everything that a normal web test case has such as gotoPage, getting values by Xpath, etc. You may want to refer to the documentation for JWebUnit to get a grasp of the basic concepts of a web test case.

For the package structure, Atlassian's standard style is to include "it." at the front of the package to indicate that it is for integration testing. Please note that this naming standard is COMPULSARY as the testing frame work filters the tests by the package name, so integration tests must have "it." as the start of the package.
eg.
it.com.my.confluence.plugin

Helpers

A few extra things that the AbstractConfluencePluginWebTestCase provides includes helpers (as you do not have access to the managers directly), here is a list of helpers that you can access including:

Helper

AttachmentHelper

BandanaHelper

BlogPostHelper

CommentHelper

GroupHelper

IndexHelper

MailServerHelper

PageHelper

SpaceHelper

UserHelper

These helpers are basically beans for each of their relevant entities but typically provides some extra functionally such as create, delete and update.

The 3 special cases from this list are the more back-end helpers including:

  • BandanaHelper - providing access to a single value stored in Confluence's bandana
  • IndexHelper - provides access to a single indexed item
  • MailServerHelper - gives you access and ability to update the mail servers configured for the server, useful if you need to mail things out as part of your tests.

When you are doing any operation with the helpers, typically they return a boolean to say if the process completed successfully or not, so when you are calling these options it is a good idea to use an assertTrue() around the function to check that the process completed properly.

Creating/Fetching Entities in Confluence

The way that this is done is via the related Helper classes for each entity, you can get the helpers by using the getxxxHelper() and getxxxHelper(Params) methods, the Helpers basically uses the remote API in Confluence to fetch the entities, then once you have the entities, simply use the .read() method to grab all the data related to that entity.

Creating Entities

The way to create entities is to basically get an empty helper instance (by using the getxxxHelper() method), populating it with data and then using the .create() method in the helper, please note that it is recommended to create everything as part of the setUp and then remove them as part of the tearDown process, otherwise you will get issues when running multiple tests.

Sample Entity Creation

Space:

Page:

User:

Fetching Entities

The way to fetch entities is to basically use the getxxxHelper(Params) method and it will give you the helper object with the information about the requested entity.

Sample Entity Fetching

User:

Group:

Updating Entities

To update entities, all you have to do is to first fetch the entity (refer to #Sample Entity Fetching for more details) then change some of the values and then invoke the .update() method in the helper.

Sample Entity Updating

User:

Using Your Own Test Data

If your plugin requires a lot of setup to test (for example reporting will require quite a lot of content to test the functionality properly), you might want to have a pre-populated Confluence backup for use with testing. Take a site export of your entire Confluence instance and put it in the following directory structure:

Confluence will load up this site export as part of the testing.

Sample Integration Test Class Code

Constructor:

Single Test:

Sample Test Cases

Here is a list of some sample integration tests used in Atlassian Supported plugins to test out some core Confluence macros to help you get an idea on how these tests are used.

Running Your Tests

This is the simplest part, simply use the following command:

or

Note: you can specify build specific parameters by using -Dparameter=value (eg. -Dcargo.wait=true), for a list of relevant parameters please consult the Properties List

The results will be printed to command line and a summary will be outputted to the target/surefire-reports/ directory, the build will ONLY be successful if it passes ALL tests.

Configuring Your Bamboo Build Plan

To actually configure Bamboo to do your testing automatically as part of the build and produce meaningful output you will need to do the changes listed below.

If you have never set up a Bamboo build plan before, you may want to check the Atlassian documentation for Creating a Plan to get you started.

Modifying the Builder

Basically all you need to do is to add integration-test as part of the command to be executed, please note that this should be BEFORE the package phase.

It should look something like the following:

Adding to the Artifacts

It is useful to have access to the test results via the build server directly, to do this we will need to add another configuration to the list of artifacts, add the following:

Artifact Label

Artifact Copy Pattern

Source Directory

surefire-reports

surefire-reports/*.*

target

After each build, you will find a set of surefire-reports under Artifacts, which you can view to see how many tests were executed, how long it took to run, environment details and in cases where a test failed, details on where it failed.

Appendix

Confluence Plugin Parent pom.xml

The parent pom.xml being used for integration testing can be found on the Atlassian svn under
confluence-plugin-base.

The referenced version (version 19) is the latest version release as of the time this article was written.

Often projects span both collaboration and integration domains - where a collaboration project requires integration. A few recent projects requiring integration with Microsoft Dynamics CRM 4.0 with different integration technologies have helped build extremely useful product knowledge for integrating against Dynamics. I've posted an article with a few tips and tricks! The article is included below:

We've implemented a number of integrations between Dynamics 4.0 and other applications and what follows are some tips for integrating against it with whatever integration technology you are using.

CRM WSDL

CRM provides one WSDL which provides methods for interacting with all entities in CRM. Altering CRM data or entities at the database level is not supported by Microsoft and should be avoided!

The WSDL is updated dynamically whenever customisations to CRM are made (so you can interact with new custom entities/attributes). However most integration tools require delete/reimport of new WSDL (cannot update) which may break existing integrations.

The WSDL provides the following methods:

  • Fetch - for fetching any requested attributes of records in an entity with any attribute restrictions (eg. all Product GUIDs with a UOM of 'PC'). This method can be slow. One way to optimise is to prefetch all the data in an entity without retriction (default 5000 record restriction in CRM) and store the data locally in some kind of data structure/database then query the local object (or database).
  • Retrieve - for retrieving a single record in an entity based on GUID
  • RetrieveMultiple - similar to fetch but uses a QueryExpression for strong types instead of XML formats
  • Create - creates a record, returns the GUID of new record
  • Update - updates a record, one record at a time based on GUID. Warning empty string values will overwrite data fields.
  • Delete - deletes one record at a time based on GUID
  • Execute - used to execute business logic and special operations

Apparently there is a hotfix which turns Create into Upsert. This would turn logic like Fetch->Branch->Create/Update into just Create.

CRM Entities and Attributes

To find what entities and attributes are available for interacting with CRM follow the steps below (you could read the WSDL also):

  • Login to CRM instance
  • Goto Settings
  • Goto Customization
  • Goto Customize entities

Here you have a list of all entities in the CRM instance (including customised ones). Double click an entity and goto the Attributes menu to get a list of an entities attributes. The attributes are the values which need to be used as fields using web service integration.

CRM Logout

Its not quite 'logout' but at least its NOT autologin resulting in inability to change users:

  1. In IE goto Tools -> Internet Options
  2. Under Security goto Local Intranet -> Custom Level
  3. Last item in the list: User Authentication -> 'Prompt for user name and password'

Now you'll be prompted each time you load IE rather than it remembering and you not being able to logout. Of course you can try to close browser, delete temporary files/history/passwords etc. but this only seems to work some of the time.

CRM Authentication

By default uses NTLM through IIS. NTLM requires authentication to an IIS server configured to an Active Directory (or similar) user repository. The credentials are then forwarded through to CRM (credentials checked against CRM user repository which can be imported from Active Directory or created directly). Accounts in both Active Directory and CRM must match identically! For some integration software this will require the user running the application to match a valid login as they cannot overwrite NTLM credentials - others can. If NTLM is proving problematic, you can switch to basic authentication on the web service in CRM (through the IIS menu -> find the CRM web service -> right click properties -> security -> change to basic).

CRM Miscellaneous

  • CRM fetch returns a maximum of 5000 records by default. To workaround this restriction see this blog.
  • CRM upsert - after encountering this bug the issue resolution seems to indicate upsert like behaviour available in the fixed version.

 
Posted by Kynan Fraser on 23rd February 2009.

Introduction

One of the most common structures that I build in Confluence deployments is a dynamic page list. It is useful for a huge range of applications, from a CRM to a FAQ.

The basic idea is simple - users can click on a button to add a new entry and when they are finished, the entry appears in a list of similar entries.

This page documents a simple way of doing this and then adds a few ideas about how to make the process more powerful by using some extra advanced macros. The example documented here is for a contact list. I have chosen this example because it is typically the most heavily used part of an Intranet and as Confluence is being used more and more as an Intranet, this is something everyone needs to do.

Use cases

The basic use cases that we need to address are as follows:

  1. A user adds a new Contact
  2. A user searches for a Contact
  3. A user browses for a Contact

Implementation

The following steps may be followed to implement a contacts page. Note that the user will need to be a space administrator to create templates.

Step 1

Make sure you have the appropriate plugins installed. For this example, you will need these ones:

Step 2

Build a template page that defines a generic Contact. For this example, the template is called 'contact-template'. Typically, it may be a simple table that looks something like this:

|| Forename | @FORENAME@ |
|| Surname |@SURNAME@ |
|| Phone |@PHONE@ |
|| Email | @EMAIL@|

In reality, there will be a lot more fields, but this is enough for our simple example.

Step 3

Add a label to the template - say 'contact'.

Step 4

2) Build the 'Contacts' page - this page is where is all happens. The page contains the structure that addresses all three of the use cases stated above. A very basic implementation is given below, and the exercise of making it look pretty is left to the user.

h1. Contacts Page

h2. Add a new page
{add-page:template=contact-template}

h2. Search Contacts

{search-form:autoSubmit=true}
{search-input:type=text|match=query}
{search-input:type=hidden|match=label|value=contact}
{search-submit:Search}
{search-form}

{search-results}

h2. Contact List

{contentbylabel:contact|maxResults=500}

Advanced methods

The basic steps shown above can be greatly enhanced by adding scaffolding and reporting.

Using scaffolding, he following enhancements can be made:

  • Use scaffold fields (such as {text-data} instead of the  (FIELD) inputs that templates use. This allows users to edit the page like a form even after it is first created. Scaffolding allows data validation, dynamic tables are many other cool features that make it easier for users to edit pages.
  • Use the live template feature to make all of the contacts pages source the same central template. If this is done, all contacts pages can be updated after the pages are created by modifying the template definition
  • Data can be entered into the page that can be extracted by the Reporting macros.

Using Reporting, the {contentbylabel} macro can be replaced with a much more comprehensive table of results. For instance, you could display a list with columns for the name, phone number and email address only. Reports that extract user defined data from a page must use Scaffolding to put data points in the page.

Another enhancement is to use the 'deck and card' macros to give users multiple views into the data. Our contacts list in the CustomWare Intranet a good example of what can be done by showing list of users in different regions.

These advanced methods add a lot of functionality, but keep in mind that lengthy queries can slow your server down significantly when large numbers of pages are involved. In particular, the reporting macros can be implemented so they search all of Confluence, which can slow large sites down. The {cache} macro can be used to improved response times for frequently used pages.

The Greenhouse has been setup to use [Atlassian Crowd|http://www.atlassian.com/crowd] for single sign-on. This means that all accounts have been migrated to use this infrastructure. For our 1000+ members, you may have had your password reset and if you have changed your profile photo, you may need to reset it. Other than that, smooth sailing!

Since running our business on a wiki since 2003, including our website, we've always been striving towards a level of openness and service that is unparalleled in the industry. We now have dozens of open source projects on our site, as well as many FAQ's and wiki's on various technologies, all supporting one of our core company values - Share the Knowledge.

The Greenhouse concept was created to capture all of our seedlings and ideas and putting them under a single central location to be nurtured, shared and followed.

We're excited to begin this process, and welcome you to signup and join our 950+ members to share your ideas and feedback.

Robert Castaneda, CustomWare Founder

Table of Contents

Background Information

In the past, those who wanted bilingual Japanese and English setups of JIRA on the same instance to rely on a Japanese specialized version of JIRA which is offered by imaHima, this version is usually a few increments behind the official Atlassian releases (currently up to version 3.12.2 from Atlassian Japan) an example of the imaHima Japanese-English JIRA implementation includes the Adobebugs public JIRA.

Ever since the official JIRA 3.13 release, Japanese has been added as a supported language pack that comes with the base product, many will find this to be
more appealing than the imaHima release as it contains more features and is fully supported by Atlassian, as such, some organizations have decided to implement a Japanese and English instance of JIRA 3.13. As this feature is quite new to the official JIRA product, I have conducted some investigations to learn the full Japanese capabilities made available in JIRA 3.13.

Description

I have recently completed a bilingual (Japanese and English) implementation of JIRA 3.13 for a client, and there were several key discoveries during my investigations to the internationalization capabilities and limitations of JIRA 3.13 in regards to this, I will break up my findings into 3 sections, the first will focus on the capabilities of the system to handle the actual bilingual data (mainly storage and display), the next one will focus on the capabilities of the functionality of the system (indexing, searching, etc.) and finally the presentation (basically what the user will see).

Bilingual Data Handling

The actual ability to handle the characters being entered into the system seemed to be flawless (as long as the underlying database could handle it), being able to store and retrieve anything in both Japanese and English with no apparant problems, both for field values, configuration items and even usernames and passwords, I have done some testing just to see what can be handled digitally in regards to the Japanese input types and compiled the following table:

Input Type

Field values

Login Details

Configuration Items

English

(tick)

(tick)

(tick)

Japanese - Alphanumeric

(tick)

(tick)

(tick)

Japanese - Hiragana

(tick)

(tick)

(tick)

Japanese - Full-Width Katakana

(tick)

(tick)

(tick)

Japanese - Half-Width Katakana

(tick)

(tick)

(tick)

Japanese - Kanji

(tick)

(tick)

(tick)

The only limitation that the system has is that the keys for the project cannot contain Japanese characters, but this should be understandable as the project key is used in the URL link for the project as well as any issues within the project and does not pose as a problem from the user's perspective.

System Functionality

The key note in this section is the searching module of JIRA (based on Lucene's indexing mechanisms), although this functionality is functioning as expected for a large portion of the data within the system, there are some cases where they are unable to come back with the proper results, as the searching system is completely depending on the indexing and not the database, the underlying database encoding does not affect the results of this investigation (we have tried this using multiple database encodings with the same results). The following table details the various input formats and what results they produce:

For the following tables, the first column specifies what input format was used to search the system, and each subsequent columns shows if the equivalent in each format is being returned as a result or not.

Indexing Language - Chinese/Japanese/Korean

Input Format

English

Alphanumeric

Hiragana

Full-Width Katakana

Half-Width Katakana

Kanji

English

(tick)

(tick)

(error)

(error)

(error)

(error)

Japanese - Alphanumeric

(tick)

(tick)

(error)

(error)

(error)

(error)

Japanese - Hiragana

(error)

(error)

(tick)

(error)

(error)

(error)

Japanese - Full-Width Katakana

(error)

(error)

(error)

(tick)

(error)

(error)

Japanese - Half-Width Katakana

(error)

(error)

(error)

(error)

(error)

(error)

Japanese - Kanji

(error)

(error)

(error)

(error)

(error)

(tick)

Indexing Language - English

Input Format

English

Alphanumeric

Hiragana

Full-Width Katakana

Half-Width Katakana

Kanji

English

(tick)

(error)

(error)

(error)

(error)

(error)

Japanese - Alphanumeric

(error)

(error)

(error)

(error)

(error)

(error)

Japanese - Hiragana

(error)

(error)

(tick)

(error)

(error)

(error)

Japanese - Full-Width Katakana

(error)

(error)

(error)

(tick)

(error)

(error)

Japanese - Half-Width Katakana

(error)

(error)

(error)

(error)

(tick)

(error)

Japanese - Kanji

(error)

(error)

(error)

(error)

(error)

(tick)

Indexing Language - Other

Input Format

English

Alphanumeric

Hiragana

Full-Width Katakana

Half-Width Katakana

Kanji

English

(tick)

(error)

(error)

(error)

(error)

(error)

Japanese - Alphanumeric

(error)

(error)

(error)

(error)

(error)

(error)

Japanese - Hiragana

(error)

(error)

(tick)

(error)

(error)

(error)

Japanese - Full-Width Katakana

(error)

(error)

(error)

(tick)

(error)

(error)

Japanese - Half-Width Katakana

(error)

(error)

(error)

(error)

(tick)

(error)

Japanese - Kanji

(error)

(error)

(error)

(error)

(error)

(tick)

System Functionality - Summary

From the results it seems like there is always 1 case where the search could not produce the desired results, this is a bit of a problem, so it is best to warn the users to avoid the problematic one when possible when using one of the cases.

Presentation

JIRA uses language packs to apply various languages to the default JIRA system, with a special language pack for the Japanese language, in most of the system, the translations are quite decent, however there are several cases where there were problems, the most noticeable ones included:

  1. "with open issues due to be fixed per version" - which did not get translated in the language pack (left as English)
  2. "A change log of the recent versions for this project " - which was caused by an error in the i18 parameter name within the com\atlassian\jira\plugins\projectpanels\changelog_ja_JP.properties file, the properties should be projectpanels.changelog.description and projectpanels.changelog.name as opposed to componentpanels.changelog.description and componentpanels.changelog.name

A JIRA issue has been raised in regards to the language pack, and since then, I have made the requested changes (with the help of a Japanese client) to the language pack and have submitted the changes back to Atlassian (attached to the issue), hopefully this will get incorporated back into the base product in the 3.13.2 release of JIRA.

Overall Summary

Although there are some issues in regards to the bilingual setup of JIRA 3.13, most of the issues are either cosmetic (in regards to the presentation section of the system) or minor with workarounds, so as such, JIRA is very capable of dealing with dual Japanese-English setups, and with the future improvements of the system (in versions 3.13.2), I expect the overall quality and capability of JIRA to increase beyond where it is now.