I am in the processing of learning Kotlin and ran into a problem I couldn't figure out.
I would like to extend the Java class RuntimeException in Kotlin and be able to use any one of three of its constructors, in different circumstances (based on what info I have at the time I want to throw an exception). In java my class would look like this:
public class PhotoLibException extends RuntimeException {
public PhotoLibException(String message, RuntimeException ex) {
super(message, ex);
}
public PhotoLibException(String message) {
super(message);
}
public PhotoLibException(RuntimeException ex) {
super(ex);
}
}
When I try to do this in Kotlin, I used this answer as a guide: Kotlin secondary constructor however, I had a problem trying to figure out how to invoke the appropriate super constructor correctly. For example, using functions seemed to be a good approach, like this:
fun PhotoLibException(message: String): PhotoLibException {
val ex = null
return PhotoLibException(message, ex)
}
fun PhotoLibException(ex: Exception): PhotoLibException {
val message = ""
return PhotoLibException(message, ex)
}
class PhotoLibException(message: String, ex: Exception?): RuntimeException(message, ex) {
}
However, in this Kotlin example above, I am always invoking the super constructor with two args, and not invoking the constructor most appropriate to the situation. So what I have above works, but doesn't do exactly what it would do in Java where a different constructor is invoked in each situation.
I also tried instantiating a new RuntimeException inside each fun above and casting it to PhotoLibException, but I wasn't allowed to do that.
Can anyone suggest how I would do this correctly in Kotlin?
Update: Since M11 (0.11.*), you can use secondary constructors to solve this problem:
class PhotoLibException : RuntimeException {
constructor(message: String, ex: Exception?): super(message, ex) {}
constructor(message: String): super(message) {}
constructor(ex: Exception): super(ex) {}
}
Currently, there's no way to call different super-constructors in different context from the same class. It will be supported in the upcoming months, though.
Use the #JvmOverloads annotation.
class PhotoLibException: RuntimeException {
#JvmOverloads constructor(message: String, ex: Exception?)
}
Related
I have a class that registers itself as an event handler, with an event service:
interface CommunicationService {
fun sendActivationMessage(toEmail: String)
}
abstract class EventCommunicationService : CommunicationService, AbstractEventHandler {
constructor(eventService: EventService) {
eventService.registerListener(this)
}
override fun onEvent(event: Event) {
if (event.type == EventType.USER_CREATED) {
sendActivationMessage(event.userEmail)
}
}
}
The idea being there can be an EmailCommunicationService, or a mocked testing version, etc. which don't all need to register themselves as listeners for when a user is created.
However Kotlin complains that I'm:
Leaking 'this' in constructor of non-final class EventCommunicationService
Which, well, I am. I could easily ignore the warning - but is there a better approach?
I've tried using an init { } block instead of a constructor, but the warning is the same.
I basically want a "post-construct" callback or similar that can be used to let this service register itself with the EventService provided in the constructor since that's the point of this intermediate type.
I understand why this is a problem - but I'm not sure how to reason my way to fixing it.
init blocks are really part of the constructor (in JVM terms), so that wouldn't help with the problem. It is very much not safe to ignore in general: see Leaking this in constructor warning for reasons (just ignore the accepted answer, its comments contain the real meat and so does Ishtar's answer).
One option (assumes that all subclasses have no-argument constructors, though it could be extended):
abstract class <T : EventCommunicationService> EventCommunicationServiceCompanion(private val creator: () -> T) {
operator fun invoke(eventService: EventService): T {
val obj = creator()
eventService.registerListener(obj)
return obj
}
}
// a subclass of EventCommunicationService:
class MyService private constructor () : EventCommunicationService {
companion object : EventCommunicationServiceCompanion<MyService>(MyService::new)
}
To create a MyService, you still call MyService(eventService), but this is actually the companion object's invoke method and not the constructor.
In JUnit 4 you could use a Rule to wrap a test so that you could execute code both before and after a test had run. In most cases this could be accomplished with an #Before and #After method or an ExternalResource rule. However some control flow constructs (like try-with-resources) cannot be split into two methods. In most cases, there are alternatives to these constructs which allow you to split them into two methods. For example, with try-with-resources, you can manually acquire and close a resource instead of using a try block.
The specific problem that I have run into is that the database library I use, jOOQ, only has transaction methods that take a callback. (See https://www.jooq.org/doc/latest/manual/sql-execution/transaction-management/) You cannot call something like:
context.startTransaction()
doStuff()
context.commit() // Or rollback()
In JUnit4 this is ok because you can write a rule like so (in Kotlin, but the equivalent works in Java):
class TransactionRule(private val dbSessionManager: DBSessionManager) : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
dbSessionManager.transaction {
base.evaluate()
}
}
}
}
}
Is there anything similar in JUnit 5?
You can write an InvocationInterceptor in place of the JUnit4 rule:
public class TransactionInvocationInterceptor implements InvocationInterceptor {
#Override
public void interceptTestMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
runInTransaction(() -> {
try {
invocation.proceed();
} catch (Throwable t) {
throw new RuntimeException(t);
}
});
}
}
#ExtendWith(TransactionInvocationInterceptor.class)
class InvocationInterceptorTest {
#Test
void test() {
…
}
}
One difference is that interceptTestMethod only wraps the test method, not other lifecycle methods such as beforeEach. It's possible to intercept the other lifecycle methods individually with the other methods in InvocationInterceptor, but not multiple at a time (for example, if you want to call both beforeEach and the test method in one transaction).
From what I understand you can't use the JUnit 5 test lifecycle callbacks as they would require you to follow the doStuff route with context calls Before/After that you indicate won't work.
Would using JUnit 5 Dynamic Tests instead work?
This provides for test factories consisting of collections of dynamic test with a name and an executable (lambda). You could then do something like this:
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import org.junit.jupiter.api.function.Executable;
#TestFactory
Collection<DynamicTest> transactionTestCollection() {
return Arrays.asList(
dbTest("1st dynamic test", () -> assertTrue(true)),
dbTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))
);
}
private DynamicTest dbTest(String name, Executable tst) {
return dynamicTest(name, () -> dbSessionManager.transaction(tst));
}
Per title, exceptions thrown from a ParamConverter are NOT handled the way I expect.
With an ExceptionMapper:
#Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException exception) {
return Response.serverError().entity( "It triggered" ).build();
}
}
and ParamConverter:
#Provider
(boilerplate junk)
#Override
public DateTime fromString(String value) {
throw new MyException("convert");
}
It does NOT return the "It triggered" text in a 500 error, but rather a 404.
Anticipated question : Are both providers registered?
Yes - If I throw "MyException" from a resource (within 'regular' code) it works as expected. I can also convert see the stacktrace with the "convert" message.
Is there any way to make exceptions from ParamConverters be handled by the ExceptionMapper?
I am using jersey 2.3.1, along with spring-jersey, launched in a jetty container 9.1.0.RC0
Seem from reading this, the JAX-RS spec says the implementor should wrap unhandled exceptions in a NotFoundException (404) for #QueryParam and #PathParam, and from what I tested a 400, (I'm guessing BadRequestException) for #FormParam.
"if the field or property is annotated with #MatrixParam, #QueryParam or #PathParam then an implementation MUST generate an instance of
NotFoundException (404 status) that wraps the thrown exception and no entity"
A couple ways I can see handling the exception, is to
Just handle it in the ParamConverter, e.g.
return new ParamConverter<T>() {
#Override
public T fromString(String string) {
try {
return (T)new MyObject().setValue(string);
} catch (MyException ex) {
Response response = Response.serverError().entity("Boo").build()
throw new WebApplicationException(response);
}
}
#Override
public String toString(T t) {
return t.toString();
}
};
Or just have your exception extend WebApplicationException, and return the Response there. e.g.
public class MyException extends WebApplicationException {
public MyException(String message) {
super(Response.serverError().entity(message).build());
}
}
I experienced the same behavior in Jersey 2.26.
Any Exception that extends RuntimeException gets mapped to a ParamException, which is itself a sublcass of WebApplicationException.
Assuming your MyException extends RuntimeException, it's not getting caught because your ExceptionMapper only handles MyException.
Regarding the Jersey docs saying to throw a NotFoundException: I would argue a 404 does not apply when a queryParam can't be converted. A BadRequestException seems more appropriate. And also, I can't see anything unique in the Jersey frame work when a NotFoundException is thrown besides setting the response code
To get exceptions thrown from a ParamConverter end up in an ExceptionMapper, you'll have to have your ExceptionMapper catching a more global exception, like Throwable.
Another answer suggests returning a WebApplicationException. This should be a fine solution but will NOT work if the Response object has an entity. See here: https://github.com/jersey/jersey/issues/3716
I'm developing a J2ME Application and I want to make a good practice for Exception Handling, I'm throwing some exceptions like
ConnectionNotFoundException, IOException, XmlPullParserException, OutOfMemory, Exception
I don't want to catch all these exception in each method that I make
so I think that I can make a new class
and this class will handle all others
I have made this class
public class ExceptionHandler extends Exception {
private Exception thrownException;
public ExceptionHandler(Exception thrownExc) {
this.thrownException = thrownExc;
if (thrownException.getClass().isInstance(ConnectionNotFoundException.class)) {
// DO SOMETHING
} else if (thrownException.getClass().isInstance(IOException.class)) {
// DO SOMETHING
} else if (thrownException.getClass().isInstance(XmlPullParserException.class)) {
// DO SOMETHING
} else if (thrownException.getClass().isInstance(NullPointerException.class)) {
// DO SOMETHING
} else if (thrownException.getClass().isInstance(OutOfMemoryError.class)) {
// DO SOMETHING
} else if (thrownException.getClass().isInstance(Exception.class)) {
// DO SOMETHING
}
}
}
but I don't know if that is a good way or not and also I have error when I replace the thrown exceptions with mine
so what can I do please ?
You are on the right track, that is the best way to handle errors. You can also pass a second argument called message in your custom exception class and then display message here itself. Something like this:
if (thrownException.getClass().isInstance(ConnectionNotFoundException.class)) {
Dialog.alert(msg);
}
Also from the calling class, you just callnew ExceptionHandler(exception, Constants.msg)
The Constants.java class will hold all the error messages.
I'd like to be able to use JUnit 4.7's ExpectedException #Rule in Scala. However, it doesn't seem to catch anything:
import org.junit._
class ExceptionsHappen {
#Rule
def thrown = rules.ExpectedException.none
#Test
def badInt: Unit = {
thrown.expect(classOf[NumberFormatException])
Integer.parseInt("one")
}
}
This still fails with a NumberFormatException.
To make this work with JUnit 4.11 in Scala, you should meta-annotate your annotation so that the annotation is applied only to the (synthetic) getter method, not the underlying field:
import org.junit._
import scala.annotation.meta.getter
class ExceptionsHappen {
#(Rule #getter)
var thrown = rules.ExpectedException.none
#Test
def badInt: Unit = {
thrown.expect(classOf[NumberFormatException])
Integer.parseInt("one")
}
}
EDIT: Following the release of JUnit 4.11, you can now annotate a method with #Rule.
You will use it like:
private TemporaryFolder folder = new TemporaryFolder();
#Rule
public TemporaryFolder getFolder() {
return folder;
}
For earlier versions of JUnit, see the answer below.
--
No, you can't use this directly from Scala. The field needs to be public and non-static. From
org.junit.Rule:
public #interface Rule: Annotates fields that contain rules. Such a field must be public, not static, and a subtype of TestRule.
You cannot declare a public fields in Scala. All fields are private, and made accessible by accessors. See the answer to this question.
As well as this, there is already an enhancement request for junit (still Open):
Extend rules to support #Rule public MethodRule someRule() { return new SomeRule(); }
The other option is that it non-public fields be allowed, but this has already been rejected: Allow #Rule annotation on non-public fields.
So your options are:
clone junit, and implement the first suggestion, the method, and submit a pull request
Extend the Scala class from a java class which implements the #Rule
-
public class ExpectedExceptionTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
}
and then inheriting from that:
class ExceptionsHappen extends ExpectedExceptionTest {
#Test
def badInt: Unit = {
thrown.expect(classOf[NumberFormatException])
Integer.parseInt("one")
}
}
which works correctly.
As a very newbie to Scala I am just using a very simple workaround: explicitly catch the exception and fail if your expected exception is not thrown.
Below is a sample skeleton:
try {
*your code that should throw an exception*
fail("Did not generate *the.Exception.you.expect*")
} catch {
case t: *the.Exception.you.expect* => // do nothing, it's expected :)
}
Without knowing JUnit rules, and without testing it, because I don't have an appropriate setup at hand, I go out on a limb and suggest turning thrown into a val.
I guess its some member that is initialized with something and then it gets some state and then some other machinery checks the state against something. You are always creating new ones and keep forgetting the expectation.
If Scala has something similar like static imports, then catch-exception is an alternative to JUnit 4.7's ExpectedException #Rule.
I'm still using JUnit 4, and found #Juh_'s comment instructive. This worked in Scala 2.11.0.
import org.junit.rules.ExpectedException
import org.junit.{Rule, Test}
import scala.reflect.{ClassTag, classTag}
class DeleteMe {
object Thrower {
def throwException[R <: Throwable: ClassTag](message: String): Unit = {
throw classTag[R].runtimeClass.getConstructor(classOf[String]).newInstance(message).asInstanceOf[R]
}
}
#Rule
def exceptionRule:ExpectedException = ExpectedException.none()
#Test(expected = classOf[Exception])
def checkConversionExceptions = {
val myMessage = "My Message"
exceptionRule.expectMessage(myMessage)
Thrower.throwException[Exception](myMessage)
()
}
}