Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to disable support for referencing static fields and methods for JSP 2.3 in Tomcat 8

Is it possible to turn off the support for referencing static fields and methods in Tomcat 8 which was added as part of Unified Expression Language 3.0.

We have ~4K JSP's in our application, with many ${undefined} (no scope specified) expressions, the migration to Tomcat 8 lead to significant performance degradation since the evaluation of those expression is legal 'null' value. We no longer use JSP technology for newer pages but the legacy one won't go away soon.

The problematic code is in javax.servlet.el.ScopedAttributeELResolver class, which tries to resolve the expression from ImportHandler that makes many Class.forName lookups, which fails largely due to the ClassNotFoundException.

@Override
public Object getValue(ELContext context, Object base, Object property) {
    if (context == null) {
        throw new NullPointerException();
    }

    Object result = null;

    if (base == null) {
        context.setPropertyResolved(base, property);
        if (property != null) {
            String key = property.toString();
            PageContext page = (PageContext) context
                    .getContext(JspContext.class);
            result = page.findAttribute(key);

            if (result == null) {
                // This might be the name of an imported class
                ImportHandler importHandler = context.getImportHandler();
                if (importHandler != null) {
                    Class<?> clazz = importHandler.resolveClass(key);
                    if (clazz != null) {
                        result = new ELClass(clazz);
                    }
                    if (result == null) {
                        // This might be the name of an imported static field
                        clazz = importHandler.resolveStatic(key);
                        if (clazz != null) {
                            try {
                                result = clazz.getField(key).get(null);
                            } catch (IllegalArgumentException | IllegalAccessException |
                                    NoSuchFieldException | SecurityException e) {
                                // Most (all?) of these should have been
                                // prevented by the checks when the import
                                // was defined.
                            }
                        }
                    }
                }
            }
        }
    }

    return result;
}

Update

A bug were opened for Tomcat 8 - performance problems when using scopeless optional attributes, which is closed as won't fixed. I thought that they may to add some property to disable the performance consuming code, but for now they won't, due to:

  • It would be Tomcat specific
  • It would have to be a system property since this is a spec class
  • It could not be applied per application - it would impact all applications running on that instance

Please Advise Thanks

like image 747
Maxim Kirilov Avatar asked Mar 13 '23 06:03

Maxim Kirilov


1 Answers

One way to disable the new behavior is to take advantage of Tomcat's class loading mechanism. The common class loader contains additional classes that are made visible to both Tomcat internal classes and to all web applications. The locations searched by this class loader are defined by the common.loader property in $CATALINA_BASE/conf/catalina.properties. The default setting will search the following locations in the order they are listed:

  1. Unpacked classes and resources in $CATALINA_BASE/lib
  2. JAR files in $CATALINA_BASE/lib
  3. Unpacked classes and resources in $CATALINA_HOME/lib
  4. JAR files in $CATALINA_HOME/lib

I created a new jar with one class: javax.servlet.jsp.el.ScopedAttributeELResolver, this class identical to the original one (from jsp-api.jar) except the getValue method that does the static resolution (the tomcat code is under Apache 2 license so the patch is legal). The jar placed under $CATALINA_BASE/lib folder.

The new method:

    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        if (context == null) {
            throw new NullPointerException();
        }

        Object result = null;

        if (base == null) {
            context.setPropertyResolved(base, property);
            if (property != null) {
                String key = property.toString();
                PageContext page = (PageContext) context
                        .getContext(JspContext.class);
                result = page.findAttribute(key);

//                if (result == null) {
//                    // This might be the name of an imported class
//                    ImportHandler importHandler = context.getImportHandler();
//                    if (importHandler != null) {
//                        Class<?> clazz = importHandler.resolveClass(key);
//                        if (clazz != null) {
//                            result = new ELClass(clazz);
//                        }
//                        if (result == null) {
//                            // This might be the name of an imported static field
//                            clazz = importHandler.resolveStatic(key);
//                            if (clazz != null) {
//                                try {
//                                    result = clazz.getField(key).get(null);
//                                } catch (IllegalArgumentException | IllegalAccessException |
//                                        NoSuchFieldException | SecurityException e) {
//                                    // Most (all?) of these should have been
//                                    // prevented by the checks when the import
//                                    // was defined.
//                                }
//                            }
//                        }
//                    }
//                }
            }
        }

        return result;
    }

This class will be loaded instead the original one and detour the performance issues.

Advantages:

  • Easy to roll back by simple jar deletion

Disadvantages:

  • Need to be maintained every Tomcat upgrade
like image 85
Maxim Kirilov Avatar answered Apr 25 '23 12:04

Maxim Kirilov