Having the following code running on Quarkus:
#Singleton
#RegisterForReflection
public class StoreService {
private static final Logger LOGGER = Logger.getLogger(StoreService.class);
#Inject
#RestClient
StoresApiClient client;
#CacheResult(cacheName = "stores")
#Fallback(fallbackMethod = "allFallbackStores")
public List<Store> allStores() {
// call REST API using client
}
#SuppressWarnings("unused")
public List<Store> allFallbackStores() {
try {
LOGGER.info("Falling back to internal stores list");
...
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
the fallback mechanism is working properly in regular JDK mode. On the other hand in native image mode, #Fallback annotation is not being respected and an exception is thrown after unsuccessful API call. What might be a reason for that if #RegisterForReflection annotation is in place?
Related
I've seen many a posts and Q&As on StackOverflow for custom error handling for a REST/MVC applications.
My situation is slightly different though. My application uses Java Messaging (with ActiveMQ and SpringIntegration) and
the method below is triggered as a response to a JMessage:
public BenefitVersion getVersionAt(LocalDate referenceDate) {
return versions.stream()
.filter(benefitVersion -> !benefitVersion.getStartDate().isAfter(referenceDate))
.reduce(maxBy(getBenefitVersionStartDateComparator()
.thenComparingLong(BenefitVersion::getId)))
.orElseThrow(() -> new IllegalBenefitVersionException(guid,
format("Benefit %s does not have active version on %s", guid, referenceDate)));
}
I've defined custom exception as below:
public class IllegalBenefitVersionException extends RuntimeException {
private UUID benefitId;
public IllegalBenefitVersionException(final UUID benefitId, final String message) {
super(message);
this.benefitId = benefitId;
}
}
And a custom handler for that exception:
#ControllerAdvice
public class IllegalBenefitVersionExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(IllegalBenefitVersionExceptionHandler.class);
private final DlqExceptionDetailService exceptionDetailService;
#Autowired
public IllegalBenefitVersionExceptionHandler(DlqExceptionDetailService exceptionDetailService) {
this.exceptionDetailService = exceptionDetailService;
}
#ExceptionHandler(IllegalBenefitVersionException.class)
public void handleException(Throwable t) {
IllegalBenefitVersionException exception = (IllegalBenefitVersionException) t;
exceptionDetailService.newWithBenefitIdAndSave(exception.getBenefitId(), t.getMessage());
LOGGER.error("From ExceptionHandler: {}", t.getMessage(), t);
}
}
But that never gets called. Is the reason because I use the #ControllerAdvice whereas there is no controller?
Or perhaps I need to add special component scan somewhere?
If so, how do I wire up a custom exception handler in a messaging-based, as opposed to, REST, application?
I want to make periodical REST request with a Dropwizard Backend. More concretely I want to make an GET request to an external REST API every minute and process the result.
I used the quartz here and now I try to use the jersey client to make a REST request. I use guice as my dependency injection.
My application class has the following methods
#Override
public void initialize(final Bootstrap<DockerwizardConfiguration> bootstrap) {
Job everyJob = new EveryTestJob();
bootstrap.addBundle(new JobsBundle(everyJob));
}
#Override
public void run(final DockerwizardConfiguration configuration,
final Environment environment) {
Injector injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(HelloWorldParameter.class)
.annotatedWith(Names.named("helloWorldParameter"))
.toInstance(configuration.getHelloWorldParameter());
}
});
JerseyClientConfiguration conf = configuration.getJerseyClientConfiguration();
conf.setChunkedEncodingEnabled(false);
final Client client = new JerseyClientBuilder(environment).using(conf).build(getName());
environment.jersey().register(new ExternalServiceResource(client)); // How should that be implented with guice
environment.jersey().register(injector.getInstance(HelloWorldResource.class));
}
And my EveryTestJob class is implemented as follows
#Every("1s")
public class EveryTestJob extends Job {
#Override
public void doJob(JobExecutionContext context) throws JobExecutionException {
// logic run every time and time again
}
}
I am unsure how I this can be organized.
I've been trying to figure this out for a while, and this is what I have found out:
The JobBundle is added before any Resources so the JobExecutionContext will not include the client (https://www.dropwizard.io/0.9.2/docs/manual/internals.html)
Tried using the injector but didn't work either (https://github.com/HubSpot/dropwizard-guice)
Finally I stumbled on Jersey 2.0: Create repeating job which showed how to add the client into the context!
Here's my solution:
In the resource class,
#Path("/myPath")
public class myResource {
#Inject
public myResource() {
try {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getContext().put"myResource", this); // Inserts myResource into the context
} catch (SchedulerException e) {
// Handle exception
}
}
// Other stuff for api
}
Then in the job class (I'm using Dropwizard-jobs 2.0.1 where doJobs doesn't take in any arguments so I used execute instead),
#Every("10s")
public class myJob extends Job {
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
myResource res = (myResource) context.getScheduler().getContext().get("myResource");
// Do stuff with your resource
} catch (SchedulerException e) {
// Handle exception
}
}
}
Not sure if you have access to the ExternalServiceResource, but I hope this helps!
I'm trying to use a component to load up my configuration yml file.
However, it throws a Null Pointer exception and System.out shows 'null' for application.
However, when the same pattern is used to code up a #RestController, everything works fine. Why is #component not seeing my configurations??
#Component
#ConfigurationProperties(prefix = "myconf")
public class AppConfigJSON {
private String application;
private final String applicationConfigJSON;
Logger logger = LoggerFactory.getLogger(this.getClass());
private final ReadContext acRcJson;
public AppConfigJSON(){
String json = "";
try {
System.out.println("Application: " + this.application);
json = IOUtils.toString(this.getClass().getResourceAsStream("/myconfs/"+this.application+".json"));
} catch (IOException e) {
logger.error("Error reading JSON or app YML {}", e.getStackTrace());
}
this.applicationConfigJSON = json;
this.acRcJson = JsonPath.parse(this.getApplicationConfigJSON());
}
// These functions below set by configuration
public String getApplication()
{
return application;
}
public void setApplication(String application)
{
this.application = application;
}
}
In your constructor the application variable hasn't been initialized yet, in other words, Spring needs an instance first so it can apply it's magic. You need to move your constructor logic to a method annotated with #PostContruct so the application variable is set with the property value at that point.
I am trying to test something like this:
try {
logger.info("message");
//do something
} catch(Exception e) {
logger.error(errorMessage);
}
I know that it's not a good practice to catch an Exception, but there is some legacy code and there is no time for refactoring.
So, I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
Please notice that I can`t use:
final Logger logger = LogManager.getLogger(AnaliticsService.class);
final Appender mockAppender = mock(Appender.class);
logger.addAppender(mockAppender);
final ArgumentCaptor<LoggingEvent> captor = ArgumentCaptor.forClass(LoggingEvent.class);
Log4jConfigHelper.getInstance().bufferConfiguration();
verify(mockAppender, times(x)).doAppend(captor.capture());
because I don`t know how many messages are logged when UT is running.
You should try to make a Mock for LoggerFactory.
First annotate your TestClass with:
#RunWith(PowerMockRunner.class)
#PrepareForTest({YourController.class, LoggerFactory.class})
Then make a test, which calls needed method and veryfies errors:
#Test
public void testErrorLogging() throws Exception {
mockStatic(LoggerFactory.class);
Logger logger = mock(Logger.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
YourController controller = new YourController();
controller.someMethod();
verify(logger).error(anyString());
}
Log messages are part of the user interface of your code. Code that does computations should not make assumptions about the manner in which log messages are made available to the user, the text and language of the log messages, or even whether messages are communicated as text (rather than, say, a graphical means). So computational code should delegate to an associated logger class (in the UI/presentation layer) that hides all those details.
If the computational code only requires that the associated logger conforms to an interface, and uses dependency injection for being associated with a logger class, it is easy to mock the logger to examine whether the computational code has requested logging.
So if the code to be tested is like this::
public class MyService
{
private final MyServiceLogger logger;
MyService(MyServiceLogger logger)
{
this.logger = Objects.requireNonNull(logger);
}
public void processFile(Path path) {
...
try{
...
} catch (EOFException e) {
logger.logUnexpectedEOF(path);
}
}
}
public interface MyServiceLogger
{
public logUnexpectedEOF(Path path);
}
public class MyServiceTextLogger implements MyServiceLogger
{
private final Logger textLogger = LogManager.getLogger(MyService.class);;
#Override
public logUnexpectedEOF(Path path) {
textLogger.error("unexpected EOF for file {}",path);
}
}
You can test it like this:
public class MyServiceTest
{
private static class MockMyServiceLogger implements MyServiceLogger
{
private Path path;
private int nCalls_logUnexpectedEOF;
#Override
public logUnexpectedEOF(Path path) {
++nCalls_logUnexpectedEOF;
this.path = path;
}
void assertCalled_logUnexpectedEOF(int nCalls, Path path) {
assertEquals("Called logUnexpectedEOF, nCalls", nCalls, nCalls_logUnexpectedEOF);
assertEquals("Called logUnexpectedEOF, path", path, this.path);
}
}
#Test
public void processFile_unexpectedEOF() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(1, testPath);
}
#Test
public void processFile_OK() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(0, null);
}
}
I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
You don't need to check for an exception this way. A test which throws an Exception fails.
} catch(Exception e) {
logger.error(errorMessage, e);
throw e; // report the error to the test
}
Note: when to throw an error to the testing framework it will log/print it so I suspect you don't need to be catching it in the first place.
When launching a simple REST interface with Eclipse using the jersey-container-grizzly2-http Maven dependency version 2.13, I do not get any exceptions shown after triggering errors in the browser. Other log output gets shown in the console just fine, but Exceptions just get swallowed.
I created an Exception handler which is neither instantiated or called:
package mypackage.rest;
#Provider
class ExceptionHandler implements ExceptionMapper<Throwable>
{
#Override public Response toResponse(Throwable t)
{
System.out.println("toResponse called");
t.printStackTrace();
return Response.status(Status.BAD_REQUEST).entity(t.getMessage()).build();
}
}
The Grizzly Server construction:
package mypackage.rest;
public class GrizzlyHttpUtil
{
public static final URI baseURI = UriBuilder.fromUri("http://localhost/").port(10010).build();
public static HttpServer startThisServer()
{
ResourceConfig resCon = new ResourceConfig().packages("mypackage.rest");
return server = GrizzlyHttpServerFactory.createHttpServer(baseURI, resCon);
}
}
The REST API class
package mypackage.rest;
#Path("")
public class Rest
{
#GET #Path("datasets") #Produces(MediaType.TEXT_HTML)
public static String datasets()
{
throw new RuntimeException();
}
}
Update
I got it to work with resCon.register(ExceptionHandler.class);. Why is that necessary? Why does ResourceConfig().packages(...) not handle this on its own?
I've just had to solve the same problem. Here is my initialization code that convinces Grizzly HTTP server to display errors: http://source.apidesign.org/hg/bck2brwsr/rev/18ae4fbcfb87
Logger l = Logger.getLogger("org.glassfish.grizzly.http.server.HttpHandler");
l.setLevel(Level.FINE);
l.setUseParentHandlers(false);
ConsoleHandler ch = new ConsoleHandler();
ch.setLevel(Level.ALL);
l.addHandler(ch);
I am using Grizzly 2.3.3