Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.java.programmer > #36725
| From | Arne Vajhøj <arne@vajhoej.dk> |
|---|---|
| Newsgroups | comp.lang.java.programmer |
| Subject | Re: How to handle same sanity checks |
| Date | 2017-06-25 22:55 -0400 |
| Organization | Aioe.org NNTP Server |
| Message-ID | <oipt3o$12ek$1@gioia.aioe.org> (permalink) |
| References | <87o9tgo0a9.fsf@munus.decebal.nl> <oiga3s$jrd$1@dont-email.me> <oih2hb$bad$1@Gaia.teknon.de> <87fuernwav.fsf@munus.decebal.nl> |
On 6/23/2017 12:48 AM, Cecil Westerhof wrote:
> On Thursday 22 Jun 2017 20:33 CEST, Volker Borchert wrote:
>> Eric Sosman wrote:
>>> On 6/22/2017 5:10 AM, Cecil Westerhof wrote:
>>>> In my code I have several methods that start with:
>>>> if ((n < MIN_VAL) || (n > MAX_VAL)) {
>>>> throw new InvalidParameterException(String.format(ERROR_N, n));
>>>> }
>>>>
>>>> That is not really DRY.
>>>>
>>>> I could write a method like:
>>>> private void checkN(n) {
>>>> if ((n < MIN_VAL) || (n > MAX_VAL)) {
>>>> throw new InvalidParameterException(String.format(ERROR_N, n));
>>>> }
>>>> }
>>>>
>>>> And then replace all the checks with a call to checkN. But then
>>>> the exception is generated in the method checkN instead of the
>>>> method that has a wrong parameter. What is the best way to handle
>>>> this?
>>>
>>> Tastes surely vary, but for repetitive checks I favor writing the
>>> private internal helper method. Yes, the caller of doItPlease()
>>> gets an exception originating from internalSanityCheck() instead,
>>> but that shouldn't bother anyone. doItPlease() will be in the stack
>>> trace anyhow, even though it's not at the very top.
>>
>> That can be a problem with some more-or-less-broken logging
>> frameworks, and a nuisance if trying to diagnose over the phone.
>>
>> You can always do something like
>>
>> final InvalidParameterException e
>> = new InvalidParameterException(String.format(ERROR_N, n));
>> final StackTraceElement[] s = e.getStackTrace();
>> e.setStackTrace(Arrays.copyOfRange(s, 1, s.length));
>> throw e;
>
> That is a very good idea. I have now:
> private static void checkInRange(final int n,
> final int lowLimit,
> final int highLimit,
> final String message) {
> if ((n < lowLimit) || (n > highLimit)) {
> final InvalidParameterException e =
> new InvalidParameterException(
> String.format(message, lowLimit, highLimit, n));
> final StackTraceElement[] s = e.getStackTrace();
> e.setStackTrace(Arrays.copyOfRange(s, 1, s.length));
> throw e;
> }
> }
>
> Works like a charm.
> The code is now more clear and easier to change, while the stack trace
> gives the right information.
That is probably as far as you can get in pure Java.
If you are willing to use AspectJ with annotations there is
another way.
Demo:
package paramcheck;
import java.util.function.Consumer;
public class TestMain {
@CheckParams
public static void test1(@IntIntervalCheck(minValue=1,maxValue=10) int v) {
System.out.println(v + " is OK");
}
@CheckParams
public static void test2(@StringLengthCheck(minLength=1,maxLength=10)
@StringPatternCheck(pattern="[A-Z]\\d+") String s) {
System.out.println(s + " is OK");
}
public static <T> void test(Consumer<T> f, T o) {
try {
f.accept(o);
} catch(ParamException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
public static void main(String[] args) {
test((o) -> test1(o), 5);
test((o) -> test1(o), 50);
test((o) -> test2(o), "A123");
test((o) -> test2(o), "XXXXXXXXXXXXXXX");
test((o) -> test2(o), "ABCD");
}
}
Output:
5 is OK
50 not in interval
paramcheck.ParamException: 50 not in interval
at paramcheck.TestMain.test1(TestMain.java:8)
at paramcheck.TestMain.lambda$1(TestMain.java:24)
at paramcheck.TestMain.test(TestMain.java:16)
at paramcheck.TestMain.main(TestMain.java:24)
A123 is OK
XXXXXXXXXXXXXXX invalid length
paramcheck.ParamException: XXXXXXXXXXXXXXX invalid length
at paramcheck.TestMain.test2(TestMain.java:12)
at paramcheck.TestMain.lambda$3(TestMain.java:26)
at paramcheck.TestMain.test(TestMain.java:16)
at paramcheck.TestMain.main(TestMain.java:26)
ABCD does not match pattern
paramcheck.ParamException: ABCD does not match pattern
at paramcheck.TestMain.test2(TestMain.java:12)
at paramcheck.TestMain.lambda$4(TestMain.java:27)
at paramcheck.TestMain.test(TestMain.java:16)
at paramcheck.TestMain.main(TestMain.java:27)
(see the rest of the code below)
I think it is nice code. But it also has its drawbacks - it
brings in an entire new paradigm.
Arne
package paramcheck;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface IntIntervalCheck {
public int minValue() default 0;
public int maxValue() default Integer.MAX_VALUE;
public String message() default "%d not in interval";
}
package paramcheck;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface StringLengthCheck {
public int minLength() default 0;
public int maxLength() default 255;
public String message() default "%s invalid length";
}
package paramcheck;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface StringPatternCheck {
public String pattern() default ".*";
public String message() default "%s does not match pattern";
}
package paramcheck;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckParams {
}
package paramcheck;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect
public class CheckParamsAdvice {
@Pointcut("execution(@paramcheck.CheckParams * *.*(..))")
public void checkParamsPointcut() { }
@Before("checkParamsPointcut()")
public void checkParams(JoinPoint jp) {
try {
Method m = ((MethodSignature)jp.getSignature()).getMethod();
Object[] args = jp.getArgs();
for(int i = 0; i < args.length; i++) {
ParamsChecker.check(m.getParameterAnnotations()[i], args[i]);
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
package paramcheck;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.regex.Pattern;
public class ParamsChecker {
public static void throwException(String msg) {
ParamException ex = new ParamException(msg);
StackTraceElement[] ste = ex.getStackTrace();
ex.setStackTrace(Arrays.copyOfRange(ste, 3, ste.length));
throw ex;
}
public static void check(Annotation[] ans, Object o) {
for(Annotation an : ans) {
if(an instanceof IntIntervalCheck) {
IntIntervalCheck iic = (IntIntervalCheck)an;
int v = (int)o;
if(v < iic.minValue() || iic.maxValue() < v) {
throwException(String.format(iic.message(), v));
}
}
if(an instanceof StringLengthCheck) {
StringLengthCheck slc = (StringLengthCheck)an;
String s = (String)o;
if(s.length() < slc.minLength() || slc.maxLength() < s.length()) {
throwException(String.format(slc.message(), s));
}
}
if(an instanceof StringPatternCheck) {
StringPatternCheck spc = (StringPatternCheck)an;
String s = (String)o;
if(!Pattern.matches(spc.pattern(), s)) {
throwException(String.format(spc.message(), s));
}
}
}
}
}
package paramcheck;
public class ParamException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ParamException(String msg) {
super(msg);
}
}
Back to comp.lang.java.programmer | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread
How to handle same sanity checks Cecil Westerhof <Cecil@decebal.nl> - 2017-06-22 11:10 +0200
Re: How to handle same sanity checks Martin Gregorie <martin@address-in-sig.invalid> - 2017-06-22 10:27 +0000
Re: How to handle same sanity checks Cecil Westerhof <Cecil@decebal.nl> - 2017-06-22 13:21 +0200
Re: How to handle same sanity checks Cecil Westerhof <Cecil@decebal.nl> - 2017-06-23 06:51 +0200
Re: How to handle same sanity checks Robert Klemme <shortcutter@googlemail.com> - 2017-06-24 12:06 +0200
Re: How to handle same sanity checks Martin Gregorie <martin@address-in-sig.invalid> - 2017-06-24 10:33 +0000
Re: How to handle same sanity checks Robert Klemme <shortcutter@googlemail.com> - 2017-06-24 16:46 +0200
Re: How to handle same sanity checks Martin Gregorie <martin@address-in-sig.invalid> - 2017-06-24 19:37 +0000
Re: How to handle same sanity checks Andreas Leitgeb <avl@logic.at> - 2017-06-22 11:26 +0000
Re: How to handle same sanity checks Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-06-22 07:44 -0400
Re: How to handle same sanity checks Andreas Leitgeb <avl@logic.at> - 2017-06-22 12:30 +0000
Re: How to handle same sanity checks Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-06-22 07:40 -0400
Re: How to handle same sanity checks v_borchert@despammed.com (Volker Borchert) - 2017-06-22 18:33 +0000
Re: How to handle same sanity checks Cecil Westerhof <Cecil@decebal.nl> - 2017-06-23 06:48 +0200
Re: How to handle same sanity checks Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-06-23 09:57 -0400
Re: How to handle same sanity checks Cecil Westerhof <Cecil@decebal.nl> - 2017-06-23 16:31 +0200
Re: How to handle same sanity checks Martin Gregorie <martin@address-in-sig.invalid> - 2017-06-23 17:21 +0000
Re: How to handle same sanity checks Eric Douglas <e.d.programmer@gmail.com> - 2017-06-26 05:21 -0700
Re: How to handle same sanity checks Martin Gregorie <martin@address-in-sig.invalid> - 2017-06-26 13:34 +0000
Re: How to handle same sanity checks Martin Gregorie <martin@address-in-sig.invalid> - 2017-06-26 15:22 +0000
Re: How to handle same sanity checks Arne Vajhøj <arne@vajhoej.dk> - 2017-06-25 22:55 -0400
Re: How to handle same sanity checks Marcel Mueller <news.5.maazl@spamgourmet.org> - 2017-06-22 23:07 +0200
Re: How to handle same sanity checks Joerg Meier <joergmmeier@arcor.de> - 2017-06-25 09:52 +0200
Re: How to handle same sanity checks Robert Klemme <shortcutter@googlemail.com> - 2017-06-25 12:31 +0200
Re: How to handle same sanity checks Arne Vajhøj <arne@vajhoej.dk> - 2017-06-25 14:23 -0400
Style police (Was: Re: How to handle same sanity checks) Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-06-25 17:28 -0400
Re: Style police (Was: Re: How to handle same sanity checks) Arne Vajhøj <arne@vajhoej.dk> - 2017-06-25 19:14 -0400
Re: Style police (Was: Re: How to handle same sanity checks) Arne Vajhøj <arne@vajhoej.dk> - 2017-06-25 19:19 -0400
Re: Style police (Was: Re: How to handle same sanity checks) Robert Klemme <shortcutter@googlemail.com> - 2017-06-26 23:34 +0200
csiph-web