Forum Stats

  • 3,875,512 Users
  • 2,266,931 Discussions
  • 7,912,240 Comments

Discussions

Java 8: Error message "Unknow type constant pool 18 ..." in mojarra 2.1.29: patch contribution

0923c5c3-f3ff-4c66-aaca-d8f62bdb6e6e
edited May 3, 2017 5:58AM in JavaServer Faces

When Java 8 features like streams are used with mojarra version 2.1.29, the annotation scanning in JavaClassScanningAnnotationScanner sometimes fails due to unknown constant pool entries. This problem is similar to the issue described here: https://java.net/jira/browse/JAVASERVERFACES-3732

The problem was fixed in mojarra version 2.2, however we are stuck with version 2.1 for now.

As I am unaware of any existing patch, I'd like to contribute our solution according to the class file format description for version 52.0 specified here:

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4

My suggested change of the inner class com.sun.faces.config.JavaClassScanningAnnotationScanner.ConstantPoolInfo adds handling of the missing constants

and I'd like to request one of the mojarra developers to review and incorporate that change into the mojarra source code repository.

    private static class ConstantPoolInfo {

        private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();

        public static final byte CLASS = 7;

        public static final int FIELDREF = 9;

        public static final int METHODREF = 10;

        public static final int STRING = 8;

        public static final int INTEGER = 3;

        public static final int FLOAT = 4;

        public static final int LONG = 5;

        public static final int DOUBLE = 6;

        public static final int INTERFACEMETHODREF = 11;

        public static final int NAMEANDTYPE = 12;

        public static final int ASCIZ = 1;

        public static final int UNICODE = 2;

        public static final int METHOD_HANDLE = 15;

        public static final int METHOD_TYPE = 16;

        public static final int INVOKE_DYNAMIC = 18;

        byte[] bytes = new byte[Short.MAX_VALUE];

        // ------------------------------------------------------------ Constructors

        /**

         * Creates a new instance of ConstantPoolInfo

         */

        public ConstantPoolInfo() {}

        // ---------------------------------------------------------- Public Methods

        /**

         * Read the input channel and initialize instance data structure.

         *

         * @param constantPoolSize the constant pool size for this class file

         * @param buffer the ByteBuffer used to store the bytes from <code>in</code>

         * @param in ReadableByteChannel from which the class file bytes are read

         * @return <code>true</code> if the bytes representing this classfile include one of the annotations we're looking for.

         * @throws IOException if an I/O error occurs while reading the class

         */

        public boolean containsAnnotation(int constantPoolSize, final ByteBuffer buffer, final ReadableByteChannel in) throws IOException {

            for (int i = 1; i < constantPoolSize; i++ ) {

                if ( !refill(buffer, in, 1)) {

                    return true;

                }

                final byte type = buffer.get();

                switch (type) {

                    case ASCIZ:

                    case UNICODE:

                        if ( !refill(buffer, in, 2)) {

                            return true;

                        }

                        final short length = buffer.getShort();

                        if (length < 0 || length > Short.MAX_VALUE) {

                            return true;

                        }

                        if (length > buffer.capacity()) {

                            return true;

                        }

                        if ( !refill(buffer, in, length)) {

                            return true;

                        }

                        buffer.get(bytes, 0, length);

                        /*

                         * to speed up the process, I am comparing the first few bytes to Ljava since all annotations are in the java package, the

                         * reduces dramatically the number or String construction

                         */

                        if (bytes[0] == 'L' && bytes[1] == 'j' && bytes[2] == 'a') {

                            String stringValue;

                            if (type == ASCIZ) {

                                stringValue = new String(bytes, 0, length, "US-ASCII");

                            } else {

                                stringValue = new String(bytes, 0, length, RIConstants.CHAR_ENCODING);

                            }

                            if (JavaClassScanningAnnotationScanner.isAnnotation(stringValue)) {

                                return true;

                            }

                        }

                        break;

                    case CLASS:

                    case STRING:

                        if ( !refill(buffer, in, 2)) {

                            return true;

                        }

                        buffer.getShort();

                        break;

                    case FIELDREF:

                    case METHODREF:

                    case INTERFACEMETHODREF:

                    case INTEGER:

                    case FLOAT:

                        if ( !refill(buffer, in, 4)) {

                            return true;

                        }

                        buffer.position(buffer.position() + 4);

                        break;

                    case LONG:

                    case DOUBLE:

                        if ( !refill(buffer, in, 8)) {

                            return true;

                        }

                        buffer.position(buffer.position() + 8);

                        // for long, and double, they use 2 constantPool

                        i++ ;

                        break;

                    case NAMEANDTYPE:

                        if ( !refill(buffer, in, 4)) {

                            return true;

                        }

                        buffer.getShort();

                        buffer.getShort();

                        break;

                    case METHOD_HANDLE:

                        if ( !refill(buffer, in, 3)) {

                            return true;

                        }

                        buffer.get();

                        buffer.getShort();

                        break;

                    case METHOD_TYPE:

                        if ( !refill(buffer, in, 2)) {

                            return true;

                        }

                        buffer.getShort();

                        break;

                    case INVOKE_DYNAMIC:

                        if ( !refill(buffer, in, 4)) {

                            return true;

                        }

                        buffer.getShort();

                        buffer.getShort();

                        break;

                    default:

                        if (LOGGER.isLoggable(Level.SEVERE)) {

                            LOGGER.log(Level.SEVERE, "Unknow type constant pool {0} at position {1}", new Object[] {type, i});

                        }

                        break;

                }

            }

            return false;

        }

        // ----------------------------------------------------- Private Methods

        private boolean refill(ByteBuffer buffer, ReadableByteChannel in, int requestLen) throws IOException {

            int cap = buffer.capacity();

            if (buffer.position() + requestLen > cap) {

                buffer.compact();

                int read = in.read(buffer);

                if (read < 0) {

                    return false;

                }

                buffer.rewind();

            }

            return true;

        }

    } // END ConstantPoolInfo

This discussion has been closed.