How can I enable MultiPartFeature? - jersey

My JAX-RS application has an extended Application class.
#ApplicationPath("/")
public class MyApplication extends Application {
// empty; really empty
}
How can I enable org.glassfish.jersey.media.multipart.MultiPartFeature without modifying the class? Or without the necessity of registering all resource classes/packages?

Not sure why you don't just use a ResourceConfig instead of an Application class. The only reason I can think of is portability, but the use of the Jersey specific multipart feature already breaks that portability.
But anyway, I'll try to answer this in the "most portable" way. What you can do is set a property, as you would in a web.xml. To set arbitrary properties, you can override
#Override
public Map<String, Object> getProperties() {}
in the Application subclass, and set the properties there.
#Override
public Map<String, Object> getProperties() {
Map<String, Object> props = new HashMap<>();
props.put("jersey.config.server.provider.classnames",
"org.glassfish.jersey.media.multipart.MultiPartFeature");
return props;
}
This will maintain the classpath scanning for your resources and providers. The scanning is only disabled if you override getClasses() or getSingletons() (and return non-empty sets), but getProperties() is fine.
Another Option:
Create a Feature to wrap that feature, and let the feature be discovered, as seen here
Personally, I would...
Just use a ResourceConfig, as you're already breaking portability (what's a little more breakage :-)
#ApplicationPath("/")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("packages.to.scan");
register(MultiPartFeature.class);
}
}

For me worked like below:
final ResourceConfig resourceConfig = new ResourceConfig(ApplicationConfig.class);
resourceConfig.packages("com.econorma.rest");
resourceConfig.register(MultiPartFeature.class);
ServletHolder jerseyServlet = new ServletHolder(new ServletContainer(resourceConfig));
This is ApplicationConfig class
#ApplicationPath("/")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(MultiPartFeature.class);
resources.add(EntryPoint.class);
return resources;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("jersey.config.server.provider.packages", "com.econorma.rest");
return properties;
}
}

Related

How to take a map and use it as values in spring-boot

I'm working on a spring-boot application and trying to use properties defined in a map as values that can be injected into various services. The code I am working with defines an object PropertyLoader which is able to return a map based on an environment. It looks something like this:
public interface PropertyLoader {
public Map<String, String> load(String env);
}
How can I make the entries in the map returned by this method available in #Value injections in spring components.
You can define custom application listener that will override default properties.
Old answer - below is a better way to achieve it
public class PropertyInjector implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
final ConfigurableEnvironment environment = event.getEnvironment();
final Properties props = new Properties();
//inject properties here
props.put("key", "value");
final PropertiesPropertySource propertySource = new PropertiesPropertySource("propertySource", props);
environment.getPropertySources().addFirst(propertySource);
}
}
UPDATE - I have found better way that easily works with #Value annotation
#Configuration
public class SomeConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
final PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
final Properties props = new Properties();
props.put("some_dynamic_key", "some_dynamic_value");
final PropertiesPropertySource propertySource = new PropertiesPropertySource("propertySource", props);
propertySourcesPlaceholderConfigurer.setProperties(props);
return propertySourcesPlaceholderConfigurer;
}
#Value("${some_dynamic_key}")
String property;
}
In such way value some_dynamic_key is available along all the application

Singleton property on springboot

Can I create one singleton property in spring boot?
When I use this:
public class MessengerPlatformCallbackHandler {
#Scope(value = "singleton")
private Map<String, Object> conversationID = new HashMap<>();
I got the erro: #Scope not applicable to field
tks
You need to create it this way.
#Configuration
public class ConversationIDConfig {
#Bean
#Scope(value = "singleton")
public Map<String, Object> conversationId(){
private Map<String, Object> conversationID = new HashMap<>();
}
}
And later you can inject it where ever you want as below.
public class MessengerPlatformCallbackHandler {
#Autowired
private Map<String, Object> conversationID;
}
You need to create it this way.
#Configuration
public class ConversationIDConfig {
#Bean
public Map<String, Object> conversationId(){
return new HashMap<>();
}
}
And later you can inject it where ever you want as below.
public class MessengerPlatformCallbackHandler {
#Autowired
private Map<String, Object> conversationId;
}

How to add support for more than one Thymeleaf template modes

I have a Spring Boot app using Thymeleaf 3 as the template engine for the app pages. We use the default configuration provided by spring-boot-starter-thymeleaf, with the HTML Thymeleaf templates under the src/main/resources/templates folder.
Now we would like to use Thymeleaf to generate also some javascript files, using the new javascript template mode. Those javascript templates could be located into the same HTML templates folder or another one (ex: src/main/resources/jstemplates).
I don't know if there is a way to add this configuration without changing anything in the default configuration provided by the spring-boot-starter-thymeleaf, or I have to create a full configuration for everything.
I've tried the first option with the following configuration, which works for the javascript templates, but then html templates don't work anymore.
The configuration:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter
implements ApplicationContextAware {
private static final String UTF8 = "UTF-8";
#Autowired
private SpringTemplateEngine templateEngine;
#Autowired
private ThymeleafProperties properties;
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Bean
public ThymeleafViewResolver javascriptThymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(this.templateEngine);
resolver.setCharacterEncoding(UTF8);
resolver.setContentType("application/javascript");
resolver.setViewNames(new String[] {".js"});
resolver.setCache(this.properties.isCache());
return resolver;
}
#Bean
public SpringResourceTemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/jstemplates/");
resolver.setSuffix(".js");
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
resolver.setCharacterEncoding(UTF8);
return resolver;
}
}
A test javascript controller:
#Controller
public class JavascriptController {
#RequestMapping(method = RequestMethod.GET, value = "/test.js")
public String testjs() {
return "test";
}
}
The controller for the root page:
#Controller
public class MainController {
#RequestMapping(method = RequestMethod.GET, value = "/")
public String index(Model model) {
return "index";
}
}
There is a test.js file in the src/main/resources/jstemplates folder. If I try the url http://localhost:8080/test.js, it works as expected. But if I try, for example, the main url (http://localhost:8080/) it fails with the following error:
Caused by: java.io.FileNotFoundException: class path resource
[jstemplates/index.js] cannot be opened because it does not exist
It should be looking for the templates/index.html instead, so it seems the javascriptTemplateResolver overrides or takes precedence over de default one, without falling back to it.
Is there a way to add another template mode support integrated with the default Thymeleaf configuration, or I need to configure everything from scratch?
After some debugging I finally found the solution. It seems the SpringResourceTemplateResolver doesn't check by default if the template really exists. In my example configuration, the first template resolver is the one configured for the javascript templates, and it was creating a View without looking first if the template exists.
To solve it the template checkExistence property must be set to true. Ex:
#Bean
public SpringResourceTemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/jstemplates/");
resolver.setSuffix(".js");
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
resolver.setCharacterEncoding(UTF8);
resolver.setCheckExistence(true);
return resolver;
}
The only problem I see with this configuration is that if we create a js view with the same name as a html view, it will always get the javascript one.
Edit
To solve that last issue I've made some changes in the configuration:
Remove the .js suffix in the resolver configuration
When a controller gives a javascript view name, that name already includes the .js suffix.
The final configuration is as follows:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter
implements ApplicationContextAware {
private static final String UTF8 = "UTF-8";
#Autowired
private ThymeleafProperties properties;
#Autowired
private TemplateEngine templateEngine;
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Bean
public ThymeleafViewResolver javascriptThymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(this.templateEngine);
resolver.setCharacterEncoding(UTF8);
resolver.setContentType("application/javascript");
resolver.setViewNames(new String[] {"*.js"});
resolver.setCache(this.properties.isCache());
return resolver;
}
#Bean
public SpringResourceTemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(this.applicationContext);
resolver.setPrefix("classpath:/templates/js/");
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
resolver.setCharacterEncoding(UTF8);
resolver.setCheckExistence(true);
resolver.setCacheable(this.properties.isCache());
return resolver;
}
}
And the sample controller:
#Controller
public class JavascriptController {
#RequestMapping(method = RequestMethod.GET, value = "/js/test.js")
public String testjs() {
return "test.js";
}
}
The HTML view controllers remain unchanged.

spring boot and freemarker configure TemplateDirectiveModel and TemplateMethodModelEx

I have a project built with Spring Boot + FreeMarker, and it worked fine until tonight, but I don't think I had changed anything; however it failed. Below is my FreeMarker configuration class:
#Configuration
#Slf4j
public class FreemarkerConfiguration extends FreeMarkerAutoConfiguration.FreeMarkerWebConfiguration {
/**
* autowired all implementations of freemarker.template.TemplateDirectiveModel
*/
#Autowired
Map<String, TemplateDirectiveModel> directiveModelMap;
/**
* autowired all implementations of freemarker.template.TemplateMethodModelEx
*/
#Autowired
Map<String, TemplateMethodModelEx> methodModelExMap;
private static final String CUSTOM_DIRECTIVE_SUFFIX = "Directive";
private static final String CUSTOM_METHOD_SUFFIX = "Method";
#Override
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = super.freeMarkerConfigurer();
Map<String, Object> sharedVariables = new HashMap<String, Object>();
if (!CollectionUtils.isEmpty(directiveModelMap)) {
Map<String, Object> map = new HashMap<String, Object>();
for (Map.Entry<String, TemplateDirectiveModel> entry : directiveModelMap.entrySet()) {
map.put(StringUtils.uncapitalize(entry.getKey()).replaceAll(CUSTOM_DIRECTIVE_SUFFIX, ""), entry.getValue());
}
sharedVariables.putAll(map);
}
if (!CollectionUtils.isEmpty(this.methodModelExMap)) {
Map<String, Object> map = new HashMap<String, Object>();
for (Map.Entry<String, TemplateMethodModelEx> entry : this.methodModelExMap.entrySet()) {
map.put(StringUtils.uncapitalize(entry.getKey()).replaceAll(CUSTOM_METHOD_SUFFIX, ""), entry.getValue());
}
sharedVariables.putAll(map);
}
BeansWrapper beansWrapper = new BeansWrapperBuilder(freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).build();
sharedVariables.put("enums", beansWrapper.getEnumModels());
configurer.setFreemarkerVariables(sharedVariables);
return configurer;
}
}
Problem is that
#Autowired
Map<String, TemplateDirectiveModel> directiveModelMap;
#Autowired
Map<String, TemplateMethodModelEx> methodModelExMap;
I want to inject all implementations of TemplateDirectiveModel and TemplateMethodModelEx , but both Map<String ,TemplateDirectiveModel/TemplateMethodModelEx> got null. Of course, the implementations annotated with #Compoment. I don't know why, I compared the diffs but got no answers, why the Maps instantiated after
#Override
public FreeMarkerConfigurer freeMarkerConfigurer(){ .... }
Here's my boot application
#Configuration
#SpringBootApplication
#EntityScan("com.hmxx.entity")
#EnableAspectJAutoProxy
#EnableTransactionManagement
#EnableJpaRepositories(value = {"com.hmxx.service"})
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(new Object[]{Application.class});
app.setWebEnvironment(true);
//app.setBannerMode(Banner.Mode.CONSOLE);
ConfigurableApplicationContext ctx = app.run(args);
Map<String, TemplateDirectiveModel> directiveModelMap = ctx.getBeansOfType(TemplateDirectiveModel.class);
Map<String, TemplateMethodModelEx> methodModelExMap = ctx.getBeansOfType(TemplateMethodModelEx.class);
}
#Autowired
DataInitService dataInitService;
#Override
public void run(String... args) throws Exception {
// dataInitService.initAdminUser();
}
}
And obviously Map<String, TemplateDirectiveModel>、Map<String, TemplateMethodModelEx> methodModelExMap both not null.
I want to know why the injection got null and hope to resolve it.

Spring Boot: Add new Extension-AcceptHeader mappings

Within a normal Spring Application, I have:
#Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
#Override
protected Map<String, MediaType> getDefaultMediaTypes() {
Map<String, MediaType> mediaTypes = super.getDefaultMediaTypes();
mediaTypes.put("extension", new MediaType("foo", "bar"));
return mediaTypes;
}
}
So I can do something like:
#RequestMapping(produces = "foo/bar")
public void test() { ... }
And then call:
http://.../myResource.extension
When I do this with Spring Boot, then extends WebMvcConfigurationSupport would prevent all the auto configuration.
So how can I easily register new Extension-Accept-Header mappings with Spring Boot?
This should do it, I have verified the code with Boot 1.2.1.RELEASE
#Configuration
public class EnableWebMvcConfiguration extends WebMvcAutoConfiguration.EnableWebMvcConfiguration {
#Override
protected Map<String, MediaType> getDefaultMediaTypes() {
Map<String, MediaType> mediaTypes = super.getDefaultMediaTypes();
mediaTypes.put("extension", new MediaType("foo", "bar"));
return mediaTypes;
}
}
according to avi's answer, we should use extends WebMvcAutoConfiguration.EnableWebMvcConfiguration
For adding additional extensions and media types, it might be more easy to just override configureContentNegotiation:
#Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.mediaType("extension", new MediaType("foo", "bar"));
}

Resources