Proper way to use an URCap as a dependency

Hello,
I am facing an interesting issue that I think will help many future progarmmers if we are able to find a solutio together.

Imagine you created an URCap that does something very simple, let’s say turning on and off the correct sequence of digital outputs, let’s call this URCap A.

Now imagine that you created a more complex URCap which automates the creation of a process, and in that process you need to turn on and off the correct sequence of digital outputs, let’s call this URCap B.

It would be natural to think, as a programmer, that there is no need to duplicate the code of A, it is sufficient to insert A as a child node of B.

So far, this IS possible, by inserting URCap A as a dependency in the pom.xml file of B:
<Embed-Dependency>A</Embed-Dependency>
and
<dependency>
<groupId>com</groupId>
<artifactId>A</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

This way we can use A in B’s contribution

However, this does not let us use A as a child node YET, since A will need to be registered in the B’s Activator alongside B itself.
bundleContext.registerService(SwingProgramNodeService.class, new AService(), null);
bundleContext.registerService(SwingProgramNodeService.class, new BService(), null);

Everything will now work as desired, except… not quite…

Imagine now that a third URCap, called C, also depends on A, what will happen then?

If we use the same exact code for both there will be 2 entries of URCap A on the leftside of the program tab under tha available URCaps, and we don’t want that!

What if in the future URCap A will be a dependency of 20 different URCaps?
Are we going to keep 20 exact copies of A cluttering the tab?

I have found that with some clever workaround it is possible to automatically avoid to reinstall URCaps that are already registered to the bundle, and it is even possible to unregister them!

Sorry for the wall of text but it was all needed context, here is the problem:
An URCap that depends on another URCap MUST register the dependency in their own Activator, even if the dependency was already registered in the Bundle.

Skipping registration or unregistering an URCap because it was already registered by other URCaps does not work as intended.

In the previous example, if URCap C skips registering URCap A because it was already registered by URCap B what will happen is the following:

  • There will be no errors or warnings.
  • When looking at the URCaps tab we will see one entry for each URCap: A, B and C, exactly like we want.
  • When inserting URCap A and B there will be no problem, they will behave as expected.
  • But, when inserting C, it will be unable to create its own A childnode, since it didn’t register it itself.
  • Usually the returned error is something like:
    ERROR - Null child not allowed {thread: AWT-EventQueue-0 , loggerClass: com.ur.urcap.domain.program.TreeNodeImpl}
    com.ur.urcap.api.domain.program.structure.TreeStructureException: Null child not allowed

Because when trying to create a URCap child, if the operation fails it will return null, and trying to add null to the program tree is not allowed.

I am asking for someone to create an example where they correctly deal with this issue (@jbm maybe), in particular it would be extremely appreciated if you could convert the example here:

But instead of having the Activator registering both Services in the same file, splitting the 2 contributions in 2 standalone URCaps and then clearly showing that the parent one MUST depend on the child one.

Anyways, sorry again for the wall of text, if I get confirmation that what I want to do is currently impossible (having URCaps that depend on other URCaps which work even if it wasn’t their Activator to register the required URCap) I will create a similar post as a feature request.

Thanks for your time!

Hello Chio,
if i understand correctly, you want to insert an urcapnode(in the context it is A-node) by another URCap (B-node or C-node), and meanwhile do not want the A-node apprears three times in the program tab,
i’ve tried your approch which is working well, regarding the “three times” issue, if you can set in the service class: configuration.setUserInsertable(false); then it will not showup in the program tab, it’s just one small recommendation:)

It’s been a long time since and it will be a long time until I will have my hands on another URCAP, however your solution might just work!
Thanks :slight_smile:

1 Like

For future readers, the suggestion of @funing.hu works with a small detail:

All URCaps that depend on another URCap must register their dependencies themselves, however, simply adding configuration.setUserInsertable(false); in their Service class is NOT enough.

This is because it is reasonable to assume that the original URCap could be user insertable, what i had to do was to “overload” the constructor of the service classes of my URCaps, right after the public class AService declaration I added:

private boolean hidden;
public AService(boolean hidden){this.hidden = hidden;}
public AService(){new AService(false);}

Then in the Activator class of all URCaps i keep a list of dependencies and do something like this:

private SwingProgramNodeService[] dependencies = new SwingProgramNodeService[] {new AService(true)};

for ( SwingProgramNodeService s : dependencies) {
    bundleContext.registerService(SwingProgramNodeService.class, s, null);
}
bundleContect.registerService(SwingProgramNodeService.class, new BService(), null);

Here URCap B registers its own dependencies with the constructor that takes true as an argument, meaning that its dependencies are registered but hidden, and then at the end it registers itself without any arguments, since B should alo be user insertable.

Hope that it might help someone along the way :slight_smile:

2 Likes