package com.perforce.client.api; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; /** * Java adapter to native Error class. * Accumulates and reports layered errors. * * <p> The basic idea of Error is that the top level caller should * have one on its stack and hand it by reference down to all lower * layers. If any operation fails, it can add its description of the * error to the structure. After each operation that can potentially * fail, the caller should use {@link #test} to check for errors. The * top level should do the check and then report if necessary. * * <p> Alternately, the {@link #checkException} method can be used to * check Error and raise a Java exception if necessary. * * <p> Each error code is composed of 5 parts. * <pre> * sev - ErrorSeverity (4 bits) * arg - # of arguments, error specific (4 bits) * gen - generic error (8 bits) * sub - subsystem id (6 bits) * cod - code within subsystem, error specific (10 bits) * </pre> */ public class P4Error { /** * ErrorSeverity (4 bits). * @see #severity * @see "error.h" */ public static final enum Severity { /** * nothing yet */ EMPTY, /** * something good happened */ INFO, /** * something not good happened */ WARN, /** * user did somthing wrong */ FAILED, /** * system broken -- nothing can continue */ FATAL; /** * Unboxes this enum. * @return a byte primative */ public byte value() { return (byte) ordinal(); } /** * Finds the enum designated by a primative value. * @param s a Severity code * @exception IllegalArgumentException if <var>s</var> does not * designate a Severity */ public static Severity valueOf(int s) { try { return values()[s]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("valueOf(" + s + ")", e); } } } /** * Generic error classification (8 bits). * @see #generic * @see "errornum.h" */ public static final enum Generic { /** * misc */ NONE ((byte) 0), // The fault of the user /** * request not consistent with dox */ USAGE ((byte) 0x01), /** * using unknown entity */ UNKNOWN ((byte) 0x02), /** * using entity in wrong context */ CONTEXT ((byte) 0x03), /** * trying to do something you can't */ ILLEGAL ((byte) 0x04), /** * something must be corrected first */ NOTYET ((byte) 0x05), /** * protections prevented operation */ PROTECT ((byte) 0x06), // No fault at all /** * action returned empty results */ EMPTY ((byte) 0x11), // not the fault of the user /** * inexplicable program fault */ FAULT ((byte) 0x21), /** * client side program errors */ CLIENT ((byte) 0x22), /** * server administrative action required */ ADMIN ((byte) 0x23), /** * client configuration inadequate */ CONFIG ((byte) 0x24), /** * client or server too old to interact */ UPGRADE ((byte) 0x25), /** * communications error */ COMM ((byte) 0x26); private static final Generic[] VALUES = new Generic[0x400]; // 8 bits private final byte hexVal; private Generic(byte n) { hexVal = n; } /** * Unboxes this enum. * @return a byte primative */ public byte value() { return hexVal; } /** * Finds the enum designated by a primative value. * @param g a Generic code * @exception IllegalArgumentException if <var>g</var> does not * designate a Generic */ public static Generic valueOf(int g) { try { if (VALUES[g] == null) { throw new IllegalArgumentException("valueOf(" + g + ")"); } return VALUES[g]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("valueOf(" + g + ")", e); } } static { for (Generic g : EnumSet.allOf(Generic.class)) VALUES[g.value()] = g; } } /** * Subsystem id (6 bits). * @see #subsystem * @see "errornum.h" */ public static final enum Subsystem { /** * OS error */ OS, /** * Misc support */ SUPP, /** * librarian */ LBR, /** * messaging */ RPC, /** * database */ DB, /** * database support */ DBSUPP, /** * data manager */ DM, /** * top level of server */ SERVER, /** * top level of client */ CLIENT, /** * pseudo subsystem for information messages */ INFO, /** * pseudo subsystem for help messages */ HELP, /** * pseudo subsystem for spec/comment messages */ SPEC, /** * P4FTP server */ FTPD; /** * Unboxes this enum. * @return a byte primative */ public byte value() { return (byte) ordinal(); } /** * Finds the enum designated by a primative value. * @param s a Subsystem code * @exception IllegalArgumentException if <var>s</var> does not * designate a Subsystem */ public static Subsystem valueOf(int s) { try { return values()[s]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("valueOf(" + s + ")", e); } } } /** * Extracts code within subsystem, error specific. * @param code a 32-bit <code>int</code> error code * @return 10 bits */ public static final int subCode(final int code) { return (code >> 0) & 0x3ff; } /** * Extracts the subsystem id, defined in errornum.h. * @param code a 32-bit <code>int</code> error code * @return 6 bits */ public static final int subsystem(final int code) { return (code >> 10) & 0x3f; } public static final Subsystem toSubsystem(final int code) { return Subsystem.valueOf(subsystem(code)); } /** * Extracts the generic error id, defined in errornum.h. * @param code a 32-bit <code>int</code> error code * @return 8 bits */ public static final int generic(final int code) { return (code >> 16) & 0xff; } public static final Generic toGeneric(final int code) { return Generic.valueOf(generic(code)); } /** * Extracts number of arguments, error specific. * @param code a 32-bit <code>int</code> error code * @return 4 bits */ public static final int argCount(final int code) { return (code >> 24) & 0x0f; } /** * Extracts error severity. * @param code a 32-bit <code>int</code> error code * @return 4 bits */ public static final int severity(final int code) { return (code >> 28) & 0x0f; } public static final Severity toSeverity(final int code) { return Severity.valueOf(severity(code)); } /** * Extracts subsystem + subcode. * @param code a 32-bit <code>int</code> error code * @return 16 bits */ public static final int uniqueCode(final int code) { return code & 0xffff; } /** the native peer instance of the error */ long instance = 0; long id() { return nId(this.instance); } int code() { return nCode(this.instance); } String fmt() { return nFmt(this.instance); } /** * Test to see if there was an error made. * * @return if there is an error or not. * **/ public boolean test() { return nTest(this.instance); } P4Error() { this.instance = nNewInstance(); javaCreatedTheInstance = true; } /** * Make a new P4Error that will just bind to an already * existing native Error. * * @param instance a native peer to bind this to. * @return the java interface. * **/ static P4Error makeJavaPeer(long instance) { return new P4Error(instance); } /** * Take a P4ClientException, and make sure that the * error on the native side is set. * * <p> You would call this when you've caught a P4ClientException * and want to convey that to the native side. * * @param err the exception that we use to see what to set. * **/ void convertException(P4ClientException err) { int code = err.getErrorCode(); if (code != 0) nSetErrorCode(this.instance, code); else nSetMessage(this.instance, err.toString()); } void setError(String msg, Severity sev, Generic gen, Exception err) { Subsystem sub = Subsystem.CLIENT; int code = (sev.value() << 28) | (gen.value() << 16) | (sub.value() << 10) | err.getClass().hashCode() & 0x03FF; nSetErrorCode(this.instance, code); nSetMessage(this.instance, msg); } /** * Checks if there was an exception on the native side. * If so, throws the corresponding P4ClientException. * * <p> You would use this if you wanted to convert from P4's Error * handling system to a more java-like exception mechanism. * * @exception P4ClientException containing the error code from Error, * but only if one needs to be thrown. * **/ public void checkException() throws P4ClientException { // Check to see if there was an error. if (!test()) return; // There was, so get the native one and, as best we // can, throw the corresponding exception. final long id = id(); final int code = code(); final String fmt = fmt(); if (ids2Cons.size() == 0) initIds2Cons(); Constructor c = (Constructor) ids2Cons.get(id); if (c == null) throw new P4ClientException(code, fmt); try { throw (P4ClientException) c.newInstance(new Object[] {code, fmt}); } catch (InstantiationException e) { P4ClientException p4e = new P4ClientException(code, fmt); p4e.initCause(e); throw p4e; } catch (IllegalAccessException e) { P4ClientException p4e = new P4ClientException(code, fmt); p4e.initCause(e); throw p4e; } catch (IllegalArgumentException e) { P4ClientException p4e = new P4ClientException(code, fmt); p4e.initCause(e); throw p4e; } catch (InvocationTargetException e) { P4ClientException p4e = new P4ClientException(code, fmt); p4e.initCause(e.getCause()); throw p4e; } } /** * If this instance was create to pass to the native side, then a * native pointer was allocated and we need to clean that up, * otherwise, this was created as a face to an Error that was * created outside of Java and we do not want to clean that up. */ protected void finalize() throws Throwable { if (javaCreatedTheInstance) nDeleteInstance(this.instance); super.finalize(); } /** to track if we created this or the native side did */ private boolean javaCreatedTheInstance; // // methods to create and dispose of native Error pointers. // private static native long nNewInstance(); private static native void nDeleteInstance(long instance); private static Map<Long, Constructor> ids2Cons = new HashMap<Long, Constructor>(); private static Map<Class, Integer> ex2Codes = new HashMap<Class, Integer>(); public static int codeOf(Class<? extends P4ClientException> c) { if (ex2Codes.isEmpty()) initIds2Cons(); return ex2Codes.get(c); } private static void initIds2Cons() { long[] ids = nGetErrorIdPtrs(); int[] codes = nGetErrorIdCodes(); String[] names = nGetErrorIdNames(); assert(ids.length == names.length); final String pk = P4Error.class.getPackage().getName(); for (int i = 0; i < ids.length; i++) { try { Class c = Class.forName(pk + ".P4" + names[i] + "Exception"); addCons(ids[i], codes[i], c); } catch (ClassNotFoundException e) { e.printStackTrace(System.err); // keep going } } } private static void addCons(long id, int code, Class c) { Class[] sig = new Class[] {Integer.TYPE, String.class}; try { ids2Cons.put(id, c.getConstructor(sig)); ex2Codes.put(c, code); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } /** * Make a P4Error to use as a "face" to an actual, pre-existing * native Error instance. * * @param instance the pre-existing native instance. * **/ private P4Error(long instance) { this.instance = instance; javaCreatedTheInstance = false; } private static native int[] nGetErrorIdCodes(); private static native String[] nGetErrorIdNames(); private static native String[] nGetErrorIdFmts(); private static native long[] nGetErrorIdPtrs(); private static native void nSetErrorId(long errInstance, long id); private static native void nSetErrorCode(long errInstance, int code); private static native void nSetMessage(long errInstance, String message); private static native boolean nTest(long instance); private static native String nFmt(long instance); private static native long nId(long instance); private static native int nCode(long instance); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 4197 | Paul Krause | Better error passing from Java o C | ||
#2 | 4184 | Paul Krause | fix convertException bug; add missing classses; use enums for error codes | ||
#1 | 4181 | Paul Krause |
Rename //guest/paul_krause/perforce/api/java/wcvm/com/... //guest/paul_krause/perforce/api/java/wcvm/javax/... To //guest/paul_krause/perforce/api/java/wcvm/src-15/... |
||
//guest/paul_krause/perforce/api/java/wcvm/com/perforce/client/api/P4Error.java | |||||
#8 | 4177 | Paul Krause | parameterize container types - requires tiger or pizza | ||
#7 | 4173 | Paul Krause | add serialVersionUID | ||
#6 | 4129 | Paul Krause | use msgclient.h in place of old errclient.h | ||
#5 | 4107 | Paul Krause | fix JNI sigs for ErrorId | ||
#4 | 4103 | Paul Krause | ditch clienterror.h | ||
#3 | 4100 | Paul Krause | fix type problems | ||
#2 | 4097 | Paul Krause | remove support for unimplemented abort() function | ||
#1 | 4073 | Paul Krause | branch com.perforce.api package from michael_bishop | ||
//guest/michael_bishop/P4APIForJava/java/com/perforce/client/api/P4Error.java | |||||
#1 | 430 | Michael Bishop |
Initial checkin. Seems to work. Not very much testing. Not very much documentation. Some more commenting needs to take place. But, it's there to experiment with. |