I am trying to introduce Guice(v3.0) in my project. I am using embedded tomcat(v7.0.34) and Jersey(v1.18) to host rest services.
Before introducing any Guice dependency injection I had the following configuration
//Main Class
Context context = tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
tomcat.addServlet(context, "Jersey REST Service", new ServletContainer(new DefaultResourceConfig(EntityResource.class)));
context.addServletMapping( "/rest/*", "Jersey REST Service");
tomcat.start();
tomcat.getServer().await();
//EntityResource
#Path("entity")
public class EntityResource {
final EntityService entityService;
public EntityResource()
{
this.entityService = new EntityService();
}
#Path("")
#Produces("application/json")
#GET
public Entity getEntity(){
return entityService.getEntity();
}
This worked fine. I was able to do GET on /rest/entity.
After adding Guice's constructor injection to EntityResource it looks like this
final EntityService entityService;
#Inject
public EntityResource(EntityService entityService)
{
this.entityService = entityService;
}
#Path("")
#Produces("application/json")
#GET
public Entity getEntity() {
return entityService.getEntity();
}
This gives an error "Missing dependency for constructor public com.my.rest.EntityResource(com.my.service.EntityService) at parameter index 0". I am guessing this is because of Guice's constructor injection.
Adding servlet in web.xml made it work
//web.xml
<web-app>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.my.servlet.MyServletListener</listener-class>
</listener>
//MyServletListener
public class MyServletListener extends GuiceServletContextListener {
#Override
public Injector getInjector() {
return Guice.createInjector(new JerseyServletModule(){
#Override
protected void configureServlets() {
bind(EntityResource.class);
bind(EntityService.class).to(com.my.impl.EntityService.class).in(Singleton.class);
serve("/rest/*").with(GuiceContainer.class);
}
});
}
}
Still looking for how to do this without web.xml.
Related
this is my full function
#RequestMapping(value = "/file",headers = "content-type=multipart/*", method = RequestMethod.POST)
#ResponseBody
public String fileUpload(HttpServletRequest request,Model model) throws IOException {
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
MultipartFile files = multipartHttpServletRequest.getFile("files");
if(files!=null) {
//do somethings
log.info(files);
return "true";
}else {
Part file = request.getPart("files");
log.info(file.getSize());
return "false";
}
}
and the MultipartFile files is null but the Part file is the result which I expected;
why? and how can i get file directly from multipartHttpServletRequest.getFile("files")
and my CommonsMultipartResolver config is like this
#Bean
public CommonsMultipartResolver multipartResolver(){
CommonsMultipartResolver commonsMultipartResolver=new CommonsMultipartResolver();
commonsMultipartResolver.setMaxInMemorySize(3000000);
return commonsMultipartResolver;
}
Make sure you have multipart filter in web.xml. This should be put before Spring Security filter chain if you are using that.
<filter>
<filter-name>multipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>multipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Define multipart resolver as (name is important):
#Bean
public MultipartResolver filterMultipartResolver()
{
return new StandardServletMultipartResolver();
}
Since request.getPart works you have a recent version of Servlet API and you don't need Commons FileUpload bean/dependency, remove.
Is there any other way to configure a context parameter excluding the one which uses web.xml as in the below example?
<context-param>
<param-name>com.sun.faces.numberOfLogicalViews</param-name>
<param-value>10</param-value>
</context-param>
Since Servlet 3.0, you can programmatically set them via ServletContext#setInitParameter().
Just put below ServletContextListener anywhere in your web project. The #WebListener annotation will make the container to automatically pickup and run it during webapp startup.
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
servletContext.setInitParameter("com.sun.faces.numberOfLogicalViews", "10");
}
#Override
public void contextDestroyed(ServletContextEvent event) {
// NOOP.
}
}
If you're using embedded Tomcat, it is like this:
Context.getServletContext()
.getServletRegistrations()
.get("webdavservlet")
.setInitParameter("listings", "true");
In the classic web.xml type configuration you could configure context parameters like so
web.xml
...
<context-param>
<param-name>p-name</param-name>
<param-value>-value</param-value>
</context-param>
...
How is this achieved in spring-boot. I have a filter that requires parameters.
I'm using #EnableAutoConfiguration and have included <artifactId>spring-boot-starter-jetty</artifactId> in my pom.
You can set parameters using the server.servlet.context-parameters application property. For example:
server.servlet.context-parameters.p-name=p-value
In Spring Boot 1.x, which is no longer supported, this property was named server.context-parameters:
servlet.context-parameters=p-name=p-value
Alternatively, you can configure parameters programmatically by declaring a ServletContextInitializer bean:
#Bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "-value");
}
};
}
You can actually achieve this using Java config. If you have filter that requires some parameters, just put them in your application.yml (or .properties), inject them using #Value in your config class and register them in FilterRegistrationBean.
For example:
#Value("${myFilterParam}")
private String myFilterParam;
#Bean(name="myFilter")
public FilterRegistrationBean myFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
filterRegistrationBean.setInitParameters(Collections.singletonMap("p-name", "p-value"));
return filterRegistrationBean;
}
Also JavaDoc for FilterRegistrationBean:
http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/embedded/FilterRegistrationBean.html
Update
You can register parameters for servlet context in SpringBootServletInitializer#onStartup() method. Your Application class can extend the SpringBootServletInitializer and you can override the onStartup method and set the parameters there. Example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "p-value");
super.onStartup(servletContext);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Other alternative is to define ServletContextInitializer bean as suggested by Andy Wilkinson.
Since Spring Boot 2.0.0 they updated the way to add context param:
server.servlet.context-parameters.yourProperty.
You can see more updates on this link
Also you can define InitParameterConfiguringServletContextInitializer in your configuration. Example:
#Bean
public InitParameterConfiguringServletContextInitializer initParamsInitializer() {
Map<String, String> contextParams = new HashMap<>();
contextParams.put("p-name", "-value");
return new InitParameterConfiguringServletContextInitializer(contextParams);
}
If we define webapp specific servlet filters in WAR's own web.xml, then the order of execution of the filters will be the same as the order in which they are defined in the web.xml.
But, if we define those filters using #WebFilter annotation, what is the order of execution of filters, and how can we determine the order of execution?
You can indeed not define the filter execution order using #WebFilter annotation. However, to minimize the web.xml usage, it's sufficient to annotate all filters with just a filterName so that you don't need the <filter> definition, but just a <filter-mapping> definition in the desired order.
For example,
#WebFilter(filterName="filter1")
public class Filter1 implements Filter {}
#WebFilter(filterName="filter2")
public class Filter2 implements Filter {}
with in web.xml just this:
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/url1/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/url2/*</url-pattern>
</filter-mapping>
If you'd like to keep the URL pattern in #WebFilter, then you can just do like so,
#WebFilter(filterName="filter1", urlPatterns="/url1/*")
public class Filter1 implements Filter {}
#WebFilter(filterName="filter2", urlPatterns="/url2/*")
public class Filter2 implements Filter {}
but you should still keep the <url-pattern> in web.xml, because it's required as per XSD, although it can be empty:
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern />
</filter-mapping>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern />
</filter-mapping>
Regardless of the approach, this all will fail in Tomcat until version 7.0.28 because it chokes on presence of <filter-mapping> without <filter>. See also Using Tomcat, #WebFilter doesn't work with <filter-mapping> inside web.xml
The Servlet 3.0 spec doesn't seem to provide a hint on how a container should order filters that have been declared via annotations. It is clear how about how to order filters via their declaration in the web.xml file, though.
Be safe. Use the web.xml file order filters that have interdependencies. Try to make your filters all order independent to minimize the need to use a web.xml file.
If Spring exists in your project you could use org.springframework.core.annotation.Order annotation over the class. F.e. #Order(0), #Order(1) etc.
Make the servlet filter implement the spring Ordered interface.
Declare the servlet filter bean manually in configuration class.
import org.springframework.core.Ordered;
public class MyFilter implements Filter, Ordered {
#Override
public void init(FilterConfig filterConfig) {
// do something
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// do something
}
#Override
public void destroy() {
// do something
}
#Override
public int getOrder() {
return -100;
}
}
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
public class MyAutoConfiguration {
#Bean
public MyFilter myFilter() {
return new MyFilter();
}
}
I am using Spring 3 with sitemesh. I would like to refer to spring context bean in decorator page defined in sitemesh.
The problem is that SiteMesh filter is working outside the Spring context, so request object on sitemesh decorator jsp page is native HttpServletRequest and not wrapper with useful functions to access context and etc.
Is there a way to somehow configure both spring and sitemesh to have access to Spring context in decorator page?
I had the same issue and solved my problem by using a filter. I created an environment filter that I could use for setting environment data for all requests. Autowire the bean you need to have access too in the filter.
#Component
public class EnvironmentFilter extends OncePerRequestFilter {
#Autowired
Object bean;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
request.setAttribute("bean", bean); // add bean or just specific properties of bean.
filterChain.doFilter(request, response);
}
}
Configure the filter in web.xml, be sure to use the same pattern for the filter mapping as you have for Sitemesh filter.
<filter>
<filter-name>environmentFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>environmentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The attributes set from your filter are now available from your decorator page.
Start by creating a singleton for whatever you fancy, I am just setting a String, but any Class will work:
public class MySiteEnvironment {
private String someConfigurationParameter;
public String getSomeConfigurationParameter() {
return someConfigurationParameter;
}
public void setSomeConfigurationParameter(String someConfigurationParameter) {
this.someConfigurationParameter = someConfigurationParameter;
}
/* SINGLETON */
private static final MySiteEnvironment INSTANCE = new MySiteEnvironment();
private MySiteEnvironment() {
}
public static MySiteEnvironment getInstance() {
return INSTANCE;
}
}
Next you need to inject the value:
<bean id="mySiteEnvironment" class="MySiteEnvironment" factory-method="getInstance">
<property name="someConfigurationParameter" value="myValueOrBean"/>
</bean>
Finally you access it by:
<%# page import="MySiteEnvironment" %>
<% pageContext.setAttribute("env", MySiteEnvironment.getInstance()); %>
Now you can use expression language to access the environment
I'm not aware of a way to do what you're asking, but there's another alternative as well. You can declare the HttpServletRequest in your controller method parameters. Just put the model objects on the request if they need to be available to Sitemesh. The JSP code looks exacty the same whether the backing context is the servlet request or the Spring MVC model.
I resolved this problem reimplementing the sitemesh filter:
#Component
class SitemeshSpringFilter extends PageFilter implements ApplicationContextAware {
ApplicationContext applicationContext;
#Override
public void doFilter(ServletRequest rq, ServletResponse rs,
FilterChain chain) throws IOException, ServletException {
def newRq = new ContextExposingHttpServletRequest(
rq, getApplicationContext(), null);
super.doFilter(newRq, rs, chain);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
In the web.xml, declare this filter:
<filter>
<filter-name>sitemeshSpringFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>sitemeshSpringFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Now, the sitemesh filter will use ContextExposingHttpServletRequest instead normal request.