I have a class that implements ManagedServiceFactory like this:
public class GreeterFactory implements ManagedServiceFactory {
private volatile BundleContext bundleContext =
FrameworkUtil.getBundle(GreeterFactory.class).getBundleContext();
private final Map<String, ServiceRegistration> registrations = new ConcurrentHashMap<>();
#Override
public String getName() {
return "Greeter Factory Implementation";
}
/**
* Greeter Service Factory
* #param pid this is the PID of the Configuration received.
* #param dictionary the Configuration to prepare the service.
* #throws ConfigurationException
*/
#Override
public void updated(String pid, Dictionary<String, ?> dictionary) throws ConfigurationException {
String message = (String) dictionary.get("message");
if (message == null) {
throw new ConfigurationException("message",
"Required property 'message' missing");
}
GreeterService greeter;
synchronized (registrations) {
if (registrations.containsKey(pid)) {
greeter = (GreeterService) bundleContext.getService(registrations.get(pid).getReference());
} else {
// For each new configuration, the factory register a new service with
// the given properties/configuration
greeter = new GreeterImpl();
ServiceRegistration greeterRegistration =
bundleContext.registerService(GreeterService.class.getName(),
greeter,
dictionary);
System.out.print("\nRegistering Config-PID: " + pid + "\n");
registrations.put(pid, greeterRegistration);
}
}
greeter.setMessage(message);
}
#Override
public void deleted(String pid) {
ServiceRegistration component = null;
synchronized (registrations) {
component = registrations.remove(pid);
}
// Calling services from a synchronized block can lead to deadlocks,
// so Dependency Manager must be called outside.
if(component != null) {
bundleContext.ungetService(component.getReference());
}
}
}
The factory works OK. I also have a test case to consume the services created for each configuration sent by the ConfigurationAdmin service, here is the test case:
Configuration configuration1 = configurationAdmin.createFactoryConfiguration("example.factoryservice.greeter", null);
Dictionary properties = new Properties();
properties.put("message", "Hello factory world 1!");
configuration1.update(properties);
TimeUnit.SECONDS.sleep(1);
Configuration configuration2 = configurationAdmin.createFactoryConfiguration("example.factoryservice.greeter", null);
properties = new Properties();
properties.put("message", "Hello factory world 2!");
configuration2.update(properties);
TimeUnit.SECONDS.sleep(1);
ServiceReference<GreeterService> sRef = context.getServiceReference(GreeterService.class);
GreeterService greeterService = context.getService(sRef);
assertEquals("Hello factory world 1!", greeterService.sayHello());
greeterService = context.getService(sRef);
assertEquals("Hello factory world 2!", greeterService.sayHello()); // FAILS!!
Now, I am kind of lost here and I cannot find any documentation about this part, but how do I determine in code what Greeter service to use depending on the configuration I need?
I created in code 2 Greeter configurations, the factory then registered a couple of Greeter services, each with a different configuration, how I decide in code an instance of a Greeter service with Configuration 1?
ManagedServiceFactory is pretty low level. Unless you want to implement a technology, you do not need it. In case you want to implement business logic, use one of the Component Models.
But, to answer your specific question:
You register the GreeterService with the service properties that you get from the configuration. That means that you can filter on these services.
Note, that BundleContext has a function where you can pass OSGi service filter as well. E.g.:
Collection<ServiceReference<GreeterService>> sRefs =
context.getServiceReferences(GreeterService.class,
"(message=Hello factory world 2!)");
Related
I have a runnable task where i am trying to Autowire field but when i do it the task doesn't run .
When i Autowire the field outside the runnable it works fine . Why is this happening ? Also is there any other cleaner way to get new instances of a autowired field inside runnable other than Autowiring it inside ?
Here's my runnable method `
Runnable task = new Runnable() {
#Autowired
ICruxPanelClientService CruxPanelClientService;
public void run (){
CruxPanelClientService.setCruxWebServiceBaseURL("http://10.41.181.23:8080");
CronCruxModel m = new CronCruxModel();
m = model1.get(model_var);
System.out.println("EXECUTING");
System.out.println(m.getService_status() + " ---------EXEexecution");
System.out.println(m.getCat_name() + "Executing Name ");
// time = m.getService_time();
UpdateCategoryRequest updateCategoryRequest = new UpdateCategoryRequest();
CategoryModel categoryModel = new CategoryModel();
categoryModel.setColor(m.getCat_color());
categoryModel.setIcon(m.getCat_icon());
categoryModel.setIconWhite(m.getCat_icon_white());
categoryModel.setName(m.getCat_name());
categoryModel.setId(m.getCat_id());
categoryModel.setKey(m.getCat_catkey());
categoryModel.setIndexOrder(m.getCat_indexOrder());
updateCategoryRequest.setCategory(categoryModel);
CruxPanelClientService.updateCategory(updateCategoryRequest);
GetServiceDataIdByCategoryIdRequest request1 = new GetServiceDataIdByCategoryIdRequest();
request1.setId(m.getCat_id());
GetServiceDataIdByCategoryIdResponse response1 = CruxPanelClientService.getServiceDataIdByCategoryId(request1);
ArrayList<ServiceModel> service = new ArrayList<ServiceModel>();
service = response1.getServiceModels();
JSONArray json = new JSONArray();
if(m.getService_order_succ_msg()==null)
{
json = new JSONArray();
}
else {
json = new JSONArray(m.getService_order_succ_msg());
}
String message = m.getService_order_succ_msg();
for (int j=0;j<service.size();j++)
{
UpdateServiceMasterRequest req = new UpdateServiceMasterRequest();
ServiceModel s = new ServiceModel();
s=service.get(j);
;
JSONObject obj = new JSONObject();
if(json.length()==0 )
{
String ms = null;
s.setOrderSuccessMessage(ms);
req.setServiceModel(s);
}
else {
String message1 = json.get(j).toString();
if(message1.equals(null) || message1.equals("")) {
String ms = null;
s.setOrderSuccessMessage(ms);
req.setServiceModel(s);
}
else {
s.setOrderSuccessMessage(message1);
req.setServiceModel(s);
}
}
CruxPanelClientService.updateServiceMaster(req);
}
m.setService_status("executed");
UpdateCronCruxRequest q = new UpdateCronCruxRequest();
q.setCronCruxModel(m);
CruxPanelClientService.updateCronCrux(q);
}
};`
The problem is spring doesn't control creation of your runnable. There are couple possible solutions:
Put you runnable creation in some service, repository, controller, component or what ever handled by spring:
Example:
#Service
public class SomeService {
#Autowired
private ICruxPanelClientService cruxPanelClientService;
public Runnable newRunnable() {
return new Runnable() {
public void run() {
cruxPanelClientService <- will be visible here and injected
}
}
}
}
Create runnable as bean with prototype scope
Example:
#Configuration
public class Runnableconfiguration {
#Bean
#Scope("prototype")
public Runnable newRunnbale(final ICruxPanelClientService cruxPanelClientService) {
return new Runnable() {
public void run() {
cruxPanelClientService <- will be visible here
}
}
}
}
#Autowire can't be used for anonymous classes (because you call new, not Spring), you can autowire a field in outer class and then use that field inside your Runnable.
Or make the Runnable a full blown class (not anonymous) and make it a bean (and autowire, e.g. using the id)
Spring does not #autowire anything into unmanaged instances (instances you create with new, in contrast to instances created and managed by Spring).
This leaves you following options:
#autowire the dependency outside and pass it to the Runnable (or make it accessible to the Runnable).
create a new class subclassing Runnable, and #autowire it where you need it.
Instead of having the dependency injected, look it up (applicationContext.getBean(..)) when needing it.
Be careful with scopes and lazy initialization when using Threads, as some of the Spring scopes (e.g. "request" or "session" scope) are thread-local bound (e.g. when using a lazy request-scoped dependency in a new Thread, which when executed is no longer associated with the current request).
In addition to all mentioned above, maybe it is better to inject the service from outside your runnable and not use #Autowired inside your runnable. So in your case:
Runnable task = new Runnable() {
#Autowired
ICruxPanelClientService CruxPanelClientService;
would become:
#Autowired
private ICruxPanelClientService CruxPanelClientService;
Runnable task = new Runnable() {
...
}
I'm getting problems trying to call a remote service deployed in an EAR from another EAR.
I do not specify any names for my EJB, no matter they are #Local or #Remote and so just use annotation and inject it via #EJB.
This is what i have:
EAR A/
lib/any lib jar (including API jar for remote service B)
war
ejb module(s) with service A calling remote service B
EAR B/
lib/any API lib jar
ejb module with service B
Additional information: Service B implement both #Local and #Remote interfaces, and Service A inject Service B with Remote interface via:
#EJB private MyRemoteInterface remoteService;
This structure works perfectly fine with jboss server, but with websphere (8.5.5.1) one i must bind names onto my remote EJB. If do not add bindings on both EARs (i did it though admin console directly not to have to edit ejb-jar.xml) then my remote bean is not resolved at runtime.
Of course, i have to make it work with WAS, else i won't not post :)
My question: is that normal to be forced to name remote EJB with WebSphere or is it a regression (from any previous version) ? I'm expecting the injection #EJB on remote beans to works with automatic resolution on types, but maybe i'm wrong somewhere ?
Solution:
Because lookup must be done to make the resolution work, i decided to add the lookup configuration part onto the client ejb-jar.xml file(s). This is done automatically by maven plugin execution, with lookup name based on remote interface full name (package included) as this is the default binding WebSphere use if nothing is specified in EJB implementation.
I've chosen this solution for two reasons:
i don't want to do the lookup in my code (duplicate code with no interest)
i need to make it automatic and transparent for other developers
Thanks bkail for the answer.
Finally, for business delays reason i've written a CDI extension to make the job.
The extension scan all injection point with Remote contract and proxify them. Proxies are #ApplicationScoped managed beans created on demand, and their job only consists in:
lookup the target bean related to the scanned remote contract
delegate the execution of the called remote method
This solution also offers me the possibility to handle lookup operation on different machine(s) by configuration though ENV variables so that deployement by container (i.e Docker) would easily works (which is one of our target in a comming future)
EDIT: CDI extension code below
RemoteEjbExtension.java:
public class RemoteEjbExtension implements Extension {
/**
* This method is fired by the container for every Java EE component class
* supporting injection that may be instantiated by the container at runtime,
* including every managed bean declared using javax.annotation.ManagedBean,
* EJB session or message-driven-bean, enabled bean, enabled interceptor or
* enabled decorator.
*
* #param pit the event that has been fired
*/
<T> void processInjectionTarget(#Observes final ProcessInjectionTarget<T> pit) {
for (AnnotatedField<? super T> field : pit.getAnnotatedType().getFields()) {
if (field.getJavaMember().getType().isAnnotationPresent(Remote.class)) {
RemoteProxyFactory.putIfAbsent(field.getJavaMember().getType());
}
}
}
/**
* This method is fired by the container when it has fully completed the
* bean discovery process, validated that there are no definition errors
* relating to the discovered beans, and registered Bean and ObserverMethod
* objects for the discovered beans, but before detecting deployment problems.
*
* #param abd AfterBeanDiscovery fired events
* #param bm Allows a portable extension to interact directly with the container.
* Provides operations for obtaining contextual references for beans,
* along with many other operations of use to portable extensions.
*/
#SuppressWarnings("unchecked")
void afterBeanDiscovery(#Observes final AfterBeanDiscovery abd, final BeanManager bm) {
// Roll over discovered remote interfaces
for (final Entry<String, Class<?>> remoteClassEntry : RemoteProxyFactory.getProxyClassEntries()) {
// Proxy that points to the remote EJB
final Object remoteProxy;
final Class<?> remoteClass = remoteClassEntry.getValue();
try {
// Build a proxy that fetches the remote EJB using JNDI
// and delegate the call.
remoteProxy = RemoteProxyFactory.Builder.createEJBRemoteProxy(remoteClass);
} catch (Exception e) {
throw new IllegalStateException("Proxy creation for " + remoteClass.getCanonicalName() + " failed.", e);
}
final InjectionTarget<Object> it;
try {
AnnotatedType<Object> at = ((AnnotatedType<Object>) bm.createAnnotatedType(remoteProxy.getClass()));
it = bm.createInjectionTarget(at);
} catch (Exception e) {
throw new IllegalStateException("Injection target for " + remoteClass.getCanonicalName() + " is invalid.", e);
}
final Bean<?> beanRemoteProxy = RemoteProxyFactory.Builder.createBeanForProxy(remoteProxy, it, remoteClass, ApplicationScoped.class);
abd.addBean(beanRemoteProxy);
}
}
}
RemoteProxyFactory.java:
public final class RemoteProxyFactory {
/** The JNDI initial context */
private static InitialContext CTX;
static {
try {
RemoteProxyFactory.CTX = new InitialContext();
} catch (NamingException e) {
throw new IllegalStateException("Unable to get initial context.", e);
}
}
private static final Map<String, Class<?>> REMOTE_EJB_CLASS_MAP = new ConcurrentHashMap<String, Class<?>>();
/**
* Register given class into proxy map
* #param remoteEJBContractClass the remote contract's class to register
*/
public static void putIfAbsent(final Class<?> remoteEJBContractClass) {
// Works only for same class-loader. You would change this code
// and transform the map to handle multiple class-loader for same contract.
// In our current configuration there is no need as APIs / IMPL libraries share the same CL.
if (!REMOTE_EJB_CLASS_MAP.containsKey(remoteEJBContractClass.getSimpleName())) {
REMOTE_EJB_CLASS_MAP.put(remoteEJBContractClass.getSimpleName(), remoteEJBContractClass);
}
}
public static Set<Entry<String, Class<?>>> getProxyClassEntries() {
return REMOTE_EJB_CLASS_MAP.entrySet();
}
public static InitialContext getContext() {
return RemoteProxyFactory.CTX;
}
public static final class Builder {
private static final Logger LOGGER = Logger.getLogger(Builder.class.getName());
/**
* Create a new proxy that lookup the remote EJB
* though JNDI.
* #param remoteEJBClazz the remote class contract
* #return a new remote EJB proxy
*/
public static Object createEJBRemoteProxy(final Class<?> remoteEJBClazz) {
return Proxy.newProxyInstance(remoteEJBClazz.getClassLoader(), new Class[] {
remoteEJBClazz
}, new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
Object ejbInstance = null;
try {
// Pull the remote EJB from the JNDI
ejbInstance = RemoteProxyFactory.getContext().lookup(remoteEJBClazz.getName());
} catch (Exception e) {
throw new IllegalStateException("Remote EJB not found : " + remoteEJBClazz.getSimpleName(), e);
}
// Delegates the call to the remote EJB
return method.invoke(ejbInstance, args);
}
});
}
/**
* Create a bean for given proxy / injection target / type / scope
* #param proxy the proxy object
* #param it the injection target
* #param clazz the proxy type
* #param targetScope the returned managed bean' scope
* #return the managed bean handling given proxy
*/
public static <T extends Object> Bean<T> createBeanForProxy(final T proxy, final InjectionTarget<T> it, final Class<?> clazz, final Class<? extends Annotation> targetScope) {
return new Bean<T>() {
#Override
public T create(final CreationalContext<T> ctx) {
return proxy;
}
#Override
public void destroy(final T instance, final CreationalContext<T> ctx) {
it.preDestroy(instance);
it.dispose(instance);
ctx.release();
}
#Override
public Class<?> getBeanClass() {
return clazz;
}
#Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
#Override
public String getName() {
return clazz.toString();
}
#Override
public Set<Annotation> getQualifiers() {
Set<Annotation> qualifiers = new HashSet<Annotation>();
qualifiers.add(new AnnotationLiteral<Default>() {
/** Default serial-id. */
private static final long serialVersionUID = 1L;
});
qualifiers.add(new AnnotationLiteral<Any>() {
/** Default serial-id. */
private static final long serialVersionUID = 1L;
});
return qualifiers;
}
#Override
public Class<? extends Annotation> getScope() {
return targetScope;
}
#Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
#Override
public Set<Type> getTypes() {
Set<Type> types = new HashSet<Type>();
types.add(clazz);
return types;
}
#Override
public boolean isAlternative() {
return false;
}
#Override
public boolean isNullable() {
return false;
}
};
}
}
}
This is working as expected for WebSphere Application Server, and it is not a regression. The javadoc only requires automatic binding for #EJB when the type is within the same application:
If no explicit linking information is provided and there is only one
session bean within the same application that exposes the matching
client view type, by default the EJB dependency resolves to that
session bean.
I have a spring application and now need some suggestions for the below mentioned scenario.
I have a method in a scheduler, and for each time a scheduler runs I want to create a new bean object in the method as shown in the below code for worker object. So how can I achieve this?
while(itr.hasNext()){
Device dev = itr.next();
connDetails = new ConnectionDetails(dev.getIpaddress(), dev.getPort(), dev.getPassword(), dev.getPassword());
ScheduledMessage worker = ?;
worker.set_id(dev.get_id());
worker.setConnDetails(connDetails);
executor.execute(worker);
}
In your SpringConfig.xml:
<!-- Enable the annotation usage (bean injection for instance) -->
<context:annotation-config />
<!-- Message bean (several instances) -->
<bean id="scheduledMessageBean" class="com.myapp.ScheduledMessage" scope="prototype" />
In your java class:
ScheduledMessage worker = BasicBeanFactory.getInstance().getBean("scheduledMessageBean");
You can check there ยง3.2.1 how to build the BeanFactory for your application (requires Spring Application context): http://docs.spring.io/spring/docs/1.2.9/reference/beans.html
For instance (not tested example):
public class BasicBeanFactory
{
private static BasicBeanFactory instance = null;
/** The application context. */
private ApplicationContext ctx = null;
protected BasicBeanFactory() {
// Init spring context
ctx = new ClassPathXmlApplicationContext("SpringConfig.xml");
}
public Object getBean(String name) throws BeanInstantiationException {
Object bean = null;
try {
bean = ctx.getBean(name);
} catch (BeansException e) {
throw new BeanInstantiationException(e);
}
return bean;
}
public static BasicBeanFactory getInstance() {
if (instance == null) {
instance= new BasicBeanFactory();
}
return instance;
}
}
Get object from current Spring context that will work well!
This main is calling for a session-bean in the same project to send a message to a message-driven-bean in a separate project.
The other project is fully deployed and running without error.
For this project, the session-bean ejb can be deployed. The problem is when i try to run my client main code, netbeans can deploy the code but gives me (WARNING: Exception creating ejb object : [SSSbean]) upon running it. I simply don't see why the ejb object can't be created. Any ideas?
Session Bean is below:
public class SSSbean implements SSSbeanRemote {
#Resource(name = "jms/Topic")
private static Topic topic;
#Resource(name = "jms/TopicConnectionFactory")
private static ConnectionFactory topicFactory;
public SSSbean () {}
#Override
public void createMessage(String messageData) throws JMSException {
Connection topicConnection = null;
Session session = null;
MessageProducer producer = null;
topicConnection = topicFactory.createConnection();
session = topicConnection.createSession(true,0);
topicConnection.start();
producer = session.createProducer(topic);
TextMessage tm = session.createTextMessage();
tm.setText(messageData);
producer.send(tm);
}
#Override
#Remove
public void remove() {
System.out.println("SSSBean:remove()");
}
}
Main is below:
public class Main {
#EJB
private static SSSbeanRemote ss;
public static void main(String[] args) {
// TODO code application logic here
Main client = new Main();
client.bX();
ss.remove();
}
private void bX() {
System.out.println("Main: Client started... ");
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("Enter Suggestion: ");
String suggestion = sc.nextLine();
try{
ss.createMessage(suggestion);
continue;
} catch (JMSException j) {
System.out.println("Error: "+ j.toString());
}
}
}
}
You are trying to inject an EJB within a code that is not managed by the Container.
When your execute your client code, the main() method just ignores the #EJB annotation. The only one that knows what #EJB means and how to to inject a Bean is the Container. Try to execute the client code inside a Aplication Client Container
If you want to retrieve EJB bean from an application which is not managed by container, you can retrieve it from the InitialContext.
Maybe that will bring you little bit closer: https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI
I have 2 modules containing classes:
blog.model.ArticleDAO
blog.model.CategoryDAO
users.model.UserDAO
users.model.UserGroupDAO
All these DAOs have a dependency on the same service, but I need to inject a different instance based on the package.
I mean the module blog should have a specific instance of MyService, and the module users should have another instance of MyService.
I don't want to create 2 named services because some day I may want to use the same service for all DAOs. Or I could also want to inject another specific instance for a specific class...
Is there a way to inject a service based on the package of a class?
A way to say:
inject foo (instance of MyService) into classes that are in blog.*
inject bar (instance of MyService) into classes that are in users.*
but keeping all my classes unaware of that! Their configuration should only state "Inject an instance of MyService".
First I want to say, I find this a strange requirement. I am also wondering why your DAOs need a Service. In a normal layered design, this is the opposite (the Service uses the DAO).
However I find the challenge interesting, I tried to use a FactoryBean to create a Java Proxy class which would redirect at runtime to the correct instance of MyService depending of the caller package. Here is the code:
public class CallerPackageAwareProxyFactoryBean implements
FactoryBean<MyService>, ApplicationContextAware {
private Class<?> targetServiceType;
private ApplicationContext applicationContext;
private InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of service locator proxy.
return System.identityHashCode(proxy);
} else if (ReflectionUtils.isToStringMethod(method)) {
return "Service dispatcher: " + targetServiceType.getName();
} else {
String callerPackageFirstLevel = getCallerPackageFirstLevel();
Map<String, ?> beans = applicationContext
.getBeansOfType(targetServiceType);
for (Map.Entry<String, ?> beanEntry : beans.entrySet()) {
if (beanEntry.getKey().startsWith(callerPackageFirstLevel)) {
return method.invoke(beanEntry.getValue(), args);
}
}
throw new IllegalArgumentException(
String.format(
"Could not find any valid bean to forward call for method %s.",
method.getName()));
}
}
private String getCallerPackageFirstLevel() {
Throwable t = new Throwable();
StackTraceElement[] elements = t.getStackTrace();
String callerClassName = elements[3].getClassName();
return callerClassName.split("\\.")[0];
}
};
public MyService getObject() throws Exception {
return (MyService) Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class<?>[] { MyService.class },
invocationHandler);
}
public Class<?> getObjectType() {
return MyService.class;
}
public boolean isSingleton() {
return true;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setTargetServiceType(Class<?> targetServiceType) {
this.targetServiceType = targetServiceType;
}
}
I didn't had to change anything to the Dao or Service configuration. I just had to add the creation of the FactoryBean in the Spring context:
<bean id="myService" class="stackoverflow.CallerPackageAwareProxyFactoryBean">
<property name="targetServiceType" value="a.b.c.MyService" />
</bean>
Maybe a few comments:
The caller package can only be get by creating an exception and looking at the stacktrace.
The code of the InvocationHandler is inspired from ServiceLocatorFactoryBean.
I am still wondering if there is an easier way but I think there is not.
You could replace part of the InvocationHandler to use a configuration Map (package => MyService bean name)
I would not recommend using such code in a productive environment.