How to set InternalResourceViewResolver prefix with value from database? - java

I have a CMS based on Spring MVC 4 and I want user to choose from different visual themes. They are stored in separate folders.
For now theme name is hardcoded in Properties.THEME_NAME, but I want to make the value stored in database and loaded as prefix part for InternalResourceViewResolver. So user can change it and switch to another theme. Is it possible to make this changes dinamically when app is running?
My code for configuration:
#EnableWebMvc
#Configuration
#ComponentScan({ "shop.main.*" })
#Import({ SecurityConfig.class })
#PropertySource("classpath:application.properties")
public class AppContextConfig extends WebMvcConfigurerAdapter {
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/pages/" + Properties.THEME_NAME + "/");
resolver.setSuffix(".jsp");
return resolver;
}
// other methods
}

One way of achieving my goal is to extend InternalResourceViewResolver and override getter method to return prefix loaded dinamicaly.
So configuration changes to:
#Bean
public ThemedResourceViewResolver internalResourceViewResolver() {
ThemedResourceViewResolver resolver = new ThemedResourceViewResolver();
resolver.setSuffix(".jsp");
return resolver;
}
And the custom resolver class:
public class ThemedResourceViewResolver extends InternalResourceViewResolver {
#Autowired
private SitePropertyDAO sitePropertyDAO;
protected String getPrefix() {
String prefix = "/pages/" + Properties.THEME_NAME + "/";
SiteProperty property = sitePropertyDAO.findOneByName(Constants.THEME);
if (property != null) {
prefix = "/pages/" + property.getContent() + "/";
}
return prefix;
}
}
I used this approach because I need not only to change the CSS files path or properties, but folders where my .jsp views are stored, so different themes have different layouts.

Related

What is the best way to configure PropertySourcesPlaceholderConfigurer / resource folders?

I have a (gradle/java/eclipse) project with what seems to me to be quite a standard folder structure
...main
java
...controller
...service
resources
webapp
...resources
WEB-INF
I'm having an issue I just don't understand although I have worked around it in a very messy way. If I specify the controllers folder in the component scan of my WebMvcConfigurerAdapter then the services classes can't pick up properties using the configured PropertySourcesPlaceholderConfigurer bean. If I broaden the component scan out the jsp files don't pick up the css includes!
So with this config class all is well but the properties aren't resolved int he service implementation class
config class
#EnableWebMvc
#Configuration
#ComponentScan({ "comm.app.controller" })
#PropertySource({ "classpath:app.properties" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
//used to handle html put and delete rest calls
#Bean(name = "multipartResolver")
public CommonsMultipartResolver createMultipartResolver() {
CommonsMultipartResolver resolver=new CommonsMultipartResolver();
resolver.setDefaultEncoding("utf-8");
return resolver;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Service implementation class begins
#Service("appService")
#PropertySource({ "classpath:app.properties" })
public class appServiceImpl implements appService {
private RestTemplate restTemplate = new RestTemplate();
#Value("${property.reference}")
private String propref;
...
In this case ${property.reference} is not picked up but the view page styling (picked up from ...webapp\resources... .css) is fine.
If I change
#ComponentScan({ "comm.app.controller" })
to
#ComponentScan({ "comm.app" })
The properties are picked up (presumably because the propertyConfig bean comes into scope ?) but the local styling files can't be found ? Any link references to files webapp\resources... .css fails.
In the end I came to a bad (!?) solution of
1) keeping the scope of #ComponentScan({ "comm.app.controller" })
2) hacking the serviceimplementation class as so...
#Configuration
#Service("appService")
#PropertySource({ "classpath:app.properties" })
public class appServiceImpl implements appService {
private RestTemplate restTemplate = new RestTemplate();
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
#Value("${property.reference}")
private String propref;
...
Can anyone tell me if I've configured the path the the resources file incorrectly or maybe should be doing something differently with the propertyConfig bean ? (maybe injecting it or declaring another one in a different configuration file ?)

Spring i18n question marks instead of text

I hava i18n, but experience problems with russian letters. I have question marks ????? instead of text. Configuration:
#Bean
public LocaleResolver localeResolver(){
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(new Locale("ru"));
resolver.setCookieName("locale");
resolver.setCookieMaxAge(60 * 60 * 24 * 365 * 10);
return resolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor changeInterceptor = new LocaleChangeInterceptor();
changeInterceptor.setParamName("lang");
return changeInterceptor;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
Also I am using Thymeleaf.
<h1 th:text="#{message}"></h1>
Just a guess: in "addInterceptor" you are constructing the LocaleChangeInterceptor manually yourself. If this class depends on other spring beans (like MessageSource) then these are not injected. Find a way to let spring instantiate the LocaleChangeInterceptor (e.g. by using one of the various getBean() of the application context).
Your files encoding is wrong. You should set it to UTF-8. If you use Intellij IDEA you can find it in the right bottom corner. If this option is disabled go to Settings -> Editor -> File Encodings and change the Default encoding for properties files parameter

How to include my class into Spring context?

We're running a Spring setup where I don't see any XML config files, seems everything is done via annotation.
I've got some custom component classes in a specific package I need added to the spring context for autowiring and I annotated the class with #Component but it's not making a difference. Am I missing another annotation?
There is one loop I have where I needed to do a component scan to discover all the classes in the package, maybe I can just add them there since I'd already have a BeanDefinition handle on them. If so, what would I have to do?
for (BeanDefinition bd : scanner.findCandidateComponents("com.blah.target")) {
// how to add it to context here?
}
If you don't see any XML config file, then the project should have a package springconfig with a java file called WebConfig.java. This is exact equivalent of XML config file.
Below is a snippet of a typical Webconfig.java
package .....springconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
<...>
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="<your source package>")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
String dir="/resources/";
registry.addResourceHandler("/images/**").addResourceLocations(dir + "images/");
...
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".jsp");
return resolver;
}
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(100);
return resolver;
}
}
Check out this tutorial: Simple Spring MVC Web Application It is very nicely explained here.

Spring MVC - Return static page

I'm struggling with trying to return a static web page from a spring MVC controller.
I followed this tutorial: http://www.tutorialspoint.com/spring/spring_static_pages_example.htm and yet it still isn't working.
This is how I defined the configuration (used configuration class):
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan({ "com.my.web.api"})
#ImportResource("classpath:db-context.xml")
public class ApiServletConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix("/resources/");
internalResourceViewResolver.setSuffix("*.html");
return internalResourceViewResolver;
}
}
The controller method:
#RequestMapping(value = "/{id}/view", method = RequestMethod.GET, produces = "text/html")
#ResponseBody
public String getPostByIdHtml( #PathVariable String id) throws IOException {
return "/resources/Post.html";
}
Under the webapp folder there's a folder named "resources" and under which there's a file "Post.html". What else should I do in order to get this page returned as HTML instead of getting the string "resources/Post.html"?
Thanks for the help.
Please remove the annotation #ResponseBody. Your browser should be redirected to the desired page once the annotation is removed.
This annotation indicates that the value returned by a method in your controller should be bound to the web response body. In your case, you do not need that: you need Spring to render page /resources/Post.html, so no need for this annotation.

Prioritize wildcard mappings in Spring

I'm wondering if it's possible to set priorities for controllers for wildcard mappings. For example, I want to be able to map the following:
/**
/static/**
/resources/**
And have the static and resources take precedence before the catchall. I've tried having my global controller implementing Ordered and using ResourceHandlerRegistry.setOrder() for the latter two (and also setting the order on the viewResolver to a lower priorty), but that doesn't seem to work. I haven't found any real good resources detailing how to do this. Anyone know how, or if it's even possible?
Thanks for any help provided.
Edit:
Config is as follows:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.mysite" )
public class BaseConfig extends WebMvcConfigurerAdapter {
/**
* Allow us to map a custom directory for resources.
*/
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/");
registry.addResourceHandler("/inc/**").addResourceLocations("/WEB-INF/views/ajaxincludes/");
registry.setOrder(1);
}
/**
* view resolver
*
* #return
*/
#Bean
public InternalResourceViewResolver configureInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setOrder(2);
return resolver;
}
}

Categories