Function Plugin ClassNotFoundException - Spring Framework AnnotationConfiguration

I have created a function plugin that uses a Spring ApplicationContext to manage service objects.  The context is instantiated in a static initializer block of the @Category class.  @Function methods use the getBean() method to access services required by the functions.  When I deploy the plugin, the configuration class can not be found when my @Category class is initialized (ClassNotFoundException).  I've also deployed with a modification to use package scanning, which deploys successfully without the exception, but fails when the function is used from a user interface.  The failure is caused by one of the services not being available from the application context when the @Function method attempts to retrieve it, so it still remains that the classes are not being found on the classpath.  I have already double checked the JAR file to ensure the classes were added.

This sounds like a class loader issue.  Perhaps OSGi not permitting the reflection required by Spring ??  Is it possible to run small Spring applications within plugin JARs?  Below is my approach with real names redacted.

@MyFunctionsCategory
public class MyFunctionsFacade {
    
    // make the Spring container static in case the Appian plugin architecture creates new instances of plugin objects
    // ensures that the container is only created and loaded once
    private static final AnnotationConfigApplicationContext context;
    
    static {
        context = new AnnotationConfigApplicationContext();
        context.register(MyConfig.class);        
        context.refresh();
    }

    @Function    
    public MyCDT myCustomFunction(@Parameter Integer id, @Parameter String param2){
        MyService service = context.getBean(MyService.class);

        MyCDT result = service.serviceMethod(id, param2);
        return result;
    }
}

  Discussion posts and replies are publicly visible

Parents
  • I was able to resolve this issue several days ago, though I would share the solution in case this helps anyone else.  There are several requirements for this to work.

    1. Make sure that all versions of Spring, Hibernate, Atlassian, OSGi JARs match the current versions used within Appian to avoid class version conflicts.  With few exceptions these should also be provided dependencies and excluded from your plugin JAR.

    2. JavaConfig is not supported, an XML application context must be used, but context-component-scan can still be used

    3. The application context must be created using the class NonValidatingOsgiBundleXmlApplicationContext

    4The BundleContext is needed to create the application context

    5. In order to reference the above application context class and BundleContext, Atlassian and OSGi plugins must be in your classpath as provided dependencies.

    Sample Code:

    Bundle bundle = FrameworkUtil.getBundle(getClass());         
    URL uri = bundle.getResource("path/to/context/config.xml");
          
    context = new NonValidatingOsgiBundleXmlApplicationContext(new String[]{ uri.toString() });
    context.setBundleContext(bundle.getBundleContext());
    context.refresh();

Reply
  • I was able to resolve this issue several days ago, though I would share the solution in case this helps anyone else.  There are several requirements for this to work.

    1. Make sure that all versions of Spring, Hibernate, Atlassian, OSGi JARs match the current versions used within Appian to avoid class version conflicts.  With few exceptions these should also be provided dependencies and excluded from your plugin JAR.

    2. JavaConfig is not supported, an XML application context must be used, but context-component-scan can still be used

    3. The application context must be created using the class NonValidatingOsgiBundleXmlApplicationContext

    4The BundleContext is needed to create the application context

    5. In order to reference the above application context class and BundleContext, Atlassian and OSGi plugins must be in your classpath as provided dependencies.

    Sample Code:

    Bundle bundle = FrameworkUtil.getBundle(getClass());         
    URL uri = bundle.getResource("path/to/context/config.xml");
          
    context = new NonValidatingOsgiBundleXmlApplicationContext(new String[]{ uri.toString() });
    context.setBundleContext(bundle.getBundleContext());
    context.refresh();

Children
No Data