Java rest server : make a unit test - java

I try to make a unit test for a standalone rest server. If I run the rest server it work nice. But I don't know how to make the UnitTest running.
My main class :
public class Main {
private static final int DEFAULT_PORT = 8080;
private final int serverPort;
private final Server restServer;
public Main(final int serverPort) throws Exception {
this.serverPort = serverPort;
restServer = configureServer();
restServer.start();
restServer.join();
}
public void close() throws Exception {
if (restServer != null) {
restServer.stop();
}
}
private Server configureServer() {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.packages(Main.class.getPackage().getName());
resourceConfig.register(JacksonFeature.class);
ServletContainer servletContainer = new ServletContainer(resourceConfig);
ServletHolder sh = new ServletHolder(servletContainer);
Server server = new Server(serverPort);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.addServlet(sh, "/*");
server.setHandler(context);
return server;
}
public static void main(String[] args) throws Exception {
int serverPort = DEFAULT_PORT;
if (args.length >= 1) {
try {
serverPort = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new Main(serverPort);
}
The resource class :
#Path("builder")
public class ReportBuilderResource {
#POST
#Path("/build")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.TEXT_PLAIN})
public String makeReport(final ReportDescription reportDescription) {
return reportDescription.getName();
}
}
My Unit test class :
public class ReportBuilderResourceTest extends JerseyTest {
#Override
public AppDescriptor configure() {
return new WebAppDescriptor.Builder()
.initParam(WebComponent.RESOURCE_CONFIG_CLASS, ClassNamesResourceConfig.class.getName())
.initParam(ClassNamesResourceConfig.PROPERTY_CLASSNAMES, ReportBuilderResource.class.getName())
.build();
}
#Test
public void testBuildReport() throws Exception {
System.out.println("Test Build Report");
ReportDescription reportDescription = new ReportDescription();
JSONObject jsonObject = new JSONObject(reportDescription);
resource().path("builder/").post(jsonObject.toString());
}
And the output log :
juil. 31, 2015 9:48:53 AM com.sun.jersey.test.framework.spi.container.inmemory.InMemoryTestContainerFactory$InMemoryTestContainer <init>
INFO: Creating low level InMemory test container configured at the base URI http://localhost:9998/
Running com.fdilogbox.report.serveur.ReportBuilderResourceTest
juil. 31, 2015 9:48:53 AM com.sun.jersey.test.framework.spi.container.inmemory.InMemoryTestContainerFactory$InMemoryTestContainer start
INFO: Starting low level InMemory test container
juil. 31, 2015 9:48:53 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.19 02/11/2015 03:25 AM'
Test Build Report
juil. 31, 2015 9:48:54 AM com.sun.jersey.api.container.filter.LoggingFilter filter
INFO: 1 * Server in-bound request
1 > POST http://localhost:9998/builder/
1 > Content-Type: text/plain
1 >
{"name":null,"report":null}
juil. 31, 2015 9:48:54 AM com.sun.jersey.api.container.filter.LoggingFilter$Adapter finish
INFO: 1 * Server out-bound response
1 < 405
1 < Allow: OPTIONS
1 <
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.497 sec <<< FAILURE! - in com.fdilogbox.report.serveur.ReportBuilderResourceTest
testBuildReport(com.fdilogbox.report.serveur.ReportBuilderResourceTest) Time elapsed: 0.496 sec <<< ERROR!
com.sun.jersey.api.client.UniformInterfaceException: Client response status: 405
at com.sun.jersey.api.client.WebResource.voidHandle(WebResource.java:709)
at com.sun.jersey.api.client.WebResource.post(WebResource.java:238)
at com.fdilogbox.report.serveur.ReportBuilderResourceTest.testBuildReport(ReportBuilderResourceTest.java:47)
I think the server is not "running" for the test. How can'I do this?

Your resource listens to "/builder", but the only method inside listens to "/builder/build". Since there is no method listening to #post and "/builder" you get a Http 405 - Method not allowed.
You can either remove #Path("/build") from the "makeReport" method, or change resource().path("builder/build")... in your test.
Btw:
You only need to add one of these contatiner adapters and this snippet to run a unit tests with Jersey 2:
public class ReportBuilderResourceTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(ReportBuilderResource.class);
}
...
}

Related

Jersey Test fails when running method from main package -> NullPointerException at target(...)

So I have tested it with this example:
Path: pkg > src > test > java > rest > SimpleJerseyTest
public class SimpleJerseyTest extends JerseyTest {
#Path("hello")
public static class HelloResource {
#GET
public String getHello() {
return "Hello World!";
}
}
#Override
protected Application configure() {
return new ResourceConfig(HelloResource.class);
}
#Test
public void test() {
Response response = target("hello").request().get();
assertEquals("Http Response should be 200: ", Response.Status.OK.getStatusCode(), response.getStatus());
assertEquals("Http Content-Type should be: ", MediaType.TEXT_HTML, response.getHeaderString(HttpHeaders.CONTENT_TYPE));
String content = response.readEntity(String.class);
System.out.println("Gotten response: " + content);
assertEquals("Content of ressponse is: ", "Hello World!", content);
}
}
This is the output of the test:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Jul 24, 2020 11:29:07 AM org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory$GrizzlyTestContainer <init>
INFO: Creating GrizzlyTestContainer configured at the base URI http://localhost:9998/
Jul 24, 2020 11:29:08 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9998]
Jul 24, 2020 11:29:08 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Gotten response: Hello World!
Jul 24, 2020 11:29:08 AM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [localhost:9998]
Process finished with exit code 0
But when I put the REST service class at in the main package, it doesnt work anymore (NullPointerException):
Package: pkg > src > main > java > rest > BookService
#Path("books")
public class BookService {
#GET
public String getAll() {
return "test";
}
}
And then the Test:
Package: pkg > src > test > rest > BookServiceTest
class BookServiceTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(BookService.class);
}
#Test
void get() {
Response response = target("books").request().get();
}
}
This is the output of the test:
java.lang.NullPointerException
at org.glassfish.jersey.test.JerseyTest.target(JerseyTest.java:541)
at org.glassfish.jersey.test.JerseyTest.target(JerseyTest.java:555)
at rest.BookServiceTest.get(BookServiceTest.java:21)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
// ...
Process finished with exit code -1
What is the difference? Does anyone have a clue, why it works with a static inner class, but not with a external one?
Figured it out - I used the wrong import.
Instead of import org.junit.jupiter.api.Test it should be import org.junit.Test.

Cannot run parallel tests with #Factory testng annotation on java

So I use #Factory to run one test with 5 different emails, but I get wrong number of arguments exception and I can't see the full error trace on console. I use TestNG. Here's my code:
package com.task.lab.facadetask;
public class GmailTest {
private WebDriver driver;
private static List<User> usersList;
static List<TestMessage> mess;
public GmailTest(){}
#Factory(dataProviderClass = GmailTest.class, dataProvider = "getData")
public GmailTest(WebDriver driver,List<User> usersList, List<TestMessage> mess ){
this.driver = driver;
GmailTest.usersList = usersList;
GmailTest.mess = mess;
}
#BeforeMethod
public void setUpDriver(){
driver = DriverObject.getDriver();
}
#DataProvider
public static Object[][] getData() {
File usersXml = new File("src\\\\main\\\\java\\\\com\\\\task\\\\lab\\\\facadetask\\\\testdata\\\\users.xml");
try {
usersList = JAXB.unmarshal(usersXml);
} catch (JAXBException e) {
e.printStackTrace();
}
TestMessages messages = UnMarshell.unmarshaller();
assert messages != null;
mess = messages.getTestMessages();
return new Object[][]{
{mess.get(0), usersList.get(0)},
{mess.get(1), usersList.get(1)},
{mess.get(2), usersList.get(2)},
};
}
#Test
public void testGmail(TestMessage message, User users) {
String gmailURL = "https://accounts.google.com/signin";
driver.get(gmailURL);
Login loginPage = new Login();
loginPage.login(users.getEmail(), users.getPassword());
GmailMessage gmailPage = new GmailMessage();
gmailPage.sendMessage(message.getReceiver(), message.getSubject(), message.getMessage());
gmailPage.removeMessage();
Assert.assertTrue(gmailPage.isRemoved());
}
#AfterMethod
public void quitBrowser(){
try{
driver.close();
}finally{
driver.quit();
}
}
}
My assumption is that it could be caused by changing the original non static lists of users and messages to static, but DataProvider method needs to be static. Could someone guide me on what am I doing wrong?
UPD:So, I removed #BeforeMethod and included driver in #DataProvider as Krishnan suggested but it gives me the same error, wrong number of arguments. Here's what the DataProvider starts with for now:
#DataProvider
public static Object[][] getData() {
driver = DriverObject.getDriver();
File usersXml = new File //The rest remains the same
Also, I tried to initialize driver in BeforeMethod but in this case Test doesn't see it. It looks like this:
#BeforeMethod
public void setUpDriver(){
WebDriver driver = DriverObject.getDriver();
}
Maybe someone can provide me with a valid analogue of Factory so I can run 5 parallel tests simultaniously? I am open for suggestions.
Your factory method is defined to accept 3 arguments.
#Factory(dataProviderClass = GmailTest.class, dataProvider = "getData")
public GmailTest(WebDriver driver,List<User> usersList, List<TestMessage> mess ){
this.driver = driver;
GmailTest.usersList = usersList;
GmailTest.mess = mess;
}
Your data provider is only providing 2 parameters. WebDriver is not being provided by your data provider.
You can do one of the following :
Either enhance your data provider to include the WebDriver object via a call to DriverObject.getDriver() and remove your #BeforeMethod method (or)
Alter your constructor's signature to not accept a WebDriver instance, but initialize the class's WebDriver instance via the #BeforeMethod.
That should fix your problem.
EDIT: The question has been updated. So updating my answer as well.
Looking at the updates to the question, the answer is still the same on a high level. Now including a sample as well, which explains the answer.
Mocking how the User class could look like
import java.util.ArrayList;
import java.util.List;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static List<User> newUsers(String... names) {
List<User> users = new ArrayList<>();
for (String name : names) {
users.add(new User(name));
}
return users;
}
}
Mocking how the TestMessage class could look like
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TestMessage {
private String text;
public TestMessage(String text) {
this.text = text;
}
public String getText() {
return text;
}
public static List<TestMessage> newMessages(int howMany) {
List<TestMessage> msgs = new ArrayList<>();
for (int i = 0; i < howMany; i++) {
msgs.add(new TestMessage(UUID.randomUUID().toString()));
}
return msgs;
}
}
Here's how the test class should look like
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
public class GmailTest {
private WebDriver driver;
private List<User> users;
private List<TestMessage> testMessages;
#Factory(dataProvider = "getData")
public GmailTest(WebDriver driver, List<User> users, List<TestMessage> testMessages) {
this.driver = driver;
this.users = users;
this.testMessages = testMessages;
}
#Test
public void testMethod() {
Assert.assertNotNull(driver);
Assert.assertNotNull(users);
Assert.assertNotNull(testMessages);
}
#AfterClass
public void cleanupDrivers() {
if (driver != null) {
driver.quit();
}
}
#DataProvider(name = "getData")
public static Object[][] getData() {
List<User> users = User.newUsers("Jack", "Daniel", "John");
int size = users.size();
List<TestMessage> testMessages = TestMessage.newMessages(size);
Object[][] data = new Object[size][1];
for (int i = 0; i < size; i++) {
data[i] = new Object[]{new FirefoxDriver(), Collections.singletonList(users.get(i)),
Collections.singletonList(testMessages.get(0))};
}
return data;
}
}
Here's the execution logs
1518271888011 geckodriver INFO geckodriver 0.19.1
1518271888131 geckodriver INFO Listening on 127.0.0.1:14727
1518271888627 mozrunner::runner INFO Running command: "/Applications/Firefox.app/Contents/MacOS/firefox-bin" "-marionette" "-profile" "/var/folders/mj/81r6v7nn5lqgqgtfl18spfpw0000gn/T/rust_mozprofile.5mkpumai11hO"
1518271889362 Marionette INFO Enabled via --marionette
2018-02-10 19:41:30.336 plugin-container[53151:969522] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0xad33, name = 'com.apple.tsm.portname'
See /usr/include/servers/bootstrap_defs.h for the error codes.
1518271890773 Marionette INFO Listening on port 52891
1518271890793 Marionette WARN TLS certificate errors will be ignored for this session
Feb 10, 2018 7:41:30 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
1518271890961 geckodriver INFO geckodriver 0.19.1
1518271891060 geckodriver INFO Listening on 127.0.0.1:6639
2018-02-10 19:41:31.225 plugin-container[53152:969613] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0xaa37, name = 'com.apple.tsm.portname'
See /usr/include/servers/bootstrap_defs.h for the error codes.
1518271891259 mozrunner::runner INFO Running command: "/Applications/Firefox.app/Contents/MacOS/firefox-bin" "-marionette" "-profile" "/var/folders/mj/81r6v7nn5lqgqgtfl18spfpw0000gn/T/rust_mozprofile.npquNnysdwGI"
1518271891832 Marionette INFO Enabled via --marionette
2018-02-10 19:41:32.786 plugin-container[53155:969741] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0xab3f, name = 'com.apple.tsm.portname'
See /usr/include/servers/bootstrap_defs.h for the error codes.
1518271893243 Marionette INFO Listening on port 53150
1518271893342 Marionette WARN TLS certificate errors will be ignored for this session
Feb 10, 2018 7:41:33 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
1518271893499 geckodriver INFO geckodriver 0.19.1
1518271893590 geckodriver INFO Listening on 127.0.0.1:48408
2018-02-10 19:41:33.681 plugin-container[53156:969822] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0x7c37, name = 'com.apple.tsm.portname'
See /usr/include/servers/bootstrap_defs.h for the error codes.
1518271893810 mozrunner::runner INFO Running command: "/Applications/Firefox.app/Contents/MacOS/firefox-bin" "-marionette" "-profile" "/var/folders/mj/81r6v7nn5lqgqgtfl18spfpw0000gn/T/rust_mozprofile.65SomKttNwQP"
1518271894377 Marionette INFO Enabled via --marionette
2018-02-10 19:41:35.326 plugin-container[53159:969958] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0x1523b, name = 'com.apple.tsm.portname'
See /usr/include/servers/bootstrap_defs.h for the error codes.
1518271895785 Marionette INFO Listening on port 53451
1518271895824 Marionette WARN TLS certificate errors will be ignored for this session
Feb 10, 2018 7:41:35 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
[GFX1-]: Receive IPC close with reason=AbnormalShutdown
1518271896172 addons.xpi WARN Exception running bootstrap method shutdown on activity-stream#mozilla.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIObserverService.removeObserver]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: resource://activity-stream/lib/SnippetsFeed.jsm :: uninit :: line 125" data: no] Stack trace: uninit()#resource://activity-stream/lib/SnippetsFeed.jsm:125 < onAction()#resource://activity-stream/lib/SnippetsFeed.jsm:141 < _middleware/</<()#resource://activity-stream/lib/Store.jsm:51 < Store/this[method]()#resource://activity-stream/lib/Store.jsm:30 < uninit()#resource://activity-stream/lib/Store.jsm:153 < uninit()#resource://activity-stream/lib/ActivityStream.jsm:278 < uninit()#resource://gre/modules/addons/XPIProvider.jsm -> jar:file:///Applications/Firefox.app/Contents/Resources/browser/features/activity-stream#mozilla.org.xpi!/bootstrap.js:80 < shutdown()#resource://gre/modules/addons/XPIProvider.jsm -> jar:file:///Applications/Firefox.app/Contents/Resources/browser/features/activity-stream#mozilla.org.xpi!/bootstrap.js:196 < callBootstrapMethod()#resource://gre/modules/addons/XPIProvider.jsm:4406 < observe()#resource://gre/modules/addons/XPIProvider.jsm:2270 < GeckoDriver.prototype.quit()#driver.js:3381 < despatch()#server.js:560 < execute()#server.js:534 < onPacket/<()#server.js:509 < onPacket()#server.js:508 < _onJSONObjectReady/<()#transport.js:500
===============================================
Default Suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================
Process finished with exit code 0

Failed to open channel for context EJBReceiverContext

When I run the ejb3 remote client the service deployed in the server, WildFly8.1.0 Final I get the expected results. But now I am trying to benchmark this and when I run the client through the JMH #Benchmark method it runs fine for but after some iterations in warm up process it gives the following error.
Mar 04, 2015 12:29:15 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext#13689c
56, receiver=Remoting connection EJB receiver [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection#25d8cd6c,channel=jboss.ejb,nodename=mast
er:server-three]} on channel Channel ID c1426f51 (outbound) of Remoting connection 3836a177 to /192.168.17.150:8330
Result is given by [ host: isurug ], [ Result: ONE ]
____ejb:poc_ear-1.0/poc_ejbs-1.0//DataGridServiceImpl!com.oms.ejbs.stateless.DataGridService
Mar 04, 2015 12:29:15 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
Mar 04, 2015 12:29:15 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext#538da6
b, receiver=Remoting connection EJB receiver [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection#25d8cd6c,channel=jboss.ejb,nodename=maste
r:server-three]} on channel Channel ID bed866b3 (outbound) of Remoting connection 3836a177 to /192.168.17.150:8330
Result is given by [ host: isurug ], [ Result: ONE ]
____ejb:poc_ear-1.0/poc_ejbs-1.0//DataGridServiceImpl!com.oms.ejbs.stateless.DataGridService
Mar 04, 2015 12:29:15 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver$1 handleFailed
ERROR: Failed to open channel for context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext#62af85f9, receiver=Remoting connection EJB rece
iver [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection#25d8cd6c,channel=jboss.ejb,nodename=master:server-three]}
org.jboss.remoting3.ProtocolException: Too many channels open
at org.jboss.remoting3.remote.RemoteConnectionHandler.handleOutboundChannelOpen(RemoteConnectionHandler.java:185)
at org.jboss.remoting3.remote.RemoteConnectionHandler.open(RemoteConnectionHandler.java:318)
at org.jboss.remoting3.ConnectionImpl.openChannel(ConnectionImpl.java:75)
at org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection.openChannel(ConnectionPool.java:227)
at org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver.associate(RemotingConnectionEJBReceiver.java:169)
at org.jboss.ejb.client.EJBClientContext.registerEJBReceiver(EJBClientContext.java:375)
at org.jboss.ejb.client.EJBClientContext.registerEJBReceiver(EJBClientContext.java:327)
at org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector.setupEJBReceivers(ConfigBasedEJBClientContextSelector.java:159)
at org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector.getCurrent(ConfigBasedEJBClientContextSelector.java:115)
at org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector.getCurrent(ConfigBasedEJBClientContextSelector.java:47)
at org.jboss.ejb.client.EJBClientContext.getCurrent(EJBClientContext.java:271)
at org.jboss.ejb.client.EJBClientContext.requireCurrent(EJBClientContext.java:281)
at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:176)
at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:144)
at com.sun.proxy.$Proxy1.getData(Unknown Source)
at com.oms.client.EJBInvoker.invokeDataGridService(EJBInvoker.java:71)
at com.test.MyBenchmark.testEjbClient(MyBenchmark.java:43)
at com.test.generated.MyBenchmark_testEjbClient.testEjbClient_avgt_jmhLoop(MyBenchmark_testEjbClient.java:160)
at com.test.generated.MyBenchmark_testEjbClient.testEjbClient_AverageTime(MyBenchmark_testEjbClient.java:129)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.openjdk.jmh.runner.LoopBenchmarkHandler$BenchmarkTask.call(LoopBenchmarkHandler.java:210)
at org.openjdk.jmh.runner.LoopBenchmarkHandler$BenchmarkTask.call(LoopBenchmarkHandler.java:192)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Error says too many channels open, so is it like I should close the channel after a call? Or what is the cause for this and what is the solution.
Following is my remote client.
public class EJBInvoker {
/**
* Invoke Data Grid Service.
*/
public void invokeDataGridService() {
EJBInvoker client = null;
try {
for (int i = 0; i < 1; i++) {
client = new EJBInvoker();
String dataGridServiceJndi = "ejb:poc_ear-1.0/poc_ejbs-1.0//DataGridServiceImpl!com.oms.ejbs.stateless.DataGridService";
DataGridService dataGridService = (DataGridService) client.lookupRemoteEJB(dataGridServiceJndi);
String result = dataGridService.getData("1");
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Lookup for Remote EJB
*
* #return {#link Object} instance.
* #throws NamingException
* naming exception.
*/
#SuppressWarnings({"unchecked", "rawtypes"})
private Object lookupRemoteEJB(String jndiName) throws NamingException {
Context context = null;
try {
Properties clientProperties = new Properties();
clientProperties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
clientProperties.put("remote.connections", "default");
clientProperties.put("remote.connection.default.port", "8330");
clientProperties.put("remote.connection.default.host", "192.168.17.150");//192.168.16.40
clientProperties.put("remote.connection.default.username", "admin");
clientProperties.put("remote.connection.default.password", "1qaz2wsx#");
clientProperties.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
EJBClientConfiguration ejbClientConfiguration = new PropertiesBasedEJBClientConfiguration(clientProperties);
ContextSelector<EJBClientContext> contextSelector = new ConfigBasedEJBClientContextSelector(ejbClientConfiguration);
EJBClientContext.setSelector(contextSelector);
Properties properties = new Properties();
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
context = new InitialContext(properties);
System.out.println("____" + jndiName);
return context.lookup(jndiName);
} finally {
if (context != null) {
context.close();
}
}
}
}
We can't create too many InitialContext. In my application, I made InitialContext as a static variable; so in the whole app, only a instance.
That solved the issue.

SSL with Grizzly and Jersey

I'm trying to get grizzly to use SSL encryption and still work fine with Jersey. I've looked all over the Internet, and I find all kinds of different attempts at SSL with Grizzly and Jersey. Seems like there are different ways of doing it depending on which version you are using, and how you decided to implement it. I haven't been able to get any examples to work with my code yet.
Here's how I start up my server:
static HttpServer startSecureServer() throws IOException{
ResourceConfig rc=new PackagesResourceConfig("server.grizzlyresources");
SSLContextConfigurator sslCon=new SSLContextConfigurator();
sslCon.setKeyStoreFile(ConfigLoader.getKeystoreLocation()); // contains server keypair
sslCon.setKeyStorePass(ConfigLoader.getKeystorePassword());
System.out.println("Starting server on port "+ConfigLoader.getHttpsServerPort());
HttpServer secure=GrizzlyServerFactory.createHttpServer(BASE_URI_SECURED, rc);
secure.stop();
HashSet<NetworkListener> lists=new HashSet<NetworkListener>(secure.getListeners());
for (NetworkListener listener : lists){
listener.setSecure(true);
SSLEngineConfigurator ssle=new SSLEngineConfigurator(sslCon);
listener.setSSLEngineConfig(ssle);
secure.addListener(listener);
System.out.println(listener);
}
secure.start();
return secure;
}
private static URI getBaseURISecured(){
return UriBuilder.fromUri("https://0.0.0.0/").port(ConfigLoader.getHttpsServerPort()).build();
}
private static final URI BASE_URI_SECURED = getBaseURISecured();
ConfigLoader loads in information from a config file. When I run this code, it starts up the server, it finds the resources in the server.grizzlyresources package, and it works great! Except for one thing. The server isn't secured. I can telnet into it and send an HTTP request in plain text for one of my resources, and it will return it. So the code works for starting up the server, but the whole SSL part of it is just being bypassed. Any ideas how to fix this or why it might be doing this?
Here's the output to the console when I run it:
Starting server on port 9999
Jan 13, 2014 9:51:08 AM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
server.grizzlyresources
Jan 13, 2014 9:51:08 AM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
class server.grizzlyresources.SessionResource
class server.grizzlyresources.LoginResource
Jan 13, 2014 9:51:08 AM com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Jan 13, 2014 9:51:08 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.12 02/15/2012 04:51 PM'
Jan 13, 2014 9:51:09 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [0.0.0.0:9999]
Jan 13, 2014 9:51:09 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Jan 13, 2014 9:51:09 AM org.glassfish.grizzly.http.server.NetworkListener stop
INFO: Stopped listener bound to [0.0.0.0:9999]
NetworkListener{name='grizzly', host='0.0.0.0', port=9999, secure=true}
Jan 13, 2014 9:51:09 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [0.0.0.0:9999]
Jan 13, 2014 9:51:09 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
I'm using Grizzly 2.2.1, and Jersey 1.12.
Thanks a bunch!
Sorry to take so long to post this up here. Alexey's answer led me to the working solution, which is a lot like Wolfgang Fahl's code. Here's what I ended up with:
static HttpServer startSecureServer() throws IOException
{
System.out.println("Starting server on port " + ConfigLoader.getHttpsServerPort());
ResourceConfig rc = new PackagesResourceConfig("com.kinpoint.server.grizzlyresources");
SSLContextConfigurator sslCon = new SSLContextConfigurator();
sslCon.setKeyStoreFile(ConfigLoader.getKeystoreLocation()); // contains server keypair
sslCon.setKeyStorePass(ConfigLoader.getKeystorePassword());
HttpHandler hand = ContainerFactory.createContainer(HttpHandler.class, rc);
HttpServer secure = GrizzlyServerFactory.createHttpServer(BASE_URI_SECURED, hand, true,
new SSLEngineConfigurator(sslCon, false, false, false));
return secure;
}
The second parameter in the SSLEngineConfigurator tells it not to use client mode. That was what was messing me up. Thanks for the help.
IMO you can use different Factory method to initialize secured Grizzly HttpServer:
HttpServer secure = GrizzlyServerFactory.createHttpServer(BASE_URI_SECURED,
ContainerFactory.createContainer(HttpHandler.class, rc),
true,
new SSLEngineConfigurator(sslCon));
If you initialize the server like this, you don't need to stop and reconfigure it again.
Hope this will help.
I have a nice and tested example using Grizzly 2.3.3 in:
https://github.com/danielnuriyev/scriptedstuff/tree/master/src/com/scriptedstuff/server
The following code works with Grizzly 2.3.7 and I am using Jersey 1.18 - this includes code for SSL Client Authentication - if you don't have the keystores this feature will simply be ignored.
/**
* create a Server based on an url and possibly a ResourceConfig
*
* #param url
* #param rc
* #param secure
* - true if SSL should be used
* #param contextPath
* #return
* #throws Exception
*/
public HttpServer createHttpServer(String url, ResourceConfig rc,
boolean secure, String contextPath) throws Exception {
// HttpServer result = GrizzlyServerFactory.createHttpServer(url, rc);
// http://grepcode.com/file/repo1.maven.org/maven2/com.sun.jersey/jersey-grizzly2/1.6/com/sun/jersey/api/container/grizzly2/GrizzlyServerFactory.java#GrizzlyServerFactory.createHttpServer%28java.net.URI%2Ccom.sun.jersey.api.container.grizzly2.ResourceConfig%29
HttpServer result = new HttpServer();
final NetworkListener listener = new NetworkListener("grizzly",
settings.getHost(), settings.getPort());
result.addListener(listener);
// do we need SSL?
if (secure) {
listener.setSecure(secure);
SSLEngineConfigurator sslEngineConfigurator = createSSLConfig(true);
listener.setSSLEngineConfig(sslEngineConfigurator);
}
// Map the path to the processor.
final ServerConfiguration config = result.getServerConfiguration();
final HttpHandler handler = ContainerFactory.createContainer(
HttpHandler.class, rc);
config.addHttpHandler(handler, contextPath);
return result;
}
/**
* create SSL Configuration
*
* #param isServer
* true if this is for the server
* #return
* #throws Exception
*/
private SSLEngineConfigurator createSSLConfig(boolean isServer)
throws Exception {
final SSLContextConfigurator sslContextConfigurator = new SSLContextConfigurator();
// override system properties
final File cacerts = getStoreFile("server truststore",
"truststore_server.jks");
if (cacerts != null) {
sslContextConfigurator.setTrustStoreFile(cacerts.getAbsolutePath());
sslContextConfigurator.setTrustStorePass(TRUSTSTORE_PASSWORD);
}
// override system properties
final File keystore = getStoreFile("server keystore", "keystore_server.jks");
if (keystore != null) {
sslContextConfigurator.setKeyStoreFile(keystore.getAbsolutePath());
sslContextConfigurator.setKeyStorePass(TRUSTSTORE_PASSWORD);
}
//
boolean clientMode = false;
// force client Authentication ...
boolean needClientAuth = settings.isNeedClientAuth();
boolean wantClientAuth = settings.isWantClientAuth();
SSLEngineConfigurator result = new SSLEngineConfigurator(
sslContextConfigurator.createSSLContext(), clientMode, needClientAuth,
wantClientAuth);
return result;
}

Spring 3.1 WebApplicationInitializer & Embedded Jetty 8 AnnotationConfiguration

I'm trying to create a simple webapp without any XML configuration using Spring 3.1 and an embedded Jetty 8 server.
However, I'm struggling to get Jetty to recognise my implementaton of the Spring WebApplicationInitializer interface.
Project structure:
src
+- main
+- java
| +- JettyServer.java
| +- Initializer.java
|
+- webapp
+- web.xml (objective is to remove this - see below).
The Initializer class above is a simple implementation of WebApplicationInitializer:
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
public class Initializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("onStartup");
}
}
Likewise JettyServer is a simple implementation of an embedded Jetty server:
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
public class JettyServer {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setResourceBase("src/main/webapp");
webAppContext.setContextPath("/");
webAppContext.setConfigurations(new Configuration[] { new AnnotationConfiguration() });
webAppContext.setParentLoaderPriority(true);
server.setHandler(webAppContext);
server.start();
server.join();
}
}
My understanding is that on startup Jetty will use AnnotationConfiguration to scan for
annotated implementations of ServletContainerInitializer; it should find Initializer and wire it in...
However, when I start the Jetty server (from within Eclipse) I see the following on the command-line:
2012-11-04 16:59:04.552:INFO:oejs.Server:jetty-8.1.7.v20120910
2012-11-04 16:59:05.046:INFO:/:No Spring WebApplicationInitializer types detected on classpath
2012-11-04 16:59:05.046:INFO:oejsh.ContextHandler:started o.e.j.w.WebAppContext{/,file:/Users/duncan/Coding/spring-mvc-embedded-jetty-test/src/main/webapp/}
2012-11-04 16:59:05.117:INFO:oejs.AbstractConnector:Started SelectChannelConnector#0.0.0.0:8080
The important bit is this:
No Spring WebApplicationInitializer types detected on classpath
Note that src/main/java is defined as a source folder in Eclipse, so should be on the classpath. Also note that the Dynamic Web Module Facet is set to 3.0.
I'm sure there's a simple explanation, but I'm struggling to see the wood for the trees! I suspect the key is with the following line:
...
webAppContext.setResourceBase("src/main/webapp");
...
This makes sense with a 2.5 servlet using web.xml (see below), but what should it be when using AnnotationConfiguration?
NB: Everything fires up correctly if I change the Configurations to the following:
...
webAppContext.setConfigurations(new Configuration[] { new WebXmlConfiguration() });
...
In this case it finds the web.xml under src/main/webapp and uses it to wire the servlet using DispatcherServlet and AnnotationConfigWebApplicationContext in the usual way (completely bypassing the WebApplicationInitializer implementation above).
This feels very much like a classpath problem, but I'm struggling to understand quite how Jetty associates itself with implementations of WebApplicationInitializer - any suggestions would be most appreciated!
For info, I'm using the following:
Spring 3.1.1
Jetty 8.1.7
STS 3.1.0
The problem is that Jetty's AnnotationConfiguration class does not scan non-jar resources on the classpath (except under WEB-INF/classes).
It finds my WebApplicationInitializer's if I register a subclass of AnnotationConfiguration which overrides configure(WebAppContext) to scan the host classpath in addition to the container and web-inf locations.
Most of the sub-class is (sadly) copy-paste from the parent. It includes:
an extra parse call (parseHostClassPath) at the end of the configure method;
the parseHostClassPath method which is largely copy-paste from
AnnotationConfiguration's parseWebInfClasses;
the getHostClassPathResource method which grabs the first non-jar URL
from the classloader (which, for me at least, is the file url to my
classpath in eclipse).
I am using slightly different versions of Jetty (8.1.7.v20120910) and Spring (3.1.2_RELEASE), but I imagine the same solution will work.
Edit: I created a working sample project in github with some modifications (the code below works fine from Eclipse but not when running in a shaded jar) - https://github.com/steveliles/jetty-embedded-spring-mvc-noxml
In the OP's JettyServer class the necessary change would replace line 15 with:
webAppContext.setConfigurations (new Configuration []
{
new AnnotationConfiguration()
{
#Override
public void configure(WebAppContext context) throws Exception
{
boolean metadataComplete = context.getMetaData().isMetaDataComplete();
context.addDecorator(new AnnotationDecorator(context));
AnnotationParser parser = null;
if (!metadataComplete)
{
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
{
parser = createAnnotationParser();
parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", new WebServletAnnotationHandler(context));
parser.registerAnnotationHandler("javax.servlet.annotation.WebFilter", new WebFilterAnnotationHandler(context));
parser.registerAnnotationHandler("javax.servlet.annotation.WebListener", new WebListenerAnnotationHandler(context));
}
}
List<ServletContainerInitializer> nonExcludedInitializers = getNonExcludedInitializers(context);
parser = registerServletContainerInitializerAnnotationHandlers(context, parser, nonExcludedInitializers);
if (parser != null)
{
parseContainerPath(context, parser);
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
parseHostClassPath(context, parser);
}
}
private void parseHostClassPath(final WebAppContext context, AnnotationParser parser) throws Exception
{
clearAnnotationList(parser.getAnnotationHandlers());
Resource resource = getHostClassPathResource(getClass().getClassLoader());
if (resource == null)
return;
parser.parse(resource, new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
//TODO - where to set the annotations discovered from WEB-INF/classes?
List<DiscoveredAnnotation> annotations = new ArrayList<DiscoveredAnnotation>();
gatherAnnotations(annotations, parser.getAnnotationHandlers());
context.getMetaData().addDiscoveredAnnotations (annotations);
}
private Resource getHostClassPathResource(ClassLoader loader) throws IOException
{
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
for (URL url : urls)
if (url.getProtocol().startsWith("file"))
return Resource.newResource(url);
}
return null;
}
},
});
Update: Jetty 8.1.8 introduces internal changes that are incompatible with the code above. For 8.1.8 the following seems to work:
webAppContext.setConfigurations (new Configuration []
{
// This is necessary because Jetty out-of-the-box does not scan
// the classpath of your project in Eclipse, so it doesn't find
// your WebAppInitializer.
new AnnotationConfiguration()
{
#Override
public void configure(WebAppContext context) throws Exception {
boolean metadataComplete = context.getMetaData().isMetaDataComplete();
context.addDecorator(new AnnotationDecorator(context));
//Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any
AnnotationParser parser = null;
if (!metadataComplete)
{
//If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations
if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
{
_discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
_discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
}
}
//Regardless of metadata, if there are any ServletContainerInitializers with #HandlesTypes, then we need to scan all the
//classes so we can call their onStartup() methods correctly
createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
{
parser = createAnnotationParser();
parse(context, parser);
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
}
}
private void parse(final WebAppContext context, AnnotationParser parser) throws Exception
{
List<Resource> _resources = getResources(getClass().getClassLoader());
for (Resource _resource : _resources)
{
if (_resource == null)
return;
parser.clearHandlers();
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
{
if (h instanceof AbstractDiscoverableAnnotationHandler)
((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
}
parser.registerHandlers(_discoverableAnnotationHandlers);
parser.registerHandler(_classInheritanceHandler);
parser.registerHandlers(_containerInitializerAnnotationHandlers);
parser.parse(_resource,
new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
}
}
private List<Resource> getResources(ClassLoader aLoader) throws IOException
{
if (aLoader instanceof URLClassLoader)
{
List<Resource> _result = new ArrayList<Resource>();
URL[] _urls = ((URLClassLoader)aLoader).getURLs();
for (URL _url : _urls)
_result.add(Resource.newResource(_url));
return _result;
}
return Collections.emptyList();
}
}
});
I was able to resolve in an easier but more limited way by just providing explicitly to the AnnotationConfiguration the implementation class (MyWebApplicationInitializerImpl in this example) that I want to be loaded like this:
webAppContext.setConfigurations(new Configuration[] {
new WebXmlConfiguration(),
new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) throws Exception {
MultiMap<String> map = new MultiMap<String>();
map.add(WebApplicationInitializer.class.getName(), MyWebApplicationInitializerImpl.class.getName());
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
}
});
Jetty 9.0.1 contains an enhancement which allows for scanning of annotations of non-jar resources (ie classes) on the container classpath. See comment #5 on the following issue for how to use it:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=404176#c5
Jan
The code below did the trick in my maven project:
public static void main(String[] args) throws Exception {
Server server = new Server();
ServerConnector scc = new ServerConnector(server);
scc.setPort(Integer.parseInt(System.getProperty("jetty.port", "8080")));
server.setConnectors(new Connector[] { scc });
WebAppContext context = new WebAppContext();
context.setServer(server);
context.setContextPath("/");
context.setWar("src/main/webapp");
context.getMetaData().addContainerResource(new FileResource(new File("./target/classes").toURI()));
context.setConfigurations(new Configuration[]{
new WebXmlConfiguration(),
new AnnotationConfiguration()
});
server.setHandler(context);
try {
System.out.println(">>> STARTING EMBEDDED JETTY SERVER, PRESS ANY KEY TO STOP");
System.out.println(String.format(">>> open http://localhost:%s/", scc.getPort()));
server.start();
while (System.in.available() == 0) {
Thread.sleep(5000);
}
server.stop();
server.join();
} catch (Throwable t) {
t.printStackTrace();
System.exit(100);
}
}
Based on my testing and this thread http://forum.springsource.org/showthread.php?127152-WebApplicationInitializer-not-loaded-with-embedded-Jetty I don't think it works at the moment. If you look in AnnotationConfiguration.configure:
parseContainerPath(context, parser);
// snip comment
parseWebInfClasses(context, parser);
parseWebInfLib (context, parser);
it seems coupled to a war-like deployment rather than embedded.
Here is an example using Spring MVC and embedded Jetty that might be more useful:
http://www.jamesward.com/2012/08/13/containerless-spring-mvc
It creates the Spring servlet directly rather then relying on annotations.
To those experiencing this lately, it appears this gets around the issue:
#Component
public class Initializer implements WebApplicationInitializer {
private ServletContext servletContext;
#Autowired
public WebInitializer(ServletContext servletContext) {
this.servletContext = servletContext;
}
#PostConstruct
public void onStartup() throws ServletException {
onStartup(servletContext);
}
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("onStartup");
}
}
To make it work on Jetty 9 set attribute AnnotationConfiguration.CLASS_INHERITANCE_MAP on WebAppContext
webAppContext.setAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP, createClassMap());
And here is how to create this map:
private ClassInheritanceMap createClassMap() {
ClassInheritanceMap classMap = new ClassInheritanceMap();
ConcurrentHashSet<String> impl = new ConcurrentHashSet<>();
impl.add(MyWebAppInitializer.class.getName());
classMap.put(WebApplicationInitializer.class.getName(), impl);
return classMap;
}
I placed that solution on gitHub
What about just setting the context attribute that tells the scanner which things belong on the container classpath that need to be scanned?
context attribute:
org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern
./servlet-api-[^/].jar$
It is designed to be used with jar names, but you could just match everything.
You'd need to use the WebInfConfiguration as well as the AnnotationConfiguration classes.
cheers
Jan
In our case these lines helped in Jetty startup code:
ClassList cl = Configuration.ClassList.setServerDefault(server);
cl.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration");
Jetty 9 version of "magomarcelo" answer:
context.setConfigurations(
new org.eclipse.jetty.webapp.Configuration[] { new WebXmlConfiguration(), new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) throws Exception {
final ClassInheritanceMap map = new ClassInheritanceMap();
final ConcurrentHashSet<String> set = new ConcurrentHashSet<>();
set.add(MyWebAppInitializer.class.getName());
map.put(WebApplicationInitializer.class.getName(), set);
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
} });
For Jetty 9, if you have webjars, the solution provided does not work straight away as those Jars need to be on the classpath and the JAR contents need to be available as resources for your webapp. So, for that to work together with webjars, the config would have to be:
context.setExtraClasspath(pathsToWebJarsCommaSeparated);
context.setAttribute(WebInfConfiguration.WEBINF_JAR_PATTERN, ".*\\.jar$");
context.setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, ".*\\.jar$");
context.setConfigurations(
new org.eclipse.jetty.webapp.Configuration[] {
new WebInfConfiguration(), new MetaInfConfiguration(),
new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) throws Exception {
final ClassInheritanceMap map = new ClassInheritanceMap();
final ConcurrentHashSet<String> set = new ConcurrentHashSet<>();
set.add(MyWebAppInitializer.class.getName());
map.put(WebApplicationInitializer.class.getName(), set);
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
} });
The order here is important (WebInfConfiguration has to come before MetaInf).
Solution that worked for me and does not involve scanning, but uses WebApplicationInitializer class that you provide. Jetty version: 9.2.20
public class Main {
public static void main(String... args) throws Exception {
Properties properties = new Properties();
InputStream stream = Main.class.getResourceAsStream("/WEB-INF/application.properties");
properties.load(stream);
stream.close();
PropertyConfigurator.configure(properties);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setResourceBase("resource");
webAppContext.setContextPath(properties.getProperty("base.url"));
webAppContext.setConfigurations(new Configuration[] {
new WebXmlConfiguration(),
new AnnotationConfiguration() {
#Override
public void preConfigure(WebAppContext context) {
ClassInheritanceMap map = new ClassInheritanceMap();
map.put(WebApplicationInitializer.class.getName(), new ConcurrentHashSet<String>() {{
add(WebInitializer.class.getName());
add(SecurityWebInitializer.class.getName());
}});
context.setAttribute(CLASS_INHERITANCE_MAP, map);
_classInheritanceHandler = new ClassInheritanceHandler(map);
}
}
});
Server server = new Server(Integer.parseInt(properties.getProperty("base.port")));
server.setHandler(webAppContext);
server.start();
server.join();
}
}
The source (in russian) of this code snippet is here: https://habrahabr.ru/post/255773/
did a simple maven project to demonstrate how it can be done cleanly.
public class Console {
public static void main(String[] args) {
try {
Server server = new Server(8080);
//Set a handler to handle requests.
server.setHandler(getWebAppContext());
//starts to listen at 0.0.0.0:8080
server.start();
server.join();
} catch (Exception e) {
log.error("server exited with exception", e);
}
}
private static WebAppContext getWebAppContext() {
final WebAppContext webAppContext = new WebAppContext();
//route all requests via this web-app.
webAppContext.setContextPath("/");
/*
* point to location where the jar into which this class gets packaged into resides.
* this could very well be the target directory in a maven development build.
*/
webAppContext.setResourceBase("directory_where_the_application_jar_exists");
//no web inf for us - so let the scanning know about location of our libraries / classes.
webAppContext.getMetaData().setWebInfClassesDirs(Arrays.asList(webAppContext.getBaseResource()));
//Scan for annotations (servlet 3+)
final AnnotationConfiguration configuration = new AnnotationConfiguration();
webAppContext.setConfigurations(new Configuration[]{configuration});
return webAppContext;
}
}
and that's all - the spring WebApplicationInitializer that you use will get detected without explicitly letting jetty server know about the existence of such an app initializer.

Categories

Resources