How to implement JIRA plugin caching?

Hello,

I'm new to JIRA development and I'm following this article on implementing plugin caching:  https://developer.atlassian.com/confdev/development-resources/confluence-developer-faq/how-do-i-cache-data-in-a-plugin

However, after installing my plugin and trying to test it, it does not behave as expected.

Each time I make a request to my plugin, it basically instantiates a new instance of my plugin.  The constructor is called for every request I make, so a new CacheManager is created and each request does the expensive retrieval of data from the database.  In other words, each request is like visiting my plugin for the very first time and effectively there is no caching because of this.

Why does my plugin not live beyond the scope of a single request?  Is this perhaps a plugin scope configuration that I need to configure?  I read the article about JIRA plugin lifecycle: https://developer.atlassian.com/jiradev/jira-platform/building-jira-add-ons/jira-plugins2-overview/jira-plugin-lifecycle#JIRAPluginLifecycle-Componentinitialisation  and also read articles about "JIRA plugin scope" but they're not talking about plugin scope as it relates to development.

Thanks in advance!

 

1 answer

1 accepted

0 vote
Alexey Matveev Community Champion Dec 18, 2017

Hello,

You need to inject CacheManager. In this case CacheManager always stays the same. Also you can make your class as a bean adding Named annotation to the class. It is difficult to say for sure what is wrong with your code because you did not provide it.

Hi Alexey,

Thanks for your help!  I am injecting the CacheManager into my constructor and also I have just tried adding the "Named" annotation, however it still does not work.  Here is a simplified version of my plugin code:

@Named("MyPlugin")
@Scanned
public class MyPlugin extends JiraWebActionSupport
{
private static final Logger log = LoggerFactory.getLogger(MyPlugin.class);

private MyDBService db;

@ComponentImport
private final PermissionManager permissionMgr;

@ComponentImport
private final CacheManager cachMgr;

private final Cache myCache;
private List<MyField> myFields;

@Inject
public MyPlugin(MyDBService mydb, PermissionManager permissionMgr, CacheManager cachMgr) {
this.db = mydb;
this.permissionMgr = permissionMgr;
this.cachMgr = cachMgr;
myCache = cachMgr.getCache(MyPlugin.class.getName() + "-MyCache.cache",
new MyDataLoader(),
new CacheSettingsBuilder().expireAfterAccess(50, TimeUnit.MINUTES).build());
log.debug("MyPlugin Constructor called!");
}

@Override
public String execute() throws Exception {

myFields = (List<MyField>) myCache.get("myData")
return super.execute();
}

private class MyDataLoader implements CacheLoader<String,List<MyField>> {

public List<MyField> load(@Nonnull String s) {

if(s.equals("myData")) {
log.debug("retrieved data from db!");
return db.getAllData();
}

return null;
}
}

}

 

Alexey Matveev Community Champion Dec 18, 2017

You put @Named annotation and made atlas-clean and then atlas-run? And then every time you enter your webwork item you fall into the constructor? If so, make you private MyDataLoader class as a bean and pass it to the constructor.

Alexey Matveev Community Champion Dec 18, 2017

Also I found this article

https://community.atlassian.com/t5/Answers-Developer-Questions/Cache-2-CacheLoader-is-called-on-every-cache-read/qaq-p/555222

The point is that

myCache = cachMgr.getCache(MyPlugin.class.getName() + "-MyCache.cache",
new MyDataLoader(),
new CacheSettingsBuilder().expireAfterAccess(50, TimeUnit.MINUTES)
.build());

must be called once. So I would make a bean and make the myCache as a property of the bean. Then I would inject it into the constructor of your Webwork. 

But That would be true only if the constructor of your Webwork is called everytime you call the webwork. I need to check it. But I can not check it right now. But I ll try to check it tomorrow.

Hi Alexey, 

Thank you so much for your help.  I did some quick testing and it seems your suggestion will work:

  - create a separate dedicated 'cache bean' with the cache objects as properties (and instantiate the cache properties in this bean's constructor -- which will only be called once)

  - inject this dedicated 'cache bean' into my plugin bean's constructor

Even though my plugin bean constructor is called once per request (intended by design or not), the 'cache bean' constructor will only be invoked once when the bean is initially injected (so the cache objects stay the same and the cached data are reused). 

Thanks again, Alexey!

Best regards,

victor

Oh btw, re: your question about atlas-clean atlas-run..

I was just making my changes, then doing:  atlas-mvn package

Then I'd uninstall and reinstall the newly compiled .jar into the JIRA instance and test it.

(But I also tried doing atlas-clean and then atlas-mvn package and it didn't fix my problem.. it seems since my plugin constructor is called many times, the only solution is to refactor out the instantiation of the cache objects into a separate bean and inject back into my plugin)

Suggest an answer

Log in or Sign up to answer
How to earn badges on the Atlassian Community

How to earn badges on the Atlassian Community

Badges are a great way to show off community activity, whether you’re a newbie or a Champion.

Learn more
Community showcase
Published yesterday in Jira

5 ways you can make the most of Jira Software and Bitbucket Cloud

As part of the Bitbucket product team I'm always interested in better understanding what kind of impact the use of our tools have on the way you work. In a recent study we conducted of software devel...

58 views 0 5
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you