In this article I would like to create a Jira plugin, where I could define beans, using Java-based container configuration, and apply AOP principles to log information about method invocation in the plugin.
Let's do it step by step.
1. Create a Jira plugin.
Open terminal and run the following command:
atlas-create-jira-plugin
You will be asked about the plugin properties. Here are the values for the properties:
Define value for groupId: : ru.matveev.alexey.plugins.spring
Define value for artifactId: : spring-tutorial
Define value for version: 1.0.0-SNAPSHOT: :
Define value for package: ru.matveev.alexey.plugins.spring: :groupId: ru.matveev.alexey.plugins.spring
artifactId: spring-tutorial
version: 1.0.0-SNAPSHOT
package: ru.matveev.alexey.plugins.spring
Y: : Y
2. Adjust the pom.xml file.
Change the scope of the atlassian-spring-scanner-annotation dependency from compile to provided:
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>compile</scope>
</dependency>
Delete the atlassian-spring-scanner-runtime dependency.
Change the value of the property atlassian.spring.scanner.version to 2.0.0
Add the following dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.5.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
<scope>provided</scope>
</dependency>
Add the following lines to the maven-jira-plugin in the instructions section:
<DynamicImport-Package>*</DynamicImport-Package>
This line will let your plugin resolve spring classes at runtime.
3. Create the interface and an implementation of the HelloWorld object:
HelloWorld.java:
package ru.matveev.alexey.plugins.spring.api;
public interface HelloWorld {
String getMessage();
void setMessage(String value);
}
HelloWorldImpl.java:
package ru.matveev.alexey.plugins.spring.impl;
public class HelloWorldImpl implements HelloWorld {
private static final Logger LOG = LoggerFactory.getLogger(HelloWorldImpl.class);
private String message = "Hello World!!!";
private final ApplicationProperties applicationProperties;
public HelloWorldImpl(ApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
}
public String getMessage() {
LOG.debug("getMessage executed");
return applicationProperties.getDisplayName() + " " + this.message;
}
public void setMessage(String value) {
LOG.debug("setMessage executed");
message = value;
}
}
4. Create Java classes, which will log information about the HelloWorld methods on their execution.
HijackAroundMethod.java
package ru.matveev.alexey.plugins.spring.aop;
import java.util.Arrays;
public class HijackAroundMethod implements MethodInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(HijackAroundMethod.class);
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
LOG.debug("HijackAroundMethod : Method name : "
+ methodInvocation.getMethod().getName());
LOG.debug("HijackAroundMethod : Method arguments : "
+ Arrays.toString(methodInvocation.getArguments()));
LOG.debug("HijackAroundMethod : Before method hijacked!");
try {
Object result = methodInvocation.proceed();
LOG.debug("HijackAroundMethod : Before after hijacked!");
return result;
} catch (IllegalArgumentException e) {
LOG.debug("HijackAroundMethod : Throw exception hijacked!");
throw e;
}
}
}
HijackBeforeMethod.java
package ru.matveev.alexey.plugins.spring.aop;
public class HijackBeforeMethod implements MethodBeforeAdvice
{
private static final Logger LOG = LoggerFactory.getLogger(HijackBeforeMethod.class);
public void before(Method method, Object[] objects, Object o) throws Throwable {
LOG.debug("HijackBeforeMethod : method {} in", method.toString());
}
}
5. Create a Java-based container configuration file.
Config.java
package ru.matveev.alexey.plugins.spring.config;
@Component
@Configuration
public class Config{
@Bean(name = "helloWorld")
@Scope("prototype")
public HelloWorld helloWorld(@ComponentImport ApplicationProperties applicationProperties) {
return new HelloWorldImpl(applicationProperties);
}
@Bean(name="hijackBeforeMethodBean")
public HijackBeforeMethod hijackBeforeMethod() {
return new HijackBeforeMethod();
}
@Bean(name="hijackAroundMethodBean")
public HijackAroundMethod hijackAroudnMethod() {
return new HijackAroundMethod();
}
@Bean (name = "helloWorldBeforeProxy")
@Scope("prototype")
public ProxyFactoryBean proxyBeforeFactoryBean(@ComponentImport ApplicationProperties applicationProperties) {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(helloWorld(applicationProperties));
proxyFactoryBean.setProxyTargetClass(true);
proxyFactoryBean.setInterceptorNames("hijackBeforeMethodBean");
return proxyFactoryBean;
}
@Bean (name = "helloWorldAroundProxy")
@Scope("prototype")
public ProxyFactoryBean proxyAroundFactoryBean(@ComponentImport ApplicationProperties applicationProperties) {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(helloWorld(applicationProperties));
proxyFactoryBean.setProxyTargetClass(true);
proxyFactoryBean.setInterceptorNames("hijackAroundMethodBean");
return proxyFactoryBean;
}
}
6. Create two servlet modules.
The Servlet modules will be used for testing the application.
Open terminal and run the command below:
atlas-create-jira-plugin-module
When you are asked to choose the module number choose 21 (Servlet)
Choose a number (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34): 21
When you are asked about the properties of the module, enter:
Enter New Classname MyServlet: : MyServlet1
Enter Package Name ru.matveev.alexey.plugins.spring.servlet: :
Show Advanced Setup? (Y/y/N/n) N: : N
Then you will be asked, if you want to create another module, answer Y
Add Another Plugin Module? (Y/y/N/n) N: : Y
Choose again the Servlet module (Number 21) and fill the module properties as below:
Enter New Classname MyServlet: : MyServlet2
Enter Package Name ru.matveev.alexey.plugins.spring.servlet: :
Show Advanced Setup? (Y/y/N/n) N: : N
Answer N, when you are asked about adding another module:
Add Another Plugin Module? (Y/y/N/n) N: : N
7. Add code to the Servlet modules.
MyServlet1.java
package ru.matveev.alexey.plugins.spring.servlet;
public class MyServlet1 extends HttpServlet{
private static final Logger log = LoggerFactory.getLogger(MyServlet1.class);
private final HelloWorld helloWorld;
@Inject
public MyServlet1(@Qualifier("helloWorldBeforeProxy") HelloWorld helloWorld) {
this.helloWorld = helloWorld;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
log.debug("MyServlet1 called");
resp.setContentType("text/html");
String message = "<html><body>" + helloWorld.getMessage() + "</body></html>";
helloWorld.setMessage("message changed MyServlet");
resp.getWriter().write(message);
}
}
MyServlet2.java
package ru.matveev.alexey.plugins.spring.servlet;
public class MyServlet2 extends HttpServlet{
private static final Logger log = LoggerFactory.getLogger(MyServlet2.class);
private final HelloWorld helloWorld;
@Inject
public MyServlet2(@Qualifier("helloWorldAroundProxy") HelloWorld helloWorld) {
this.helloWorld = helloWorld;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
log.debug("MyServlet2 called");
resp.setContentType("text/html");
String message = "<html><body>" + helloWorld.getMessage() + "</body></html>";
helloWorld.setMessage("message changed MyServlet");
resp.getWriter().write(message);
}
}
8. Run the plugin
Open terminal and run the command below:
atlas-run
When Jira started, you should open your browser at:
http://localhost:2990/jira/
Go to Cog item -> System -> Logging and Profiling and set the Debug logging level for the ru.matveev.alexey package:
Go to http://localhost:2990/jira/plugins/servlet/myservlet1:
Our Servet Worked.
Let's see the logs to make sure that the helloWorldBeforeProxy worked too:
[INFO] [talledLocalContainer] 2018-03-31 05:47:26,957 http-nio-2990-exec-10 DEBUG admin 347x281x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet1 [r.m.a.p.spring.servlet.MyServlet1] MyServlet1 called
[INFO] [talledLocalContainer] 2018-03-31 05:47:26,957 http-nio-2990-exec-10 DEBUG admin 347x281x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet1 [r.m.a.p.spring.aop.HijackBeforeMethod] HijackBeforeMethod : method public java.lang.String ru.matveev.alexey.plugins.spring.impl.HelloWorldImpl.getMessage() in
[INFO] [talledLocalContainer] 2018-03-31 05:47:26,996 http-nio-2990-exec-10 DEBUG admin 347x281x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet1 [r.m.a.p.spring.impl.HelloWorldImpl] getMessage executed
[INFO] [talledLocalContainer] 2018-03-31 05:47:26,997 http-nio-2990-exec-10 DEBUG admin 347x281x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet1 [r.m.a.p.spring.aop.HijackBeforeMethod] HijackBeforeMethod : method public void ru.matveev.alexey.plugins.spring.impl.HelloWorldImpl.setMessage(java.lang.String) in
[INFO] [talledLocalContainer] 2018-03-31 05:47:26,997 http-nio-2990-exec-10 DEBUG admin 347x281x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet1 [r.m.a.p.spring.impl.HelloWorldImpl] setMessage executed
We can see that the HijackBeforeMethod bean was called before each method. It means we also succeeded in AOP.
Let's refresh the page:
We can see that the message variable in the HelloWorldImpl class changed. We declared the HelloWorld bean as prototype. It means, that if we call the MyServlet2 servlet, a new instance of the helloWorld bean will be created and the message variable will have the "Hello World" value. Let's check it.
Go to http://localhost:2990/jira/plugins/servlet/myservlet2:
We can see that the message variable has the expected value. The prototype scope worked.
Now let's see in the logs:
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,100 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.servlet.MyServlet2] MyServlet2 called
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,100 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Method name : getMessage
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,100 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Method arguments : []
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,100 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Before method hijacked!
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,113 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.impl.HelloWorldImpl] getMessage executed
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,114 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Before after hijacked!
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,114 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Method name : setMessage
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,114 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Method arguments : [message changed MyServlet]
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,114 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Before method hijacked!
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,114 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.impl.HelloWorldImpl] setMessage executed
[INFO] [talledLocalContainer] 2018-03-31 05:56:24,114 http-nio-2990-exec-6 DEBUG admin 356x283x1 1o6djq5 127.0.0.1 /plugins/servlet/myservlet2 [r.m.a.p.spring.aop.HijackAroundMethod] HijackAroundMethod : Before after hijacked!
We can see that the helloWorldAround bean worked as well.
Everything worked as we planned.
You can find the code here:
https://bitbucket.org/alex1mmm/spring-tutorial/src/97c4ac7a145336fcadc2c3922046d09406696361?at=V1
Alexey Matveev
software developer
MagicButtonLabs
Philippines
1,574 accepted answers
2 comments