I create a context and assign as tomcat context with urlpattern /api.
Now, I add DefaultServlet to Tomcat and create a filter and map for jersey resourceConfig.
Context context = tomcat.addContext("/api", base.getAbsolutePath());
Tomcat.addServlet(context, "default", new DefaultServlet());
context.addServletMapping("/*", "default");
final FilterDef def = new FilterDef();
final FilterMap map = new FilterMap();
def.setFilterName("jerseyFilter");
def.setFilter(getJerseyFilter());
context.addFilterDef(def);
map.setFilterName("jerseyFilter");
map.addURLPattern("/*");
context.addFilterMap(map);
tomcat.start();
private static Filter getJerseyFilter(){
final ResourceConfig config = new ResourceConfig()
.packages(Main.class.getPackage().getName())
// create instance of Resource and dynamically configure to ResourceConfig
.register(new Resource(new Core(), configuration))
.register(JspMvcFeature.class) // register jspMVC
.property(ServletProperties.FILTER_FORWARD_ON_404, true);
return new ServletContainer(config);
}
Now, I am successfully able to access localhost:PORT/api/<end_point>
Now my question is, I want to assign a path /serve/* in ServletMapping like
Tomcat.addServlet(context, "default", new DefaultServlet());
context.addServletMapping("/serve/*", "default");
Then I want to access the resource on localhost:PORT/api/serve/<end_point>
but it prompt error
HTTP Status 404 -
type Status report
message
description The requested resource is not available.
Apache Tomcat/8.0.26
Related
My goal is to configure a Jetty server to work with the Vaadin 11 framework.
What I was trying was the following:
public static void main(final String[] args) {
final Server server = new Server();
final ServerConnector httpConnector = new ServerConnector(server);
httpConnector.setPort(8080);
final ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
final ServletHolder servletHolder = new ServletHolder(new VaadinServlet());
contextHandler.addServlet(servletHolder, "/*");
final WebAppContext context = new WebAppContext();
context.setServer(server);
context.setContextPath("/");
context.setClassLoader(Thread.currentThread().getContextClassLoader());
server.addConnector(httpConnector);
server.setHandler(contextHandler);
server.setHandler(context);
try {
server.start();
} catch (final Exception e) {
e.printStackTrace();
}
}
#Route("/")
public class MainView extends Div {
public MainView() {
System.out.println("main");
setText("aha bye");
}
}
But the MainView is never called.
How can I tell Jetty to forward the requests to Vaadin?
Answering my own question here.
But this will still not work as you expect. Why? Because you have 2 different contexts (contextHandler and context) on the same contextPath /.
But again, this seems backwards, as your WebAppContext has no resource base or war declared, so it's not doing anything for you in your sample code.
Joakim Erdfelt is absolutely right. I messed up the code with two contexts and I either have to set a resource base or a war-file. Many thanks to you as you pointed out the way to the solution!
Thanks to Leif Åstrand too for the hint of finding all classes with the #Route annotation!
I separated the server code from the "client" code and build the war-file for the jetty server.
What I use as a working solution is the following:
final WebAppContext context = new WebAppContext();
context.setServer(httpServer);
context.setContextPath("/");
context.addServlet(new ServletHolder(new TestServlet()), "/*");
context.setWar(warFile.getAbsolutePath());
context.setExtractWAR(true);
context.setClassLoader(Thread.currentThread().getContextClassLoader());
context.setInitParameter("ui", MainUI.class.getCanonicalName());
httpServer.setHandler(context);
In the Testservlet (extending VaadinServlet) I have the following two methods:
#SuppressWarnings("unchecked")
private void findRouteClasses() {
final Set<Class<?>> routeClasses = ReflectionUtils.findClassesWithAnnotation(Route.class,
"com.example.myproject");
this.routeClasses = new HashSet<>();
for (final Class<?> clazz : routeClasses)
this.routeClasses.add((Class<? extends Component>) clazz);
}
#Override
protected void servletInitialized() throws ServletException {
final ServletContext context = getServletContext();
final Object routeRegistryObject = context.getAttribute(RouteRegistry.class.getName());
if (routeRegistryObject == null)
throw new ServletException("routeRegistryObject is null");
if ((routeRegistryObject instanceof RouteRegistry) == false)
throw new ServletException("routeRegistryObject is not of type RouteRegistry");
final RouteRegistry routeRegistry = (RouteRegistry) routeRegistryObject;
findRouteClasses();
try {
routeRegistry.setNavigationTargets(routeClasses);
} catch (final InvalidRouteConfigurationException e) {
throw new ServletException(e);
}
super.servletInitialized();
}
All classes with the annotation com.vaadin.flow.router.Route are seached and then set as the navigation targets.
server.setHandler(contextHandler);
server.setHandler(context);
You replaced your contextHandler with the context with that code.
Try this instead ...
HandlerList handlers = new HandlerList();
handlers.addHandler(contextHandler);
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler()); // to report errors if nothing above matches
server.setHandler(handlers);
But this will still not work as you expect.
Why? Because you have 2 different contexts (contextHandler and context) on the same contextPath /.
The first context in your HandlerList will be used, and the next one will never be called.
Why? Because once you enter a context, you do not exit it (that context MUST serve a response, even an error).
You could just modify your WebAppContext to include the VaadinServlet (eliminating the need for ServletContextHandler entirely)
eg:
final WebAppContext context = new WebAppContext();
final ServletHolder servletHolder = new ServletHolder(new VaadinServlet());
context.addServlet(servletHolder, "/*");
But again, this seems backwards, as your WebAppContext has no resource base or war declared, so it's not doing anything for you in your sample code.
If it were me, and I'm using embedded-jetty, I would avoid using a WebAppContext entirely and just stick with the ServletContextHandler only.
I'm running Jersey 2.26-b09 on top of Grizzly, and I'm using the following code to start the Grizzly HTTP server:
public void start() {
URI uri = UriBuilder.fromPath("").scheme("http").host("localhost").port(8084).path("/rest").build();
Map<String, String> params = new HashMap<>(16);
String applicationClassName = RestApplication.class.getName();
String applicationPackageName = RestApplication.class.getPackage().getName();
String productionPackageName = ProductionService.class.getPackage().getName();
params.put(ServletProperties.JAXRS_APPLICATION_CLASS, applicationClassName);
params.put(ServerProperties.PROVIDER_PACKAGES, productionPackageName + "," + applicationPackageName);
HttpServer server = GrizzlyWebContainerFactory.create(uri, params);
server.start();
}
The RestApplication class extends Application, and has a #ApplicationPath("/system") annotation.
The ProductionService class is a REST resource with a #Path("/production") annotation.
I can see that the path specified in the #ApplicationPath is ignored: my resources can be accessed at /rest/production and not at /rest/system/production.
I've tried to change the URI to /rest/system instead of /rest, but to no avail:
URI uri = UriBuilder.fromPath("").scheme("http").host("localhost").port(8084).path("/rest/system").build();
The application is deployed in the root context /rest, not /rest/system.
What am I missing?
Of course as a workaround I could change the resource path from "/production" to "/system/production", but I would like to know why the application path is ignored.
I've changed the code that creates and initializes the server to:
public void start() {
URI uri = UriBuilder.fromPath("").scheme("http").host("localhost").port(8084).build();
Map<String, String> params = new HashMap<>(16);
String applicationPackageName = RestApplication.class.getPackage().getName();
String productionPackageName = ProductionService.class.getPackage().getName();
params.put(ServerProperties.PROVIDER_PACKAGES, productionPackageName + "," + applicationPackageName);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
WebappContext context = new WebappContext("system", "/rest/system");
ServletRegistration registration = context.addServlet("jersey", ServletContainer.class);
registration.setInitParameters(params);
registration.addMapping("/*");
context.deploy(server);
server.start();
}
A Web Application context is created and serves the resources at the desired path. Since the servlet container initializer is not invoked in this programmatic approach, the ServletProperties.JAXRS_APPLICATION_CLASS property is not set.
I thought that setting this property were doing the job, but it does not. Thanks to #peeskillet for the hint.
I am in process of migrating jetty 7 to jetty 8,
I am getting this error:
no suitable method found for
addFilter(Class,String,int)
servletContext.addFilter(JsonTokenFilter.class, "/*", FilterMapping.REQUEST);
What I am trying to do is:
// servlet context
ServletContextHandler servletContext = new ServletContextHandler( ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
// set session manager
SessionHandler sessionHandler = new GatewaySessionHandler(sessionManager);
servletContext.setSessionHandler(sessionHandler);
sessionHandler.addEventListener(new SessionListener());
sessionHandler.addEventListener(new CsrfGuardHttpSessionListener());
sessionManager.setSecureRequestOnly(Utilities.getConfigBoolean("fievel.jetty.secureCookies", true));
sessionManager.setHttpOnly(Utilities.getConfigBoolean( "fievel.jetty.httpCookies", true));
setSessionSettings();
// add json rpc signal if session token invalid
servletContext.addFilter(JsonTokenFilter.class, "/*", FilterMapping.REQUEST);
The last line is the one that is giving error.
If I refer ServletContextHandler for jetty 8,
addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches)
is ideally the type of code that I should write.
Can anybody give me a clue where am I going wrong?
i think you should be writing something like this:
servletContext.addFilter(JsonTokenFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
since it expects an EnumSet instead of int
I've tried the tutorial from eclipse jetty tutorial
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContext.setContextPath("/");
server.setHandler(servletContext);
servletContext.addServlet(new ServletHolder(new HelloServlet()),"/*");
servletContext.addFilter(TestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
server.start();
server.join();
}
the output is:
2016-08-10 15:13:58.054:INFO:oejs.Server:jetty-8.1.19.v20160209
TestFilter Init
2016-08-10 15:13:58.186:INFO:oejs.AbstractConnector:Started SelectChannelConnector#0.0.0.0:8080
TestFilter doFilter running
so the filter is registered for sure in this way
I'm trying to retrieve resource from Jasperserver repository using its java API, according to jasper report server ultimate guide, I should get an instance of the ExecutionContext interface:
ExecutionContext context = JasperServerUtil.getExecutionContext();
then, get an instance of the RepositoryService interface:
RepositoryService repositoryService = ...; //how??
Now I can get the file using the following code:
FileResourceData fileResourceData = repositoryService.getContentResourceData(context, "/examples/report.pdf");
my question is how can I get the RepositoryService instance?
ApplicationContext ctx = StaticApplicationContext.getApplicationContext();
String repositoryServiceName = "repositoryService";
RepositoryService repositoryService = (RepositoryService) ctx.getBean(repositoryServiceName);
ExecutionContext context = JasperServerUtil.getExecutionContext();
Resource resource = repositoryService.getResource(context, fileURI);
I need to test a servlet, which is working fine now.
The servlet needs to use a Spring service, so it is modified for that this way:
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(
this, config.getServletContext()); // ImageServlet.java line 49
After migration to Spring 4, the test broke and currently it throws this exception:
java.lang.IllegalStateException:
No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.
getRequiredWebApplicationContext(WebApplicationContextUtils.java:84)
at org.springframework.web.context.support.SpringBeanAutowiringSupport.
processInjectionBasedOnServletContext(SpringBeanAutowiringSupport.java:107)
at package.ImageServlet.init(ImageServlet.java:49)
at in.nasv.utils.ImageServletTest.accessingImageViaHttp(ImageServletTest.java:45)
Here is the portion of code of ImageServletTest:
// prepare servlet instance
MockServletConfig config = new MockServletConfig(
new MockServletContextPatched());
ImageServlet servlet = new ImageServlet();
servlet.init( config ); // ImageServletTest, line 45
And this patched class (is not actually patched now):
public class MockServletContextPatched extends MockServletContext{ }
What am I supposed to do to avoid this "IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?" ?
I found an solution. But clear enough, but an solution.
Now servlet initialization is:
MockServletContext servletContext = new MockServletContextPatched();
MockServletConfig config = new MockServletConfig( servletContext );
ImageServlet servlet = new ImageServlet();
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext( "spring-data-app-context.xml" );
DefaultListableBeanFactory dlbf = new DefaultListableBeanFactory(appContext.getBeanFactory());
GenericWebApplicationContext gwac = new GenericWebApplicationContext(dlbf);
servletContext.setAttribute(GenericWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, gwac);
gwac.setServletContext(servletContext);
gwac.refresh();
servlet.init( config );
Preparing request and response in standard way:
MockHttpServletResponse response = new MockHttpServletResponse();
URL serverUrl = new URL( propertyExtendedService.getServerAddress(true) );
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI( "/what-you-want" );
request.setPathInfo( "/" + TEST_IMAGE );
request.setContentType("image/jpeg");
request.addHeader("Accept", "image/jpeg;image/jpg;" );
Final step is to call the filter and assert returned values:
servlet.doGet( request, response );
assertEquals( response.getStatus(), 200 );
// assert everything you want
Update: the updated documentation for getServletContext() is now online.
It is not necessary to implement a custom MockServletContextPatched class just to configure a custom MIME type in Spring's MockServletContext.
Since Spring's MockServletContext uses the Java Activation Framework (JAF) to implement the ServletContext.getMimeType(String) method, it is quite easy to configure a custom MIME type via JAF's MimetypesFileTypeMap.addMimeTypes(String) method as follows.
MockServletContext mockServletContext = new MockServletContext();
MimetypesFileTypeMap mimetypesFileTypeMap =
(MimetypesFileTypeMap) MimetypesFileTypeMap.getDefaultFileTypeMap();
mimetypesFileTypeMap.addMimeTypes("text/enigma enigma");
assertEquals("text/enigma", mockServletContext.getMimeType("filename.enigma"));
In the above JUnit based test code, I configured a custom MIME type "text/enigma" for files that have the extension .enigma.
Hope this helps!
Regards,
Sam (author of the Spring TestContext Framework)
p.s. I created JIRA issue SPR-12126 in order to improve the documentation of MockServletContext.