How to unregister an OSGI/Blueprint Service from within the Service? - osgi

In my application I have a Service ChatProtocolClient. The implementation is a tcp client which connects to a remote server in the blueprint "init-method" and disconnects in the "destroy-method".
I also have another bundle that uses this ChatProtocolClient's connection to read and post messages from a channel, ChatChannel.
Currently I have an xml file that creates a bean of the ChatProtocolClient, and creates a bean ChatChannel in which a reference to the created ChatProtocolClient service is injected.
But how can I handle disconnects from the server? I'd want to tell the Blueprint framework that my ChatProtocolClient instance is unusable now and it should unregister this instance.
Preferably Blueprint would then automatically call the destroy-method on all dependent beans (beans in which Blueprint injected this service reference) and initialize a new ChatProtocolClient bean and all beans that were destroyed because the dependency failed.
How can this be done?

I found a way to implement this. In this solutions it's not Blueprint that recreates instances of all dependent services. It goes like this:
Connection "Watchdog" beans.
Instead of creating "ChatProtocolClient" Beans, I created ConnectionWatchDog beans from xml. In these beans the BundleContext is injected and the connection properties are set from the .xml file.
The ConnectionWatchDog then tries to create/connect a ChatProtocolClient instance. If connection succeeds, it registers a service in the BundleContext (with bundleContext.registerService(..)). The ServiceRegistration is kept in the watchdog. The watchdog tests the connection on set interval (it runs it's own Thread). If the connection appears to have failed; the watchdog calls the serviceRegistration.unregister() and cleans up the remainders of the client connection instance, ands starts the whole proces of creating, connecting and registering an new ChatProtocolClient instance.
The ChatChannel
The ChatChannel is now configured in Blueprint with a . The xml looks like this:
<blueprint xmlns=...>
<reference-list id="chat-connection" member-type="service-object" interface="com.example.ChatProtocolClientInterface">
<reference-listener bind-method="onBind" unbind-method="onUnbind" ref="Channel1"/>
</reference-list>
<bean id="Channel1" class="ChatChannel" init-method="startUp">
<property name="chatProtocolClient" ref="chat-connection">
... some other properties ...
</bean>
</blueprint>
The member-type set to service-object, means that when a service is registered or unregistered, the ChatChannel will be informed with the "onBind" and "onUnbind" methods. As parameter, they'll get a ChatProtocolClientInterface instance.
I'm not sure whether this is the only or best solution, but it works for me. Note that with this example xml, you'll also need a setter for "chatProtocolClient"; currently I don't use the list that is set by blueprint and I only use the onBind and onUnbind methods.

Related

Are services in AEM really singleton?

I have an interface which I have implemented. I have annoted the impl with #Component and #Service of the package org.apache.felix.scr.annotations.
I wrote a simple constructor for my impl
public MyImpl(){
LOG.info("New instance created!!");
}
I also added loggers in #activate and #deactivate method.
I expected to see "New instance created!!" only once BUT I can see activate and deactivate method being called per request I make on a page(This service is invoked by A Sling Model which is used in that page)
What I saw was "New instance created!!" logged several times.
This means the OSGi container create multiple instances of my Service and called the activate and deactivate method every time.
This shows that this is not a Singleton.
The Object should be discarded only when I uninstall my bundle.
Please help me understand what is going on here.
I WANT TO IMPLEMENT A TRUE SINGLETON IN AEM
I have implemented this in AEM 6.5 instance which uses Apache Felix.
Edit:
Adding Service properties:
aemRootUrl http://localhost:8080
api.http.connections_manager.timeout 60000
api.http.cookie_max.age 18000
api.http.max_connections 200
api.http.max_connections_per_host 20
api.http.timeout.connection 300000
api.http.timeout.socket 300000
api.server.ssl.trust_all_certs true
api.server.url https://10asdasdsad
api.server.username admin
component.id 3925
component.name com.example.foundation.core.connection.impl.HybrisConnectionImpl
non_akamai.api.server.url hadasdadasd
service.bundleid 585
Service PID com.example.foundation.core.connection.impl.HybrisConnectionImpl
service.scope bundle
Using Bundles com.example.dumb-foundation.core (585)
Values altered to hide client specific information
EDIT::
I've removed the SCR annotations and replaced them with OSGI annotations here I've explictly specified
#Component(service =HybrisConnection.class, immediate=true,scope = ServiceScope.SINGLETON)
But still is shows as scope=bundle.
Should I enforce Singleton and OSGi annotations on it's dependencies as well for this to be a proper Singleton?
In declarative services (which is what you use behind the scenes) there are some cases when a component (and its service) is unpublished.
By default a simple component with immediate=true will come up when the bundle starts and go down when it stops.
If your component has any mandatory service dependencies (#Reference) then it will only be active while all dependencies are present. So if at least one dependent service goes away the component will be deactivated.
In addition the component might get restarted when config is not present at start but added later. If you want to avoid this make the config required.
Every thing #Christian Schneider said is true.
They AEM services are Singletons but are deactivated/unpublished at times. This might be for various reasons.
I faced a horrible issue because of ConfigurationAdmin service. Using this services caused our OSGi config files to be bound to the wrong bundle i.e. SlingModels. bundle within AEM.
the only way to access this is by getting the service using configAdmin.getConfig(PID).setBundleLocation(null);
BUT Doing this causes the service that is linked to this configuration to restart.
So every time I did config.setBundleLocation(null) the service restarted.
The best and most awesome way to resolve this is use OCD to define configuration for OSGi Services linked to OSGi config.xmls
AND NEVER EVER EVER use configuration Admin
If you want to access properties of another service Say ServiceA want to read ServiceB's title property set in com.example.serivce.impl.ServiceB.xml
Then in ServiceB in the #activate method read the props from OCD config and set it in instance level and have ServiceA inject ServiceB as it's dependency and use the property needed.
eg.
class ServiceA{
#Reference
private ServiceB serviceB;
public void someMethod(){
serviceB.getTitle(); // Successfully read property of another service i.e.
ServiceB without using ConfigurationAdmin.
}
}

How to start/stop nt-aws:s3-inbound-channel-adapter manually

How to customize start/stop of aws s3 inbound-channel-adapter . I want to set auto-startup="false" initially and start manually when server starts.Looking for a solution which is similar like we have the below solution for file inbound channel adaptor.
inboundFileAdapterChannel.send(new GenericMessage("#'s3FilesChannelId.adapter'.start()"));
Config:
If i try the same approach for s3 inbound adapter channel . I am getting the below error
APPLICATION FAILED TO START
Description:
A component required a bean named 's3FilesChannelId.adapter' that could not be found.
Action:
Consider defining a bean named 's3FilesChannelId.adapter' in your configuration.
Let's assume we have a channel adapter like this:
<int-aws:s3-inbound-channel-adapter id="s3Inbound"
channel="s3Channel"
session-factory="s3SessionFactory"
auto-create-local-directory="true"
auto-startup="false"
delete-remote-files="true"
preserve-timestamp="true"
filename-pattern="*.txt"
local-directory="."
remote-file-separator="\"
local-filename-generator-expression="#this.toUpperCase() + '.a' + #fooString"
comparator="comparator"
temporary-file-suffix=".foo"
local-filter="acceptAllFilter"
remote-directory-expression="'foo/bar'">
<int:poller fixed-rate="1000"/>
</int-aws:s3-inbound-channel-adapter>
Pay attention to the auto-startup="false" and to the id="s3Inbound".
So, it isn't going to be started automatically after application context initialization.
However using that s3Inbound id we can do that manually whenever it is convenient for us.
Your story about inboundFileAdapterChannel is not clear though, but you still can inject a Lifecycle for the mentioned channel adapter and perform its start():
#Autowired
#Qualifier("s3Inbound")
private Lifecycle s3Inbound;
...
this.s3Inbound.start();
The piece of code about inboundFileAdapterChannel seems like a reference to the Control Bus approach, but that's already slightly different story: https://docs.spring.io/spring-integration/docs/current/reference/html/system-management-chapter.html#control-bus

Passing a Spring bean to a Camel component

I have a custom component of type FooComponent which is added to the route by the following lines:
from("foo://bar?args=values&etc")
.bean(DownstreamComponent.class)
...
FooComponent creates an endpoint and consumer (of type FooConsumer) which in turn emits messages which get to the DownstreamComponent and the rest of the pipeline.
For monitoring, I need the FooComponent consumer to call a method on a non-Camel object, which I'm creating as a Spring bean. The Camel pipeline is very performance sensitive so I'm unable to divide the FooComponent into two halves and insert the monitor call as a Camel component between them (my preferred solution, since FooComponent shouldn't really have to know about the monitor). And I'm reluctant to turn the method call into a Camel Message that will be picked up by the monitoring component later in the pipeline, as the pipeline filtering becomes complicated later and I don't want to meddle with it more than necessary.
Somewhere inside FooConsumer, I have:
// in the class
#Autowired
Monitor monitor;
// inside the consumer's run method
monitor.noticeSomething();
The problem is that monitor will never be set to the Monitor bean which is created in the rest of the application. As I understand it, it's because FooConsumer itself is not visible to Spring -- an object of that type is created normally inside FooComponent.
So, how can I get FooComponent to find the Monitor instance that it needs to use?
Can I pass it in when the route is created? This seems tricky because the definition is a faux URL "foo://bar?args=values&etc"; I haven't found how to pass Java objects that way.
Can I get Spring to find that #Autowired annotation inside FooConsumer and inject the monitor object somehow?
If you have a singleton instance of Monitor you ought to be able to #Autowire it in the FooComponent class. As Camel will let Spring dependency inject when creating the FooComponent.
Then you can pass on the monitor instance when you create the endpoint / consumer from your component.
The easiest thing to do is to create a Monitor property on the FooComponent class, and wire it in like any other bean.
<bean id="monitorBean" class="my.Monitor"/>
<bean id="foo" class="my.FooComponent">
<property name="monitor" ref="monitorBean"/>
</bean>
Then in your FooConsumer, when you need to get hold of the monitor, call:
Monitor monitor = ((FooComponent) getEndpoint().getComponent()).getMonitor();
If you were changing the monitor bean on a per-endpoint basis, you could use Camel's nifty # syntax to locate a bean with that id, and inject it into an Endpoint property.
foo:something?monitor=#monitorBean
Then to use it in your FooConsumer you simply say:
Monitor monitor = ((FooEndpoint) getEndpoint()).getMonitor();

Exposing multiple implementations of a interface as OSGI service

I have an interface that has two implementations. I want to expose both implementations as OSGi services, but when I am doing that one overrides the other. Please find the configuration that I am doing:
<bean id="formService" class="com.dbt.form.service.FormService"/>
<bean id="formAPIService" class="com.dbt.form.service.FormAPIService"/>
<osgi:service
ref="formAPIService"
interface="com.dbt.form.service.ifc.IFormService"/>
<osgi:service
ref="formService"
interface="com.dbt.form.service.ifc.IFormService" />
Here formService is overriden by formAPIService implementation.
Please help me on how to sort out this issue.
The second service does NOT override the first... both of these services will be published separately, and you can confirm this by typing the inpect cap service command in the OSGi Gogo shell.
What MAY happen is that your consumer code will only choose one of the available service instances. In this case you need to write your consumer to either bind to all instances, or use a combinations of rankings or target filters to determine which particular service you want. You should give more information on how you are using these services since that is where the problem lies (probably).
Read this page...Chapter 8. The Service Registry section 8.2.2.3.
You can use bean-name attribute of osgi reference tag. While importing a service bean-name refers to the id attribute of that service when its exported.

how to read a jms queue from a non hosted java application?

I'm trying to read messages from a jms queue created in "Sun App Server" from a non-hosted application (console app) but I get the following error:
NoInitialContextException
Cannot instantiate class: javax.jms.TopicConnectionFactory
with this code:
Properties env = new Properties( );
env.put(Context.INITIAL_CONTEXT_FACTORY, "javax.jms.TopicConnectionFactory");
InitialContext jndi = new InitialContext(env);
and I have referenced the j2ee.jar library that contains the class but certainly, the class is an interface.
Can I access the queue from a non-hosted application??
Aitor;
When you say "Sun App Server", I'm not sure what that means, but I will assume it is Glassfish.
There are 2 separate steps to acquiring remote JMS resources.
You need to create a remote JNDI connection which requires a valid InitialContextFactory class name.
Once you have a the connection, you can look up the TopicConnectionFactory.
For item #1, this link demonstrates how to make a remote JNDI connection.
For item #2, once you have a JNDI context, you will also need to know the JNDI name of the TopicConnectionFactory which will look something like:
TopicConnectionFactory tcf = (TopicConnectionFactory) jndi.lookup("jms/TopicConnectionFactory");
One aspect you need to keep in mind is that the j2ee.jar library contains the generic Java EE interfaces for the JMS classes, but you will also need a library in your classpath that contain the JMS implementation concrete classes. This also goes for the JNDI connection. This tutorial provides a concise list as:
Applicationserver JNDI Lookup
/lib/appserv-rt.jar
/lib/appserv-admin.jar
/lib/javaee.jar /lib/j2ee.jar
Client Lib
/imq/lib/jms.jar
/imq/lib/imq.jar
/imq/lib/imqutil.jar
/lib/install/applications/jmsra/jmsra.jar

Resources