I have the below FeignClient:
#FeignClient(name="FooMS",fallback=CustomerFeign.CustomerFeignImpl.class)
public interface CustomerFeign {
#RequestMapping(value="/bar/{phoneNo}")
List<Long> getFriends(#PathVariable("phoneNo") Long phoneNo);
class CustomerFeignImpl implements CustomerFeign{
#Override
public List<Long> getFriends(Long phoneNo) {
return new ArrayList<Long>(108);
}
}
}
When the FooMS instance is down, I get a 500 error instead of the fallback being executed. Why is this happening?
adding #Component and feign.hystrix.enabled=true works fine
Tag your CustomerFeignImpl as a #Component or create a #Bean out of it.
This works for me with 2020.0.3:
In application.properties
feign.circuitbreaker.enabled=true
In pom.xml
<spring-cloud.version>2020.0.3</spring-cloud.version>
and
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Thank you, rostlvan!
I am outlining my implementation below:
I am using Spring Cloud version 2020.0.4 and the following configuration worked for me:
in pom.xml, I have these dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Though I'm not sure if we need to have both openfeign and hystrix dependencies. Someone can validate that!
In my application.properties I have feign.circuitbreaker.enabled=true
In my Main Application class, I have
#SpringBootApplication
#EnableFeignClients
public class MySpringBootApplication{
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
And finally, my Feign Client, fallback and fallback factory:
UserServiceFeignClient.java
#FeignClient(name = "USER-SERVICE", fallbackFactory = UserServiceFallbackFactory.class)
public interface UserServiceFeignClient {
#GetMapping("/api/users/{userId}")
public ResponseEntity<User> getUser(#PathVariable String userId);
}
UserServiceFeignClientFallback.java
public class UserServiceFeignClientFallback implements UserServiceFeignClient{
#Override
public ResponseEntity<User> getUser(String userId) {
return ResponseEntity.ok().body(new User());
}
}
And, UserServiceFeignClientFallbackFactory.java:
#Component
public class UserServiceFallbackFactory implements FallbackFactory<UserServiceFeignClientFallback>{
#Override
public UserServiceFeignClientFallback create(Throwable cause) {
return new UserServiceFeignClientFallback();
}
}
Was facing the problem myself, until I stumbled upon the answer from #rostlvan
Related
I'm feeling stupid to ask this, but I can't understand where I'm wrong with my code.
The context is :
a Spring Boot application (1.5.7) with an embedded Jetty server and a
controller to expose some endpoints
a unique #Configuration class, where some of my beans are defined (Singleton and Prototype scopes)
a #Service that uses some beans defined in my #Configuration class
The problem is:
a NoSuchBeanDefinitionException for one of my #Configuration bean.
Now the details:
My SpringBootApplication :
#SpringBootApplication
public class HbbTVApplication {
public static void main(String[] args) {
SpringApplication.run(HbbTVApplication.class, args);
}
}
My #Configuration class:
#Configuration
#Profile(value = { "dev", "int", "pre", "pro" })
public class StandaloneFrontalConfig extends WebMvcConfigurerAdapter {
#Value("${kafka.bootstrap-servers}")
private String bootstrapServers;
#Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return props;
}
#Bean
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfigs());
}
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/standalone/");
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*");
}
};
}
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
#Bean
public Security securityManager() {
return new Security();
}
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KngAflow getTechnicalCookie() {
return new KngAflow();
}
#Bean
public EmbeddedServletContainerCustomizer customizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
customizeJetty((JettyEmbeddedServletContainerFactory) container);
}
}
private void customizeJetty(JettyEmbeddedServletContainerFactory jetty) {
jetty.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(Server server) {
for (Connector connector : server.getConnectors()) {
if (connector instanceof ServerConnector) {
HttpConnectionFactory connectionFactory = ((ServerConnector) connector)
.getConnectionFactory(HttpConnectionFactory.class);
connectionFactory.getHttpConfiguration().setCookieCompliance(CookieCompliance.RFC2965);
}
}
}
});
}
};
}
}
My #Service:
#Service
public class CookieService implements services.CookieService, InitializingBean {
/**
* Serializable
*/
private static final long serialVersionUID = -1997257884335775587L;
#Autowired
ApplicationContext app;
#Override
public Cookie createTechnicalCookie() {
return new Cookie(app.getBean(KngAflow.class), null);
}
#Override
public void afterPropertiesSet() throws Exception {
if (app != null) {
for (String bean : app.getBeanDefinitionNames()) {
System.out.println("Bean: " + bean);
}
}
}
}
And the "non defined" bean:
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties({ "security", "maxAge", "domain", "updated" })
public class KngAflow implements Serializable, InitializingBean {
#JsonProperty(value = "did")
private String did;
#JsonProperty(value = "checksum")
private String checksum;
#Autowired
private Security security;
private Integer maxAge;
private String domain;
private boolean updated = false;
public KngAflow() {
domain = ".mydomain.com";
}
#Override
public void afterPropertiesSet() throws Exception {
did = UUID.randomUUID().toString();
maxAge = 365 * 24 * 60 * 60;
checksum = security.encrypt(did + security.md5(did));
}
}
NB: Classes are not complete, and there are more classes in my project. I only put what I saw as relevant information.
If something else is needed, just ask me please.
By the way, all the endpoints are defined into a unique #Controller class, and all the endpoints are working except those needing the getTechCookie #Bean.
So, my problem occurs in runtime execution. When I start my Spring Boot app, Jetty is started and listening on the configured port.
Nevertheless, if you look at the CookieService #Service, I'm listing all the bean names defined in the autowired context and my getTechnicalCookie (a.k.a KngAflow) #Bean is missing. I can't understand why.
Of course, when I invoke my #controller to execute my #Service code, the NoSuchBeanDefinitionException is thrown executing the line app.getBean(KngAflow.class).
I tried to use a bean name instead of bean type, no change.
For testing purpose (as it doesn't make sense from a logical point of view), I defined my bean getTechCookie #Bean as a Singleton scoped bean, and the name is still missing from the ApplicationContext.
And the last but not least thing is: Everything works fine with Eclipse!
I mean, all my devs are done using Eclipse IDE. My Spring Boot app is built with Maven and executing it inside Eclipse works correctly (and my getTechCookie Bean is defined and listed).
When I package my app using the Maven Spring Boot plugin and execute it using java -jar, my getTechCookie (KngAflow.class) bean is missing. Nevertheless, this class is present inside the jar.
Spring parameters to launch the spring boot app are spring default values (port 8080, no SSL, ...) and the active.profiles are always between dev, int, pre or pro (those defined in my #Configuration class)
What am I doing wrong?
Thanks!
If it helps, I add my POM definition:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.mydomain.bigdata</groupId>
<artifactId>mybigapp</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
<include>application.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
EDIT: I changed my #Service class to "force" spring to accept my class as a prototype bean, and it works. It's very ugly but it works. But if someone could help me to find what's wrong, I don't like this workaround:
#Override
public void afterPropertiesSet() throws Exception {
if (!context.containsBeanDefinition(KngAflow.class.getName()))
context.registerBeanDefinition(KngAflow.class.getName(),
BeanDefinitionBuilder.genericBeanDefinition(KngAflow.class).setScope("prototype").getBeanDefinition());
}
I made a following simple application to reproduce issue.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
run(Application.class, args);
}
}
#Configuration
#Profile("dev")
public class BeanConfiguration {
#Bean
#Scope(scopeName = SCOPE_PROTOTYPE)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
public class PrototypeBean {}
#Service
#Slf4j
public class SingletonBean implements InitializingBean {
#Autowired
private ApplicationContext context;
public PrototypeBean getPrototypeBean() {
return context.getBean(PrototypeBean.class);
}
#Override
public void afterPropertiesSet() throws Exception {
for (String name : context.getBeanDefinitionNames()) {
Class<?> c = context.getBean(name).getClass();
log.debug("===> Name: {}, Type = {}", name, c.getTypeName());
}
}
}
#RestController
#RequestMapping("/bean")
public class BeanRestController {
#Autowired
private SingletonBean singletonBean;
#GetMapping("/name")
public String getName() {
return singletonBean.getPrototypeBean().getClass().getName();
}
}
When I execute application with -Dspring.profiles.active=dev setting
Then I see in the log without no issue and REST endpoint gives back response properly:
===> Name: prototypeBean, Type = PrototypeBean
But if I execute application without profile setting
Then I see error in the log and REST endpoint raise exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'PrototypeBean' available
We wrote a custom ClientDetailsService for our OAuth2 authorization server:
public class MyClientDetailsService implements ClientDetailsService {
#Override
public ClientDetails loadClientByClientId(String clientId) {
log.info("Got called!");
...
}
}
The log looks like that:
... Got called!
... Got called!
... Got called!
... Got called!
... Got called!
... Got called!
Dependency:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.1.11.RELEASE</version>
</dependency>
In the official git-hub the topic was already discussed, but until today nothing was fixed so far. (https://github.com/spring-projects/spring-security-oauth/issues/141)
My question is, does anybody know a workaround for this problem? We are accessing our database with every invocation and it is very memory consuming.
you need to use the cache provided by spring-boot2.
please enable the caching in springboot by
#EnableCaching
#SpringBootApplication
#EnableCaching
class Starter {
public static void main(String[] args) {
SpringApplication.run(Starter.class, args);
}
}
then cache the loadClientByClientId
by using #Cacheable.
public class MyClientDetailsService implements ClientDetailsService {
#Override
#Cacheable("ClientDetails")
public ClientDetails loadClientByClientId(String clientId) {
log.info("Got called!");
...
}
}
I am trying to add #NotNull constraint into my Person object but I still can #POST a new Person with a null email. I am using Spring boot rest with MongoDB.
Entity class:
import javax.validation.constraints.NotNull;
public class Person {
#Id
private String id;
private String username;
private String password;
#NotNull // <-- Not working
private String email;
// getters & setters
}
Repository class:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends MongoRepository<Person, String> {
}
Application class:
#SpringBootApplication
public class TalentPoolApplication {
public static void main(String[] args) {
SpringApplication.run(TalentPoolApplication.class, args);
}
}
pom.xml
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.BUILD-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
...
When I #POST a new object via Postman like:
{
"username": "deadpool",
"email": null
}
I still get STATUS 201 created with this payload:
{
"username": "deadpool",
"password": null,
"email": null
....
....
}
I had the same problem, but just enabling validation didn't work for me, this did work with both JPA and MongoDb to save anyone else spending ages on this. Not only does this get validation working but I get a nice restful 400 error rather than the default 500.
Had to add this to my build.gradle dependencies
compile('org.hibernate:hibernate-validator:4.2.0.Final')
and this config class
#Configuration
public class CustomRepositoryRestConfigurerAdapter extends RepositoryRestConfigurerAdapter {
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("afterCreate", validator());
validatingListener.addValidator("beforeCreate", validator());
validatingListener.addValidator("afterSave", validator());
validatingListener.addValidator("beforeSave", validator());
}
}
i found it better to make my own version of #NotNull annotation which validates empty string as well.
#Documented
#Constraint(validatedBy = NotEmptyValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface NotEmpty {
String message() default "{validator.notEmpty}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class NotEmptyValidator implements ConstraintValidator<NotEmpty, Object> {
#Override
public void initialize(NotEmpty notEmpty) { }
#Override
public boolean isValid(Object obj, ConstraintValidatorContext cxt) {
return obj != null && !obj.toString().trim().equals("");
}
}
You can either use the following code for validating
#Configuration
#Import(value = MongoAutoConfiguration.class)
public class DatabaseConfiguration extends AbstractMongoConfiguration
{
#Resource
private Mongo mongo;
#Resource
private MongoProperties mongoProperties;
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Override
protected String getDatabaseName() {
return mongoProperties.getDatabase();
}
#Override
public Mongo mongo() throws Exception {
return mongo;
}
}
Normally, the #RestRepository will resolve into a controller than handles validation by itself, except if you Override the default behavior or it by including some #HandleBeforeSave, #HandleBeforeCreate, ... into your code.
A solution is to remove the #HandleBeforeSave, #HandleBeforeCreate, ...
and then spring will handle the validation again.
Or if you want to keep them, you can provide a handler for any object validation like this:
#Component
#RepositoryEventHandler
public class EntityRepositoryEventHandler {
#Autowired
private Validator validator;
#HandleBeforeSave
#HandleBeforeCreate
public void validate(Object o) {
Set<ConstraintViolation<Object>> violations = this.validator.validate(o);
if (!violations.isEmpty()) {
ConstraintViolation<Object> violation = violations.iterator().next();
// do whatever your want here as you got a constraint violation !
throw new RuntimeException();
}
}
}
So, I have the next class:
public class MyCustomClass {
private String someField;
public String getSomeField() {
return someField;
}
public void setSomeField(String someField) {
this.someField = someField;
}
}
I would like to handle methods that return an instance of this class:
#Aspect
public class CustomAspect {
#AfterReturning(
pointcut = "execution(* com.*.*(..))",
returning = "val")
public void handleCustom(JoinPoint joinPoint, MyCustomClass val) {
System.out.println(val.getSomeField());
}
}
But it's working only if the type of val is Object:
public void handleCustom(JoinPoint joinPoint, Object val) {//...}
I tried to make something like this:
if (val instanceof MyCustomClass) {
System.out.println(((MyCustomClass) val).getSomeField());
}
But it doesn't work, in debug mode I saw that the type is ArrayList and it's empty.
I know, I'm a beginner in AOP, but Could you please advise some workarounds that could help me?
updated:
Maven dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
AspectConfig (imported to the Application class):
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages="com.aspect")
public class AspectConfig {
#Bean
public CustomAspect customAspect () {
return new CustomAspect ();
}
}
I tried to use CustomAspect as a #Component as well.
The custom object is just a DTO that is taken from maven artifact and is obtained via rest request.
So, I DO NOT have problems if the type of val is Object, everything works fine. But I do want to have MyCustomClass there instead of Object. Is it possible?
update 2:
#RestController
#RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController{
#Autowired
private MyService myService;
#RequestMapping(value = "dosmth")
public ResponseEntity<?> doSmth() {
SomeObject res = myService.doSmthAndGet();
return new ResponseEntity<>(res, HttpStatus.OK);
}
}
#Service
public class MyService{
public SomeObject doSmthAndGet() {
return SomeObject.of(getMyCustomClass());
}
private MyCustomClass getMyCustomClass() {
MyCustomClass result = new MyCustomClass();
result.setSomeField("Hello, Stack!");
return result;
}
}
It seems that #AfterReturning doesn't work for my submethod:
private MyCustomClass getMyCustomClass()
It's only triggered by the method that was called from Controller:
public SomeObject doSmthAndGet()
I tried to use different executions for #AfterReturning...
What am I doing wrong?
I've found a useful article that explains how to make Jersey to use SLF4J instead of JUL. Now my unit test looks like (and it works perfectly):
public class FooTest extends JerseyTest {
#BeforeClass
public static void initLogger() {
java.util.logging.Logger rootLogger =
java.util.logging.LogManager.getLogManager().getLogger("");
java.util.logging.Handler[] handlers = rootLogger.getHandlers();
for (int i = 0; i < handlers.length; i++) {
rootLogger.removeHandler(handlers[i]);
}
org.slf4j.bridge.SLF4JBridgeHandler.install();
}
public FooTest() {
super("com.XXX");
}
#Test
public void testSomething() throws Exception {
// ...
}
}
My pom.xml includes these dependencies:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
It works perfectly, but I don't want to make the same configuration in every unit test. It's an obvious code duplication, which I would like to avoid. How can I do this more effectively?
ps. Maybe it's not possible to optimize the code above and I'm doing the best I can?
If you are using the client API you can manually redirect the logs to slf4j (note that it may break in future versions although it seems unlikely):
Logger LOG = LoggerFactory.getLogger(MyClass.class); //slf4j logger
WebTarget ws = ClientBuilder.newClient(config)
.register(new LoggingFilter(new JulFacade(), true));
private static class JulFacade extends java.util.logging.Logger {
JulFacade() { super("Jersey", null); }
#Override public void info(String msg) { LOG.info(msg); }
}
The best way to do it is through a custom Listener. Being initialized before JSF servlet it should configure jul-to-slf4j bridge in contextInitialized(ServletContextEvent).
This worked for me:
public abstract class JerseyTestSFL4J extends JerseyTest {
static {
// Get JerseyTest to use SLF4J instead of JUL
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
}
and then having my tests extend JerseyTestSFL4J.
What it sounds like is you'd want the JUL/SLF4J configuration handle before JUnit starts testing so it could be covered for all tests? Here's a way you could do that.
Output
MySuite.init()
MySuite()
getSuiteTests()
MyTest.init()
MyTest()
test()
Code
#RunWith(AbstractTestSuite.TestSuiteRunner.class)
public abstract class AbstractTestSuite {
public static class TestSuiteRunner extends Suite {
public TestSuiteRunner(Class<?> klass) throws Exception {
super(klass, ((Class<? extends AbstractTestSuite>) klass).newInstance().getSuiteClasses());
}
}
public Class<?>[] getSuiteClasses() {
List<Class<?>> all = new ArrayList<Class<?>>();
for (Class<?> testClass : getSuiteTests()) {
all.add(testClass);
}
return all.toArray(new Class<?>[0]);
}
protected abstract Iterable<Class<?>> getSuiteTests();
}
public class MySuite extends AbstractTestSuite {
public static class MyTest {
static {
System.out.println("MyTest.init()");
}
public MyTest() {
System.out.println("MyTest()");
}
#Test
public void test() {
System.out.println("test()");
assertTrue(true);
}
}
static {
System.out.println("MySuite.init()");
}
public MySuite() {
System.out.println("MySuite()");
}
#Override
protected Iterable<Class<?>> getSuiteTests() {
System.out.println("getSuiteTests()");
return Arrays.asList(new Class<?>[] {MyTest.class});
}
}
In my app Jersey logging (with proper pom.xml and logback.xml) works fine only with
SLF4JBridgeHandler.install();
For tests you can make base abstract test with common configuration and extends other JUnit from it:
public abstract class AbstractTest {
private static final Logger LOG = LoggerFactory.getLogger(AbstractTest.class);
static {
SLF4JBridgeHandler.install();
}
#Rule
public ExpectedException thrown = ExpectedException.none();
#Rule
// http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev
public Stopwatch stopwatch = new Stopwatch() {
#Override
protected void finished(long nanos, Description description) {
...
Slf4jLogger from org.apache.cxf:cxf-core package is another option. It implements java.util.logging.Logger and delegate cals to slf4J.
Jersey server:
ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
config.register(
new Slf4jLogger(this.getClass().getName(), null));
Jersey client:
ClientBuilder
.newClient()
.register(
new LoggingFeature(
new Slf4jLogger(this.getClass().getName(), null)));