I'm inquiring about the need to proceed with the development of Spring boot 1.X version, but one part is blocked.
I use Embedded Tomcat, so I have to register with #Bean for ErrorReportValue.
I'm working on moving 400 Bad Request to Custom Error page, but I found a way.
However, they are classes that can be applied from Spring 2.X or higher, so they are not applied to Spring 1.4 version.
The replaceable code was similar but failed. What are some ways to get that function to 1.4 as well?
If you look at the code below, there are codes available from Spring 2.X and above. "TomcatWebSocketServletWebServerCustomizer" and "TomcatServletWebServerFactory" .
Because of these two codes, it is not possible to proceed in version 1.X. It would be nice to upgrade the version, but it will be difficult due to the circumstances.
Among the replaceable codes, "TomcatWebSocketContainerCustomizer" and "Tomcat EmbeddedServletContainerFactory" were available, but were not compatible with "StandardHost".
Help me.
Thank you.
#Bean
public TomcatWebSocketServletWebServerCustomizer errorValveCustomizer() {
return new TomcatWebSocketServletWebServerCustomizer() {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addContextCustomizers((context) -> {
Container parent = context.getParent();
if (parent instanceof StandardHost) {
((StandardHost) parent).setErrorReportValveClass("com.java.lmh.errors.CustomError");
}
});
}
#Override
public int getOrder() {
return 100; // needs to be AFTER the one configured with TomcatWebServerFactoryCustomizer
}
};
}
}
Related
In my Java app that based on Spring Boot, I am trying to implement a caching mechanism for the following service method:
#Override
public List<EmployeeDTO> findAllByCountry(Country country) {
final Map<Pair<UUID, String>, List<CountryTranslatable>> valueList
= countryRepository...
// code omitted for brevity
}
After several examples regarding to this issue, I decided on the approach mentioned on A Guide To Caching in Spring.
However, I am a little bit confused as it contains Spring and Spring Boot implementations and uses different annotation examples. I think I should start from 3.1. Using Spring Boot section as I use Spring Boot, but I am not sure about which Caching Annotation I should use (4.1. #Cacheable seems to be ok but I am not sure).
So, where should I put SimpleCacheCustomizer and how can I apply that approach for my service method above (findAllByCountry)? Any simple example would really be appreciated as I am new in Spring.
You don't need any customizations if you are a starter, and you want only the basics then do the following
#Configuration
#EnableCaching
public class CachingConfig {
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
The provided article states, return new ConcurrentMapCacheManager("addresses"); but you can use the default constructor and the relevant cache for adresses will be created later with #Cacheable("addresses"). So no need for this to be in configuration.
You also need
#Cacheable("employeesList")
#Override
public List<EmployeeDTO> findAllByCountry(Country country) {
final Map<Pair<UUID, String>, List<CountryTranslatable>> valueList
= countryRepository...
// code omitted for brevity
}
ready to go, that is the basic setup
If you want to customize the autoconfigured cachemanager then only you should implement CacheManagerCustomizer interface.
In usual cases you don't need to customize the autoconfigured cachemanager. The example has been given in the link you attached.
Your understanding on the cacheable annotation is also correct and it should work fine for you.
You can put that component class with other classes in the component scan range.
You should put your SimpleCacheCustomizer along with your others Spring configuration class. That way, your component will be scanned and loaded by Spring.
#Component
public class SimpleCacheCustomizer
implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
#Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(asList("employeesList", "otherCacheName"));
}
}
To use the cache with your service, add the annotation #Cacheable("employeesList")
#Cacheable("employeesList")
#Override
public List<EmployeeDTO> findAllByCountry(Country country) {
final Map<Pair<UUID, String>, List<CountryTranslatable>> valueList
= countryRepository...
// code omitted for brevity
}
If you want to verify the cache is working, just enable sql_query in your Spring configuration and check that findAllByCountry is no longer making any request to the DB.
In one of my projects I've already upgraded Jersey from version 2.14 to 2.23. But I'm struggling many hours with one problem. My project defines its own ExceptionMapper for a ValidationException, but unfortunately Jersey already has a built-in exception mapper for this exception and I cannot override it.
I have registered correctly (I checked it) my own mapper which is presented below:
#Provider
public class ValidationExceptionMapper implements
ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
but it is never being called. Jersey always pick up the org.glassfish.jersey.server.validation.internal.ValidationExceptionMapper.
I've also tried to use #Priority annotation for my custom mapper, but unfortunately Jersey doesn't take it into account.
So what is going on? It worked perfectly fine in the previous Jersey version, so it seems to be a regression bug.
I give up. Any clues?
It really turned out to be a regression bug in Jersey, introduced in January 2015.
Bug is related with two Jersey's extensions: for Weld and bean validation.
Because without Weld container started, my custom ValidationExceptionMapper mapper takes precedence over the built-in one provided by the jersey-bean-validation module, so my goal is achieved.
I've filled a bug report under JERSEY-3153, later moved as the issue #3425.
To be honest, I'm never ever going to use Weld + Jersey again... I'm so tired with this combination. Through the last two years I've encountered around 10 bugs already. I'm really tired.
Anyway, I hope it will help somebody.
UPDATE:
As #Justin Jose noticed in the comments below, there is also another workaround for the mentioned bug. We can use HK2 bindings, to override the problematic built-in mapper:
register(new AbstractBinder() {
#Override
protected void configure() {
bind(my.custom.ValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class);
}
});
Jersey's built-in ValidationExceptionMapper is registered via ValidationFeature. Probably, replacing Jersey's ValidationFeature with your own version can do the trick. It can be done as follows.
Firstly, disable auto-discoverable ValidationFeature
property(ServerProperties.BV_FEATURE_DISABLE, true);
Next step is to register a clone of Jersey's validation feature
public static class ValidationFeatureClone implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new ValidationBinder());
context.register(NewValidationExceptionMapper.class);
context.register(ValidationErrorMessageBodyWriter.class);
return true;
}
}
In the clone, you should specify your new ExceptionMapper.
Finally, register your new Feature
register(ValidationFeatureClone.class)
UPDATE:
From Jersey 2.20 onwards, default ValidationExceptionMapper can be overwritten using HK2 binding as shown below.
register(new AbstractBinder() {
#Override
protected void configure() {
bind(NewValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class).ranked(10);
}
});
I found a way to get it to work with newer Jersey releases again, which I also posted under your bug report.
One needs to build Jersey locally with the changed code, specifically the jersey-bean-validation artifact.
Locate org.glassfish.jersey.server.validation.internal.ValidationBinder and comment out the following two lines in configure():
bind(ValidationExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
bind(ValidationErrorMessageBodyWriter.class).to(MessageBodyWriter.class).in(Singleton.class);
It's kind of ironic that the source code comment above those lines says that they're supposed to allow users to register their own providers.
Unfortunately the bug still exists and cause headaches...
In my case easiest solution was providing custom ExceptionMapper specifically for ConstraintViolationException.
public class CVExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(final Throwable t) {
...
}
}
and then registering it as always with:
context.register(CVExceptionMapper.class);
Spring Boot automatically initializes the underlying logging system using the LoggingApplicationListener. This is a nice thing if the application I'm developing runs isolated or standalone.
However I'm developing a web application that will be deployed into the WSO2 Application Server, which offers unified logging (using log4j), with features like central log level management (at runtime via web interface), business reporting etc.
If I use Spring Boot "as is", it logs everything completely on its own. My first shot was, to remove spring-boot-starter-logging and manually add slf4j-api as provided. This works to some extent, since the LoggingApplicationListener now overrides settings of the global logmanager provided by WSO2 (and even causes global appenders to be closed).
The only "solution" I came up with is to remove the listener via reflection. Then Spring Boot starts to behave exactly as it should (logging via the global logger and not overriding the pre defined log levels, output formats, appenders, etc.)
That "solution" looks like this:
#SpringBootApplication
public class MyApp extends SpringBootServletInitializer {
public static void main(String... args) {
SpringApplication.run(MyApp.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
try {
Field appField = SpringApplicationBuilder.class.getDeclaredField("application");
appField.setAccessible(true);
SpringApplication app = (SpringApplication)appField.get(builder);
Field listenersField = SpringApplication.class.getDeclaredField("listeners");
listenersField.setAccessible(true);
List<ApplicationListener<?>> listeners = (List<ApplicationListener<?>>) listenersField.get(app);
for (int i = listeners.size() - 1; i >= 0; --i) {
if (listeners.get(i) instanceof LoggingApplicationListener) {
listeners.remove(i);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return builder.sources(MyApp.class);
}
}
Is there any better solution to my problem that's maybe less hacky which I may have overlooked during my research and code analysis?
thank you for you post it is very helpful.
I had the same problem with Websphere Aplication Server: After spring boot context initialized I had no more logs. This solution is equivalent but less dirty by overriding the run method of SpringBootServletInitializer:
#Override
protected WebApplicationContext run(SpringApplication application) {
Collection<ApplicationListener<?>> listeners =
new ArrayList<>();
for (ApplicationListener<?> listener: application.getListeners()) {
if (!(listener instanceof LoggingApplicationListener)) {
listeners.add(listener);
}
}
application.setListeners(listeners);
return super.run(application);
}
Since Spring Boot 1.4 the LoggingSystem autoconfiguration can be disabled.
Take a look at the Custom Log Configuration section of the Spring documentation:
You can force Spring Boot to use a particular logging system by using the org.springframework.boot.logging.LoggingSystem system property. The value should be the fully qualified class name of a LoggingSystem implementation. You can also disable Spring Boot’s logging configuration entirely by using a value of none.
For Tomcat, for example, set the environment variable JAVA_OPTS:
JAVA_OPTS="-Dorg.springframework.boot.logging.LoggingSystem=none"
To use jamon in spring, it's described to use JamonPerformanceMonitorInterceptor and put it to springs AOP-mechanism via a applicationContext.xml. It's explained, and there's an example within the tests in it's sources. Unfortunately, I want to build a spring-boot application without any xml-configuration.
Is it possible to use some annotations to include the JamonPerformanceMonitorInterceptor to spring?
Better late than never...
I had the very same situation: I needed to configure JAMon without any XML configuration. Most of the examples online (including the comments in the JAMon source code) advertise XML configuration flexibility, but I couldn't find any examples with annotation based configuration. Also annotation-based configs are not necessarily less flexible, they just need to be conceptually separated and not confused with functional parts of the application. I think such advisor can be a good example:
#Component
public class MonitoringAdvisor extends AbstractPointcutAdvisor {
private final StaticMethodMatcherPointcut pointcut = new StaticMethodMatcherPointcut() {
#Override
public boolean matches(Method method, Class<?> targetClass) {
return targetClass.isAnnotationPresent(RestController.class);
}
};
#Override
public Pointcut getPointcut() {
return this.pointcut;
}
#Override
public Advice getAdvice() {
return new JamonPerformanceMonitorInterceptor(true, true);
}
}
This advisor would let Spring/AOP know to run JAMon monitoring advice on any method of Spring bean annotated with #RestContrller. This advisor should be configured/added to the same Spring context as rest controllers.
Note, that in my case I specifically wanted to monitor my rest controllers. One can adapt the advisor according to his/her own needs. (In my code I use a more advanced/configurable version of the presented advisor)
Is this Spring Boot sample application helpful?
Here is the relevant part of the Spring AOP manual.
I am new to Java and spring.I need to know how we can achieve URL rewriting in Java and Spring. For example in .NET environment we can achieve this by using following code:
Global.asax.cs:
protected void Application_BeginRequest(object sender, EventArgs e) {
try {
string fullOrigionalpath = Request.Url.ToString();
if (fullOrigionalpath.Contains("/Home-Page")) {
Context.RewritePath("~/home.aspx"); return;
}
}
}
Similarly,we need to achieve in Java and Spring.
Can we have anything related to this in Java and Spring?
If we cannot do using above code,How we can achieve URL Rewriting?
Help would be appreciated.
I would recommend using OCPsoft Rewrite (beta) or OCPsoft PrettyFaces (final), which are newer and more evolved tools for doing Java Servlet URL-rewriting.
Rewrite also has support for your tuckey configuration, if you want to take advantage of your existing configuration, and add in more powerful Java-based Rewrite configuration.
It is very stable and well tested.
package com.example;
public class ExampleConfigurationProvider extends HttpConfigurationProvider
{
#Override
public int priority()
{
return 10;
}
#Override
public Configuration getConfiguration(final ServletContext context)
{
return ConfigurationBuilder.begin()
.defineRule()
.when(Direction.isInbound().and(Path.matches("/some/{page}/.*/")))
.perform(Redirect.permanent("/new-{page}/"));
}
}
If you're using Spring >= 3, you can use #RequestMapping. See the official documentation