J tricks - Little JIRA Tricks
  • Home
  • Plugins ↓
    • JQL Tricks Plugin
      • JQL Tricks Plugin - Cloud
        • JQLT Cloud Installation
        • JQLT Cloud Configuration
        • JQLT Cloud Usage
        • JQLT Cloud License
        • JQLT Cloud FAQ
      • JQL Tricks Plugin - DC
        • JQLT DC Installation
        • JQLT DC Configuration
        • JQLT DC Usage
          • JQLT Issue Functions
          • JQLT Subtask Functions
          • JQLT Links Functions
          • JQLT Development Functions
          • JQLT Worklog Functions
          • JQLT Project Functions
          • JQLT Component Functions
          • JQLT Version Functions
          • JQLT Group Functions
          • JQLT User Functions
          • JQLT Date Functions
        • JQLT DC License
        • JQLT DC FAQ
        • JQLT DC Known Issues
        • JQLT DC Performance
      • JQL Tricks Cloud Migration
    • Simplified Planner
      • J-Planner Installation
      • J-Planner Configuration
      • J-Planner Usage
        • Creating a plan
        • Editing a plan
        • Deleting a plan
        • Viewing a plan
        • Modifying a plan
      • J-Planner FAQ
    • Atla-Search Plugin
      • Atla-Search Installation
      • Atla-Search Configuration
      • Atla-Search Usage
      • Atla-Search License
      • Atla-Search FAQ
    • Copy to subtask Plugin
    • All Plugins
  • Tutorials
  • The Book
  • Contact Us
  • Home
  • Plugins ↓
    • JQL Tricks Plugin
      • JQL Tricks Plugin - Cloud
        • JQLT Cloud Installation
        • JQLT Cloud Configuration
        • JQLT Cloud Usage
        • JQLT Cloud License
        • JQLT Cloud FAQ
      • JQL Tricks Plugin - DC
        • JQLT DC Installation
        • JQLT DC Configuration
        • JQLT DC Usage
          • JQLT Issue Functions
          • JQLT Subtask Functions
          • JQLT Links Functions
          • JQLT Development Functions
          • JQLT Worklog Functions
          • JQLT Project Functions
          • JQLT Component Functions
          • JQLT Version Functions
          • JQLT Group Functions
          • JQLT User Functions
          • JQLT Date Functions
        • JQLT DC License
        • JQLT DC FAQ
        • JQLT DC Known Issues
        • JQLT DC Performance
      • JQL Tricks Cloud Migration
    • Simplified Planner
      • J-Planner Installation
      • J-Planner Configuration
      • J-Planner Usage
        • Creating a plan
        • Editing a plan
        • Deleting a plan
        • Viewing a plan
        • Modifying a plan
      • J-Planner FAQ
    • Atla-Search Plugin
      • Atla-Search Installation
      • Atla-Search Configuration
      • Atla-Search Usage
      • Atla-Search License
      • Atla-Search FAQ
    • Copy to subtask Plugin
    • All Plugins
  • Tutorials
  • The Book
  • Contact Us

Working with Custom Field Searchers

9/20/2010

24 Comments

 
We have seen how to write a custom field in one of the previous tutorials. Writing the field is one thing but making it available to one of the JIRA's most powerful functionality, i.e. Search, is another!

So how do we do that? In most cases, you wouldn't need a custom searcher. Instead you can use the built in custom field searchers in JIRA itself. The list includes, but not restricted to, Text Field Searcher, Date Searcher, Number Searcher, User Searcher etc.

The first step of course is to determine what is the kind of Searcher your new field needs. For example, a Select field can easily be searched with Text Searcher or an Exact Text Searcher! A User Picker field can be searched with a User Searcher or a Text Searcher.  You might even want to extend one of these Searchers to add some extra functionality. Some special conditions or hacks you want to introduce! Yeah, you know what I mean!!

Now, we have the custom field ready and the Searcher decided. Let us , in this example, consider using a TextSearcher puerly because the customfield we created in the other tutorial is extending a TextCFType.

Here is how JIRA has defined the text searcher for its system custom fields:

<customfield-searcher key="textsearcher" name="Free Text Searcher"
        i18n-name-key="admin.customfield.searcher.textsearcher.name"
        class="com.atlassian.jira.issue.customfields.searchers.TextSearcher">
        <description key="admin.customfield.searcher.textsearcher.desc">Search for values using a free text search.</description>

        <resource type="velocity" name="search" location="templates/plugins/fields/edit-searcher/search-basictext.vm"/>
        <resource type="velocity" name="view" location="templates/plugins/fields/view-searcher/view-searcher-basictext.vm"/>
        <valid-customfield-type package="com.atlassian.jira.plugin.system.customfieldtypes" key="textfield"/>
        <valid-customfield-type package="com.atlassian.jira.plugin.system.customfieldtypes" key="textarea"/>
        <valid-customfield-type package="com.atlassian.jira.plugin.system.customfieldtypes" key="readonlyfield"/>
</customfield-searcher>

We are going to do the same thing in our Atlassian-plugin.xml for our custom field. It will look like this:

<customfield-searcher key="readonly-user-searcher" name="Read Only User Searcher"
        class="com.atlassian.jira.issue.customfields.searchers.TextSearcher">
        <description key="admin.customfield.searcher.textsearcher.desc">Search for Read Only User using a free text search.</description>

        <resource type="velocity" name="search" location="templates/plugins/fields/edit-searcher/search-basictext.vm"/>
        <resource type="velocity" name="view" location="templates/plugins/fields/view-searcher/view-searcher-basictext.vm"/>
        <valid-customfield-type package="com.jtricks" key="readonly-user"/>
</customfield-searcher>

I am sure you would have noticed the changes made. Just to elaborate a bit, there should be a unique key like for everything else and the class should refer to the Searcher you want to point to. It could be your custom Searcher class as we mentioned before but in this case we have chosen JIRA's TextSearcher. Then you can define your own velocity templates for the Search and view for Edit and View 'views' respectively.

And then comes the most important part where you mention your customfield within the valid-customfield-type tag. There can be multiple entries for different custom fields as you saw in the first snippet from Atlassian. In our case we have the read-only user custom field added.

There is basic but very common error you might make here. The package attribute here refers to the atlassian-plugin key where the custom field resides in and not the Java package where the Searcher class resides in! Just to make it clear, the Atlassian plugin key is the key in the first line of your atlassian-plugin.xml which is 'com.jtricks' in our case.

<atlassian-plugin key="com.jtricks" name="J-Tricks Customfields Plugin"  plugins-version="2">

This package (plugin key) along with the customfield key (readonly-user in this case) will point to the right custom field. This would also mean that you can have the same 'readonly-user' in another plugin with a different plugin key!

Now all these works fine pre 4.x or pre OSGI! With the introduction of v2 plugins, courtesy OSGI bundles, referring the JIRA classes directly in the atlassian-plugin.xml will fail sometimes because it can't resolve all the dependencies (the notorious Unsatisfied dependency errors!). This is because some of the classes are not available for dependency injection in the version 2 plugins as they were in version 1 plugins.

But don't worry, there is an easy hack to do it. Just create a dummy custom Searcher class with the constructor that does the dependency injection for you.  So you will endup doing something what Leonids suggest in the forums:

public class MySearcher extends SomeJiraSearcher {
   public MySearcher(PluginComponent ioc) {
      super(ioc, ComponentManager.getInstanceOfType(anotherType));
  }
}

Have a look at this thread and this for more details and a sample for this issue.

If nothing works, you always have the option of adding the field to the system-customfield-types.xml under WEB-INF/classes along with the JIRA system custom fields. i.e. one more valid-customfield-type entry into the relevant customfield-searcher element.

Phew! So we have the searcher defined and the stage is set to create your first filter with the new custom field. Happy coding.
24 Comments
Joe Caputo
1/9/2012 07:49:27 am

Hi there!

I'm having an issue getting searching to work with my plugin. I'm using an existing searcher. When i remove the searcher code from this xml file, the plugin loads up. However, if I keep it there, it breaks the plugin and doesn't load up. Here is my atlassian-plugin.xml file:

<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2">
<plugin-info>
<description>${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}" />
</plugin-info>

<resource type="i18n" name="i18n" location="originreadonlytext.i18n" />


<customfield-type key="origin-read-only" name="Origin Read-only Text Field"
class="com.ea.jira.plugins.customfields.OriginReadOnlyText">
<description>A text field that is only read-only. Can only be set via workflow post functions.</description>
<resource type="velocity" name="view" location="templates/plugins/fields/view/view-basictext.vm"/>
<resource type="velocity" name="edit" location="originreadonlytext/templates/edit-originreadonlytext.vm"/>
<resource type="velocity" name="xml" location="templates/plugins/fields/xml/xml-basictext.vm"/>
</customfield-type>


<customfield-searcher key="readonly-flagged-searcher" name="Read Only Flagged Searcher"
class="com.atlassian.jira.issue.customfields.searchers.TextSearcher">
<description Search for Read Only Flagged for field using a free text search.</description>

<resource type="velocity" name="search" location="templates/plugins/fields/edit-searcher/search-basictext.vm"/>
<resource type="velocity" name="view" location="templates/plugins/fields/view-searcher/view-searcher-basictext.vm"/>
<valid-customfield-type package="${project.groupId}.${project.artifactId}" key="origin-read-only"/>
</customfield-searcher>



</atlassian-plugin>

Reply
Joe Caputo
1/9/2012 07:52:38 am

My custom field is simple. It just extends the text type and I modify one velocity file so that it's read-only:

public class OriginReadOnlyText extends TextCFType {

public OriginReadOnlyText(CustomFieldValuePersister customFieldValuePersister,
StringConverter stringConverter,
GenericConfigManager genericConfigManager) {

super(customFieldValuePersister, stringConverter, genericConfigManager);
}

Reply
J-Tricks
1/9/2012 01:36:24 pm

I think you are having the same OSGI issue mentioned above. Are you seeing any error in the logs when the plugin is loaded?

Try the fix mentioned above - i.e. creating a dummy custom Searcher class with the constructor that does the dependency injection for you

Reply
Joe Caputo
1/9/2012 02:44:20 pm

Ok, I'll give that a shot Mr. Tricks. Is this the proper way of doing this now?

I do see an error when trying to enable the plugin saying it can't find my my class (something along those lines). I'll try and get the error message for you.

J-Tricks
1/10/2012 12:41:58 am

@Joe, Yes that is the only way you can do it. It is a known issue ever since OSGi Plugins came into picture. JRA hasn't made all the classes available to OSGi Bundles.

Reply
Tom Towkach link
7/20/2012 06:19:20 am

We had the problem where our plugin would run in production for a long time, then somehow JIRA disabled it. I figured out that by going to Custom Fields, JIRA was having this problem with 2 classes, so it disabled my plugin. When I rewrote the constructor, I can click on Custom Fields and not have the plugin go disabled. My constructor now:

public UserPickerInGroupSearcher(final UserResolver userResolver,
final JqlOperandResolver operandResolver,
final JiraAuthenticationContext context,
/*UserConverter userConverter, */
UserPickerSearchService userPickerSearchService,
final CustomFieldInputHelper customFieldInputHelper,
UserManager userManager) {


super(userResolver, operandResolver, context, ComponentManager.getComponent(UserConverter.class),
userPickerSearchService, customFieldInputHelper, userManager);
}

Reply
J-Tricks
7/20/2012 12:23:40 pm

Tom,

That's great. And thanks for sharing the code. Hopefully it will be useful for someone else.

Reply
Bertrand
9/28/2012 08:44:56 am

Hi,
I created a multi select custom field. It works great, but it's not searchable.
So I read your article and attempted to create a searcher.
I created one, following your example, that extends TextSearcher.
With the new textSearcher my plugin loads find and I can configure my custom field with that searcher.
But when I try using jql, it never returns anything. Going back to the Administration Console, it says I need to do a reIndex.
When I do the reIndex, I get really quickly an Exception and a stacktrace stating that String cannot be cast to ArrayList.

If I disable my plugin or even just removing the searcher from my custom field config, the re-index works fine. So the Exception I get is definitely caused by the searcher.

My assumption is that I should not be using a TextSearcher with a Multi Select field. Is that correct?

If I'm right, what should I be using? I see that there is a MultiSelectSearcher in the javadoc, but it seems like that class is packaged in jira-core (instead of jira-api), and I don't really feel like adding that dependency to my custom field plugin (based on Jira recommendation).
So what can I do/use?
Thanks for your help!

Reply
J-Tricks
9/28/2012 09:21:59 am

Unfortunately, the easy option for you is to add the jira-core dependency and extend the MultiSelectSearcher.

The only other option is to write your own searcher from scratch. Look into the MultiSelectSearcher to see how it is done!

Reply
Nabil Sayegh
2/17/2013 11:21:42 pm

Thanks for pointing out the meaning of the package field

Reply
Vijaya Kumar link
7/1/2013 12:51:55 am

I am using custom versionsearcher as below in jira 5.0.x .It works fine.

public class CustomVersionSearcher extends VersionSearcher {

public CustomVersionSearcher(VersionManager versionManager,
FieldVisibilityManager fieldVisibilityManager,
JqlOperandResolver operandResolver,
PermissionManager permissionManager, CustomFieldInputHelper customFieldInputHelper) {
super(versionManager, fieldVisibilityManager, new VersionResolver(versionManager), operandResolver,
new DefaultFieldFlagOperandRegistry() , new VersionClauseContextFactory(operandResolver, new VersionResolver(versionManager), permissionManager) ,
permissionManager, ContextSetUtil.getInstance(), ComponentAccessor.getComponent(FieldConfigSchemeClauseContextUtil.class) , new MultiClauseDecoratorContextFactory.Factory( new DefaultOperatorUsageValidator(operandResolver, ComponentAccessor.getI18nHelperFactory()) , operandResolver, ContextSetUtil.getInstance()),
customFieldInputHelper);

}

Reply
Dale Miller
6/16/2015 11:59:13 am

That seems to work for the dependency issues. I want to change the data that gets stored in the index. Can I use this method and change what gets stored in lucene? I looked at overriding the init method but can't seems to get it to work properly.

Reply
J-Tricks
6/17/2015 12:32:27 am

You will have to override the FieldIndexer used by your searcher, if you want to manipulate lucene.

Dale Miller
6/17/2015 09:52:49 am

I am trying to use the following to accompish this but some of the variables are not defined as the values are never saved in the call to the constructor. Any ideas what I am doing wrong? I always get errors at run time that fieldFlagOperandRegistry is null as well as the other values in the super constructor that just send in new objects. Examples are new VersionResolver(versionManager), new DefaultFieldFlagOperandRegistry() and new VersionClauseContextFactory(operandResolver, new VersionResolver(versionManager),permissionManager). Each of these error out. If I try to store a variable I get the dependency errors back. I think I am not understanding something here. I have tried it with and without the super.init() in the overwridden int function. Any pointers would be appreciated. I just want to override the line below and leave the rest the same as the standard one. Is that possible? I can't seem to get this code to work properly.

final ToggleVersionCustomFieldIndexer indexer = new ToggleVersionCustomFieldIndexer(fieldVisibilityManager, field);
this.searcherInformation = new CustomFieldSearcherInformation(field.getId(), field.getNameKey(), Collections.<FieldIndexer>singletonList(indexer), new AtomicReference<CustomField>(field));




public class ToggleInfoVersionSearcher extends VersionPickerSearcher
{
private VersionManager versionManager;
private VersionResolver versionResolver;
private JqlOperandResolver operandResolver;
private FieldVisibilityManager fieldVisibilityManager;
private FieldFlagOperandRegistry fieldFlagOperandRegistry;

private volatile CustomFieldSearcherInformation searcherInformation;
private volatile SearchInputTransformer searchInputTransformer;
private volatile SearchRenderer searchRenderer;
private volatile CustomFieldSearcherClauseHandler customFieldSearcherClauseHandler;
private ClauseContextFactory versionClauseContextFactory;
private PermissionManager permissionManager;
private ContextSetUtil contextSetUtil;
private FieldConfigSchemeClauseContextUtil clauseContextUtil;
private CustomFieldInputHelper customFieldInputHelper;
private I18nHelper.BeanFactory beanFactory;
private ProjectManager projectManager;
private VelocityRequestContextFactory velocityRequestContextFactory;
private ApplicationProperties applicationProperties;
private VelocityTemplatingEngine templatingEngine;

public ToggleInfoVersionSearcher(VersionManager versionManager,
FieldVisibilityManager fieldVisibilityManager,
JqlOperandResolver operandResolver,
PermissionManager permissionManager,
CustomFieldInputHelper customFieldInputHelper,
ProjectManager projectManager,
VelocityRequestContextFactory velocityRequestContextFactory,
ApplicationProperties applicationProperties,
VelocityTemplatingEngine templatingEngine)
throws ClassNotFoundException
{

super( versionManager,
fieldVisibilityManager,
new VersionResolver(versionManager),
operandResolver,
new DefaultFieldFlagOperandRegistry(),
new VersionClauseContextFactory(operandResolver, new VersionResolver(versionManager),permissionManager),
permissionManager,
ContextSetUtil.getInstance(),
ComponentAccessor.getComponent(FieldConfigSchemeClauseContextUtil.class),
customFieldInputHelper,
projectManager,
velocityRequestContextFactory,
applicationProperties,
templatingEngine);


}


/**
* This is the first time the searcher knows what its ID and names are
*
* @param field the Custom Field for this searcher
*/
public void init(CustomField field)
{
super.init(field);
final ToggleVersionCustomFieldIndexer indexer = new ToggleVersionCustomFieldIndexer(fieldVisibilityManager, field);

this.searcherInformation = new CustomFieldSearcherInformation(field.getId(), field.getNameKey(), Collections.<FieldIndexer>singletonList(indexer), new AtomicReference<CustomField>(field));
this.searchRenderer = new VersionPickerCustomFieldRenderer(field, projectManager, versionManager, fieldVisibilityManager, velocityRequestContextFactory, applicationProperties, templatingEngine, permissionManager, searcherInformation.getNameKey());
this.searchInputTransformer = new VersionPickerCustomFieldSearchInputTransformer(field, operandResolver, fieldFlagOperandRegistry, versionResolver, customFieldInputHelper);
this.customFieldSearcherClauseHandler = new SimpleCustomFieldContextValueGeneratingClauseHandle

Holger Schimanski
7/24/2013 11:41:58 pm

I have a problem with my custom field text searcher in Basic search where JIRA shows the error "not valid". Any idea, what to do? https://answers.atlassian.com/questions/191643/problem-with-custom-field-not-valid-in-basic-search-in-jira-6-0

Reply
Prameesha
6/23/2014 10:38:04 pm

Hi,

I have a requirement to have multiple values for the custom field(like a table), process it in to one entry and store it in to the database, For this purpose I added three text boxes to a table in the vm template.How can I get all the values on the update button click and send it to a method in which I can process the values and store it in the database. I also need to add a row to the table on a button click in the vm template(like a dynamic table). Is it possible to do this and if yes, how could I implement it?
I also have trouble with storing the custom field value in the database for a single field.The values are displayed on the velocity template but when I change it and update the issue,the value does not get saved. Any pointers on what I might be doing wrong?

Thanks

Reply
J-Tricks
6/24/2014 04:19:25 am

Most of this will be done as Javascript. i.e. the values in the table will be stored as a concatenated String or as a JSON (which is even better). In the template, you have to construct the JSON as the user enters the values.

Reply
Prameesha
6/24/2014 09:41:26 pm

I have followed the tutorial of adding a custom field displaying the current user, if I want to save this value in the database would overriding the 'updateValue' method be sufficient or is there more to be done? and as for the JSON how would I post this value back. Could you please explain the concept of how custom field values get updated on an issue update event.
Your help is highly appreciated.

Thanks

J-Tricks
6/25/2014 12:59:03 am

As long as you have the controlHeader correctly defined in the "Edit" template and has the customfield ID as the name of the field, the value will be saved in database correctly. Checkout the sample read only customfield example in the custom field tutorial.

Gaurav Verma link
10/13/2014 01:27:18 am

Hi,

I have a custom field which I have created by extending the AbstractCustomFieldType and implemented SortableCustomField to make the field sortable. I have implemented a custom compare method as the order of the fields is not numeric or alphabetic.

Now this field needs to be searched. If I use any of the provided searchers I loose the the custom compare functionality the I have implemented.

Is there a way that I can implement both searching and sorting for this special custom field?

Do I have to implement AbstractInitializationCustomFieldSearcher and write my own getSorter version to achieve this?

Reply
Pavan
4/27/2015 07:55:55 pm

Hi JTrick
I want to access value of custom field for Particular issue using Jira-Rest-Java-Client.
I am using below code but it is giving NULL.
issue.getField("customfield_10033").getValue();

Reply
Guy Rouillier
5/29/2015 09:11:22 am

I'm using JRJC to get custom fields:

notificationJiraIssue.setSeverity(ServerUtil.getStringPropertyFromJSONObject((JSONObject) issue.getFieldByName(
NotificationJiraIssue.FIELD_SEVERITY).getValue()));

The fields are all reported as JSON values, so you have extract the text value from the JSON.

Reply
Pavan
6/29/2015 11:15:41 pm

Hi again,
I want to implement Select List (cascade) custom field in my application.
I have created Custom Filed with same in my JIRA cloude, as well as I have inserted value to parent and child.
Now Big problem in front of me is that how to use this Custom field and its predefined values.
Please Help.

Thanks in advance...!

Reply
Ludovic
7/13/2017 08:45:27 am

Hello,

I have buy your book jira dev cookbook. In the book, their is the same sample.

<customfield-searcher key="readonly-user-searcher" name="Read Only User Searcher" class="com.atlassian.jira.issue.customfields.searchers.TextSearcher">
<description key="admin.customfield.searcher.textsearcher.desc">$read-only-user-searcher.description</description>
<resource type="velocity" name="search" location="templates/plugins/fields/edit-searcher/search-basictext.vm"/>
<resource type="velocity" name="view" location="templates/plugins/fields/view-searcher/view-search-basictext.vm"/>
<valid-customfield-type package="com.cgi.demo.custom-fields" key="read-only-user-cf"/>
</customfield-searcher>
It doesn't work for me. I have this error :Plugin Installer ERROR [c.a.plugin.manager.DefaultPluginManager] There was an error loading the descriptor 'Read Only User Searcher' of plugin 'com.cgi.demo.custom-fields'. Disabling.
[INFO] [talledLocalContainer] com.atlassian.plugin.module.ModuleClassNotFoundException: Couldn't load the class 'com.atlassian.jira.issue.customfields.searchers.TextSearcher'. This could mean that you misspelled the name of the class (double check) or that you're using a class in your plugin that you haven't provided bundle instructions for. See https://developer.atlassian.com/x/mQAN for more details on how to fix this.

Could you help me ?
Thanks.

Reply

Your comment will be posted after it is approved.


Leave a Reply.

    Enter your email address:

    Author

    Jobin Kuruvilla - Works in Adaptavist as Head of DevOps Professional Services. 

    Author of JIRA Development Cookbook and JIRA 5.x Development Cookbook.


    RSS Feed

    Categories

    All
    Acive Objects
    Ajs
    Book
    Components
    Condition
    Custom Fields
    Customization
    Events
    Gadgets
    Javascript
    Jql
    Listener
    Mail
    Permissions
    Plugin Framework
    Post Function
    Properties
    Remote Invocation
    Reporting
    Rest
    Scheduled Tasks
    Search
    Services
    Soap
    Summit
    User Interface
    Validator
    Webwork Actions
    Workflow

    Archives

    October 2016
    August 2016
    March 2016
    January 2016
    December 2015
    May 2014
    December 2013
    November 2013
    July 2013
    June 2013
    April 2013
    October 2012
    September 2012
    August 2012
    July 2012
    May 2012
    March 2012
    February 2012
    January 2012
    December 2011
    November 2011
    June 2011
    May 2011
    April 2011
    March 2011
    February 2011
    January 2011
    November 2010
    October 2010
    September 2010
    August 2010

SUPPORT
APPS
TUTORIALS
THE BOOK
© J-Tricks