I'm trying to configure embedded jetty to talk to my Jersey resources but I can't figure out how to do it. I've tried a couple of different things but nothing seems to work. The jetty tutorials don't really handle how to do it with Jersey. Any code suggestions or links are greatly appreciated
EDIT:
package pojo;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;
public class Main {
public static void main(String[] args) throws Exception {
Server server = new Server(8112);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
ServletHolder h = new ServletHolder(new ServletContainer());
h.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", "com.sun.jersey.api.core.PackagesResourceConfig");
h.setInitParameter("com.sun.jersey.config.property.packages", "resources");
h.setInitOrder(1);
context.addServlet(h, "/*");
try
{
server.start();
server.join();
}
catch (Throwable t)
{
t.printStackTrace(System.err);
}
Trying to connect to this class:
package resources;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.UriInfo;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import java.util.ArrayList;
import java.util.List;
import pojo.Party;
#Path("/parties")
public class AllPartiesResource {
#Context
UriInfo url;
#Context
Request request;
String name;
public static final Timer allTime = DBConnection.registry.timer(MetricRegistry.name("Timer","all-parties"));
#GET
#Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public List<Party> getAllParties() throws Exception
{
final Timer.Context context=allTime.time(); //start the timer
List<Party> list = new ArrayList<Party>();
DBConnection.readAllData();
list.addAll(DBConnection.getPartyCollection().values());
context.stop(); //stops timer
return list;
// ---> code for Jackson
// String string;
// DBConnection.readAllData();
// ObjectMapper jsonMapper = new ObjectMapper();
// string=jsonMapper.writeValueAsString(DBConnection.getPartyCollection());
// return string;
}
#GET
#Path("count")
#Produces(MediaType.TEXT_PLAIN)
public String getPartyCount() throws Exception
{
DBConnection.readAllData();
return String.valueOf(DBConnection.getPartyCollection().size());
}
#Path("{party}") //points to OnePartyResource.class
public OnePartyResource getParty(#PathParam("party")String party)
{
name = party;
return new OnePartyResource(url,request,party);
}
}
You're mixing 2 versions of Jersey in your code together - ServletContainer from Jersey 2.x (package org.glassfish.jersey.*) and properties from Jersey 1.x (package/prefix com.sun.jersey.*).
To deploy your app using Jersey 2.x change these two lines
h.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", "com.sun.jersey.api.core.PackagesResourceConfig");
h.setInitParameter("com.sun.jersey.config.property.packages", "resources");
from your main method to
h.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "resources");
and check the other ServerProperties you may find useful.
Related
I am trying to take a test driven development approach to building a Java based app running on App Engine, but I am having difficulties getting the setup working.
My servlet
package mobi.grocerymonkey.groceryapp;
import com.google.appengine.api.utils.SystemProperty;
import java.io.IOException;
import java.io.BufferedReader;
import java.util.Properties;
import org.json.JSONObject;
import java.util.logging.Logger;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import static com.googlecode.objectify.ObjectifyService.ofy;
import com.googlecode.objectify.ObjectifyService;
/* This is the servlet */
#WebServlet(name = "GroceryServlet", value = "/grocery")
public class GroceryServlet extends HttpServlet {
private static final Logger log = Logger.getLogger(GroceryServlet.class.getName());
#Override
public void init() throws ServletException {
log.info("context init");
ObjectifyService.init();
ObjectifyService.register(Grocery.class);
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/plain");
response.getWriter().println("Hello Kitty");
}
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
BufferedReader reader = request.getReader();
String line = null;
StringBuffer stringBuffer = new StringBuffer();
while((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
String jsonString = stringBuffer.toString();
JSONObject json = new JSONObject(jsonString);
log.info("JSON "+ jsonString);
Grocery grocery = new Grocery();
grocery.setName((String) json.get("name"));
grocery.setQuantity((Integer) json.get("quantity"));
ofy().save().entity(grocery).now();
log.info("JSON name "+ grocery.getName());
response.setContentType("application/json");
response.getWriter().println(jsonString);
}
}
web.xml file
<?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">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<filter>
<filter-name>ObjectifyFilter</filter-name>
<filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ObjectifyFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
</web-app>
My unit test
package mobi.grocerymonkey.groceryapp;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.DatastoreOptions;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.json.JSONObject;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.Reader;
import java.io.Closeable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Unit tests for {#link HelloAppEngine}.
*/
#RunWith(JUnit4.class)
public class GroceryServletTest {
private static final String MOCK_URL = "/grocery";
// Set up a helper so that the ApiProxy returns a valid environment for local testing.
private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
private Closeable closeable;
#Mock
private HttpServletRequest mockRequest;
#Mock
private HttpServletResponse mockResponse;
private StringWriter responseWriter;
private GroceryServlet servletUnderTest;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
helper.setUp();
ObjectifyService.init(new ObjectifyFactory(
DatastoreOptions.newBuilder()
.setHost("http://localhost:8081")
.setProjectId("enduring-trees-259812")
.build()
.getService()
));
closeable = ObjectifyService.begin();
// Set up some fake HTTP requests
when(mockRequest.getRequestURI()).thenReturn(MOCK_URL);
JSONObject grocery = new JSONObject();
grocery.put("name", "Beer");
Reader inputString = new StringReader(grocery.toString());
BufferedReader reader = new BufferedReader(inputString);
when(mockRequest.getReader()).thenReturn(reader);
// Set up a fake HTTP response.
responseWriter = new StringWriter();
when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter));
servletUnderTest = new GroceryServlet();
servletUnderTest.init();
}
#After public void tearDown() throws Exception {
closeable.close();
helper.tearDown();
}
#Test
public void doGetWritesResponse() throws Exception {
servletUnderTest.doGet(mockRequest, mockResponse);
// We expect our hello world response.
assertThat(responseWriter.toString())
.contains("Hello Kitty");
}
#Test
public void doPostWritesResponse() throws Exception {
JSONObject reqObj = new JSONObject();
reqObj.put("name", "Beer");
reqObj.put("quantity", 5);
StringReader reader = new StringReader(reqObj.toString());
when(mockRequest.getReader()).thenReturn(new BufferedReader(new StringReader(reqObj.toString())));
servletUnderTest.doPost(mockRequest, mockResponse);
// We expect our hello world response.
assertThat(responseWriter.toString())
.contains(reqObj.getString("name"));
}
}
The test fails with the following error message
[ERROR] Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time
elapsed: 0.103 s <<< FAILURE! - in
mobi.grocerymonkey.groceryapp.GroceryServletTest [ERROR]
doPostWritesResponse(mobi.grocerymonkey.groceryapp.GroceryServletTest)
Time elapsed: 0.078 s <<< ERROR! java.lang.IllegalStateException: You
have not started an Objectify context. You are probably missing the
ObjectifyFilter. If you are not running in the context of an http
request, see the ObjectifyService.run() method. at
mobi.grocerymonkey.groceryapp.GroceryServletTest.doPostWritesResponse(GroceryServletTest.java:109)
which is caused by this line ofy().save().entity(grocery).now() in my servlet. When I remove it, the test is run without errors.
I have tried to follow different approaches to resolve this error found here on stackoverflow but without luck.
How should the test/app be setup to be able to develop it using a test driven approach? What I am looking for is a way to be able to write the unit test first and then the actual application. But how to succeed?
(Disclaimer, I haven't worked with Java in over a decade)
UPDATE
ServletContext file
package mobi.grocerymonkey.groceryapp;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.Closeable;
import java.io.IOException;
import com.googlecode.objectify.ObjectifyService;
import static com.googlecode.objectify.ObjectifyService.ofy;
#WebListener
public class GroceryContextListener implements ServletContextListener {
private ServletContext context;
private Closeable closeable;
public void contextInitialized(ServletContextEvent event) {
this.context = event.getServletContext();
ObjectifyService.init();
this.closeable = ObjectifyService.begin();
ObjectifyService.register(Grocery.class);
System.out.println("Context initialized");
}
public void contextDestroyed(ServletContextEvent event) {
try {
this.closeable.close();
} catch(IOException ioe) {
}
}
}
Unittest file
package mobi.grocerymonkey.groceryapp;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.DatastoreOptions;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.json.JSONObject;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.Reader;
import java.io.Closeable;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
/**
* Unit tests for {#link HelloAppEngine}.
*/
#RunWith(JUnit4.class)
public class GroceryServletTest {
private static final String MOCK_URL = "/grocery";
// Set up a helper so that the ApiProxy returns a valid environment for local testing.
private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
private Closeable closeable;
#Mock
private HttpServletRequest mockRequest;
#Mock
private HttpServletResponse mockResponse;
private ServletContextListener contextListener;
private ServletContext context;
private StringWriter responseWriter;
private GroceryServlet servletUnderTest;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
helper.setUp();
contextListener = new GroceryContextListener();
context = mock(ServletContext.class);
// Set up some fake HTTP requests
when(mockRequest.getRequestURI()).thenReturn(MOCK_URL);
JSONObject grocery = new JSONObject();
grocery.put("name", "Beer");
Reader inputString = new StringReader(grocery.toString());
BufferedReader reader = new BufferedReader(inputString);
when(mockRequest.getReader()).thenReturn(reader);
// when(mockRequest.getServletContext()).thenReturn(context);
// Set up a fake HTTP response.
responseWriter = new StringWriter();
when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter));
servletUnderTest = new GroceryServlet();
}
#After
public void tearDown() throws Exception {
helper.tearDown();
}
#Test
public void doGetWritesResponse() throws Exception {
servletUnderTest.doGet(mockRequest, mockResponse);
// We expect our hello world response.
assertThat(responseWriter.toString())
.contains("Hello Kitty");
}
#Test
public void doPostWritesResponse() throws Exception {
contextListener.contextInitialized(new ServletContextEvent(context));
JSONObject reqObj = new JSONObject();
reqObj.put("name", "Beer");
reqObj.put("quantity", 5);
StringReader reader = new StringReader(reqObj.toString());
when(mockRequest.getReader()).thenReturn(new BufferedReader(new StringReader(reqObj.toString())));
servletUnderTest.doPost(mockRequest, mockResponse);
// We expect our hello world response.
assertThat(responseWriter.toString())
.contains(reqObj.getString("name"));
}
}
Servlet file
package mobi.grocerymonkey.groceryapp;
import com.google.appengine.api.utils.SystemProperty;
import java.io.IOException;
import java.io.BufferedReader;
import java.util.Properties;
import org.json.JSONObject;
import java.util.logging.Logger;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import static com.googlecode.objectify.ObjectifyService.ofy;
import com.googlecode.objectify.ObjectifyService;
/* This is the servlet */
#WebServlet(name = "GroceryServlet", value = "/grocery")
public class GroceryServlet extends HttpServlet {
private static final Logger log = Logger.getLogger(GroceryServlet.class.getName());
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/plain");
response.getWriter().println("Hello Kitty");
}
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
BufferedReader reader = request.getReader();
String line = null;
StringBuffer stringBuffer = new StringBuffer();
while((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
String jsonString = stringBuffer.toString();
JSONObject json = new JSONObject(jsonString);
log.info("JSON "+ jsonString);
Grocery grocery = new Grocery();
grocery.setName((String) json.get("name"));
grocery.setQuantity((Integer) json.get("quantity"));
ofy().save().entity(grocery).now();
log.info("JSON name "+ grocery.getName());
response.setContentType("application/json");
response.getWriter().println(jsonString);
}
}
Now I am getting a "com.google.cloud.datastore.DatastoreException: Unauthenticated" error when running the test, so looks like I am on the right way. Would I store the datastore credentials in the web.xml and then pass them to the context similar to
ObjectifyService.init(new ObjectifyFactory(
DatastoreOptions.newBuilder()
.setHost("http://localhost:8081")
.setProjectId("enduring-trees-259812")
.build()
.getService()
));
ObjectifyService.factory().register(Grocery.class);
New Update
I upgraded to Junit5 and rewrote the entire test to this
package mobi.grocerymonkey.groceryapp;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.util.Closeable;
import static com.googlecode.objectify.ObjectifyService.factory;
import static com.googlecode.objectify.ObjectifyService.ofy;
import com.googlecode.objectify.Key;
import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.testing.LocalDatastoreHelper;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import mobi.grocerymonkey.groceryapp.util.TestBase;
import mobi.grocerymonkey.groceryapp.domain.Grocery;
import mobi.grocerymonkey.groceryapp.domain.GroceryList;
public class MyFirstTest extends TestBase {
// Maximum eventual consistency.
private final static LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
.setDefaultHighRepJobPolicyUnappliedJobPercentage(100));
Closeable closeable;
#BeforeAll
public static void setUp() {
helper.setUp();
}
#AfterAll
public static void tearDown() {
helper.tearDown();
}
#BeforeEach
public void setUpEach() {
ObjectifyService.init(new ObjectifyFactory(
DatastoreOptions.getDefaultInstance().getService()));
closeable = ObjectifyService.begin();
}
#AfterEach
public void tearDownEach() {
closeable.close();
}
#DisplayName("Test MyFirstTest.testAddition()")
#Test
public void testAddition() {
assertEquals(1 + 1, 2);
}
#DisplayName("Testing testGroceryList()")
#Test
public void testGroceryList() {
factory().register(GroceryList.class);
GroceryList list = new GroceryList("Weekend Beer List");
Key<GroceryList> k1 = ofy().save().entity(list).now();
assertEquals(1+1, 2);
}
}
It is deliberately kept in a single file for now. But for some reason, the datastore cannot find the emulator that is running when the test is run. I am getting a Datastore Unauthenticated error.
I ran gcloud beta emulators datastore start and $(gcloud beta emulators datastore env-init) before running the unit test.
The problem is that you're calling ObjectifyService.init() twice, but you only called begin() on the first (abandoned) factory.
You call init() in your setUp() method, which initializes the static ObjectifyFactory. You then open a session on that factory with the ObjectifyService.begin() call.
At the end of your setUp() you call servletUnderTest.init(), which also calls ObjectifyService.init(). This replaces the static ObjectifyFactory. When you next execute your servlet and call ofy()..., you're using a factory that has not started a session.
Take a look at the code for ObjectifyService. It's quite literally just a few lines of code to wrap a static ObjectifyFactory instance.
If you have more than one servlet, this code will not work well in production either - you only want to initialize and register your classes once. I recommend doing this with a ServletContextListener.
The core of test-driven development revolves around five steps, which you repeated throughout the software development life cycle.
Test-driven development life-cycle:
Write the test
Run the test (without implementation code, test does not pass)
Write just enough implementation so the test passes
Run all tests (test pass)
Refactor
Repeat
By following this steps you can create a TDD implementation for your application.
There is no specific way for Google Cloud to do beside the steps I have specified above.
As specified in your error, you can see that you have not started an Objectify context, and you are missing the ObjectifyFilter.
Here is a implementation of a list in Java, which follows the TDD, which may be helpful to clear some of your concerns.
Hello everyone!
I'm trying to load wildfly server's system properties through JMX in Startup bean's #PostConstruct method. It works fine on the already started server instance when deployment starts, but fails while starting with server instance bootstrapping.
Wildfly 11.0.0.CR1
Startup bean code:
package ru.wildfly.test.ejb.wildflyconsulregistrar.startup;
import ru.wildfly.test.ejb.wildflyconsulregistrar.api.ConsulRegistrar;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
#Startup
#Singleton
public class WildflyConsulRegistrarStartupBean {
#Inject
private ConsulRegistrar consulRegistrar;
#PostConstruct
public void initialize() {
registerServices();
}
private void registerServices() {
consulRegistrar.registerService("WildflyTestCluster");
}
.............
}
ConsulRegistrar code:
package ru.wildfly.test.ejb.wildflyconsulregistrar.impl;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
import ru.test.ejb.wildflyconsulregistrar.api.ConsulRegistrar;
import ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.api.CurrentServerNodeSettings;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
#Dependent
public class ConsulRegistrarImpl implements ConsulRegistrar {
...............
#Inject
private CurrentServerNodeSettings currentServerNodeSettings;
.............
#Override
public void registerService(String serviceName) {
String currentNodeName = currentServerNodeSettings.getCurrentNodeName();
........................
}
.......................
}
CurrentServerNodeSettings code:
package ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.impl;
import ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.api.CurrentServerNodeSettings;
import javax.enterprise.context.Dependent;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
#Dependent
public class CurrentServerNodeSettingsWildflyImpl implements CurrentServerNodeSettings {
....................
#Override
public String getCurrentNodeName() {
String currentNodeName = getPlatformMBeanServerAttributeValue(String.class, "jboss.as:system-property=server.name", "value");
return currentNodeName;
}
private <T> T getPlatformMBeanServerAttributeValue(Class<T> valueType, String objectName, String attributeName) {
T attributeValue = null;
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
Object attributeObject = mBeanServer.getAttribute(new ObjectName(objectName), attributeName);
attributeValue = attributeObject != null ? (valueType.cast(attributeObject)) : null;
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return attributeValue;
}
}
Error message:
Caused by: java.lang.IllegalStateException:
javax.management.AttributeNotFoundException:
"WFLYCTL0216: Management resource '[(\"system-property\" => \"server.name\")]' not found"
at ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.impl.CurrentServerNodeSettingsWildflyImpl
.getPlatformMBeanServerAttributeValue(CurrentServerNodeSettingsWildflyImpl.java:41)
I have found the same issue on jboss forum https://developer.jboss.org/message/971717#971717 , but it was unanswered.
Any suggestions?
This is a dependency problem during startup, i.e. the server name is set after your #PostConstruct method gets executed. Try to load the server name lazy when it is accessed from the application for the first time.
In Wildfly there is no generic way to enforce the sequence of deployments from the application despite the definition of module dependencies. But this won't help in your case.
I have below code for making a POST call to RestAPI for Tableau system, which is working and seeing response output.
However, I would like to capture cookie from this output and need to be used for further consumption! Can somebody help me on this problem?
Code:
package com.abc.it.automation.service;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Properties;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity.HeadersBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import com.abc.it.automation.utils.SSLUtil;
public class BIaaSTableauService {
private static Properties tableau_properties = new Properties();
static {
// Loads the values from configuration file into the Properties instance
try {
tableau_properties.load(new FileInputStream("res/config.properties"));
} catch (IOException e) {
System.out.println(e);
}
}
private static final String loginURL = tableau_properties.getProperty("server.host");
private static final String siteSearchURL = tableau_properties.getProperty("site.search.url");
public static void main(String[] args) throws KeyManagementException, NoSuchAlgorithmException {
RestTemplate restTableau = new RestTemplate();
String requestLogin = "<tsRequest>"+
"<credentials name=\"svc_tableau\" password=\"xxxxxxxxx\" >"+
"<site contentUrl=\"\"/>"+
"</credentials>"+
"</tsRequest>";
SSLUtil.turnOffSslChecking();
ResponseEntity<String> responseLogin = restTableau.postForEntity(loginURL, requestLogin, String.class);
System.out.println(responseLogin.getBody());
You need to build your RestTemplate as follows.
RestTemplate restTableau = new RestTemplate(new MyClientHttpRequestFactory());
Extend ClientHttpRequestFactory as follows.
public class MyClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
private Cookie cookie;
//setters and getters.
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
this.setCookie(connection.getRequestProperty("Cookie"));
}
}
I am using Guice + Jersey + Shiro to login via a REST API and then use the same HTTP session under which I logged in to and have my permissions work for that resource.
Below is my code.
Firstly, my servlet configuration:-
public class ServletConfiguration extends GuiceServletContextListener
{
private ServletContext mServletContext;
#Override
public void contextInitialized(ServletContextEvent inEvent)
{
mServletContext = inEvent.getServletContext();
super.contextInitialized(inEvent);
}
#Override
protected Injector getInjector()
{
mServletContext.addListener(new au.com.tt.agora.configuration.CbiCleanupHttpSessionListener());
return Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets()
{
install(new TTShiroWebModule(mServletContext));
install(new ShiroAopModule());
filter("/*").through(GuiceShiroFilter.class);
bind(ShiroLoginResource.class);
bind(ShiroResource.class);
filter("/*").through(GuiceContainer.class);
}
});
}
}
Now, this is my test realm:-
package au.com.tt.agora.configuration.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class TestRealm extends AuthorizingRealm
{
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken inToken) throws AuthenticationException
{
UsernamePasswordToken upToken = (UsernamePasswordToken) inToken;
if (upToken.getUsername().equals("Kamal") || upToken.getUsername().equals("NotKamal"))
return new SimpleAuthenticationInfo(upToken.getUsername(), upToken.getPassword(), getName());
return null;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection inPrincipals)
{
String username = (String) inPrincipals.fromRealm(getName()).iterator().next();
SimpleAuthorizationInfo authzInfo = new SimpleAuthorizationInfo();
if (username.equals("Kamal"))
{
authzInfo.addStringPermission("PRODMA:READ:AU");
authzInfo.addStringPermission("PRODMA:WRITE:KB");
authzInfo.addStringPermission("SUPPMA:READ:KB");
}
else
{
authzInfo.addStringPermission("PRODMA:READ:AU");
authzInfo.addStringPermission("PRODMA:WRITE:KB");
}
return authzInfo;
}
}
This is the web module
package au.com.tt.agora.configuration.shiro;
import javax.servlet.ServletContext;
import org.apache.shiro.guice.web.ShiroWebModule;
public class TTShiroWebModule extends ShiroWebModule
{
public TTShiroWebModule(ServletContext inServletContext)
{
super(inServletContext);
}
#SuppressWarnings("unchecked")
#Override
protected void configureShiroWeb()
{
bindRealm().to(TestRealm.class);
addFilterChain("**/shiroResource/*", ANON);
}
}
Here is the resource I use to login:-
package au.com.tt.agora.configuration.jaxrs.resources;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import com.google.inject.Inject;
import au.com.tt.agora.configuration.option.ClientProvider;
import au.com.tt.agora.configuration.option.ConfigurationProvider;
import au.com.tt.agora.login.web.request.LoginRequest;
import au.com.tt.agora.login.web.request.LoginResponse;
import au.com.tt.agora.login.web.service.LoginHandler;
import au.com.tt.calypso.cbi.CalypsoException;
#Path("/{client}/shiroLogin")
public class ShiroLoginResource
{
private static final String ROUTING_TOKEN_HEADER = "proxy-jroute";
#POST
#Path("/standard")
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String login(#Context HttpServletRequest inServletRequest) throws CalypsoException
{
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken("Kamal", "Password", false));
return getSessionIdWithRouting(inServletRequest);
}
private String getSessionIdWithRouting(HttpServletRequest inRequest)
{
String sessionId = inRequest.getSession().getId();
return(sessionId);
}
}
And here is the resource I am calling:-
package au.com.tt.agora.configuration.jaxrs.resources;
import javax.servlet.http.HttpSession;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
#Path("/{client}/shiroResource")
public class ShiroResource
{
private static final Logger LOG = LoggerFactory.getLogger(ShiroResource.class);
#Inject
public ShiroResource()
{
}
#POST
#Path("requiresProdma.do")
#Produces(MediaType.TEXT_PLAIN)
#RequiresPermissions({ "PRODMA:*:*" })
public String prodmaRequired()
{
return "Success";
}
#POST
#Path("requiresSuppma.do")
#Produces(MediaType.TEXT_PLAIN)
#RequiresPermissions({ "SUPPMA:*:*" })
public String suppmaRequired()
{
Subject subject = SecurityUtils.getSubject();
subject.getPrincipal();
return "Success";
}
}
If I put a breakpoint into suppmaRequired and call this resource, I can see that subject is not authenticated.
My understanding on how Shiro works is obviously faulty, but I don't know what I am not doing. Can anyone point me in the right direction?
Not sure if it makes a difference, but I am using URL rewriting to access the web session.
Basically, I am using the fetch API to test this. Here is an example:-
fetch("http://localhost/app/tt/shiroLogin/standard", {
method: "POST",
headers: {
"Content-Type" : "application/json"
} ,
body: '{"username":"myName","password":"myPassword"}'
})
.then(function(res) {
return res.text();
})
.then(function(sessionId) {
return fetch("http://localhost/app/tt/shiroResource/requiresSuppma.do;JSESSIONID=" + sessionId,
{
method: "POST"
});
})
.then(function(res) {
return res.text();
})
.then(function(res) {
console.log(res);
});
I am also deploying to glassfish.
OK, this was not a Shiro problem in the end. I was using two different sessions going from the ShiroLoginResource to ShiroResource.
I forgot that you actually needed to inject with a session level object in Guice to force Guice to create a session. Stupid me.
Once I injected a session scoped dependency into ShiroLoginResource and interacted with it, then everything just worked.
I will keep this question open because it gives some useful code snippets.
I'm trying to get hold of some injected context (for example Session or HttpServletRequest) in a Servlet I've written, running on Grizzly, but nothing I do seems to work. The whole process seems to stall rather prematurely with the following error:
SEVERE: Missing dependency for field: javax.servlet.http.HttpServletRequest com.test.server.LolCat.hsr
The server is dead simple, it consists of two files, the static entry point (Main.java):
package com.test.server;
import java.io.IOException;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.grizzly.http.server.HttpServer;
import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
import com.sun.jersey.api.core.ClassNamesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
public class Main {
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost/").port(8080).build();
}
public static final URI BASE_URI = getBaseURI();
public static void main(String[] args) throws IOException {
ResourceConfig rc = new ClassNamesResourceConfig(LolCat.class);
HttpServer httpServer = GrizzlyServerFactory.createHttpServer(BASE_URI, rc);
System.in.read();
httpServer.stop();
}
}
and the serlvet (LolCat.java):
package com.test.server;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
#Path(value = "/lol")
public class LolCat {
#Context HttpServletRequest hsr;
#GET
#Path(value="/cat")
public String list() {
return "meow";
}
}
Specifically, it's the #Context-line in the above source file that is the source and solution to all my problems. I need it, and according to everything I've read about Jersey and Servlets it should work, but alas it does not. I've also tried using GrizzlyWebContainerFactory instead of the GrizzlyServerFactory, but to no avail.
For reference, the project is compiled with the following dependencies:
org.glassfish.grizzly:grizzly-framework:jar:2.2.21
org.glassfish.grizzly:grizzly-http:jar:2.2.21
org.glassfish.grizzly:grizzly-http-servlet:jar:2.2.21
org.glassfish.grizzly:grizzly-http-server:jar:2.2.21
com.sun.jersey:jersey-server:jar:1.17
com.sun.jersey:jersey-servlet:jar:1.17
com.sun.jersey:jersey-core:jar:1.17
javax.servlet:javax.servlet-api:jar:2.5.0
com.sun.jersey:jersey-grizzly2:jar:1.17
com.sun.jersey:jersey-grizzly2-servlet:jar:1.17
asm:asm:jar:3.3.1
This Main class works fine for me:
package com.test.server;
import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
import java.io.IOException;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import com.sun.jersey.api.core.ClassNamesResourceConfig;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.grizzly.servlet.ServletRegistration;
import org.glassfish.grizzly.servlet.WebappContext;
public class Main {
private static final String JERSEY_SERVLET_CONTEXT_PATH = "";
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost").port(8080).path("/").build();
}
public static final URI BASE_URI = getBaseURI();
public static void main(String[] args) throws IOException {
// Create HttpServer and register dummy "not found" HttpHandler
HttpServer httpServer = GrizzlyServerFactory.createHttpServer(BASE_URI, new HttpHandler() {
#Override
public void service(Request rqst, Response rspns) throws Exception {
rspns.setStatus(404, "Not found");
rspns.getWriter().write("404: not found");
}
});
// Initialize and register Jersey Servlet
WebappContext context = new WebappContext("WebappContext", JERSEY_SERVLET_CONTEXT_PATH);
ServletRegistration registration = context.addServlet("ServletContainer", ServletContainer.class);
registration.setInitParameter(ServletContainer.RESOURCE_CONFIG_CLASS,
ClassNamesResourceConfig.class.getName());
registration.setInitParameter(ClassNamesResourceConfig.PROPERTY_CLASSNAMES, LolCat.class.getName());
registration.addMapping("/*");
context.deploy(httpServer);
System.in.read();
httpServer.stop();
}
}
Try http://localhost:8080/lol/cat in your browser.
You can change JERSEY_SERVLET_CONTEXT_PATH to update Servlet's context-path.
As per developers explanations - Grizzly is not fully compliant to JAX-RS 2.0 so there will be no official contexts injections/wrapping. See Jersey Bug-1960
Applicable for Jersey + Grizzly version 2.7+
Luckily there is a way to inject Grizzly request/response objects. Kind of tricky but works
Code sample provided in one of Jersey's unit tests. See Jersey container test
So code fragment will be:
import javax.inject.Inject;
import javax.inject.Provider;
public someclass {
#Inject
private Provider<Request> grizzlyRequestProvider;
public void method() {
if (grizzlyRequestProvider != null) {
Request httpRequest = grizzlyRequestProvider.get();
// Extract what you need
}
}
}
Works fine both for filters and service methods
You can also manually register a ResourceContext
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(getBaseURI());
WebappContext context = new WebappContext("WebappContext", "/api");
ServletRegistration registration = context.addServlet("ServletContainer",
new ServletContainer(config));
registration.addMapping("/*");
context.deploy(httpServer);
Where config is your resource context.
Try something like this :-
public class Main {
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost/").port(8080).build();
}
public static void main(String[] args) throws IOException {
ResourceConfig rc = new ResourceConfig().packages("com.example");//path to you class files
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
System.in.read();
httpServer.stop();
}
}