Factory pattern to create Exceptions dynamically
Asked Answered
S

6

8

I have created Exception xml and dynamically create and throw exception.

<exception-mappings>
<exception-mapping key="exceptionkey1">
    <class-name>com.package.CheckedException</class-name>
    <message>Checked Exception Message</message>
</exception-mapping>
<exception-mapping key="exceptionkey2">
    <class-name>com.package.UnCheckedException</class-name>
    <message>UnChecked Exception Message</message>
</exception-mapping>

I create object of exception dynamically using reflection depending on the exception key.

public static void throwException(final String key) throws CheckedException, UncheckedException {
    ExceptionMapping exceptionMapping = exceptionMappings.getExceptionMappings().get(key);
    if (exceptionMapping != null) {
        try {
            Class exceptionClass = Class.forName(exceptionMapping.getClassName());
            try {
                throw ()exceptionClass.newInstance(); // line X
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

I want to know which class to typecast at line X so that I do not need to use If/else. Reason behind I do not want to use if else is, it may be possible that in future, there may be new classes added and I do not want to change this code every time new exception is added.

My base logic is my service layer will throw either CheckedException or UncheckedException. If CheckedException is thrown, it will be handled by my web layer. Also I can not throw Super parent class Exception or Throwable as my web layer only catch CheckedException. If UncheckedException is thrown, it will display exception page.

Please help me as I am not able to proceed further.

EDIT: Any other solution is also accepted.

Stover answered 5/8, 2014 at 3:4 Comment(13)
This is extremely strange. I don't understand how code can't know why it couldn't complete.Careycarfare
@Careycarfare Because it has class name as string and not the Java type.Stover
I am not sure simply knowing what class to cast to resolves the problem; If you add support for new exceptions in the future, wouldn't you need to add those types to the 'throws' directive of the throwException method?Sogdiana
@Sogdiana Its true. But it will be child class of either of these two classes. So I dont think I need to add them in throws clause.Stover
@Stover ok, that resolves 'throws' maintenance issue. Does this work? throw (exceptionClass)exceptionClass.newInstance(); or throw (exceptionClass.cast(exceptionClass.newInstance());Sogdiana
Seems like you should throw a fixed class with dynamic data. You're really trying to break the OO flow of Java for no benefit.Careycarfare
@djechlin, I am not that good at OO. Can you please let me know what I am breaking?Stover
@veefu, cast method also return Object type. and we can not cast by object reference :(Stover
@Stover try to re-think your design and see if you can come up with another one that doesn't use reflection. Once you've found it - you'll get two added-values: 1) you got rid of the casting issue 2) you're not using reflection (which should be avoided as much as possible)Chelonian
I am ready to change my exceptionClass heirarchy if required.Stover
What do your Exceptions give you that you cannot get from the standard library (Exception/RuntimeException)? This seems to me to be an unnecessary design.Trilateral
I am ok with Exception and RuntimeException also. Just wanted to throw exception dynamically based on the key passed.Stover
A better design (IMO) would be to make everything a RuntimeException, unless you can recover from it. Patterns I have seen before would be RuntimeExceptions that reflect the architectural layer (ServiceException, DaoException, etc.). Checked Exceptions should have meaningful names and be recoverable (InsufficientFundsException). Some people don't like programming to Exception and prefer to return result objects that can be interrogated. I don't think it matters, but whatever you choose should be the standard for everyone.Trilateral
G
11

Well, in the name of science, here's how you can do it. Would I recommend doing this? By no means. Would I ever do anything remotely like this myself? Probably not.

public class ExceptionFactory {
    public static void throwException(String className)
            throws CheckedException, UncheckedException {

        Class<?> exceptionClass;

        try {
            exceptionClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException(e);
        }

        try {
            if (CheckedException.class.isAssignableFrom(exceptionClass)) {
                throw exceptionClass.asSubclass(CheckedException.class)
                        .newInstance();
            } else if (UncheckedException.class
                    .isAssignableFrom(exceptionClass)) {
                throw exceptionClass.asSubclass(UncheckedException.class)
                        .newInstance();

            } else {
                throw new IllegalArgumentException(
                        "Not a valid exception type: "
                                + exceptionClass.getName());
            }
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    public static void main(String... args) {
        try {
            throwException("CheckedException");
        } catch (CheckedException e) {
            System.out.println(e);
        } catch (UncheckedException e) {
            System.out.println(e);
        }
    }
}

class CheckedException extends Exception {
}

class UncheckedException extends Exception {
}
Glottochronology answered 5/8, 2014 at 3:54 Comment(4)
@RobbyCornelissen Might as well replace generic type variables C and U with CheckedException and UncheckedException.Bucella
@StuartMarks You're absolutely right. Remnant of a snippet I had lying around. It helps to indicate how seriously FUBAR the entire thing is though :-)Glottochronology
I guess this can be simplified - I don't see the need for generics. See my solution.Dagny
Ok...it seems my design is incorrect. I have decided to quit with this design.Stover
M
2

I don't see the point of this factory. Even if you get it to work (which you can by having all the exceptions thrown by it being sub-classes of a single ancestor class), its usage would be something like this :

....
if (somethingInWrong) {
    ExceptionFactory.throwException("SomeKey");
}
....

For each key you'd still have to create an exception class to be mapped to it. Lets say SomeKeyException is the exception mapped to "SomeKey".

In that case, it's much more type safe to simply write :

....
if (somethingInWrong) {
    throw new SomeKeyException();
}
....

This way the compiler checks that you are creating an exception class that it actually knows. If you use your Factory, you might use some String that is not a valid key, and the compiler won't be able to do anything about it. Only in runtime your Factory will fail to find an exception mapped to the invalid key.

Miscreance answered 5/8, 2014 at 3:37 Comment(0)
C
2

There's no need to use reflection (as I commented above you shouldn't use reflection unless you really have to...).

You can implement the exceptions class to be something like this:

class MyExceptions {

    static void myExceptionsThrower(String key) throws Exception {
        if("illegalstate".equals(key)) {
            throw new IllegalStateException("that's my IllegalStateException bro!");
        }
        else if("illegalaccess".equals(key)) {
            throw new IllegalAccessException("that's my IllegalAccessException bro!");
        }
        // etc...
    }
}

and use it with:

MyExceptions.myExceptionsThrower(key);
Chelonian answered 5/8, 2014 at 3:37 Comment(0)
D
2

A few tweaks:

public static void throwException(final String key) throws Throwable {
    ExceptionMapping exceptionMapping =
        exceptionMappings.getExceptionMappings().get(key);
    if (exceptionMapping != null) {
        try {
            Class<Throwable> exceptionClass = 
                (Class<Throwable>)Class.forName(exceptionMapping.getClassName());
            try {
               throw exceptionClass.cast( exceptionClass.newInstance() ); // line X
            } catch (InstantiationException e) {
               e.printStackTrace();
            } catch (IllegalAccessException e) {
               e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
 }
Dagny answered 5/8, 2014 at 4:4 Comment(0)
B
1

Here's my entry into this derby. :-)

The other answers have commented on whether this is a reasonable design. I'll set these issues aside for the purpose of this answer.

A couple of my pet peeves are unnecessary warnings (even if suppressed), and exceptions that don't report what actually went wrong. In particular merely printing out a stack trace is usually insufficient. Yes, this is just test code, but when dealing with code like this -- even code that's designed to throw an exception -- one really ought to think about how to deal with errors. In this case I've chosen to represent these kinds of errors as instances of InternalError since the configuration or whatever can be wrong in a variety of ways. Specifically: if the class can't be found, if it is found but isn't a subtype of CheckedException or UncheckedException (or even an ordinary class), or if doesn't have a no-arg constructor or if it's inaccessible.

Another issue with some of the proposed solutions is that if the exception class name is "java.lang.InstantiationException" (or one of the other internally-caught exceptions) an instance of this exception type might be constructed, thrown, and then caught internally, resulting in a stack trace but not actually throwing the requested exception. I've avoided that by linearizing the logic instead of nesting try-catch blocks.

Finally, I extracted the exception-creating code into a separate method so that it can be used for both the checked and unchecked cases. This can be simplified considerably if you rearrange the exception hierarchy to allow only a single root exception (I recommend unchecked) and have exception subtypes that are handled at the web layer or are thrown out to the caller.

static void throwException(final String exClassName) throws CheckedException, UncheckedException {
    Class<?> clazz;
    try {
        clazz = Class.forName(exClassName);
    } catch (ClassNotFoundException cnfe) {
        throw new InternalError(exClassName, cnfe);
    }

    if (CheckedException.class.isAssignableFrom(clazz)) {
        throw newException(clazz.asSubclass(CheckedException.class));
    } else if (UncheckedException.class.isAssignableFrom(clazz)) {
        throw newException(clazz.asSubclass(UncheckedException.class));
    } else {
        throw new InternalError(exClassName + " is not a valid exception");
    }
}

static <X extends Throwable> X newException(Class<X> clazz) {
    X x;
    try {
        x = clazz.newInstance();
    } catch (InstantiationException|IllegalAccessException e) {
        throw new InternalError("creating instance of " + clazz, e);
    }
    return x;
}
Bucella answered 5/8, 2014 at 4:47 Comment(0)
M
0

This could be helpful to create a custom precondition exception to avoid multiple if conditions. Creates a precondition exception while checking for null pointer.

class Preconditions {
    /**
     * <p>
     *  Checks the value to be null and if null throws a new Exception with the message given.
     *  Used to reduce checking if conditions for complexity.
     * </p>
     * @param val - val to check null
     * @param exceptionClass - exception class to be thrown
     * @param args - message to be called for throwing exception
     * @throws Throwable - Common Throwable Exception.
     */
    public static void checkNotNull(final Object val, final Class<?>  exceptionClass, final Object ...args) throws Throwable {
        Class<?>[] argTypes = new Class<?>[args.length];
        Arrays.stream(args).map(WithIndex.indexed()).forEach(arg ->argTypes[arg.index()] = arg.value().getClass());
        if (null == val) throw (Throwable) exceptionClass.getConstructor(argTypes).newInstance(args);
    }

}

Then you can use it in code with:

    PreConditionUtil.checkNotNull(objectToCheck, CustomException.class, ErrorCode, "your error message", ...);
Marte answered 17/10, 2019 at 11:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.