Uploaded image for project: 'Felix'
  1. Felix
  2. FELIX-899

Version attribute missing from Import-Package on provided dependencies

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: maven-bundle-plugin-2.0.0
    • Component/s: Maven Bundle Plugin
    • Labels:
      None
    • Environment:
      Maven version: 2.0.9
      Java version: 1.6.0_11
      OS name: "linux" version: "2.6.25-gentoo-r7" arch: "amd64" Family: "unix"

      Description

      I have been using and testing out the maven-bundle-plugin-1.5.0-20081205.125536-1 (SNAPSHOT) and ran across what I believe is a bug.

      Suppose there is a project a:a:1.0.0-SNAPSHOT. This project has a single class: a.a.A. The bundle plugin has the following instructions:
      <instructions>
      <_versionpolicy>[$${version;===;${@}},$${version;=+;${@}})</_versionpolicy>
      <Bundle-RequiredExecutionEnvironment>JavaSE-1.6</Bundle-RequiredExecutionEnvironment>
      <Export-Package>$${replace;$

      {Bundle-SymbolicName}

      ;\W;.}.*;version=$

      {project.version}

      </Export-Package>
      </instructions>

      This results in an Export-Package line of:
      Export-Package: a.a;version="1.0.0.SNAPSHOT"

      So far so good. Now suppose there is a project b:b:1.0.0-SNAPSHOT. This project depends on a:a:1.0.0-SNAPSHOT (scope: provided) and the project also has a single class b.b.B which extends a.a.A. The maven-bundle-plugin is given the same instructions as project a:a above. The resulting Import-Package line is:
      Import-Package: a.a,b.b;version="[1.0.0,1.1)"

      This is not what is expected. What is expected is the following:
      Import-Package: a.a;version="[1.0.0,1.1)",b.b;version="[1.0.0,1.1)"

      Digging into the code I found that in org.apache.felix.bundleplugin.BundlePlugin (trunk rev: 723704) in function "protected Jar[] getClasspath( MavenProject currentProject ) throws ZipException, IOException" line 708 reads:
      final Collection artifacts = getSelectedDependencies( currentProject.getArtifacts() );

      When the plugin is running "currentProject.getArtifacts()" returns an empty set. This then causes the classpath not to be set properly when calling BND (none of the dependencies are available for reading their manifests). I changed the line to use "currentProject.getDependencyArtifacts()" and the manifest for b:b was correct.

      I am going to attach a file with two very simple projects which mirror what I have described here.

      1. projects2.tar.bz2
        1 kB
        Brian Atkinson
      2. projects.tar.bz2
        1 kB
        Brian Atkinson

        Activity

        Hide
        mcculls Stuart McCulloch added a comment -

        Closing as maven-bundle-plugin 2.0.0 has been released.

        Show
        mcculls Stuart McCulloch added a comment - Closing as maven-bundle-plugin 2.0.0 has been released.
        Hide
        mcculls Stuart McCulloch added a comment -

        This issue is now fixed in trunk, however I want to run through a few more tests before deploying a new snapshot

        Show
        mcculls Stuart McCulloch added a comment - This issue is now fixed in trunk, however I want to run through a few more tests before deploying a new snapshot
        Hide
        mcculls Stuart McCulloch added a comment -

        The current roadmap is here:

        https://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&mode=hide&sorter/order=DESC&sorter/field=priority&resolution=-1&pid=12310100&fixfor=12313302

        One or two of the issues require a fix in the Bnd Tool itself, and there are several issues around generating better defaults for imports/exports/versioning. If you'd like to write some unit tests or patches then you're very welcome - basically my plan is to change the @requiresDependencyResolution setting in the various mojos to "test" instead of "runtime", widen the check on the artifacts passed to Bnd, and then test it against various builds (Felix, GlassFish, etc...)

        Unfortunately the major issue I've had recently is finding a sizable enough chunk of spare time to do this

        Show
        mcculls Stuart McCulloch added a comment - The current roadmap is here: https://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&mode=hide&sorter/order=DESC&sorter/field=priority&resolution=-1&pid=12310100&fixfor=12313302 One or two of the issues require a fix in the Bnd Tool itself, and there are several issues around generating better defaults for imports/exports/versioning. If you'd like to write some unit tests or patches then you're very welcome - basically my plan is to change the @requiresDependencyResolution setting in the various mojos to "test" instead of "runtime", widen the check on the artifacts passed to Bnd, and then test it against various builds (Felix, GlassFish, etc...) Unfortunately the major issue I've had recently is finding a sizable enough chunk of spare time to do this
        Hide
        nairb774 Brian Atkinson added a comment -

        It seems that right now I have stepped on a part of the plugin which seems to be undergoing planning for improvement. I took a quick poke around (2 min maybe), but is there a roadmap anywhere for upcoming versions? I am willing to help knock out some of the planned improvements/write javadoc/unit tests if you would like the help.

        Show
        nairb774 Brian Atkinson added a comment - It seems that right now I have stepped on a part of the plugin which seems to be undergoing planning for improvement. I took a quick poke around (2 min maybe), but is there a roadmap anywhere for upcoming versions? I am willing to help knock out some of the planned improvements/write javadoc/unit tests if you would like the help.
        Hide
        lukewpatterson luke w patterson added a comment -

        Good to know. I would never have imagined bnd would find useful information from the runtime-only deps.

        "Now because Bnd can detect certain indirect uses of packages, it could add imports for code that you don't need at compile time but would need at runtime"

        I'm trying to imagine an example of that. (I probably just don't understand all the scenarios where it would add imports) My guess is that it would be for the situation where a class was interacted with exclusively through reflection. If that's the case, on the Maven side the runtime dep is considered to be "required" anyway so I wonder why it wouldn't just be directly coded to instead of through reflection. Like I said, I must be missing some fundamental background information in this area.

        Where did you learn those finer details of what bnd is capable of? I tried the usual, the bnd homepage, but it didn't explain exactly which scenarios package imports would and wouldn't be auto-detected.

        Thanks,

        Luke

        Show
        lukewpatterson luke w patterson added a comment - Good to know. I would never have imagined bnd would find useful information from the runtime-only deps. "Now because Bnd can detect certain indirect uses of packages, it could add imports for code that you don't need at compile time but would need at runtime" I'm trying to imagine an example of that. (I probably just don't understand all the scenarios where it would add imports) My guess is that it would be for the situation where a class was interacted with exclusively through reflection. If that's the case, on the Maven side the runtime dep is considered to be "required" anyway so I wonder why it wouldn't just be directly coded to instead of through reflection. Like I said, I must be missing some fundamental background information in this area. Where did you learn those finer details of what bnd is capable of? I tried the usual, the bnd homepage, but it didn't explain exactly which scenarios package imports would and wouldn't be auto-detected. Thanks, Luke
        Hide
        mcculls Stuart McCulloch added a comment -

        To be consistent with other uses of Bnd (Ant task, command-line) the minimum we need to pass to Bnd is the compilation classpath... but we could do better:

        Bnd takes information from any JARs (version details, etc.) that the final bundle depends on and uses it to augment the OSGi metadata (specifically imports).
        It doesn't really care whether it's a compile-time or run-time dependency (or even provided) - if the final bundle needs to import a package from another bundle, and it was built against a particular JAR with version information for that package, then it will apply that version to the import. Now because Bnd can detect certain indirect uses of packages, it could add imports for code that you don't need at compile time but would need at runtime - so runtime version information would be useful here too.

        Now as Niclas pointed out at the start of this thread, you should really version imports yourself rather than rely on automatic versioning - but it's in our interest to give Bnd as much information about the classpath that the bundle was compiled against, as well as the potential runtime classpath. Excluding runtime artifacts just limits what Bnd knows about the project.

        Show
        mcculls Stuart McCulloch added a comment - To be consistent with other uses of Bnd (Ant task, command-line) the minimum we need to pass to Bnd is the compilation classpath... but we could do better: Bnd takes information from any JARs (version details, etc.) that the final bundle depends on and uses it to augment the OSGi metadata (specifically imports). It doesn't really care whether it's a compile-time or run-time dependency (or even provided) - if the final bundle needs to import a package from another bundle, and it was built against a particular JAR with version information for that package, then it will apply that version to the import. Now because Bnd can detect certain indirect uses of packages, it could add imports for code that you don't need at compile time but would need at runtime - so runtime version information would be useful here too. Now as Niclas pointed out at the start of this thread, you should really version imports yourself rather than rely on automatic versioning - but it's in our interest to give Bnd as much information about the classpath that the bundle was compiled against, as well as the potential runtime classpath. Excluding runtime artifacts just limits what Bnd knows about the project.
        Hide
        lukewpatterson luke w patterson added a comment -

        Stuart wrote:
        "IMHO the same classpath used to compile the project should be passed to BND."
        and
        "But we do know what we want, which is to give Bnd more of the classpath, possibly even runtime artifacts - as this would be useful for versioning even if they're not required on the compilation classpath."

        It seems like those two statements above are at odds with each other.

        When you say "runtime artifacts", do you literally mean artifacts declared with <scope>runtime</scope> or just optional compile dependencies (<optional>true</optional>)? Maybe I just don't understand bnd well enough, how would passing runtime stuff to bnd be useful for versioning?

        Show
        lukewpatterson luke w patterson added a comment - Stuart wrote: "IMHO the same classpath used to compile the project should be passed to BND." and "But we do know what we want, which is to give Bnd more of the classpath, possibly even runtime artifacts - as this would be useful for versioning even if they're not required on the compilation classpath." It seems like those two statements above are at odds with each other. When you say "runtime artifacts", do you literally mean artifacts declared with <scope>runtime</scope> or just optional compile dependencies (<optional>true</optional>)? Maybe I just don't understand bnd well enough, how would passing runtime stuff to bnd be useful for versioning?
        Hide
        mcculls Stuart McCulloch added a comment -

        To recap: the current @requiresDependencyResolution setting means we don't get any provided scope artifacts resolved by Maven. This setting has been in place since this project started (before I took over) hence I've been wary in changing it. Several developers have tweaked this code over the years, adding checks like the warning on the missing file, but no-one has had any time to do a comprehensive review. Work on it recently has been mostly driven by JIRA issues.

        But we do know what we want, which is to give Bnd more of the classpath, possibly even runtime artifacts - as this would be useful for versioning even if they're not required on the compilation classpath. We also know how to change the code to make this happen. What I'm currently trying to minimize is the impact on projects that might rely the old behaviour (this is definitely a non-zero number) - we need to provide them with a clear migration path.

        Summary: we know it needs fixing, we know how to fix it, we haven't yet fixed it (hope to fix it Q1...)

        Show
        mcculls Stuart McCulloch added a comment - To recap: the current @requiresDependencyResolution setting means we don't get any provided scope artifacts resolved by Maven. This setting has been in place since this project started (before I took over) hence I've been wary in changing it. Several developers have tweaked this code over the years, adding checks like the warning on the missing file, but no-one has had any time to do a comprehensive review. Work on it recently has been mostly driven by JIRA issues. But we do know what we want, which is to give Bnd more of the classpath, possibly even runtime artifacts - as this would be useful for versioning even if they're not required on the compilation classpath. We also know how to change the code to make this happen. What I'm currently trying to minimize is the impact on projects that might rely the old behaviour (this is definitely a non-zero number) - we need to provide them with a clear migration path. Summary: we know it needs fixing, we know how to fix it, we haven't yet fixed it (hope to fix it Q1...)
        Hide
        lukewpatterson luke w patterson added a comment -

        Stuart wrote: "I have a fairly good grasp of Maven internals and have written various code to get transitive dependencies before when I couldn't rely on the default resolution rules. The problem isn't that we don't know what to do..."

        I'm certainly not trying to question anyone's competency/judgement. I'm just trying to fully understand the dilemma at hand. I'm interested in the future behavior because I hope to start using this plugin in my projects.

        I tend to agree with your view that
        "IMHO the same classpath used to compile the project should be passed to BND."

        I don't understand why that goal isn't reachable. Please correct me if I'm wrong, it seems you want the same thing that maven-compiler-plugin passes to javac. (i.e. compile is included, runtime is excluded, direct provided is included, transitive provided is excluded) Is that correct?

        Also, I don't understand this code from BundlePlugin:
        714 : if ( Artifact.SCOPE_COMPILE.equals( artifact.getScope() )
        715 : || Artifact.SCOPE_SYSTEM.equals( artifact.getScope() )
        716 : || Artifact.SCOPE_PROVIDED.equals( artifact.getScope() ) )
        717 : {
        718 : File file = getFile( artifact );
        719 : if ( file == null )
        720 :

        { 721 : getLog().warn( 722 : "File is not available for artifact " + artifact + " in project " 723 : + currentProject.getArtifact() ); 724 : continue; 725 : }


        If the artifact file is null, it seems like it would be better to programmatically request resolution and fail build if it can't be resolved. I think you could safely consider an unresolvable dependency to be an error.

        Show
        lukewpatterson luke w patterson added a comment - Stuart wrote: "I have a fairly good grasp of Maven internals and have written various code to get transitive dependencies before when I couldn't rely on the default resolution rules. The problem isn't that we don't know what to do..." I'm certainly not trying to question anyone's competency/judgement. I'm just trying to fully understand the dilemma at hand. I'm interested in the future behavior because I hope to start using this plugin in my projects. I tend to agree with your view that "IMHO the same classpath used to compile the project should be passed to BND." I don't understand why that goal isn't reachable. Please correct me if I'm wrong, it seems you want the same thing that maven-compiler-plugin passes to javac. (i.e. compile is included, runtime is excluded, direct provided is included, transitive provided is excluded) Is that correct? Also, I don't understand this code from BundlePlugin: 714 : if ( Artifact.SCOPE_COMPILE.equals( artifact.getScope() ) 715 : || Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) 716 : || Artifact.SCOPE_PROVIDED.equals( artifact.getScope() ) ) 717 : { 718 : File file = getFile( artifact ); 719 : if ( file == null ) 720 : { 721 : getLog().warn( 722 : "File is not available for artifact " + artifact + " in project " 723 : + currentProject.getArtifact() ); 724 : continue; 725 : } If the artifact file is null, it seems like it would be better to programmatically request resolution and fail build if it can't be resolved. I think you could safely consider an unresolvable dependency to be an error.
        Hide
        mcculls Stuart McCulloch added a comment -

        Brian - this check matches the core Maven code that assembles the compilation classpath artifacts (see getCompileArtifacts in MavenProject.java) because runtime artifacts by definition aren't used for compilation. So it's technically correct, but because of the complex interaction between this check and the lazy dependency I mentioned previously it means just compile (or default) and system scope artifacts currently reach BND. (ie. the docs shouldn't list runtime)

        However, I'm also considering passing all jars to Bnd so it has the complete set of dependency information - the lack of these jars mostly means BND has a reduced set of version information, but it also stops people from inadvertently pulling in all sorts of classes when they use "Export-Package: *" and expect that to only include compile dependencies (which is why I discourage people from setting it to *).

        The focus of the current release is all about sorting this out, while trying not to break too many projects - as well as making more jars available to BND, I'm also planning on improving the default settings so that just changing a project from "jar" to "bundle" packaging will result in the same content inside the final bundle.

        Luke - I have a fairly good grasp of Maven internals and have written various code to get transitive dependencies before when I couldn't rely on the default resolution rules. The problem isn't that we don't know what to do, we've just been waiting for the right time in development to make this breaking change.

        Actually this might turn out to be a 2.0.0 release, just to stress to people that there are major changes to how the bundleplugin processes the classpath.

        Show
        mcculls Stuart McCulloch added a comment - Brian - this check matches the core Maven code that assembles the compilation classpath artifacts (see getCompileArtifacts in MavenProject.java) because runtime artifacts by definition aren't used for compilation. So it's technically correct, but because of the complex interaction between this check and the lazy dependency I mentioned previously it means just compile (or default) and system scope artifacts currently reach BND. (ie. the docs shouldn't list runtime) However, I'm also considering passing all jars to Bnd so it has the complete set of dependency information - the lack of these jars mostly means BND has a reduced set of version information, but it also stops people from inadvertently pulling in all sorts of classes when they use "Export-Package: *" and expect that to only include compile dependencies (which is why I discourage people from setting it to *). The focus of the current release is all about sorting this out, while trying not to break too many projects - as well as making more jars available to BND, I'm also planning on improving the default settings so that just changing a project from "jar" to "bundle" packaging will result in the same content inside the final bundle. Luke - I have a fairly good grasp of Maven internals and have written various code to get transitive dependencies before when I couldn't rely on the default resolution rules. The problem isn't that we don't know what to do, we've just been waiting for the right time in development to make this breaking change. Actually this might turn out to be a 2.0.0 release, just to stress to people that there are major changes to how the bundleplugin processes the classpath.
        Hide
        lukewpatterson luke w patterson added a comment -

        What information is needed that can't be retrieved from MavenProject methods?

        Are you looking for direct or transitive provided dependencies?

        If looking for transitive, I've only found hacky ways to do that using DependencyNode and a cut-and-paste custom MavenMetadataSource and MavenMetadataSource. Just let me know if you want me to send an example.

        If looking for direct, is the problem that

        • MavenProject.getArtifacts() isn't including provided dependencies
          or
        • the provided dependencies from MavenProject.getArtifacts() have Artifact.getFile() of null ?
        Show
        lukewpatterson luke w patterson added a comment - What information is needed that can't be retrieved from MavenProject methods? Are you looking for direct or transitive provided dependencies? If looking for transitive, I've only found hacky ways to do that using DependencyNode and a cut-and-paste custom MavenMetadataSource and MavenMetadataSource. Just let me know if you want me to send an example. If looking for direct, is the problem that MavenProject.getArtifacts() isn't including provided dependencies or the provided dependencies from MavenProject.getArtifacts() have Artifact.getFile() of null ?
        Hide
        nairb774 Brian Atkinson added a comment -

        I missed that note on the docs, good call. (NOTE: only compile, runtime and system scoped dependencies are passed to BND. Provided and test dependencies are not included in the classpath seen by BND.) Looking at the code, it seems that only compile, system, and provided are allowed on the classpath (org.apache.felix.bundleplugin.BundlePlugin, trunk rev: 723704, lines 714-716). Does the code and the doc disagree or am I missing somethig else?

        Show
        nairb774 Brian Atkinson added a comment - I missed that note on the docs, good call. (NOTE: only compile, runtime and system scoped dependencies are passed to BND. Provided and test dependencies are not included in the classpath seen by BND.) Looking at the code, it seems that only compile, system, and provided are allowed on the classpath (org.apache.felix.bundleplugin.BundlePlugin, trunk rev: 723704, lines 714-716). Does the code and the doc disagree or am I missing somethig else?
        Hide
        mcculls Stuart McCulloch added a comment -

        IMHO the same classpath used to compile the project should be passed to BND. However, this appears to be harder than it should be in Maven because of the way it lazily discovers transitive dependencies (as this is computationally expensive). Maven provides a mojo setting for plugins that forces resolution of project dependencies to a certain scope phase (@requiresDependencyResolution). Without this setting a plugin gets the same dependency resolution as the previous plugin in the build, which could vary a lot depending on what plugins are configured in your POM.

        Historically, @requiresDependencyResolution has always been set to "runtime" in the bundleplugin - which means that all compile and runtime dependencies will be resolved. Unfortunately this means that provided dependencies won't be resolved, and therefore won't be passed onto BND (see the note at the top of the bundleplugin docs: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html).

        For some weird reason Maven doesn't let you set @requiresDependencyResolution to "provided", which would be the obvious choice, but I've been looking into setting it to "test". This would bring in the complete classpath, but could break projects that depend on the original reduced classpath, which is why I've not yet made this change to 1.5.0-SNAPSHOT (although it is definitely planned).

        FYI changing over to use "getDependencyArtifacts()" is definitely not a good idea, as this only provides direct project dependencies. It completely ignores any transitive dependencies. So while it works for your test POM, it would break most of the other POMs out there that rely on having the current transitive classpath.

        Show
        mcculls Stuart McCulloch added a comment - IMHO the same classpath used to compile the project should be passed to BND. However, this appears to be harder than it should be in Maven because of the way it lazily discovers transitive dependencies (as this is computationally expensive). Maven provides a mojo setting for plugins that forces resolution of project dependencies to a certain scope phase (@requiresDependencyResolution). Without this setting a plugin gets the same dependency resolution as the previous plugin in the build, which could vary a lot depending on what plugins are configured in your POM. Historically, @requiresDependencyResolution has always been set to "runtime" in the bundleplugin - which means that all compile and runtime dependencies will be resolved. Unfortunately this means that provided dependencies won't be resolved, and therefore won't be passed onto BND (see the note at the top of the bundleplugin docs: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html ). For some weird reason Maven doesn't let you set @requiresDependencyResolution to "provided", which would be the obvious choice, but I've been looking into setting it to "test". This would bring in the complete classpath, but could break projects that depend on the original reduced classpath, which is why I've not yet made this change to 1.5.0-SNAPSHOT (although it is definitely planned). FYI changing over to use "getDependencyArtifacts()" is definitely not a good idea, as this only provides direct project dependencies. It completely ignores any transitive dependencies. So while it works for your test POM, it would break most of the other POMs out there that rely on having the current transitive classpath.
        Hide
        nairb774 Brian Atkinson added a comment -

        We can take version ranges out of the game - just remove the "<versionpolicy />" instruction. The projects2 file is the same concept as before with this modification. I would expect in project b:b's pom for there to be the line:
        Import-Package: a.a;version="1.0",b.b;version="1.0"

        However the following line is there instead:
        Import-Package: a.a,b.b;version="1.0"

        All of this version range stuff misses the point. The "-classpath" directive to BND is not being filled out with the dependencies from the pom. Reading BND's documentation, it seems that the "-classpath" directive should be filled out with all the dependencies.

        I take a look at the code in teh getClasspath() function mentioned previously and it has provisions for allowing compile, system, and provided scope dependencies onto BND's classpath directive. The problem is that when this code is run, MavenProject.getArtifacts() is not returning anything. To be honest I think this part might be a bug in maven, but due to the lack of javadoc it is hard to tell if this is the case. This problem also exibits in maven 2.0.8 so I doubt it though.

        What is expected to be on the classpath directive to BND? This is the key question.

        Show
        nairb774 Brian Atkinson added a comment - We can take version ranges out of the game - just remove the "<versionpolicy />" instruction. The projects2 file is the same concept as before with this modification. I would expect in project b:b's pom for there to be the line: Import-Package: a.a;version="1.0",b.b;version="1.0" However the following line is there instead: Import-Package: a.a,b.b;version="1.0" All of this version range stuff misses the point. The "-classpath" directive to BND is not being filled out with the dependencies from the pom. Reading BND's documentation, it seems that the "-classpath" directive should be filled out with all the dependencies. I take a look at the code in teh getClasspath() function mentioned previously and it has provisions for allowing compile, system, and provided scope dependencies onto BND's classpath directive. The problem is that when this code is run, MavenProject.getArtifacts() is not returning anything. To be honest I think this part might be a bug in maven, but due to the lack of javadoc it is hard to tell if this is the case. This problem also exibits in maven 2.0.8 so I doubt it though. What is expected to be on the classpath directive to BND? This is the key question.
        Hide
        niclas Niclas Hedhman added a comment -

        I disagree that BND should "guess" the Import package version ranges. I think there is no "normal case". For you above, being a user of a.A has a very narrow version range due to the subclassing, but in many use cases a much wider version range are expected to work, and still yet for some projects even the exact dot dot release are needed. Unless you know the policies of the project, it is hard to predict a range that will actually work.

        I think that there are some discussions going on in the OSGi community to formalize the range expectations, but right now, with all the third-party stff being bundlized, I think this feature will be more harming than helping.

        Show
        niclas Niclas Hedhman added a comment - I disagree that BND should "guess" the Import package version ranges. I think there is no "normal case". For you above, being a user of a.A has a very narrow version range due to the subclassing, but in many use cases a much wider version range are expected to work, and still yet for some projects even the exact dot dot release are needed. Unless you know the policies of the project, it is hard to predict a range that will actually work. I think that there are some discussions going on in the OSGi community to formalize the range expectations, but right now, with all the third-party stff being bundlized, I think this feature will be more harming than helping.
        Hide
        nairb774 Brian Atkinson added a comment -

        Attached are the two projects I describe previously.

        Show
        nairb774 Brian Atkinson added a comment - Attached are the two projects I describe previously.

          People

          • Assignee:
            mcculls Stuart McCulloch
            Reporter:
            nairb774 Brian Atkinson
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development