Currently, I know two ways:
Specify it as an <init-param> on you web.xml
Create a class that extends ResourceConfig and add it on your web.xml
I have this class that extends ResourceConfig because I needed to register an Application Binder (AbstractBinder) for dependency injection.
Where should I define the location of my REST resources? What's the best practice here?
In the ResourceConfig, you can call packages("reource.packages"), which will do the same as scanning the package declared inside the <init-param>
public class Config extends ResourceConfig {
public Config() {
packages("...");
register(new AbstractBinder()..);
}
}
To use the Config class, you have a couple options.
Annotate it with #ApplicationPath("/appPath")
With this, no web.xml is required. You need to make sure you have the jersey-container-servlet dependency for this to work. The value in the annotation works the same way as the <servlet-mapping> inside the web.xml.
Declare the Config class inside the web.xml
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.foo.Config</param-value>
</init-param>
</servlet>
...
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/myPath/*</url-pattern>
</servlet-mapping>
You could also not use the ResourceConfig and register the binder inside a Feature, as discussed here
See Also:
Application Deployment and Runtime Environments: Servlet-based Deployment
How to use Jersey as JAX-RS implementation without web.xml?
Related
I am trying to setup Angular 7 with a maven based back-end java project into a single war file. At the moment I am trying to configure the web.xml file where I am currently having this problem. I am not sure at all if my approach is valid or 'good' therefore I will first describe what I am trying to do (if you think better on this aspect please do correct me).
So I have a couple of JAX-RS classes which I'd like to serve as a REST API. For this purpose I have created corresponding javax.ws.rs.core.Application classes to provide these REST components. Then I am including the Application classes in the web.xml file. Below are the files:
web.xml
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>backend.backendservice.StammSolvaraJahrRestApplication</servlet-name>
<servlet-class>backend.backendservice.StammSolvaraJahrRestApplication</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>backend.backendservice.StammSolvaraJahrRestApplication</servlet-name>
<url-pattern>/rmz/*</url-pattern>
</servlet-mapping>
Another variation of web.xml that I tried
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>backend.backendservice.StammSolvaraJahrRestApplication</servlet-name>
<servlet-class>backend.backendservice.StammSolvaraJahrRestApplication</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>backend.backendservice.StammSolvaraJahrRestApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>backend.backendservice.StammSolvaraJahrRestApplication</servlet-name>
<url-pattern>/rmz/*</url-pattern>
</servlet-mapping>
Application class
public class StammSolvaraJahrRestApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> sets = new HashSet<>();
sets.add(StammSolvaraJahrRest.class);
return sets;
}
}
The error that I get is: java.lang.ClassCastException: backend.backendservice.StammSolvaraJahrRestApplication cannot be cast to javax.servlet.Servlet and if I remove the <servlet-class> then I'll get No servlet class has been specified for servlet. I am following https://docs.oracle.com/cd/E24329_01/web.1211/e24983/configure.htm#RESTF183 and How to deploy a JAX-RS application? among others but it seems not to be working.
There are two ways to define your JAX-RS servlet.
1) With Application Subclass like the one you have, you can skip the web.xml config and just add the application annotation
#ApplicationPath("resources")
public class StammSolvaraJahrRestApplication extends Application
2) With web.xml config
<servlet>
<display-name>JAX-RS Servlet</display-name>
<servlet-name>package.hierarchy.StammSolvaraJahrRestApplication</servlet-name>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>package.hierarchy.StammSolvaraJahrRestApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>JaxRSServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
If you skip the servlet mapping from the last one, it will use your your #ApplicationPath specified value, or "/resources" if the previous one is missing.
The problem is just what it says. This line in your web.xml requires a javax.servlet.Servlet:
<servlet-class>backend.backendservice.StammSolvaraJahrRestApplication</servlet-class>
Since an Application is not a javax.servlet.Servlet, you're getting the error at runtime when your XML file is processed.
If you can, I would suggest that you start with a Spring Boot starter application. Spring Boot handles all of this for you. It can even embed a Tomcat server inside a jar file so that you can run your server like a simple Java application. Doing this would save you having to worry about what you're dealing with here.
I am working on a web service and I get very strange error.
This is the line from my web.xml:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>service</param-value>
</init-param>
As I know, <param-value> has to be referred in to the package my main application is. However, my application is in rest.main package, but the web service works only with the service value as defined above.
What is the problem, can somebody explain me these lines?
Have a look at the documentation regarding the jersey.config.server.provider.packages configuration property:
Defines one or more packages that contain application-specific resources and providers. If the property is set, the specified packages will be scanned for JAX-RS root resources and providers.
Servlet 2.x containers
This setting is frequently used in the web.xml deployment descriptor to instruct Jersey to scan these packages and register any found resources and providers automatically:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
org.foo.myresources,org.bar.otherresources
</param-value>
</init-param>
With this setting, Jersey will automatically discover the resources and providers in the selected packages. By default, Jersey will recursively scan the sub-packages as well.
Servlet 3.x containers
For Servlet 3.x containers, no web.xml is necessary at all. Instead, an #ApplicationPath annotation can be used to annotate a custom Application or ResourceConfig subclass and define the base application URI for all JAX-RS resources configured in the application.
Use the following to defined the packages that will be scanned:
#ApplicationPath("resources")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.myresources,org.bar.otherresources");
}
}
For more details, check the deployment section of the Jersey documentation.
Important
Always use the qualified name of the package;
Use , or ; as delimiter when declaring multiple packages.
You just need to add the package name to the
Form an Example if I have placed my resource class in com.ft.resources package
then I have to add the package name in
<init-param>
<!-- For Jersey 2.x -->
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.ft.resources</param-value>
</init-param>
Hope this might resolve your issue
If the application consists only resources and providers stored in particular packages, Jersey can scan them and register automatically.
<web-app>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.foo.rest;org.bar.rest</param-value>
</init-param>
...
</servlet>
...
</web-app>
The param-value refers to the packages which will be scanned automatically.
#EnableZuulProxy doesn't work under a servlet 2.5 container. Is there any workaround to get spring-cloud zuul work under a servet 2.5 container?
Also I could not find the annotation processor of #EnableZuulProxy. Please provide the class which propesses #EnableZuulProxy so that I can better understand what this annotation really does.
Spring Cloud is meant to be run on servlet 3.0. That being said, it is possible to get #EnableZuulProxy running on servlet 2.5. I had to figure out a hack for this as I had to get this working in Tomcat 6.
The main issue is due to the ZuulConfiguration.class, which has the method:
#Bean
#ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() { ... }
The issue here is that ServletRegistrationBean uses javax.servlet.Registration$Dynamic, which is not available until Servlet 3.0. This results in a NoClassDefFoundError.
To work around this, use the spring-boot-legacy project to first register a DispatcherServlet. Secondly, you'll have to manually create a zuul servlet.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.dm.gateway.microservicegateway.Application</param-value>
</context-param>
<listener>
<listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ContextLifecycleFilter</filter-name>
<filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ContextLifecycleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>zuul</servlet-name>
<servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
I found the best way to remove the auto servlet registration, is to just make a verbatim copy of ZuulConfig called ZuulOverrideConfig, and remove the zuulServlet() method. This is because the ZuulProxyConfiguration extends ZuulConfiguration, and it seemed to create the bean even when i tried to override it. I'm not 100% on the mechanics behind this, so there may be a better way.
The second change I made in ZuulOverrideConfig was to call an extended implementation of ZuulFilterInitializer, called 'LegacyZuulFilterInitializer`. This is because for some reason, the Zuul servlet was being crated, and able to be invoked, but no filters were bootstrapped. This extension is a hacky way to get the filters to bootstrap.
Next, I created a copy of ZuulProxyConfiguration called ZuulLegacyProxyConfiguraiton, and had it extend ZuulOverrideConfig.class.
Finally, I annotated the Application class as follows.
#EnableCircuitBreaker
#EnableDiscoveryClient
#Import(ZuulLegacyProxyConfiguration.class)
#SpringBootApplication
public class Application {....}
After all these hacks, the implementation finally worked as expected. I wouldn't suggest using this for long, as it's pretty hacky. You won't get configuration class updates automatically when moving to new versions, and I can't guarantee that something won't break randomly!
This is using Spring Cloud 1.1.4.RELEASE
Gist of all the code.
#EnableZuulProxy is from Spring Cloud which is based on Spring Boot which is Servlet 3.0 and above. If you need to use Servlet 2.5 you can use the Netflix APIs directly.
#EnableZuulProxy is meta-annotated with #Import(ZuulProxyConfiguration.class) so I guess that's what you mean when you say "propesses"? If you don't know what an #Import is, go and read up on Spring.
how can application itself be inserted in database during startup only once.
I already tried using #postconstruct to do this.
When using Spring the following are common usage scenario's.
If you are building a web application you add some configuration to web.xml, for example (example for Spring 3):
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
This makes sure the Spring application context in the file /WEB-INF/spring/dispatcher-config.xml is loaded when the web application is loaded by the container (e.g. Tomcat). Your application will in this case probably contain of controllers of some sort (classes which handle requests) and these can be set up in spring to be injected with dependencies from the application context.
If you are building a standalone application you need to start it using the main method as usual. To load the Spring application context you need to do something like this:
ApplicationContext context = new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
MyApplication app = (MyApplication )context.getBean("myApp");
app.doSomething();
This loads the application context from the classpath.
I have a Spring ApplicationListener bean registered to listen for ContextRefreshed events. For some odd reason though, I get two calls to the onApplicationEvent(ContextRefreshedEvent) method at the completion of the context initialization. Is this normal behavior or is it indicative of a problem with my configuration? I'm using Jetty 8 for my Servlet container.
My relevant web.xml configuration is as follows
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/spring/spring-config.xml</param-value>
</context-param>
<servlet>
<servlet-name>Spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet-mapping>
<servlet-name>Spring</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
Thanks!
Even though you did not specify a contextConfigLocation for your DispatcherServlet it still creates a child context and the second refreshed event is for that context. Use event.getApplicationContext() to find out which context the event is for.
it happened to me as well, on a different event-listener. (ApplicationListener<AuthenticationFailureBadCredentialsEvent>)
I suspected the ContextLoaderListener, and when I removed the declaration from the web.xml, the app was working properly. Then I had to figure out what is its purpose, of the ContextLoaderListener...
Role/Purpose of ContextLoaderListener in Spring?
the interesting answer there is:
ContextLoaderListener is optional. Just to make a point here: you can
boot up a Spring application without ever configuring
ContextLoaderListener ...just the basic minimum web.xml with
DispatcherServlet
It looks like bug.
https://jira.springsource.org/browse/SPR-6589
If you are using 3.0 try it on the latest available release which is 3.05.
I had this problem too but fixed it. I was injecting the dataSource into my DAO (and instantiating a JdbcTemplate with it)....but I also had a Spring bean configured for JDBCTemplate.
I should have been injecting my DAO with the jdbcTemplate...that avoids the duplicate.