spring conditional feature enabling - java

For this question, say, we have a MVC configuration class:
#Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
#Bean
public ViewResolver viewResolver() {
return new InternalResourceViewResolver();
}
// bla-bla
}
I have several bean definitions as usual in a configuration class. The question is while enabling some features such as #EnableCaching, #EnableMBeanExport, #EnableAspectJAutoProxy, how can i control enabling process of those features, ie:
#Configuration
// #EnableCaching instead of this
public class MvcConfig extends WebMvcConfigurationSupport {
#Autowired
SomeResource someResource;
#Bean
public SomeBean someBean() {
if(someResouce.someEvaluation(someParam)) {
enableCaching(); // is that enabling possible?
}
}
#Bean
public ViewResolver viewResolver() {
return new InternalResourceViewResolver();
}
// bla-bla
}
NOTE: I'm using spring 3.2.x

Related

Spring Boot does not resolve View

I'am trying to create a simple controller with Spring Boot
Congifuration is:
#Configuration
#EnableWebMvc
#ComponentScan (basePackages = { "ru.spb.chat.controller" })
public class WebConfig implements WebMvcConfigurer {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".html");
return bean;
}
}
and for servlet:
public class MainWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(final ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext root =
new AnnotationConfigWebApplicationContext();
root.scan("ru.spb");
sc.addListener(new ContextLoaderListener(root));
ServletRegistration.Dynamic appServlet =
sc.addServlet("mvc", new DispatcherServlet(new GenericWebApplicationContext()));
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/");
}
}
My controller.
#Controller
public class RootController {
#GetMapping ("/")
public String root() {
return "login";
}
}
But when I try to map on "/" it returns 404-ERROR
This is my project-structure:
Remove your WebConfig and remove your ServletInitializer and MainWebAppInitializer. (You can probably also remove the WebSocketConfig and use the auto-configuration from Spring Boot!).
Let your ChatApplication extend SpringBootServletInitializer and implement the configure method.
#SpringBootApplication
public class ChatApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ChatApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ChatApplication.class);
}
}
Then in your application.properties add
spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.html
Now you are using the proper Spring Boot auto-configuration AND proper way of creating a WAR which is executable.
That being said, you generally don't want a WAR (only if you use JSP, which is discouraged with embedded containers).

Spring MVC 4 controllers not called

EDIT: I realized I made a mistake in my ComponentScan as a lot of commenters pointed out, I changed it but it is still not working (still 404).
I have a project and I'm using all annotations-based configuration. Here is the configuration files:
WebConfig.java
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "src.controller")
public class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
WebInitializer.java
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
I also have a persistence config but I don't think that's relevant to this problem. Anyways, here is my root path controller:
AuthorController.java
#Controller
#RequestMapping({"/authors", "/"})
public class AuthorController {
#Autowired
AuthorService authorservice;
#RequestMapping(method=RequestMethod.GET)
public String getAuthors(ModelMap model){
System.out.println("----------called--------------"); //not printed
List<Author> authors = authorservice.getAllAuthors();
model.addAttribute("authors", authors);
return "authors";
}
}
The controller never gets called and I end up getting a 404 error.
Can you tell the name of the package to which 'AuthorController' belongs ? I think issue is with #ComponentScan(basePackages = "src"). Here you should add package name of the controller classes.
#ComponentScan(basePackages = "com.sample.app")
#ComponentScan(basePackages = "com.sample.*")
#ComponentScan(basePackages = "com.*")
All the above are valid entries for ComponentScan annotation.
The basePackages property of #ComponentScan annotation doesn't represent the folder where your java code is. It represents the root package of java classes where Spring should scan for Spring beans.
So, if you have your controllers in com.myapp.web.controllers then configure it as follows:
#ComponentScan(basePackages = "com.myapp.web.controllers")

swagger for spring: No mapping found

I am trying to make a documentation of my API with swagger.
My REST API is written with the help of spring and typical Controller looks like:
#Controller
#RequestMapping("/mymapping")
public class ParameterController {
#Autowired
Mydao myDao;
/**
* GET list
*
* #return The resulting string
*/
//produces replaced accept-header
#RequestMapping(value="/Parameter", method=RequestMethod.GET, produces={MediaType.APPLICATION_JSON_UTF8_VALUE, "application/ld+json"})
public #ResponseBody List<String> getAllParameters() {
List<String> params= myDao.getAllParameters();
return params;
}
}
To configurate the Swagger I followed the instruction here:
added a package in Maven
created a configuration Java class SwaggerConfig (I use only Java-configuration for Spring)
added resource Handlers to the existing class extending WebMvcConfigurerAdapter
added packages "io.swagger" and "springfox.documentation" to the #ComponentScan annotation of the same class extending WebMvcConfigurerAdapter
But each time when I call http://localhost:8080/spring-security-rest/api/v2/api-docs I receive
16:43:43,370 WARN qtp1072754634-13 servlet.PageNotFound:1136 - No mapping found for HTTP request with URI [/spring-security-rest/api/v2/api-docs] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-437a7f13'
The system searches the path /spring-security-rest/api/v2/api-docs among the controllers' mappings. And does not find it.
What did I forget?
Thank you in advance.
I solved mine by placing the SwaggerConfig Java Class in the same package where I had a the class with #SpringBootApplication.
Heres a suggestion:
Typically this is due to improper use of component scan. You can read more about component scan here (read the 2 questions fully :) )
Click here for the 2 questions
my SwaggerConfig class look like this:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket beerMapApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(regex("/api.*"))
.build()
.pathMapping("/")
.apiInfo(apiInfo());
}
#Bean
public UiConfiguration uiConfig() {
return UiConfiguration.DEFAULT;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("title")
.description("description")
.version("0.1")
.build();
}
}
and this is how my WebMvcConfigurerAdapter.addResourceHandlers looks:
#Configuration
#EnableWebMvc
#ComponentScan({ "com.something.server.controller" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
registry
.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}

Spring Boot internationalization (messages.properties)

I'm trying to simply add the version number of an application to a Thymeleaf fragment. I'm using Spring Boot 1.2.5. If I have a file named /resources/messages.properties defined like this:
application.version=1.0.0
And I have a Thymeleaf view with the following fragment:
Application Version: <span th:text="#{application.version}">
It's displaying something like ??application.version_en_US?? instead of 1.0.0. (I also have files named messages_en.properties and messages_en_US.properties in the classpath with the same contents too.) I am really not sure how to resolve this problem... I've spent hours on something which seems incredibly trivial...
Application.java
#SpringBootApplication
#ComponentScan(basePackages = {"org.application"})
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, ThymeleafAutoConfiguration.class})
#PropertySources(value = {#PropertySource("classpath:website.properties")})
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
WebConfig.java
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public SpelAwareProxyProjectionFactory projectionFactory() {
return new SpelAwareProxyProjectionFactory();
}
#Bean
public SessionHandler sessionHandler() {
return new SessionHandler();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/web/auth/login").setViewName("auth/login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(
"/resources/");
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
#Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH);
return localeResolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
return new ResourceBundleMessageSource();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
// Locale change interceptor
registry.addInterceptor(localeChangeInterceptor());
// Utility interceptor which helps with the "active" link styles in the navigation. --mm
registry.addInterceptor(new BaseInterceptor());
// Expire session after a period of time
registry.addInterceptor(sessionHandler());
}
}
ThymeleafConfig.java
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
// must use Legacy HTML5 as the template, otherwise Handlebars will not parse!
//
// this should hopefully be fixed in Thymeleaf 3.0
resolver.setTemplateMode("LEGACYHTML5");
resolver.setCacheable(false);
return resolver;
}
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
// Add Spring security
Set<IDialect> dialects = new HashSet<IDialect>();
engine.setAdditionalDialects(dialects);
engine.addDialect(new SpringSecurityDialect());
return engine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
viewResolver.setViewNames(new String[]{"*"});
viewResolver.setCache(false);
return viewResolver;
}
}
Will buy a virtual round of shots to whomever can resolve this issue...
i guess you could always add this in your templateEngine method:
engine.addMessageResolver(new StandardMessageResolver());
or engine.setMessageResolver(new StandardMessageResolver());
Also,from the design perspective,i would suggest you to try using the autoconfiguration for thymeleaf(removing the exclude),and many other stuff which spring boot provides automatically for you.

Spring 3.1 configuration: environment not injected

I am using the following for the spring 3.1 configuration:
#Configuration
#EnableTransactionManagement
public class DataConfig {
#Inject
private Environment env;
#Inject
private DataSource dataSource;
// #Bean
public SpringLiquibase liquibase() {
SpringLiquibase b = new SpringLiquibase();
b.setDataSource(dataSource);
b.setChangeLog("classpath:META-INF/db-changelog-master.xml");
b.setContexts("test, production");
return b;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean b = new LocalContainerEntityManagerFactoryBean();
b.setDataSource(dataSource);
HibernateJpaVendorAdapter h = new HibernateJpaVendorAdapter();
h.setShowSql(env.getProperty("jpa.showSql", Boolean.class));
h.setDatabasePlatform(env.getProperty("jpa.database"));
b.setJpaVendorAdapter(h);
return (EntityManagerFactory) b;
}
#Bean
public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
PersistenceExceptionTranslationPostProcessor b = new PersistenceExceptionTranslationPostProcessor();
// b.setRepositoryAnnotationType(Service.class);
// do this to make the persistence bean post processor pick up our #Service class. Normally
// it only picks up #Repository
return b;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager b = new JpaTransactionManager();
b.setEntityManagerFactory(entityManagerFactory());
return b;
}
/**
* Allows repositories to access RDBMS data using the JDBC API.
*/
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
#Bean(destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource db = new BasicDataSource();
if (env != null) {
db.setDriverClassName(env.getProperty("jdbc.driverClassName"));
db.setUsername(env.getProperty("jdbc.username"));
db.setPassword(env.getProperty("jdbc.password"));
} else {
throw new RuntimeException("environment not injected");
}
return db;
}
}
the issue is that the variable env is not injected and is always null.
I have not done anything about the Environment setup since I do not know if it's needed or how to. I looked at the greenhouse example and i did not find anything specifically for Environment. What should I do to make sure the env is injected?
The related files:
// CoreConfig.java
#Configuration
public class CoreConfig {
#Bean
LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
/**
* Properties to support the 'standard' mode of operation.
*/
#Configuration
#Profile("standard")
#PropertySource("classpath:META-INF/runtime.properties")
static class Standard {
}
}
// the Webconfig.java
#Configuration
#EnableWebMvc
#EnableAsync
// #EnableScheduling
#EnableLoadTimeWeaving
#ComponentScan(basePackages = "com.jfd", excludeFilters = { #Filter(Configuration.class) })
#Import({ CoreConfig.class, DataConfig.class, SecurityConfig.class })
#ImportResource({ "/WEB-INF/spring/applicationContext.xml" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**").addResourceLocations(
"/images/");
}
#Bean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver b = new BeanNameViewResolver();
b.setOrder(1);
return b;
}
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver b = new InternalResourceViewResolver();
b.setSuffix(".jsp");
b.setPrefix("/WEB-INF/jsp/");
b.setOrder(2);
return b;
}
#Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver b = new CookieLocaleResolver();
b.setCookieMaxAge(100000);
b.setCookieName("cl");
return b;
}
// for messages
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource b = new ResourceBundleMessageSource();
b.setBasenames(new String[] { "com/jfd/core/CoreMessageResources",
"com/jfd/common/CommonMessageResources",
"com/jfd/app/AppMessageResources",
"com/jfd/app/HelpMessageResources" });
b.setUseCodeAsDefaultMessage(false);
return b;
}
#Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("org.springframework.web.servlet.PageNotFound", "p404");
mappings.put("org.springframework.dao.DataAccessException",
"dataAccessFailure");
mappings.put("org.springframework.transaction.TransactionException",
"dataAccessFailure");
b.setExceptionMappings(mappings);
return b;
}
/**
* ViewResolver configuration required to work with Tiles2-based views.
*/
#Bean
public ViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
/**
* Supports FileUploads.
*/
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(500000);
return multipartResolver;
}
// for configuration
#Bean
public CompositeConfigurationFactoryBean myconfigurations()
throws ConfigurationException {
CompositeConfigurationFactoryBean b = new CompositeConfigurationFactoryBean();
PropertiesConfiguration p = new PropertiesConfiguration(
"classpath:META-INF/app-config.properties");
p.setReloadingStrategy(new FileChangedReloadingStrategy());
b.setConfigurations(new org.apache.commons.configuration.Configuration[] { p });
b.setLocations(new ClassPathResource[] { new ClassPathResource(
"META-INF/default-config.properties") });
return b;
}
#Bean
org.apache.commons.configuration.Configuration configuration()
throws ConfigurationException {
return myconfigurations().getConfiguration();
}
// and the SecurityConfig.java
#Configuration
#ImportResource({ "/WEB-INF/spring/applicationContext-security.xml" })
public class SecurityConfig {
#Bean
public BouncyCastleProvider bcProvider() {
return new BouncyCastleProvider();
}
#Bean
public PasswordEncryptor jasyptPasswordEncryptor() {
ConfigurablePasswordEncryptor b = new ConfigurablePasswordEncryptor();
b.setAlgorithm("xxxxxx");
return b;
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder b = new org.jasypt.spring.security3.PasswordEncoder();
b.setPasswordEncryptor(jasyptPasswordEncryptor());
return b;
}
}
in the applicationcontext.xml, it only imported two xmls to config cache and cassandra, so it may not be important.
Not sure why, but using the #Resource annotation worked for me. #Autowired always returned null.
The problem is with the spring security for the remember me feature. if I take this line <code> <remember-me data-source-ref="dataSource" /> </code> out. everything works fine. if this line presents, it will try to load the db before anything else and env was never injected.
If you don't use full Java EE compatible server you have to include javax.inject.jar to your project classpath to add the support of #Inject. You can also try to use spring's native #Autowired annotation.
#jfd,
I don't immediately see anything wrong with your configuration that would cause a failure to inject the Environment.
If you start from scratch with an empty #Configuration class, and then #Inject the Environment, does it work for you?
If yes, then at what point does it begin to fail?
Would you be willing to reduce the example down to the smallest possible configuration that fails and submit it as a reproduction project? The instructions here make this as simple as possible: https://github.com/SpringSource/spring-framework-issues#readme
Thanks!
I've detect a similar error for my project as mentioned here.
I've also figure out, that a call of afterproperties is necessary to get the sessionFactory.
... and yes, I'm using Spring Security too (which may be the source of the problem).
My #Configuration annotated class uses #ComponentScan for packages containing Hibernate based DAOs and a #Bean annotated method for creating the SessionFactory used by the DAOs. At runtime, a exception is thrown, mentioned that 'sessionFactory' or 'hibernateTemplate' was not found. It seems that the DAOs are constructed before the SessionFactory was created. One workaround for me was to put the component scan directive back in a XML file () and replace #ComponentScan with #ImportResource of that file.
#Configuration
//#ComponentScan(basePackages = "de.webapp.daocustomer", excludeFilters = {#ComponentScan.Filter(Configuration.class), #ComponentScan.Filter(Controller.class)})
#ImportResource({"classpath*:componentScan.xml","classpath*:properties-config.xml","classpath*:security-context.xml"})
public class AppConfig
{
...
#Bean
public SessionFactory sessionFactory() throws Exception
{
AnnotationSessionFactoryBean bean = new AnnotationSessionFactoryBean();
bean.setDataSource(dataSource());
bean.setPackagesToScan(new String[] {"de.webapp"});
bean.setHibernateProperties(hibernateProps());
bean.afterPropertiesSet();
return bean.getObject();
}
Also interesting fact: if #ComponentScan is included, a breakpoint set in method sessionFactory() was never reached !
I also had the similar issue with spring-social-sample app.
After I converted field level #Inject to constructor level inject it worked.

Categories

Resources