Are services in AEM really singleton? - osgi

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.
}
}

Related

how to Resolve "could not initialize proxy - no session" error when using Spring repository

I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
As we are dynamically switching the tenants so it looks like some configuration is missed which is closing the session or not keeping the session open to fetch the LAZY loaded objects. Which results in "could not initialize proxy - no session" error.
Please check below link to access the complete project and db schema scripts, please follow the steps given in Readme file.
Project
It will be helpful if someone can point out the issue in the code.
i tried to put service methods in #Transactional annotation but that didn't work.
I'm expecting it to make another call to the LAZY loaded object, This project is simplefied verson of the complex project, actually i have lot more lazy loaded objects.
Issue:-
I'm getting no Session error "could not initialize proxy [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - no Session"
at line 26 (/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig and MasterDatabaseConfig you've correctly added #EnableTransactionManagement, which will setup transactions when requested.
However - the outermost component that has an (implicit) #Transactional annotation is the ProductRepository (by virtue of it being implemented by the SimpleJpaRepository class - which has the annotation applied to it - https://github.com/spring-projects/spring-data-jpa/blob/864c7c454dac61eb602674c4123d84e63f23d766/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L95 )
and so your productRepository.findAll(); call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
i tried to put service methods in #Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional and not a javax.transaction.Transactional annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
#Service
#Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Service
#Transactional("tenantTransactionManager")
public #interface TenantService {
}
and then you can simply annotate your service classes with #TenantService instead of #Service:
#TenantService
public class ProductServiceImpl implements ProductService {

Mule connector config needs dynamic attributes

I have develop a new Connector. This connector requires to be configured with two parameters, lets say:
default_trip_timeout_milis
default_trip_threshold
Challenge is, I want read ${myValue_a} and ${myValue_a} from an API, using an HTTP call, not from a file or inline values.
Since this is a connector, I need to make this API call somewhere before connectors are initialized.
FlowVars aren't an option, since they are initialized with the Flows, and this is happening before in the Mule app life Cycle.
My idea is to create an Spring Bean implementing Initialisable, so it will be called before Connectors are init, and here, using any java based libs (Spring RestTemplate?) , call API, get values, and store them somewhere (context? objectStore?) , so the connector can access them.
Make sense? Any other ideas?
Thanks!
mmm you could make a class that will create the properties in the startup and in this class obtain the API properties via http request. Example below:
public class PropertyInit implements InitializingBean,FactoryBean {
private Properties props = new Properties();
#Override
public Object getObject() throws Exception {
return props;
}
#Override
public Class getObjectType() {
return Properties.class;
}
}
Now you should be able to load this property class with:
<context:property-placeholder properties-ref="propertyInit"/>
Hope you like this idea. I used this approach in a previous project.
I want to give you first a strong warning on doing this. If you go down this path then you risk breaking your application in very strange ways because if any other components depend on this component you are having dynamic components on startup, you will break them, and you should think if there are other ways to achieve this behaviour instead of using properties.
That said the way to do this would be to use a proxy pattern, which is a proxy for the component you recreate whenever its properties are changed. So you will need to create a class which extends Circuit Breaker, which encapsulates and instance of Circuit Breaker which is recreated whenever its properties change. These properties must not be used outside of the proxy class as other components may read these properties at startup and then not refresh, you must keep this in mind that anything which might directly or indirectly access these properties cannot do so in their initialisation phase or your application will break.
It's worth taking a look at SpringCloudConfig which allows for you to have a properties server and then all your applications can hot-reload those properties at runtime when they change. Not sure if you can take that path in Mule if SpringCloud is supported yet but it's a nice thing to know exists.

ServiceTracker and DS combined?

Let's imagine a bundle in which exists:
A component is responsible of listen all "Device" service instances in the service registry.
The same component needs an "adaptor factory" in order to create "Adaptors" by using the discovered devices.
The factory is owned by another bundle.
I can solve part of the problem by using a ServiceTracker (Activator + Service Tracker): the activator instantiates the ServiceTraker and it can register all changes in "Device" services.
But i can't inject to this service tracker the DS factory created in other bundle, because it will result in two instances (one created by activator AND without the member /// another created by osgi AND with member variable ok but can't listen the "Device" service changes).
So... how can i solve this scenario? How can i have a Service Tracker (perfect for me) with a DS as a class member?
Use no Activator, instead use a component or service (we will call it A) with a declarative services 'activate(ComponentContext)' method. Within the activate method, you can instantiate your ServiceTracker like normal.
When you instantiate the ServiceTracker within A's activate method, you can also pass in the AdapterFactory into the ServiceTracker. You can get the AdapterFactory by pulling it out of the BundleContext taken from ComponentContext or (even better) use DS and make it a service reference to your A component.
That said: why do you need ServiceTracker for this? Unless I misunderstand, you can use DS bind and unbind to receive events on the availability of a Service.
EDIT: An (OLD) example of Bind/Unbind behavior using multiple cardinality: http://blog.tfd.co.uk/2009/11/12/declarative-optional-multiple-references-flaky-in-osgi/
EDIT: A comparision of the two approaches but doesn't go into bind/unbind so much: http://njbartlett.name/2010/08/05/when-servicetrackers-trump-ds.html
EDIT2: That said: my general policy is to not use an Activator except in super rare cases. Use DS, ipojo, etc and use the components you define with those techs in order get access to the BundleContext to build more low level objects like ServiceTrackers.

Can't get reference of ConfigurationAdmin in RAP

I'm trying to configure the osgi-jax-rs-connector in my RAP application.
The README says to use the Configuration Admin Service for configuration.
ServiceReference caRef = context
.getServiceReference(ConfigurationAdmin.class.getName());
The code above always returns null for the ServiceReference.
What's the correct way to obtain a reference to the ConfigurationAdmin.
Does another bundle needs to be started before?
If you run Equinox please make sure that the Config Admin bundle (org.eclipse.equinox.cm) is installed and started.
Trying to get a ServiceReference this way is setting yourself up for disaster. This code can't handle 99% of the cases of what happens in OSGi: the config admin might not be there, the config admin bundle is started after you, the config admin bundle is in another start level, the config admin bundle is stopped, and the config admin is updated. The core OSGi API is very powerful, and is used by much middleware, but should not ever be used for application code since it requires way to much understanding of the underlying model and is very error prone.
By far the easiest and most reliable solution is to use Declarative Services (DS) with the annotations:
#Component
public class MyClass implements MyService {
ConfigurationAdmin ca;
#Reference void setCA(ConfigurationAdmin ca) { this.ca = ca; }
public void doMyService() {
// ... you can safely use ca
}
}
And Gunnar might be right, maybe have not installed a Configuration Admin service in the first place. However, with your current snippet your code is going to blow up anyway at another time.

OSGi: Is it valid to register services within a declarative service component activation method?

This is a duplicate of another question, but copying from the other one:
I've run into an issue with Felix SCR where I get the message:
ServiceFactory.getService() resulted in a cycle
The reason this appers to be occuring is because within an activation method, call it ServiceAImpl (which provides ServiceA), the service registers another service, call it ServiceB.
I have another service component, call it ServiceCImpl, which depends on both ServiceA and ServiceB. By ServiceAImpl registering ServiceB, ServiceCImpl has become satisfied and within the same call to activate ServiceAImpl, the ServiceCImpl binding methods are called. When the binding method for ServiceA is called, the cycle is detected and the component fails to initialize.
Maybe there is a way to allow the SCR to wait to bind the ServiceCImpl or maybe I need to be registering ServiceB differently?
I guess what doesn't make sense is why the Felix SCR will activate ServiceCImpl within the activation method of ServiceAImpl. I wouldn't think that ServiceCImpl would be considered satisfied until after the activation method has exited. Perhaps this is an issue of using declarative services while still registering services directly with the framework?
Haven't tried other SCR implementations, like Equinox's version, but I might try that to see if there is a difference, but maybe somebody knows if this is a OSGi thing or a Felix thing?
Additional Info: As to why ServiceB is not a service component...ServiceA actually has a service reference of 0..n for another service, call it ServiceD. Everytime a ServiceD interface is provided by a component, a ServiceB is registered using the same service object. Normally the same provider of ServiceD could provide ServiceB, but the idea is to make the overall interface for developers more simplistic so they don't have to provide multiple service interfaces (also, ServiceB has some properties that are set automatically that would have to be done manually and possibly could be done incorrectly).
The reason this appears to be happening is that ServiceAImpl is a delayed component that has already been loaded so ServiceA is in fact already resgistered before the component is activated. However, when another component comes along that needs ServiceA, this causes ServiceAImpl to be activated. Part of the activation process of ServiceAImpl is to register ServiceB which immediately causes ServiceCImpl to be activated. A change was made in FELIX-2368 to immediately activate a component by making most SCR operations synchronous.
A workaround is to make ServiceAImpl an immediate component which isn't desired as it shouldn't be activated if nothing needs the service. In this case, the activation method is completed when the component is loaded and by the time it is needed and bound to another component there is no problem.
I've tried to recreate this scenario with a small set of test bundles and the ProSyst OSGi FW.
However, if I register a service with DS, I am not able to register an other service from within the activate() method. If I just use DS to obtain services, I can register services as usual. So there is probably really a problem with DS/SCR...
Example:
public class ServiceAImpl implements ServiceA, ServiceB, ManagedService {
public void activate(ComponentContext _context) {
_context.getBundleContext().registerService(
ManagedService.class.getName(),
this,
null);
_context.getBundleContext().registerService(
ServiceB.class.getName(),
this,
null);
}
#Override
public void doA() {
System.out.println("Doing A Stuff");
}
#Override
public void doB() {
System.out.println("Doing B Stuff");
}
#Override
public void updated(Dictionary arg0) throws ConfigurationException {
}
}
This class will register 2 Services (ServiceB, ManagedService) with this bnd. file:
Private-Package: org.test.impl
Service-Component: org.test.impl.ServiceAImpl
Bundle-Category: test
but only 1 service (ServiceA) with this sample:#
Private-Package: org.test.impl
Service-Component: org.test.impl.ServiceAImpl;provide:=org.test.ServiceA
Bundle-Category: test
So probably you should/have to try to register the services either via DS/SCR or the "classic way" via bundle context.

Resources