Lucene - Core
  1. Lucene - Core
  2. LUCENE-3151

Make all of Analysis completely independent from Lucene Core

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 4.0-ALPHA
    • Fix Version/s: 4.9, 5.0
    • Component/s: None
    • Labels:
      None

      Description

      Lucene's analysis package, including the definitions of Attribute, TokenStream, etc. are quite useful outside of Lucene (for instance, Mahout uses them) for text processing. I'd like to move the definitions, or at least their packaging, to a separate JAR file so that one can consume them w/o needing Lucene core. My draft idea is to have a definition area that Lucene core is dependent on and the rest of the analysis package can then be dependent on the definition area. (I'm open to other ideas as well)

      1. LUCENE-3151.patch
        485 kB
        Grant Ingersoll
      2. LUCENE-3151.patch
        12 kB
        Grant Ingersoll
      3. LUCENE-3151.patch
        11 kB
        Grant Ingersoll

        Activity

        Grant Ingersoll created issue -
        Hide
        Michael McCandless added a comment -

        +1

        Show
        Michael McCandless added a comment - +1
        Hide
        Robert Muir added a comment -

        I agree, and wanted to mention we shouldn't limit ourselves based on packaging.

        for example we can have analyzers-def and analyzers-impl, but actually shove the analyzers-def into the lucene-core jar for simplicity/packaging purposes if we want.

        but this way you could still use the analyzers without the lucene core if you wanted.

        Show
        Robert Muir added a comment - I agree, and wanted to mention we shouldn't limit ourselves based on packaging. for example we can have analyzers-def and analyzers-impl, but actually shove the analyzers-def into the lucene-core jar for simplicity/packaging purposes if we want. but this way you could still use the analyzers without the lucene core if you wanted.
        Hide
        Grant Ingersoll added a comment -

        Analysis could even be released independently. I've got a start to a patch that I hope to put up today as a POC.

        Show
        Grant Ingersoll added a comment - Analysis could even be released independently. I've got a start to a patch that I hope to put up today as a POC.
        Hide
        Grant Ingersoll added a comment -

        I would propose to add:
        lucene/src/analysis-defs that would contain all of the analysis declarations (including attributes) and that the main build would depend on it being built first. I thought about moving it to modules/analysis, but that makes for some clunky Ant, IMO (although, I'm not sure if this is less clunky.)

        but actually shove the analyzers-def into the lucene-core jar for simplicity/packaging purposes if we want.

        I'm not sure on shoving them into lucene-core just b/c I wonder if people might think they need both jars then b/c they don't know if it's in core. Not sure on that one, so I'm not ruling it out.

        Show
        Grant Ingersoll added a comment - I would propose to add: lucene/src/analysis-defs that would contain all of the analysis declarations (including attributes) and that the main build would depend on it being built first. I thought about moving it to modules/analysis, but that makes for some clunky Ant, IMO (although, I'm not sure if this is less clunky.) but actually shove the analyzers-def into the lucene-core jar for simplicity/packaging purposes if we want. I'm not sure on shoving them into lucene-core just b/c I wonder if people might think they need both jars then b/c they don't know if it's in core. Not sure on that one, so I'm not ruling it out.
        Hide
        Grant Ingersoll added a comment -

        doesn't fully compile yet (but core does) due to our recursive build system, but at least fleshes out the proposed directory layout. I may, however, change src/declarations to src/common and then we would have lucene-common.jar. I was surprised by how much I needed to move out of core (e.g. BytesRef)

        Show
        Grant Ingersoll added a comment - doesn't fully compile yet (but core does) due to our recursive build system, but at least fleshes out the proposed directory layout. I may, however, change src/declarations to src/common and then we would have lucene-common.jar. I was surprised by how much I needed to move out of core (e.g. BytesRef)
        Grant Ingersoll made changes -
        Field Original Value New Value
        Attachment LUCENE-3151.patch [ 12480754 ]
        Hide
        Robert Muir added a comment -

        Looks like it makes sense that we would have to pull out these classes to do it now... but here are a few thoughts maybe for discussion... this stuff certainly should not block this issue, its hard refactorings and a lot of work, but just ideas for the future.

        As far as analyzers:

        • does the lucene-core/common jar need to have all the tokenAttributes? Maybe it should only have the ones that the indexer etc actually consume, and things like TypeAttribute, FlagsAttribute, KeywordAttribute, Token, etc should simply be moved to the analysis module?
        • does the lucene-core/common jar need to have Tokenizer/TokenFilter/CharFilter/CharReader/etc. Seems like it really only needs TokenStream and those could also be moved to the analysis module.
        • currently I think its bad that the analyzers depend upon so many of lucene's util package (some internal)... long term we want to get rid of the cumbersome backwards compatibility methods like Version and ideally have a very minimal interface between core and analysis so that you could safely just use your old analyzers jar file, etc... maybe we should see how hard it is to remove some of these util dependencies?

        So in a way, this issue is related to LUCENE-2309...

        Show
        Robert Muir added a comment - Looks like it makes sense that we would have to pull out these classes to do it now... but here are a few thoughts maybe for discussion... this stuff certainly should not block this issue, its hard refactorings and a lot of work, but just ideas for the future. As far as analyzers: does the lucene-core/common jar need to have all the tokenAttributes? Maybe it should only have the ones that the indexer etc actually consume, and things like TypeAttribute, FlagsAttribute, KeywordAttribute, Token, etc should simply be moved to the analysis module? does the lucene-core/common jar need to have Tokenizer/TokenFilter/CharFilter/CharReader/etc. Seems like it really only needs TokenStream and those could also be moved to the analysis module. currently I think its bad that the analyzers depend upon so many of lucene's util package (some internal)... long term we want to get rid of the cumbersome backwards compatibility methods like Version and ideally have a very minimal interface between core and analysis so that you could safely just use your old analyzers jar file, etc... maybe we should see how hard it is to remove some of these util dependencies? So in a way, this issue is related to LUCENE-2309 ...
        Hide
        Grant Ingersoll added a comment -

        It's not too bad, except for the build system's recursive nature. Not sure how to get around that yet.

        I did it for Token. I think the others are useful at the definition layer if someone wants just this piece of analysis, but not all of Lucene's implementations. But, could be persuaded otherwise.

        QueryParserBase has a dep. here, so if we could fix that, then we might be able to do this. That being said, they are useful constructs for someone who wants them w/o all of Lucene's implementations.

        I've got a new patch that helps here w/ some, but some of those utils are pretty useful in the context of a common area, I guess.

        Show
        Grant Ingersoll added a comment - It's not too bad, except for the build system's recursive nature. Not sure how to get around that yet. I did it for Token. I think the others are useful at the definition layer if someone wants just this piece of analysis, but not all of Lucene's implementations. But, could be persuaded otherwise. QueryParserBase has a dep. here, so if we could fix that, then we might be able to do this. That being said, they are useful constructs for someone who wants them w/o all of Lucene's implementations. I've got a new patch that helps here w/ some, but some of those utils are pretty useful in the context of a common area, I guess.
        Hide
        Lance Norskog added a comment -

        Architects remove dependencies

        For external use, this locksteps the external user (Mahout for example) to changes in these data structures. It's a direct coupling. This is how you get conflicting dependencies, what the Linux people call "RPM Hell".

        If you can make a minimal class for export, then have Lucene use a larger class, that might work. Here is a semi-coupled design:

        public class ITerm
        • A really minimal API that will never be changed, only added onto.
        • Code that uses this API will always work- that is the contract.
          • clone() is banned (via UnsupportedOperationException).
          • If a class implements clone(), all subclasses must also implement it.
        • I would also ban equals & hashCode- if you want these, make your own subclass that delegates to a real Term subclass.
        public class Term extends ITerm
        • This is what Lucene uses.
        • It can be versioned.
        • If you code to this, you lock your binaries to Lucene release jars.

        Here is a fully-decoupled design:

        • Separate suite of main Lucene objects, with minimal features as above.
        • Separate Lucene library that xlates/wraps/etc. between this parallel suite and the Lucene versions. Lucene exports this jar and works very hard to avoid version changes.

        It's a hard problem all around, and different solutions have failed in their own ways. Error-handling is a particularly big problem. Using these objects in parallel brings its own funkiness.

        Show
        Lance Norskog added a comment - Architects remove dependencies For external use, this locksteps the external user (Mahout for example) to changes in these data structures. It's a direct coupling. This is how you get conflicting dependencies, what the Linux people call "RPM Hell". If you can make a minimal class for export, then have Lucene use a larger class, that might work. Here is a semi-coupled design: public class ITerm A really minimal API that will never be changed, only added onto. Code that uses this API will always work- that is the contract. clone() is banned (via UnsupportedOperationException). If a class implements clone(), all subclasses must also implement it. I would also ban equals & hashCode- if you want these, make your own subclass that delegates to a real Term subclass. public class Term extends ITerm This is what Lucene uses. It can be versioned. If you code to this, you lock your binaries to Lucene release jars. Here is a fully-decoupled design: Separate suite of main Lucene objects, with minimal features as above. Separate Lucene library that xlates/wraps/etc. between this parallel suite and the Lucene versions. Lucene exports this jar and works very hard to avoid version changes. It's a hard problem all around, and different solutions have failed in their own ways. Error-handling is a particularly big problem. Using these objects in parallel brings its own funkiness.
        Robert Muir made changes -
        Fix Version/s 4.1 [ 12321140 ]
        Fix Version/s 4.0 [ 12314025 ]
        Hide
        Grant Ingersoll added a comment -

        I'm resurrecting this. I'm now thinking that we just put some build targets into Lucene that make it easy to build this, instead of rearranging the packaging.

        Show
        Grant Ingersoll added a comment - I'm resurrecting this. I'm now thinking that we just put some build targets into Lucene that make it easy to build this, instead of rearranging the packaging.
        Hide
        Grant Ingersoll added a comment -

        Hmm, this gets wonky with some of the dependencies. Ideally, I'd like to keep this isolated to just the analysis package in core and util (ideally not even that, but do need things like ArrayUtil, BytesRef, etc.), however not sure that can be done w/o some refactoring. For instance, Analyzer has a dependency on IndexableField, all so that it can check to see whether it is tokenized or not. Could it just take in a boolean for getOffsetGap indicating whether it is tokenized or not? It also has a dependency on AlreadyClosedException, which, I suppose could be moved to Util.

        There also a number of imports for Javadocs which are probably useful, but a bit odd in terms of packaging for this particular thing.

        Show
        Grant Ingersoll added a comment - Hmm, this gets wonky with some of the dependencies. Ideally, I'd like to keep this isolated to just the analysis package in core and util (ideally not even that, but do need things like ArrayUtil, BytesRef, etc.), however not sure that can be done w/o some refactoring. For instance, Analyzer has a dependency on IndexableField, all so that it can check to see whether it is tokenized or not. Could it just take in a boolean for getOffsetGap indicating whether it is tokenized or not? It also has a dependency on AlreadyClosedException, which, I suppose could be moved to Util. There also a number of imports for Javadocs which are probably useful, but a bit odd in terms of packaging for this particular thing.
        Hide
        Robert Muir added a comment -

        Grant: I agree.

        I guess it would be good to figure out exactly what the desired goals are:

        1. is the goal to just use analyzers.jar without having lucene core.jar for aesthetic reasons (no lucene jar file)
        2. is instead the goal to be able to depend on an analyzers.jar without causing classpath hell for users that might want to use a different version of lucene in their app, when all you need is the analyzers?

        If its just the second, maybe we just need some fancy jar-jar packing. We would have to target
        a different package name or something like that so there are no conflicts: then again this might be something the end
        user could just do themselves without us doing anything (maven has plugins for this type of thing, etc?). Then
        they could deal with what the renamed packages should be etc?

        Show
        Robert Muir added a comment - Grant: I agree. I guess it would be good to figure out exactly what the desired goals are: is the goal to just use analyzers.jar without having lucene core.jar for aesthetic reasons (no lucene jar file) is instead the goal to be able to depend on an analyzers.jar without causing classpath hell for users that might want to use a different version of lucene in their app, when all you need is the analyzers? If its just the second, maybe we just need some fancy jar-jar packing. We would have to target a different package name or something like that so there are no conflicts: then again this might be something the end user could just do themselves without us doing anything (maven has plugins for this type of thing, etc?). Then they could deal with what the renamed packages should be etc?
        Hide
        Grant Ingersoll added a comment -

        My goal is #1 (I have the same goal for the FST package). I want to be able to use analyzers independently of Lucene and I don't want to have to bring in whatever dependencies other parts of Lucene might have (which is admittedly small at the moment). Doing this also achieves #2, I suppose. I've almost got a patch ready that just makes this build sugar, but I wonder if it is better to separate out the code if #1 is the goal.

        Show
        Grant Ingersoll added a comment - My goal is #1 (I have the same goal for the FST package). I want to be able to use analyzers independently of Lucene and I don't want to have to bring in whatever dependencies other parts of Lucene might have (which is admittedly small at the moment). Doing this also achieves #2, I suppose. I've almost got a patch ready that just makes this build sugar, but I wonder if it is better to separate out the code if #1 is the goal.
        Hide
        Robert Muir added a comment -

        I don think it is from my perspective. Really this isnt a common use case of lucene and
        will make things awkward and confusing (harder to use) to have a lots of jar files.

        Show
        Robert Muir added a comment - I don think it is from my perspective. Really this isnt a common use case of lucene and will make things awkward and confusing (harder to use) to have a lots of jar files.
        Hide
        Grant Ingersoll added a comment -

        Here's a first draft at this. The packaging looks more or less right, but I haven't fully tested it yet. The main downsides to this approach are:

        1. Minor loss of Javadoc due to references to things like IndexWriter, DoubleField, etc. I kept the references, just removed the @link, which allowed me to drop the import statement
        2. We need to somehow document that this jar is for standalone use only. It's probably a minor issue, but going forward, people could get into classloader hell with this if they are mixing versions. Of course, that's always the case in Java, so caveat emptor.
        Show
        Grant Ingersoll added a comment - Here's a first draft at this. The packaging looks more or less right, but I haven't fully tested it yet. The main downsides to this approach are: Minor loss of Javadoc due to references to things like IndexWriter, DoubleField, etc. I kept the references, just removed the @link, which allowed me to drop the import statement We need to somehow document that this jar is for standalone use only. It's probably a minor issue, but going forward, people could get into classloader hell with this if they are mixing versions. Of course, that's always the case in Java, so caveat emptor.
        Grant Ingersoll made changes -
        Attachment LUCENE-3151.patch [ 12537324 ]
        Hide
        Grant Ingersoll added a comment -

        I should add: to run this, for now, do

        ant jar-analyzer-definition

        . Still need to make sure it fully hooks into the rest of the build correctly, too.

        Show
        Grant Ingersoll added a comment - I should add: to run this, for now, do ant jar-analyzer-definition . Still need to make sure it fully hooks into the rest of the build correctly, too.
        Hide
        Robert Muir added a comment -

        Hey Grant: I know it sounds silly but can we split out the getOffsetGap API change into a separate issue?
        This would be nice to fix ASAP.

        I dont understand why it takes IndexableField or took Fieldable. All the other methods here like
        getPositionIncrementGap take "String fieldName". I think this one should too.

        I dont think it needs a boolean for tokenized either: returning a 0 for NOT_ANALYZED fields.
        If you choose NOT_ANALYZED, that should mean the Analyzer is not invoked!

        If you want to do expert stuff control the offset gaps between values for NOT_ANALYZED fields,
        then just analyze it instead, with keyword tokenizer!

        Show
        Robert Muir added a comment - Hey Grant: I know it sounds silly but can we split out the getOffsetGap API change into a separate issue? This would be nice to fix ASAP. I dont understand why it takes IndexableField or took Fieldable. All the other methods here like getPositionIncrementGap take "String fieldName". I think this one should too. I dont think it needs a boolean for tokenized either: returning a 0 for NOT_ANALYZED fields. If you choose NOT_ANALYZED, that should mean the Analyzer is not invoked! If you want to do expert stuff control the offset gaps between values for NOT_ANALYZED fields, then just analyze it instead, with keyword tokenizer!
        Hide
        Grant Ingersoll added a comment -
        Show
        Grant Ingersoll added a comment - LUCENE-4240
        Hide
        Grant Ingersoll added a comment -

        Updated version for trunk. Updated packaging to now include all of Util, as it was getting ridiculous trying to get the exact list. I tested this setup by taking the jar produced here, plus the common analyzers jar and put them in a standalone project and tested them and it seemed to work.

        Thus, I think this is mostly done and ready to commit. I'd say the only issue left is to say how we want to document this so that people aren't confused. My suggestion would be to collocate a file name README-analyzers-def.txt alongside the jar that explains it. Otherwise, we could just put it in the README.

        Show
        Grant Ingersoll added a comment - Updated version for trunk. Updated packaging to now include all of Util, as it was getting ridiculous trying to get the exact list. I tested this setup by taking the jar produced here, plus the common analyzers jar and put them in a standalone project and tested them and it seemed to work. Thus, I think this is mostly done and ready to commit. I'd say the only issue left is to say how we want to document this so that people aren't confused. My suggestion would be to collocate a file name README-analyzers-def.txt alongside the jar that explains it. Otherwise, we could just put it in the README.
        Grant Ingersoll made changes -
        Attachment LUCENE-3151.patch [ 12537417 ]
        Hide
        Robert Muir added a comment -

        I tested this setup by taking the jar produced here, plus the common analyzers jar and put them in a standalone project and tested them and it seemed to work.

        Personally I dont feel comfortable with that as a testing strategy. There is nothing to prevent someone from breaking this jar in the future (e.g. if i import something from o.a.l.index into an analyzer for some reason).

        If we cannot test this, can we just make it an optional target (e.g. not part of package). Generally this is pretty expert to do (it must be, it has no javadocs, etc etc), so I think its fair the people who need this could just run the ant target from the source release.

        Show
        Robert Muir added a comment - I tested this setup by taking the jar produced here, plus the common analyzers jar and put them in a standalone project and tested them and it seemed to work. Personally I dont feel comfortable with that as a testing strategy. There is nothing to prevent someone from breaking this jar in the future (e.g. if i import something from o.a.l.index into an analyzer for some reason). If we cannot test this, can we just make it an optional target (e.g. not part of package). Generally this is pretty expert to do (it must be, it has no javadocs, etc etc), so I think its fair the people who need this could just run the ant target from the source release.
        Hide
        Robert Muir added a comment -

        For example, I just searched for org.apache.lucene.index in the analyzers-common source and there is code using IndexReader, TermsEnum, etc.

        Show
        Robert Muir added a comment - For example, I just searched for org.apache.lucene.index in the analyzers-common source and there is code using IndexReader, TermsEnum, etc.
        Hide
        Grant Ingersoll added a comment -

        For example, I just searched for org.apache.lucene.index in the analyzers-common source and there is code using IndexReader, TermsEnum, etc.

        Ugh. I wonder if that just makes all of this a moot point. I'll take a look. I was thinking about how to more reliably test it yesterday, but didn't implement it. I guess ideally we could exercise all the analyzers independently on some content, or just run the analyzers test suite somehow.

        Show
        Grant Ingersoll added a comment - For example, I just searched for org.apache.lucene.index in the analyzers-common source and there is code using IndexReader, TermsEnum, etc. Ugh. I wonder if that just makes all of this a moot point. I'll take a look. I was thinking about how to more reliably test it yesterday, but didn't implement it. I guess ideally we could exercise all the analyzers independently on some content, or just run the analyzers test suite somehow.
        Hide
        Grant Ingersoll added a comment -

        For:

        1. IndexReader – It's mostly just in tests, except the QueryStopWordAnalyzer
        2. TermsEnum – Same thing, a test and the QueryStopWordAnalyzer
        3. Synonym package has dependency on DataOutput and ByteArray* from store (which can be added to the base packaging)

        So, basically, the issue would be with the QueryStopWordAnalyzer (the tests aren't an issue)

        Show
        Grant Ingersoll added a comment - For: IndexReader – It's mostly just in tests, except the QueryStopWordAnalyzer TermsEnum – Same thing, a test and the QueryStopWordAnalyzer Synonym package has dependency on DataOutput and ByteArray* from store (which can be added to the base packaging) So, basically, the issue would be with the QueryStopWordAnalyzer (the tests aren't an issue)
        Hide
        Lance Norskog added a comment - - edited

        Is it intended to support jars from different Lucene versions? Would a "unit test" for this project include old versions of jars retained as binaries?

        Show
        Lance Norskog added a comment - - edited Is it intended to support jars from different Lucene versions? Would a "unit test" for this project include old versions of jars retained as binaries?
        Hide
        Robert Muir added a comment -

        Why didn't the compiler catch these things?

        Show
        Robert Muir added a comment - Why didn't the compiler catch these things?
        Hide
        Uwe Schindler added a comment -

        How about using a tools to collect all realy needed dependencies from core and package it as lucene-core4analysis-min.jar? JARJAR can do this (as ANT task, without renaming classes, just to collect the dependent ones from core). We would then also not need to remove the Javadocs (NRQ,...), Grant's patch removed.

        Show
        Uwe Schindler added a comment - How about using a tools to collect all realy needed dependencies from core and package it as lucene-core4analysis-min.jar? JARJAR can do this (as ANT task, without renaming classes, just to collect the dependent ones from core). We would then also not need to remove the Javadocs (NRQ,...), Grant's patch removed.
        Hide
        Grant Ingersoll added a comment -

        Why didn't the compiler catch these things?

        Not sure I follow. There really isn't compilation involved at this point and they are runtime dependencies that fail.

        @Uwe: it's possible, but I suspect the IndexReader dep. is going to bring in a lot, which seems a little silly given it is all just used in the QueryStopWordAnalyzer, which could easily be collapsed into just using the StopFilter and some example code for people. I'm not that familiar w/ JARJAR, but if you want to try it and we can compare.

        Show
        Grant Ingersoll added a comment - Why didn't the compiler catch these things? Not sure I follow. There really isn't compilation involved at this point and they are runtime dependencies that fail. @Uwe: it's possible, but I suspect the IndexReader dep. is going to bring in a lot, which seems a little silly given it is all just used in the QueryStopWordAnalyzer, which could easily be collapsed into just using the StopFilter and some example code for people. I'm not that familiar w/ JARJAR, but if you want to try it and we can compare.
        Hide
        Grant Ingersoll added a comment - - edited

        @Robert, @Uwe, any more thoughts on this one? I hate to see this derailed by one single little used Analyzer that has a workaround solution anyway. I'm going to try to get more tests in place this weekend, or at least soon.

        Show
        Grant Ingersoll added a comment - - edited @Robert, @Uwe, any more thoughts on this one? I hate to see this derailed by one single little used Analyzer that has a workaround solution anyway. I'm going to try to get more tests in place this weekend, or at least soon.
        Hide
        Robert Muir added a comment -

        For the long term I like Uwe's idea better I think rather than restricting which javadocs
        in core can link to what and restricting which files the analyzers can use.

        Separately we should fix that Analyzer

        Show
        Robert Muir added a comment - For the long term I like Uwe's idea better I think rather than restricting which javadocs in core can link to what and restricting which files the analyzers can use. Separately we should fix that Analyzer
        Mark Miller made changes -
        Fix Version/s 5.0 [ 12321663 ]
        Mark Miller made changes -
        Fix Version/s 4.2 [ 12323899 ]
        Fix Version/s 4.1 [ 12321140 ]
        Robert Muir made changes -
        Fix Version/s 4.3 [ 12324143 ]
        Fix Version/s 5.0 [ 12321663 ]
        Fix Version/s 4.2 [ 12323899 ]
        Uwe Schindler made changes -
        Fix Version/s 4.4 [ 12324323 ]
        Fix Version/s 4.3 [ 12324143 ]
        Hide
        Steve Rowe added a comment -

        Bulk move 4.4 issues to 4.5 and 5.0

        Show
        Steve Rowe added a comment - Bulk move 4.4 issues to 4.5 and 5.0
        Steve Rowe made changes -
        Fix Version/s 5.0 [ 12321663 ]
        Fix Version/s 4.5 [ 12324742 ]
        Fix Version/s 4.4 [ 12324323 ]
        Adrien Grand made changes -
        Fix Version/s 4.6 [ 12324999 ]
        Fix Version/s 5.0 [ 12321663 ]
        Fix Version/s 4.5 [ 12324742 ]
        Simon Willnauer made changes -
        Fix Version/s 4.7 [ 12325572 ]
        Fix Version/s 4.6 [ 12324999 ]
        David Smiley made changes -
        Fix Version/s 4.8 [ 12326269 ]
        Fix Version/s 4.7 [ 12325572 ]
        Hide
        Uwe Schindler added a comment -

        Move issue to Lucene 4.9.

        Show
        Uwe Schindler added a comment - Move issue to Lucene 4.9.
        Uwe Schindler made changes -
        Fix Version/s 4.9 [ 12326730 ]
        Fix Version/s 5.0 [ 12321663 ]
        Fix Version/s 4.8 [ 12326269 ]

          People

          • Assignee:
            Unassigned
            Reporter:
            Grant Ingersoll
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:

              Development