Can I deploy an EJB under multiple names in a similar way as this is possible in Spring using a name alias?
According to EJB 3.0 specification:
At declaration time:
name
The annotation element name defines the bean “name” and defaults to the unqualified name of the bean class. The bean name must be unique in the scope of the module containing the EJB.
Referencing a EJB:
beanName
The beanName element specifies the bean “name” as declared in the #Stateful and #Stateless annotations via the name element or in the deployment descriptor via the element. The beanName element is most useful when more than one EJB implement the same business interface in an application: the beanName lets the developer reference a specific EJB in a specific module.
So, yes you could but the ejb must be packaged in different modules and then point it through the beanName.
Not As Spring., there could be ways but that is vendor specific, NOT PORTABLE.
Yes, it is possible to deploy same EJB under multiple names or aliases using ejb-jar.xml descriptor.
Moreover both name defined in the EJB xml descriptor and one defined in the annotation will be taken into account (although I would not risk mixing between xml descriptor and annotations in this case for portability reasons).
This example works on JBoss AS 7.1.1.Final:
#Local
public interface PingWorker {
String ping(String name);
}
#Stateless(name = "PingAlias") // first name
public class PingWorkerBean implements PingWorker {
#Override
public String ping(String name) {
return "Hello " + name;
}
}
<ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:ejb="http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0">
<enterprise-beans>
<session>
<ejb-name>PingWorker</ejb-name> <!-- second name -->
<ejb-class>com.xyz.service.PingWorkerBean</ejb-class>
<session-type>Stateless</session-type>
</session>
<session>
<ejb-name>PingProcessor</ejb-name> <!-- third name -->
<ejb-class>com.xyz.service.PingWorkerBean</ejb-class>
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
</ejb-jar>
In referencing component you can declare:
#Stateless
public class PingBean implements Ping {
#EJB(beanName = "PingAlias")
private PingWorker pingWorkerByAnnotaion;
#EJB(beanName = "PingWorker")
private PingWorker pingWorkerByDescriptorOne;
#EJB(beanName = "PingProcessor")
private PingWorker pingWorkerByDescriptorTwo;
}
and all three references should be satisfied. On deployment there are three EJBs deployed which expose the same interface and implementation but with different names:
java:global/ear-app-1/ejb-1/PingProcessor!com.xyz.service.PingWorker
java:app/ejb-1/PingProcessor!com.xyz.service.PingWorker
java:module/PingProcessor!com.xyz.service.PingWorker
java:global/ear-app-1/ejb-1/PingProcessor
java:app/ejb-1/PingProcessor
java:module/PingProcessor
java:global/ear-app-1/ejb-1/PingAlias!com.xyz.service.PingWorker
java:app/ejb-1/PingAlias!com.xyz.service.PingWorker
java:module/PingAlias!com.xyz.service.PingWorker
java:global/ear-app-1/ejb-1/PingAlias
java:app/ejb-1/PingAlias
java:module/PingAlias
java:global/ear-app-1/ejb-1/PingWorker!com.xyz.service.PingWorker
java:app/ejb-1/PingWorker!com.xyz.service.PingWorker
java:module/PingWorker!com.xyz.service.PingWorker
java:global/ear-app-1/ejb-1/PingWorker
java:app/ejb-1/PingWorker
java:module/PingWorker
As you may see this works differently than aliases in Spring. While in Spring aliases are only different names that refer to the same instance or set of instances (depends on the scope) - in EJB you need to declare EJBs multiple times. In the EJB container these become separate EJBs in terms of instance pools etc.
Depending on what you are trying to achieve - you may also consider overriding referenced EJB name in referencing EJB (override annotation beanName with descriptor <ejb-link/>). More on this here:
Choose EJB to be injected without recompiling.
Related
I have an EJB application which is Deployed in WebSphere 8
I am trying to connect it from Standalone java program following way
public static void main (String[] args) throws Exception
{
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
props.put(javax.naming.Context.PROVIDER_URL, "iiop//localhost:2809");
Context ctx = new InitialContext(props);
Object o = ctx.lookup("MyAppHome");
}
But this is leading to below exception
javax.naming.NameNotFoundException: Name "MyAppHome" not found in context
"serverlocal:CELLROOT/SERVERROOT".
at com.ibm.ws.naming.ipbase.NameSpace.lookupInternal(NameSpace.java:1228)
Further looking into it I found following supporting link https://www.ibm.com/developerworks/community/forums/html/threadTopic?id=ac01caaf-d2aa-4f3f-93b3-6f3d4dec3e6b
Here the answerer suggested using fully qualified bean name
java:global/ProjectName/ModuleName/BeanName!FullyQualifiedNameOfRemoteInterface or BeanName#FullyQualifiedNameOfRemoteInterface.
If it's a correct fix. Where can I find the Project name, Module Name and Bean Name? I do have xmi files in the project. Thanks!
The easiest way to determine the JNDI name for an EJB in WebSphere is to look for the binding information in the server logs. The JNDI names associated with each EJB interface are logged using the message ID CNTR0167I. Fore example:
CNTR0167I: The server is binding the com.example.DatabaseBean interface of the DatabaseBean enterprise bean in the TestProject.war module of the TestProject application. The binding location is: java:global/TestProject/DatabaseBean!com.example.DatabaseBean
Notice in this example that there does not appear to be both a project name (i.e. application name) and module name, because this test project was a standalone WAR module with an EJB packaged in it. Project/Application name is only used for EAR files.
Also note that a CNTR0167I is logged for JNDI names in both the java:global and SERVERROOT name contexts. So, in the error scenario above, it could be that the EJB was bound to ejb/MyAppHome and not just MyAppHome, so you can also determine the correct SERVERROOT JNDI name from the CNTR0167I messages as well. Either JNDI name may be used to lookup an EJB.
I'm trying to expose some EJBs as REST web service using JAX-RS annotations. When I deploy war file containing EJB Jar in WEB-INF/lib to Wildfly 8, I can see in web admin panel EJB Jar as deployed, But I cannot reach REST endpoints and get 404.
This is content of web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/separated/*</url-pattern>
</servlet-mapping>
</web-app>
This is a sample session bean I'm trying to serve as web service and put in jar file:
#Stateless(name = "TestSessionEJB")
#LocalBean
public class TestSessionBean {
#PersistenceContext(unitName = "TestPU")
private EntityManager em;
public AuthenticationSessionBean() {
}
#GET
#Path("ep")
public String testEP() {
return "Hello from testEP()";
}
}
I cannot reach testEP through /<war_file_name>/separated/ep. Added ejb-jar.xml descriptor to WEB-INF/, still no success. I made another service with classes compiled and deployed directly in war file's WEB-INF/classes:
#ApplicationPath("/integrated")
public class TestRestApp extends Application {
}
#Path("/ep")
public class TestRestEp {
#GET
public String doGet() {
return "success";
}
}
Here I can reach doGet() through /<war_file_name>/integrated/ep.
Am I missing something? Can I deploy EJBs as separated jar files and expose them as REST web services with no wrapper?
UPDATE:
I annotated TestSessionBean with ApplicationPath("separated") and made it extending from javax.ws.rs.Application. Still getting 404 but this time It's different; 404 without "Not Found" body. If I make an endpoint path same as an endpoint in TestRestApp, e.g #Path("ep") It maps to endpoint in TestRestApp and I get "success" instead of "Hello from testEP()" by navigating to /<war_file_name>/separated/ep. If I annotate a method in TestSessionBean with a path not defined in TestRestApp result is 404. I cleared my web.xml out of servlet definitions and still same result.
First
Simply annotating an EJB class's method with JAX-RS annotations will not make the method a JAX-RS resource method. You need to make TestSessionBean a root resource class by annotating it with #Path, like you did with TestRestEp. Or you can make this class a Sub-Resource and have a Root resource pass the request to this class. I'd just stick to the former, if you have no idea what the latter means.
Second
Keeping in mind Wildfly (JBoss) modular architceture
Stated in the Resteasy (Wildfly's JAX-RS implementation) Reference Guide:
Resteasy and JAX-RS are automically loaded into your deployment's classpath, if and only if you are deploying a JAX-RS Application.
That being said, your web.xml doesn't create a JAX-RS application. You are basically depending on the (default JAX-RS specified) javax.ws.rs.Application servlet, which is only loaded if the JAX-RS module is loaded into the Server.
When you have an Application subclass, with the #ApplicationPath annotation, this creates a JAX-RS application and the JAX-RS module is loaded into the Server and the classpath will be scanned for resource classes.
Also stated in the JAX-RS spec:
The resources and providers that make up a JAX-RS application are configured via an application-supplied subclass of Application. An implementation MAY provide alternate mechanisms for locating resource classes and providers (e.g. runtime class scanning) but use of Application is the only portable means of configuration.
So I would stick to the Application subclass. No web.xml needed
We have a MDB listening to a Queue reading data and sending data to another Queue
#MessageDriven(
activationConfig = { #ActivationConfigProperty(
propertyName = "destinationType", propertyValue = "javax.jms.Queue"
) },
mappedName = "jms/dataQ")
public class DataMDB implements MessageListener {
#Resource(name="jms/dataQueueConnectionFactory")
private ConnectionFactory connectionfactory;
#Resource(name="jms/dataDestinationQ")
private Destination destination;
...
}
and an XML (ibm-ejb-jar-bnd.xml) with bean configuration
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar-bnd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://websphere.ibm.com/xml/ns/javaee"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-ejb-jar-bnd_1_0.xsd"
version="1.0">
<message-driven name="DataMDB">
<jca-adapter activation-spec-binding-name="eis/dataListenerMDB"
destination-binding-name="jms/dataQ" />
<resource-ref name="jms/dataQueueConnectionFactory"
binding-name="jms/dataQueueConnectionFactory" />
<resource-env-ref name="jms/dataDestinationQ"
binding-name="jms/dataDestinationQ" />
</message-driven>
</ejb-jar-bnd>
and Activation specification for this MDB on WebSphere
As I have seen the examples over Google, this is the typical example of MDB and WAS Activation setup.
We have a problem here as all the JNDI names seen here are hardcoded in Java code anotations as well as in the ibm-ejb-jar-bnd.xml file.
So is there a way that these JNDI names can be brought outside the EJB project, so we could build one project for all customers and customers are free to have their Standard JNDI Names.
Else we have to build different .ear for each customer and which is not ideal.
Thanks in advance people.
Any ideas are welcome.
All values defined in the ibm-ejb-jar-bnd.xml maps references to the actual JNDI names. This can be overridden for each of your customers during application installation (mapping references to JNDI names steps in the admin console), after application installation, or during installation using scripts.
The binding file (ibm-ejb-jar-bnd.xml) provides only 'default names', in case you dont want to change them during installation.
I need to modify a user session object (SessionScoped bean - CDI) in a Servlet, so I have to obtain that bean somehow. I used injection in the following way:
#Inject
private UserSession user;
where UserSession is the SessionScoped CDI bean. user methods are called from either doPost or doGet servlet methods.
This works perfectly; every time the #Inject annotation injects the appropriate UserSession bean, but I don't understand how this behavior is achieved.
I assumed that the beans, annotated with #Inject, are injected only once (when the object - Servlet instance in this case - is created), but it is obviously a wrong presumption.
So, when are these beans injected into the servlet? Per request? And how does this approach avoids conflicts (one servlet instance - multiple threads to deal with it) when there are multiple UserSession objects?
The CDI uses the proxy pattern. The injected instance is actually not the real instance, but a proxy which locates the real instance depending on the current context and delegates all methods to it (like as how EJBs work). The autogenerated class of your UserSession bean looks roughly like this:
public UserSessionCDIProxy extends UserSession implements Serializable {
public String getSomeProperty() {
UserSession instance = CDI.resolveItSomehow();
return instance.getSomeProperty();
}
public void setSomeProperty(String someProperty) {
UserSession instance = CDI.resolveItSomehow();
instance.setSomeProperty(someProperty);
}
}
This mechanism allows you to inject instances of a narrower scope in instances of a broader scope and allows you to still get the expected instance in the current context. The standard JSF #ManagedProperty annotation doesn't support it, simply because it does not use a proxy, but injects the desired instance directly. That's why it's not possible to inject something of a narrower scope by #ManagedProperty.
See also:
Backing beans (#ManagedBean) or CDI Beans (#Named)?
Get JSF managed bean by name in any Servlet related class
When using #EJB, does each managed bean get its own #EJB instance?
How to choose the right bean scope?
Your answer lies in the C of CDI, which stands for Contexts.
What happens is that not the actual bean is injected, but a proxy. This proxy is contextual and resolves to the actual session scoped bean depending on the context of the caller on who's behalf the proxy is executed.
It seems natural that a HttpServlet running in OSGi environment (i.e. registered in OSGi HttpService) would want to call some OSGi services to accomplish it's tasks. The question is how to obtain references to these OSGi service inside the servlet.
One way would be to inject dependencies into the HttpServlet instance that is being registered to the OSGi HttpService like this:
MyServlet servlet = new MyServlet();
servlet.setFooService(fooService);
httpService.registerServlet("/myservlet", servlet, initparams, context);
I'm not sure if this is a valid approach since in non-OSGi environment the servlet life-cycle is managed by the Web Container and hence the service reference would not be injected for the servlet instances created later on.
There is another way to solve this when using PAX Web as an implementation of the OSGi HttpService. PAX Web exports the OSGi BundleContext into the ServletContext as a special attribute "osgi-bundlecontext". The BundleContext can then be used to obtain necessary service references:
public void init(ServletConfig servletConfig) throws ServletException {
ServletContext context = servletConfig.getServletContext()
BundleContext bundleContext =
(BundleContext) context.getAttribute("osgi-bundlecontext");
ServiceReference serviceRef =
bundleContext.getServiceReference("com.foo.FooService")
}
However this approach is rather ugly and ties you to a concrete implementation of the OSGi HttpService. Do you know any other (and possibly better) solution to this problem?
If you use a setter for the dependency on the service, as you have shown, it can work outside of OSGi as well. You just need to use some other dependency injection mechanism. If there is none, you could provide a subclass that initializes the servlet using JNDI lookups or from the servlet context.
public class MyServlet_AdapterForMissingDI extends MyServlet{
public void init(ServletConfig config){
setFooService(getItFromSomewhere());
}
}
The point being that if you have DI capabilities that can inject setFooService, you can just use the same servlet in OSGi and elsewhere, if you do not (and still want to support this case), you provide an adapter.
On a related note, check out Felix SCR to configure your object's dependencies, and Pax Web Extender Whiteboard, which takes care of hooking your servlet up with the HttpService.
Specifically, without SCR and Whiteboard, you need to think about the case when the fooService becomes unavailable later, or the HttpService gets started after your servlet.
In these cases your servlet would have a reference to a dead service that prevents the bundle from being garbage-collected, or your servlet would not be registered with the HttpService.
Update: Here is the SCR descriptor I use for one of my servlets. SCR handles servlet instantiation, life-cycle, registration (via Whiteboard), and dependencies. There is no OSGi-specific code in the servlet. There is not even the need for a BundleActivator anymore (SCR registers all services):
<component name="oracle.statusServlet" >
<implementation class="mypackage.DataSourceStatusServlet"/>
<property name="service.description" value="Oracle DataSource status servlet" />
<property name="alias" value="/OracleDataSourceStatus" />
<property name="servlet-name" value="Oracle DataSource status servlet" />
<service>
<provide interface="javax.servlet.Servlet" />
</service>
<reference name="DATASOURCES"
interface="javax.sql.DataSource"
cardinality="0..n" policy="dynamic"
bind="bindDataSource" unbind="unbindDataSource"/>
</component>
The dependencies for the servlet are specified in the reference tag. SCR will do the service lookup and binding.
May be an old post and you already might have got the answer..
Are you launching felix or whatever OSGi container yourself. If that is the case you can set the bundle context as an attribute to the servlet context.
Whats wrong in using an http service by PAX. ultimately the thread management and other aspects are taken care of by the servlet container in which you run this http service.
You could inject the services into some object, which is then queried by the servlets.