I am working on a OSGi application(with felix scr annotations) which exposes a service. The Service registers with external api's by passing String values.
listener.addSchemaChangeListener(new ChangeListener()
{
#Override
public void schemaChange(ChangeEvent changeEvent)
{
String schemaName = changeEvent.getSchemaName();
if (null != myBuilder && schemaList.contains(schemaName))
{
initVariables();
}
}
}, "SCHEMA1");
Service uses the above piece of code to register the listeners for mulitple values "SCHEMA1", "SCHEMA1", "SCHEMA3" ...
I am planning to reuse this service in various bundles. But i want to listen only for SCHEMA1 changes instead of all.
#Reference (name = "ServiceListener"", policy =
ReferencePolicy.DYNAMIC, cardinality =
ReferenceCardinality.MANDATORY_UNARY, bind = "bind", unbind =
"unbind", referenceInterface = ServiceListener.class)
private AtomicReference myServiceListener = new AtomicReference<>();
If i try to use it in another service with #Reference then there is no provision to pass values to service to listen only for particular schema changes so that
the service can be resued across my bundle by only passing the list of schema to listen instead of all. Because activate method will be called once the service is binded properly in the usage class(component).
Is there any provision in OSGi to acheive this functionality ?
You have included very little description of how your application is actually working, which makes this question challenging to answer.
From the code that you have shared it looks as though you are following quite a bad pattern. The Listener pattern is a source of many synchronisation problems and memory leaks, and the whiteboard pattern should be preferred when you are in OSGi.
The whiteboard pattern is pretty simple. Rather than having each listener look up a service and register with it, you invert the model. The source of events (in this case schema changes) looks for listener services that are registered in the OSGi service registry. That way the listeners are simple to write and filter, and there is no messy and error-prone add/remove listener logic to code.
A better model would use service properties to select particular schemas and look something like this (using the standard OSGi annotations).
Listener 1 (listens to changes for SCHEMA1)
#Component(
property="schemaName=SCHEMA1")
public class MyListener implements ChangeListener {
// Your implementation in here
}
Listener 2 (listens to changes for SCHEMA1, SCHEMA2, and SCHEMA3)
#Component(
property={"schemaName=SCHEMA1",
"schemaName=SCHEMA2",
"schemaName=SCHEMA3"})
public class MyListener implements ChangeListener {
// Your implementation in here
}
Example Source of events for Schema1:
#Component
public class MyListener implements ChangeListener {
#Reference(policy=DYNAMIC, target="(schemaName=SCHEMA1)")
private final List<ChangeListener> listeners = new CopyOnWriteArrayList<>();
private void onSchemaChange(ChangeEvent event) {
listeners.forEach(l -> l.schemaChange(event);
}
// Rest of your implementation in here
}
One way is to create one service for each schema. You can do this by supplying the schema name as a config value and using several configs. Each such service will then also hav this config parameter as a service property. So the clients then can filter for the schema property.
If you do not want to use these configs then you could create one service that offers a factory. Each client then would bind the factory and create an instance by supplying the schema name in the create method of the factory.
Related
I've spent a long time on learning OSGi, but I still feel as though a crucial piece of the puzzle is missing.
This is my use case:
I am using JAX-RS (Grizzly) to create a REST API. I have an interface that has many various implementations. My solution should be able to add new implementations at any time.
Based on some form of input, I have to get a hold of one these specific instances. For example, lets say I register two interface implementations with Felix Interface A and Interface B. A user should be able to ask for Implementation B
By using the command line we get from running felix.jar(Apache Felix Gogo ) I have been able to install and start my own bundles. The problem I now face is how I from one of my controllers is supposed to retrieve any of these implementation.
Here is the code for the activator of one of my implementations.
public class MyClassActivator implements BundleActivator {
#Override
public void start(BundleContext context) throws Exception {
System.out.println("Starting ImplementationA");
Hashtable<String, String> props = new Hashtable<>();
props.put("Identifier", "ImplementationA");
context.registerService(MyInterface.class.getName(), new MyClassA(), props);
}
#Override
public void stop(BundleContext context) throws Exception {
System.out.println("Stopping ImplementationA");
}
private class ImplementationA implements MyInterface {
/*my implementation*/
}
}
From one of my JAX-RS classes, I want to, somehow, do this:
MyInterface myclassA = getBundle("ImplementationA");
The String ImplementationA is the same string I placed in the props map.
What I have tried so far is
BundleContext bc = FrameworkUtil.getBundle(MyInterface.class).getBundleContext();
This however just returns null, it doesn't seem to actually be "talking" to my felix instance.
So my questions are how do I get an interface from Felix? And is what I want to do even possible with OSGi?
Your question is confusing since you mix terms for services and bundles. A bundle is an installable unit which contains code. That code can register and consume services. Services are object which, typically, implement some interface which is shared between the bundle providing the service and the bundles consuming the services.
So the first order of business is to make sure the service interface's package is exported by some bundle and imported by all the bundles which plan to participate in providing and consuming the service. This is necessary to ensure type safety. That is, the consuming bundles can safely cast the service object to the expected type of the service.
Once that is done, as you observer, there can be multiple providers of a service. When a provider registers a service, they can specify some metadata about the service in the form of key/value properties. Your example shows this in the Identifier property. When a consumer looks up a service, a filter string can be specified which can specify information to be checked against a service' metadata to select from among multiple provided services.
public class MyServiceConsumer implements BundleActivator {
#Override
public void start(BundleContext context) throws Exception {
System.out.println("Looking for ImplementationA");
ServiceReference<MyInterface>[] refs =
context.getServiceReferences(MyInterface.class, "(Identifier=ImplementationA)");
MyInterface service = context.getService(refs[0]);
}
}
The above is terrible code; don't actually use it. It does not handle there being no service when the consumer bundle is activated (refs == null), nor does is it prepared for the service to go away. I strongly recommend you use OSGi Declarative Services when writing bundles. It makes service use and dealing with the dynamic super simple.
#Component
public class MyServiceConsumer {
MyInterface service;
#Reference(target="(Identifier=ImplementationA)")
private void bindService(MyInterface s) {
service = s;
}
#Activate
private activate() {
// do work
}
#Deactivate
private deactivate() {
// do work
}
}
This is a component which will only be instantiated when a matching service is present. It will be called at bindService to inject the service instance, the activate will be called to enable to component to do it work. If the injected service goes away, the component will be called at deactivate and then discarded. If later another matching service comes along, a new instance of the component will be activated.
See http://enroute.osgi.org/ for a tutorial on OSGi app dev.
We are using Apache Felix annotations to handle all the OSGi stuff in our application. I have a provider class that talks to a server. I have a consumer class that does stuff with data from the server. What I want is to create another provider instance (new class implementing interface) that is for debug purposes only that returns canned responses to requests by the consumer. Ideally I would like the consumer to be unaware of this handoff. It's provider service reference would simply be replaced.
The use case: When the developer is running on a machine without access to the actual server, he presses a button in our running app to switch from the real provider instance to our debug provider instance.
What is the recommended way to accomplish this?
Example code:
public interface IProvider{
public String getDataFromServer();
}
#Component
#Service(value=IProvider.class)
public class RealProvider implements IProvider{
#Override
public String getDataFromServer(){
...
}
}
#Component
#Service(value=IProvider.class)
public class DebugProvider implements IProvider{
#Override
public String getDataFromServer(){
return "Hello World";
}
}
#Component
public class Consumer{
private #Reference IProvider provider;
public void doSomething(){
provider.getDataFromServer();
}
}
If the two providers are in separate bundles, you can stop Bundle A and start Bundle B to switch between implementations of the service.
If the two providers are in the same bundle, you'd need to either drop down to the OSGI API and register/unregister the services manually, or create a proxy version of IProvider that has a debugMode flag and delegates to the specific implementation.
I am looking for a simple way to control the instantiation of a declarative service.
What I want is something like a factory which is asked to return an instance of a component, but where I can control the instantiation.
Currently I just found the ComponentFactory where I just can call newInstance with a given dictionary. But what I really want, is something like this, assuming IComponent is the declarative service interface and MyComponent is the implementation class
public class MyComponentFactory implements ? {
public IComponent newInstance() {
return new MyComponent("firstParameter", "secondParameter");
}
}
Is there something possible like this with declarative services, or do I need to use my own service registration in a bundle activation code?
Thanks in advance
DS does not provide for the level of instance creation indirection you are looking for. DS will always use the public no-args constructor and then call the specified activator method to complete the instance initialization.
One alternative for such service instantiation control is using a combination of DS and CM.
You must set configuration-policy="require" in the DS xml and use CM to create a configuration instance that will be used to pass a Dictionary containing all properties that you need (only types supported by DS of course) to the created service instance. You can even use a propertie file plus Felix File Install for that connfiguration.
If that is not enough you have another alternative that is to track for new created service and call a setup(args) method just after you have added a configuration using CM.
Suppose this is a configuration data object that I read from DB/XML:
class ConfigurationClass{
//some configurations
public String getString(){
}
}
Now this is the class that uses this configuration object
class ConfigurationUser implements Reloadable{
private ConfigurationClass configuration;
//some extra logic required for making object ready to use
public void init(){
// some new object is created based on configuration
SomeObject obj = new SomeObject(configuration.getString());
//register to configuration manager
}
#Override
public void reload(){
//does whole reloading stuff
//creates a new instance of configuration class and reads new configuration which is not appropriate
}
}
Now when I have to reload the configuration then suppose I create a new instance of ConfigurationClass and inject it in ConfigurationUser class.
But I will also have to re-initialize the object, so that it can create new configuration dependent objects.
Is this a proper design to do it or there is a better approach? I thought of using Spring or Google Juice for DI, but again I will have to provide the callback to the configuration user class, that configuration has been reloaded.
I am new to using IoC containers so dont know if it is possible. Need to use this in real time server application so have to be strict about garbage production and performance.
Current solution:
public interface Reloadable{
public void reload();
}
I have a configuration manager to which I register all the configuration user instances, and when reload is to be done, manager just calls reload and it reads new configuration and re initializes all dependent objects.
Any help would be appreciated.
I think you're looking for an Observer/Listener design pattern. Your configuration user should be an "observer" and a configuration itself will be a "subject". First, the observer registers itself in a subject. Next, every time a subject is changed it notifies all observers, and they reload (or do anything they wish).
I have an application which is part JavaEE (the server side) part JavaSE (the client side). As I want that client to be well architectured, I use Weld in it to inject various components. Some of these components should be server-side #EJB.
What I plan to do is to extend Weld architecture to provide the "component" allowing Weld to perform JNDI lookup to load instances of EJBs when client tries to reference them. But how do I do that ?
In other worrds, I want to have
on the client side
public class ClientCode {
public #Inject #EJB MyEJBInterface;
}
on the server-side
#Stateless
public class MyEJB implements MyEJBInterface {
}
With Weld "implicitely" performing the JNDI lookup when ClientCode objects are created. How can I do that ?
Basically, doing so requires write a so-called portable CDI extension.
But, as it is quite long and requires a few tweaks, let me explain it further.
Portable extension
Like weld doc explains, first step is to create a class that implements the Extension tagging interface, in which one will write code corresponding to interesting CDI events. In that precise case, the most interesting event is, to my mind, AfterBeanDiscovery. Indeed, this event occurs after all "local" beans have been found by CDI impl.
So, writing extension is, more opr less, writing a handler for that event :
public void loadJndiBeansFromServer(
#Observes AfterBeanDiscovery beanDiscovery, BeanManager beanManager)
throws NamingException, ClassNotFoundException, IOException {
// Due to my inability to navigate in server JNDI naming (a weird issue in Glassfish naming)
// This props maps interface class to JNDI name for its server-side
Properties interfacesToNames = extractInterfacesToNames();
// JNDI properties
Properties jndiProperties = new Properties();
Context context = new InitialContext();
for (Entry<?, ?> entry : interfacesToNames.entrySet()) {
String interfaceName = entry.getKey().toString();
Class<?> interfaceClass = Class.forName(interfaceName);
String jndiName = entry.getValue().toString();
Bean<?> jndiBean = createJndIBeanFor(beanManager, interfaceClass, jndiName, jndiProperties);
beanDiscovery.addBean(jndiBean);
}
}
Creating the bean is not a trivial operation : it requires transforming "basic" Java reflection objects into more advanced weld ones (well, in my case)
private <Type> Bean<Type> createJndIBeanFor(BeanManager beanManager, Class<Type> interfaceClass,
String jndiName, Properties p) {
AnnotatedType<Type> annotatedType = beanManager
.createAnnotatedType(interfaceClass);
// Creating injection target in a classical way will fail, as interfaceClass is the interface of an EJB
JndiBean<Type> beanToAdd = new JndiBean<Type>(interfaceClass, jndiName, p);
return beanToAdd;
}
Finally, one has to write the JndiBean class. But before, a small travel in annotations realm is required.
Defining the used annotation
At first, I used the #EJB one. A bad idea : Weld uses qualifier annotation method calls result to build hashcode of bean ! So, I created my very own #JndiClient annotation, which holds no methods, neither constants, in order for it to be as simple as possible.
Constructing a JNDI client bean
Two notions merge here.
On one side, the Bean interface seems (to me) to define what the bean is.
On the other side, the InjectionTarget defines, to a certain extend, the lifecycle of that very bean.
From the literature I was able to find, those two interfaces implementations often share at least some of their state. So I've decided to impelment them using a unique class : the JndiBean !
In that bean, most of the methods are left empty (or to a default value) excepted
Bean#getTypes, which must return the EJB remote interface and all extended #Remote interfaces (as methods from these interfaces can be called through this interface)
Bean#getQualifiers which returns a Set containing only one element : an AnnotationLiteral corresponding to #JndiClient interface.
Contextual#create (you forgot Bean extended Contextual, didn't you ?) which performs the lookup :
#Override
public T create(CreationalContext<T> arg0) {
// Some classloading confusion occurs here in my case, but I guess they're of no interest to you
try {
Hashtable contextProps = new Hashtable();
contextProps.putAll(jndiProperties);
Context context = new InitialContext(contextProps);
Object serverSide = context.lookup(jndiName);
return interfaceClass.cast(serverSide);
} catch (NamingException e) {
// An unchecked exception to go through weld and break the world appart
throw new LookupFailed(e);
}
}
And that's all
Usage ?
Well, now, in my glassfish java client code, I can write things such as
private #Inject #JndiClient MyRemoteEJB instance;
And it works without any problems
A future ?
Well, for now, user credentials are not managed, but I guess it could be totally possible using the C of CDI : Contexts ... oh no ! Not contexts : scopes !
Section 3.5 of the CDI spec should help out. You may want to use some of the properties on the EJB annotation as well. Also, (probably don't need to tell you this) make sure you have JNDI set up correctly on the client to reference the server, and pack any of the needed interfaces into your client jar.