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,575 accepted answers
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
2 comments