Expand my Community achievements bar.

SOLVED

Managing OSGi services programatically

Avatar

Community Advisor

Hi Team,

 

Can anyone help what would be perfect way to achieve this requirement:

We have number of OSGi services in our project and divided into 2 sets.

Based on a parameter passed during build, we need to enable one set of OSGi services and disable other set.

For Example:

We have OSGi service1, service2, service3, service4.

If param == enableFirstSet ----> then enable service1 & service2

If param == enableSecondSet ----> then enable service3 & service4

 

Also,

I have tried below code in activate method to deactivate individual services  based on param but the problem is their attached OCD(configuration) still available in OSGi configuration tab as those configurations attached at bundle level rather than component level.

 

context.disableComponent((String) properties.get(Constants.SERVICE_PID));

 Any pointer will be good.

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

Andrew gave a few suggestions how you could achieve it, but I have a more generic question: Why do you want to do this during runtime? Because then you cannot deploy the same artifact(s) on any environment, but you will have specialized builds. Which could make sense if you have multiple production environments. But for every other case (multiple/different development stages) I would definitely use runmodes.

 

And even if you have different PROD environments because you client1, client2 and client3 and they run the same application bundle with some specialities, you still can use runmodes. Just give them the name "client1", "client2" and "client3", and still have all services/components in the same bundle.

 

If this approach doesn't work, you should really think of splitting the functionality in separate bundles and build dedicated deployment packages. Then it is not a decision of the application which services/components to start and stop, but it's a decision at deployment time.

 

There are ways to stop and start services, but these operations are not persisted, unless you switch the value of the service property "enabled" and persist it (see https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html, search for "enabled"; this is R7 but also works for OSGI R6). But be warned: This is a real hack and from my point of view a sign of bad application design and architecture. I would always try to solve this by runmodes.

 

 

View solution in original post

12 Replies

Avatar

Community Advisor

Do you have 2 bundles or one?

If 2 bundles are there then in maven apps pom.xml, skip one of them to be packaged based on param.



Arun Patidar

Avatar

Community Advisor

@arunpatidar  Only single bundles is there where all the OSGi services lies.

Avatar

Employee

One way to do what you want is to make it so an OSGi configuration needs to be available for the component to be enabled (this is called ConfigurationPolicy.REQUIRE on the OSGi Component).  See here, for example:

http://www.computepatterns.com/43/osgi-component-in-aem-that-is-active-only-in-specific-run-mode-say...

 

Then using this, you can leverage runmodes like /apps/myapp/config.qaauthor, /apps/myapp/config.qapublish.  Or you can enable them by removing the config of one component and enabling the config of the other.

 

Also, you might consider thinking about it differently.  Instead of having different OSGi components / services active for different builds, maybe have a logical controller that checks a method on the service interface that returns boolean (true/false) to see if the service is "enabled".

Avatar

Community Advisor

Hi @Andrew_Khoury , Thanks for your suggestions.

But I have few doubts:

  1. The services needs to be controlled based on the build param not the run modes. If I'm using ConfigurationPolicy as require then how we can dynamically create and remove corresponding OSGi service configuration file from my Java code or controller based on param?
  2. Could you please elaborate your second point: "Also, you might consider thinking about it differently.  Instead of having different OSGi components / services active for different builds, maybe have a logical controller that checks a method on the service interface that returns boolean (true/false) to see if the service is "enabled"."

Avatar

Employee

If runmodes aren't preferrable as this is build specific and not environment specific then you could dynamically set the OSGi configurations via your code:

http://www.nateyolles.com/blog/2015/10/updating-osgi-configurations-in-aem-and-sling

You might consider to have some separate projects in your maven builds that build different package for deploy per build that have different OSGi configs.  There are many ways to achieve what you want, it is just to decide how to implement it.

 

https://maven.apache.org/plugins/maven-resources-plugin/examples/copy-resources.html

 

As for clarification to my other point:

> Also, you might consider thinking about it differently. Instead of having different OSGi components / services active for different builds, maybe have a logical controller that checks a method on the service interface that returns boolean (true/false) to see if the service is "enabled".

What I meant there is to refactor your code.  Instead of using different OSGi services for different builds then programmatically *enable* or *disable* the functionality via boolean flag.  Or use less services and run the correct functionality per the build.  You might look at sling feature flags feature https://sling.apache.org/documentation/the-sling-engine/featureflags.html

Avatar

Correct answer by
Employee Advisor

Andrew gave a few suggestions how you could achieve it, but I have a more generic question: Why do you want to do this during runtime? Because then you cannot deploy the same artifact(s) on any environment, but you will have specialized builds. Which could make sense if you have multiple production environments. But for every other case (multiple/different development stages) I would definitely use runmodes.

 

And even if you have different PROD environments because you client1, client2 and client3 and they run the same application bundle with some specialities, you still can use runmodes. Just give them the name "client1", "client2" and "client3", and still have all services/components in the same bundle.

 

If this approach doesn't work, you should really think of splitting the functionality in separate bundles and build dedicated deployment packages. Then it is not a decision of the application which services/components to start and stop, but it's a decision at deployment time.

 

There are ways to stop and start services, but these operations are not persisted, unless you switch the value of the service property "enabled" and persist it (see https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html, search for "enabled"; this is R7 but also works for OSGI R6). But be warned: This is a real hack and from my point of view a sign of bad application design and architecture. I would always try to solve this by runmodes.

 

 

Avatar

Employee

I agree with @Jörg_Hoh, the concept of having different builds removing / adding configs could be prone to issues and isn't the recommended approach. Leveraging runmodes is good for environment specific stuff and if you have site / path specific functionality then maybe you could drive that via Sling Context Aware configs:
https://sling.apache.org/documentation/bundles/context-aware-configuration/context-aware-configurati...

 

Otherwise, in general, adding / removing OSGi components per build would generally be discouraged.  OSGi is designed to be dynamic, I can't see a reason why you would need to enable / disable the components per different build.

Can you elaborate on your use case?

Avatar

Community Advisor

Hi @Jörg_Hoh @Andrew_Khoury @Theo_Pendle  Thanks you all for these valuable suggestions.

 

Sorry, I wasn't much clear with my use case earlier.

Let me explain you my use case. Actually I am working on a aem plug-in and it supports two functionalities and they share some common code base and few OSGi services separately for each functionality. The goal is to have 3 artifacts with all three possibilities.(first, second and both) And we don't want our customers to see other OSGi Configuration if we have build the artifact for different functionality to avoid confusion. So instead of removing the aem services, I have removed their specific OSGI-INF during build time based on build parameter passed by creating maven profile and maven shade plugin.

Does that make sense?

 

Regards,

Arpit Varshney

Avatar

Employee
In that case it sounds like you should have 2 separate bundles, one for each of the two functionalities. Then only install the respective bundle depending on what you would want the customer to see.

Avatar

Employee Advisor
as @Andrew_Khoury suggested. Also I wonder if the wish (or need?) not to show customers any OSGI configuration they are not supposed to see, qualifies this much technical work. Best is either splitting the bundles or abandoning this requirement completely.

Avatar

Level 10

Hi Arpit,

Using Maven profiles, it is possible to build your bundle using some source .java files and not others (hence deploying one service and not another).

Here is what I came up with, I recorded a short video (<2min) to illustrate how it works: https://youtu.be/Mqq9L6GsEp4 

And here is the relevant XML to be added to your pom.xml file:

    <profiles>
        <profile>
            <id>service-a</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <excludes>
                                <exclude>**/core/service/impl/b/**</exclude>
                            </excludes>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>service-b</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <excludes>
                                <exclude>**/core/service/impl/a/**</exclude>
                            </excludes>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>