As I didn't found any good tutorial for RestEasy + WildFly, I'm following this (https://robferguson.org/blog/2016/12/02/getting-started-with-resteasy/) tutorial and created a simple Hello World example of JAX-RS project using Resteasy 3.7.0.Final + Wildfly 13 + Java 8 + maven. This tutorial is actually using Jetty Http server.
Here is the Resource class :
#Path("/hello")
#Produces(MediaType.TEXT_PLAIN)
public class MessageResource {
#GET
#Path("/{param}")
public Response printMessage(#PathParam("param") String msg) {
String result = "Hello " + msg + "!";
return Response.status(200).entity(result).build();
}
}
And a class extending javax.ws.rs.core.Application - (all Rest resources will be registered by this class automatically)
#ApplicationPath("")
public class HelloWorldApplication extends Application {
public HelloWorldApplication() {}
#Override
public Set<Object> getSingletons() {
HashSet<Object> set = new HashSet<Object>();
set.add(new MessageResource());
return set;
}
}
In addition I have added jboss-deployment-structure.xml (which is not part of the tutorial) under WEB-INF to exclude the following modules :
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<exclude-subsystems>
<subsystem name="resteasy" />
</exclude-subsystems>
<exclusions>
<module name="javaee.api" />
<module name="javax.ws.rs.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs" />
</exclusions>
<local-last value="true" />
</deployment>
</jboss-deployment-structure>
I suppose this excludes resteasy modules bundled with Wildfly (please correct me if I'm wrong), since I'm using latest version of RestEasy in pom.xml :
<properties>
<resteasy.version>3.7.0.Final</resteasy.version>
</properties>
In maven configuration I have removed the jetty plugin, and rest of the part in pom.xml is same as in tutorial example here, except the RestEasy version.
https://github.com/Robinyo/resteasy/blob/master/examples/helloworld/pom.xml
But when I try to run the example with URL http://localhost:8080/hello/World, I'm getting 404 - Not Found error.
Is there any part missing in maven configuration or wildfly configuration?
Related
I am building a web app using java ee as backend and angular as frontend. As application server i have chosen Wildfly 13. As a build tool for backend I have chosen Maven. I have created this structure for the project :
-Project
---Project-ear
---Project-model
---Project-service
---Project-service-api
Neither of this modules packages a war file, I package 3 jars and in the end I assemble them in an ear file using
"Project-ear"
I have tried to consume the exposed service inside
"Project-service"
but I cant. It seems I don't set the right url. Can somebody help me please?
I have tried to access the following urls:
http://localhost:8080/Project-ear/Project-service/resources/test/testDtos/,
http://localhost:8080/resources/test/testDtos/
I have created the RestActivator class :
#ApplicationPath("/resources")
public class RestActivator extends Application {
}
And I have created a Resource class :
#Path("/test")
public class TestResource {
#GET
#Path("/testDtos")
#Produces(MediaType.APPLICATION_JSON)
public Response getWorkflowDiagram() {
TestDto testDto = new TestDto();
testDto.setFirstName("Test");
testDto.setLastName("Test");
return Response.ok(testDto).build();
}
}
I don't get any error message, I just can't access the URL endpoint.
When you configure the Application Server (Wildfly) and add the ear artifact, it creates a target folder in your ear-folder. There you can find a file called application.xml. You have to set the context-root like that:
<?xml version="1.0" encoding="UTF-8"?>
<application 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/application_7.xsd"
version="7">
<module>
<web>
<web-uri>test.war</web-uri>
<context-root>/api</context-root>
</web>
</module>
</application>
Your URL depends on what you have set in context-root in application.xml and how have set the application path in your root class (RestActivator).
In this example:
localhost:8080/api/resources
And to get access to your method in TestResource:
localhost:8080/api/resources/test/testDtos
I have a very simple web application running on WebSphere Application Server 18.0.0.2. The app is packaged into WAR and put under dropins (for simplicity).
My server.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<featureManager>
<feature>javaee-8.0</feature>
</featureManager>
<httpEndpoint id="defaultHttpEndpoint" httpPort="9080" httpsPort="9443" />
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>
<!-- THE JAR IS THERE (UNDER Liberty lib directory) -->
<library id="H2JDBCLib">
<fileset dir="${wlp.install.dir}/lib" includes="h2-1.4.197.jar"/>
</library>
<!-- AND THIS IS MY DATA SOURCE DEFINITION -->
<dataSource id="h2test" jndiName="jdbc/h2test">
<jdbcDriver libraryRef="H2JDBCLib"/>
<properties.db2.jcc databaseName="testdb" serverName="localhost" portNumber="8082" user="sa" />
</dataSource>
</server>
I have a very simple entity and a service (stateless EJB):
#Stateless
public class CustomerService {
#PersistenceContext(unitName = "h2test")
private EntityManager entityManager;
public List<Customer> getAllCustomers() {
return entityManager
.createNamedQuery(FIND_ALL, Customer.class)
.getResultList();
}
}
And my persistence.xml under META-INF looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="h2test" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/h2test</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
</persistence-unit>
</persistence>
I thought these simple configs should be enough to be able to deploy and run this hello-world-type of app. But I am getting an error at runtime:
[ERROR ] CNTR0019E: EJB throws an exception when invoking "getAllCustomers".
Details: javax.ejb.EJBException: The java:comp/env/com.my.app.service.CustomerService/entityManager reference of type javax.persistence.EntityManager for the null component in the my-app.war module of the my-app application cannot be resolved.
at com.ibm.wsspi.injectionengine.InjectionBinding.getInjectionObject(InjectionBinding.java:1489)
at [internal classes]
at com.my.app.service.EJSLocalNSLCustomerService_22d8d9f5.getAllCustomers(EJSLocalNSLCustomerService_22d8d9f5.java)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
EntityManager injection fails. From IBM's docs it's not clear what else should be done.
I have no other XML files (config files) in my app.
Am I missing something ?
The main issue I see is the <datasource> definition in server.xml, you have used the <properties.db2.jcc> element, which corresponds to the IBM DB2 JCC JDBC driver. Since Liberty does not have a dedicated <properties.h2> configuration, you must use the generic <properties> config element, as well as defining the DataSource class names on your <jdbcDriver> element.
The config should look something like this:
<dataSource id="h2test" jndiName="jdbc/h2test">
<!-- Define the DataSource class names on the <jdbcDriver> element -->
<jdbcDriver
javax.sql.XADataSource="org.h2.jdbcx.JdbcDataSource"
javax.sql.ConnectionPoolDataSource="org.h2.jdbcx.JdbcDataSource"
javax.sql.DataSource="org.h2.jdbcx.JdbcDataSource"
libraryRef="H2JDBCLib"/>
<!-- set the connection URL on the <properties> element.
this corresponds to the setURL() method on H2's JdbcDataSource class.
you may also list any properties here that have a corresponding setXXX method on H2's JdbcDataSource class -->
<properties URL="jdbc:h2:mem:testdb"/>
</dataSource>
Also, it would be better if you put your H2 JDBC driver somewhere under ${server.config.dir} or ${shared.resource.dir}, since ${wlp.install.dir}/lib is where jars of the Liberty runtime are. You don't want to mix your application jars in with those!
<library id="H2JDBCLib">
<fileset dir="${server.config.dir}" includes="h2-1.4.197.jar"/>
</library>
Lastly, ensure that your persistence.xml file is in the correct location in your WAR application. It should be at WEB-INF/classes/META-INF/persistence.xml
As an intermediate debugging step, you can check to make sure the DataSource is resolvable from your application like this:
#Resource(lookup = "jdbc/h2test")
DataSource ds;
// ...
ds.getConnection().close();
Once you get that part working, move onto injecting your EntityManager. Also, be sure to check ${server.config.dir}/logs/messages.log for more detailed error messages if things are going wrong.
i'm trying to learn how to separate java EE components.
i want to separate my jpa entities, ejbs, and web client into different projects
so i developed four different projects and compiled them in their own jars/wars
DomainEntities - contains my JPA entities
RemoteInterface - contains remote interfaces (i'm trying to separate the implementation)
RemoteEJB - implements the interface from RemoteInterface (project 2)
WebApp Client (i have a servlet that calls RemoteInterface to test if the ejb is working)
DomainEntities' jar structure is
DomainEntities
-META-INF
-MANIFEST.MF
-entities
-Student.class
RemoteEJB's jar structure is
RemoteEJB
-META-INF
-MANIFEST.MF
-persistence.xml
-sample
MyStatelessBean.class
The persistence.xml in RemoteEJB is (based on searching the web):
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="RemoteEJBPU" transaction-type="JTA"><provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/pwucdb</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<!--<jar-file>DomainEntities.jar</jar-file>-->
</persistence-unit>
</persistence>
the jta datasource is defined in my glassfish server.
the exclude-unlisted-classes and jar-file tags are based on my research on separating the jpa entities on a different jar. when i uncomment the jar-file tag, the ejb cannot be deployed to the server,so i comment it.
the servlet that calls uses the ejb is:
#WebServlet(name = "NewServlet", urlPatterns = {"/NewServlet"})
public class NewServlet extends HttpServlet {
#EJB
private MyRemoteInterface myRemoteInterface;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println(myRemoteInterface.getMessage());
out.println("Num of records retrived: " + myRemoteInterface.getStudents().size());
}
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
when i try to access the servlet i am having this warning/error
WARNING: The collection of metamodel types is empty. Model classes may not have been found during entity search for Java SE and some Java EE container managed persistence units. Please verify that your entity classes are referenced in persistence.xml using either <class> elements or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element
WARNING: A system exception occurred during an invocation on EJB MyStatelessBean method public java.util.List sample.MyStatelessBean.getStudents()
javax.ejb.EJBException
.....
Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Error compiling the query [SELECT s FROM Student s]. Unknown entity type [Student].
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1477)
if i comment out exclude-unlisted-classes tag, uncomment the jar-file tag and deploy. i get this server warning:
a jar-file [DomainEntities.jar] specified in persistence.xml is not found
The server also looked for a file called [...\build\DomainEntities_jar].
I recommend to bundle that JAR's and WAR's into an EAR-Archive.
If you use maven you could add this plugin to the EAR's pom:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.8</version>
<configuration>
<version>6</version>
<encoding>${project.build.sourceEncoding}</encoding>
<defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
<fileNameMapping>no-version</fileNameMapping>
<displayName>${project.artifactId}</displayName>
<modules>
<jarModule>
<groupId>${project.groupId}</groupId>
<artifactId>domain</artifactId>
</jarModule>
<ejbModule>
<groupId>${project.groupId}</groupId>
<artifactId>ejb</artifactId>
</ejbModule>
</modules>
</configuration>
</plugin>
Thus all libs of the EAR will be copied into /APP-INF/lib.
In this scope its now accessible from the EJB view. Just call it like this:
<jar-file>APP-INF/lib/domain.jar</jar-file>
Note the
<fileNameMapping>no-version</fileNameMapping>
thats really important if you want to change the version of your domain-archive frequently otherwise you have to edit the EJB's persistence.xml all the time ;)
Some additions:
If you bundle everything into an EAR-Archive its not neccessary to use Remote Interfaces anymore. You just need Local Interfaces for that. Thats a good thing because accessing an EJB over Remote Interfaces is much slower than accessing them with Local Interfaces. Thats because Java serializes all objects to send them over the network.
I'm trying to deploy a simple web application under JBoss AS 7.1 which comes bundled with resteasy. According to the documentation all that is needed is (at bare minimum) is an empty web.xml, a class with annotated #ApplicationPath("/mypath") and #Path("/other_stuff") for your other classes
The documentation I'm following is here:
https://docs.jboss.org/author/display/AS7/JAX-RS+Reference+Guide
https://docs.jboss.org/author/display/AS7/Java+API+for+RESTful+Web+Services+(JAX-RS)
Still, when I hit:
host:8080/warname/applicationpath/path
I receive a 404 error on the webpage but nothing in the logs.
Is there a configuration file I need to change in order for JAX-RS to work?
Thank you in advance for any help!
Empty web.xml will do.
Just add some resteasy dependency to your classpath. For instance, if you use maven you can add this to your pom.xml:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
<scope>provided</scope> <!-- provided if youre deploying to jboss as 7.1 -->
</dependency>
Then set up the application using only this class:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/rest")
public class JaxRsActivator extends Application {
}
Just to make sure, add a resource like this:
#Path("/hello")
public class HelloResource {
#GET
#Produces("text/plain")
public String helloResource() {
return "Hello! It's "+new Date();
}
}
And that's all you need. Deploy it at a JBoss AS 7.1 and get to it, say:
http://127.0.0.1:8080/mywarname/rest/hello
Edit:
I have created a java war maven project with the bare minimum strucutre:
-pom.xml
-src
|
--main
|
--java
|
--rest
|
--HelloResource.java
--JaxRsActivator.java
I called it simpleRest as seen below. All the archives are exactly as shown:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>simpleRest</groupId>
<artifactId>simpleRest</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.1.GA</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<!-- So maven doesn't require web.xml -->
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
HelloResource.java
package rest;
import java.util.Date;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
#Path("/hello")
public class HelloResource {
#GET
#Produces("text/plain")
public String helloResource() {
return "Hello! It's "+new Date();
}
}
JaxRsActivator.java:
package rest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/rest")
public class JaxRsActivator extends Application {
}
This generates a simpleRest.war (through mvn clean package). I then deploy it to a freshly installed JBoss AS 7.1.1.Final. As you can see, no reference is made to JAX-RS in the log during the deploy:
22:48:08,677 INFO [org.jboss.as.server.deployment] (MSC service thread 1-1) JBAS015876: Starting deployment of "simpleRest.war"
22:48:09,318 INFO [org.jboss.web] (MSC service thread 1-4) JBAS018210: Registering web context: /simpleRest
22:48:09,492 INFO [org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018559: Deployed "simpleRest.war"
After that, the URL is available as expected:
http://127.0.0.1:8080/simpleRest/rest/hello
Notice that everything else gives a 404 error. But it is a different kind of 404.
http://127.0.0.1:8080/simpleRest/ gives:
HTTP Status 404 - /simpleRest/
That is a page not found error. On the other hand, http://127.0.0.1:8080/simpleRest/rest gives:
HTTP Status 404 - Could not find resource for relative : / of full path: http://127.0.0.1:8080/simpleRest/rest
That is a resource (REST service) not found error. This way you know JAX-RS is acting, though it did not have a handler for that path.
Take a look at jboss quickstarts: http://www.jboss.org/jdf/quickstarts/jboss-as-quickstart/
You can get them from http://www.jboss.org/jbossas/downloads
These are working out of box. For helloword-rs quickstart I can see web.xml with content:
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
and also comment:
One of the way of activating REST Servises is adding these lines, the server is responsible for adding the corresponding servlet automatically. If the src folder, org.jboss.as.quickstarts.rshelloworld.HelloWorld class has the Annotations to receive REST invocation.
If you are using JBoss AS 7.1, you also need to add the "resteasy.resources" context parameter. You also need to send the init-param to the HttpServletDispatcher servlet.
I found the solution at the following link : http://www.javaroots.com/2013/05/creating-rest-services-with-rest-easy.html
Just one typo error(maybe!!) in the above link. In the "RootRestService" change the method signature of the getClasses() method to public Set<Class<?>> getClasses().
Thats it! It solved my 3 day headache. Hope it helps you too!! :)
Even though acdcjunior's answer is great and very thorough, i'd like to reinfoce Andrzej's answer.
It works as a charm and it is by far the more straightforward one. The JBoss quickstart samples (also available on GitHub) are always a great resource to answer questions like this one.
I want to test an Java EE application with Arquillian. A simple setup is working to put some test classes onto the server managed by Arquillian to test them.
Now, I want to use a EAR file which was build during the build process. With
#Deployment
public static EnterpriseArchive createDeployment() {
File earFile = ...
EnterpriseArchive archive = ShrinkWrap.createFromZipFile(EnterpriseArchive.class, earFile);
return archive;
}
I am able to put the EAR via Arquillian to JBoss. I see the deployment and there are no errors during deployment. Only Arquillian returns with an error that it can not find the test class, which is obviously ok.
Now is the question where to put the test class to. I can put the test class into the test.war put into the EAR by Arquillian, but I get an ArquillianServletRunner not found exception. When I put the test classes into JAR files as module or library the test classes are not found when put as module or the injects do not work when put as libraries due to dependency issues.
Where to I have to put the test classes to???
My arquillian.xml:
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<defaultProtocol type="Servlet 3.0" />
<container qualifier="jboss7" default="true">
<configuration>
<property name="jbossHome">${cargo.dir}/jboss-as-dist-7.1.1.Final/jboss-as-7.1.1.Final</property>
</configuration>
</container>
<engine>
<property name="deploymentExportPath">target/deployments</property>
</engine>
</arquillian>
You can run your test class on the client side using #Deployment(testable = false), but this has the downside that you cannot use the persistence extention (and maybe others) at the moment.
My example code:
#RunWith(Arquillian.class)
public class PersonWebServiceIT {
private PersonWebService service;
#Deployment(testable = false)
public static Archive<?> createDeployment() {
return ShrinkWrap
.create(ZipImporter.class, "test.war")
.importFrom(
new File("simple-webservice-1.0.0-SNAPSHOT.war"))
.as(WebArchive.class);
}
#Test
public void testFindPersons(#ArquillianResource URL deploymentUrl) {
....
}
}
Putting the test classes to the war inside the ear did work for me
WebArchive war = ear.getAsType(WebArchive.class, "/mywarname.war");
war.addClass(MyTestClass.class);
When I am dealing with existing EAR, I prefer to separate the WAR that runs the tests, from the actual tests that I put in special JAR along with other testing EJBs. I have posted an example how do I do it with the necessary application.xml manipulation under similar question: https://stackoverflow.com/a/17036383/1667977