Forward ported from 2.3-gae: FREEMARKER-80: Avoid calling synchonized PropertyDescriptor.getReadMethod() when getting a property in a template. Will see if it matters.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/ef4674e8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/ef4674e8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/ef4674e8 Branch: refs/heads/3 Commit: ef4674e84dd8802112555c456a1fdcc884f92f6c Parents: fa92465 Author: ddekany <[email protected]> Authored: Tue Oct 3 20:23:32 2017 +0200 Committer: ddekany <[email protected]> Committed: Tue Oct 3 20:23:32 2017 +0200 ---------------------------------------------------------------------- .../freemarker/core/model/impl/BeanModel.java | 20 ++------ .../core/model/impl/ClassIntrospector.java | 51 +++----------------- .../core/model/impl/FastPropertyDescriptor.java | 39 +++++++++++++++ 3 files changed, 50 insertions(+), 60 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef4674e8/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java index accadaf..9dbe5b6 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java @@ -19,8 +19,6 @@ package org.apache.freemarker.core.model.impl; -import java.beans.IndexedPropertyDescriptor; -import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -180,21 +178,9 @@ public class BeanModel } TemplateModel resultModel = UNKNOWN; - if (desc instanceof PropertyDescriptor) { - PropertyDescriptor pd = (PropertyDescriptor) desc; - Method readMethod = pd.getReadMethod(); - if (readMethod != null) { - // Unlike in FreeMarker 2, we prefer the normal read method even if there's an indexed read method. - resultModel = wrapper.invokeMethod(object, readMethod, null); - // cachedModel remains null, as we don't cache these - } else if (desc instanceof IndexedPropertyDescriptor) { - // In FreeMarker 2 we have exposed such indexed properties as sequences, but they can't support - // the getCollectionSize() method, so we have discontinued that. People has to call the indexed read - // method like any other method. - resultModel = UNKNOWN; - } else { - throw new IllegalStateException("PropertyDescriptor.readMethod shouldn't be null"); - } + if (desc instanceof FastPropertyDescriptor) { + // Unlike in FreeMarker 2, we always use the normal read method, and ignore the indexed read method. + resultModel = wrapper.invokeMethod(object, ((FastPropertyDescriptor) desc).getReadMethod(), null); } else if (desc instanceof Field) { resultModel = wrapper.wrap(((Field) desc).get(object)); // cachedModel remains null, as we don't cache these http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef4674e8/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java index cc083d4..94eca36 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java @@ -206,7 +206,7 @@ class ClassIntrospector { * Gets the class introspection data from {@link #cache}, automatically creating the cache entry if it's missing. * * @return A {@link Map} where each key is a property/method/field name (or a special {@link Object} key like - * {@link #CONSTRUCTORS_KEY}), each value is a {@link PropertyDescriptor} or {@link Method} or + * {@link #CONSTRUCTORS_KEY}), each value is a {@link FastPropertyDescriptor} or {@link Method} or * {@link OverloadedMethods} or {@link Field} (but better check the source code...). */ Map<Object, Object> get(Class<?> clazz) { @@ -321,8 +321,7 @@ class ClassIntrospector { int mdsSize = mds.size(); IdentityHashMap<Method, Void> argTypesUsedByIndexerPropReaders = null; for (int i = mdsSize - 1; i >= 0; --i) { - final MethodDescriptor md = mds.get(i); - final Method method = getMatchingAccessibleMethod(md.getMethod(), accessibleMethods); + final Method method = getMatchingAccessibleMethod(mds.get(i).getMethod(), accessibleMethods); if (method != null && isAllowedToExpose(method)) { decision.setDefaults(method); if (methodAppearanceFineTuner != null) { @@ -336,7 +335,7 @@ class ClassIntrospector { } PropertyDescriptor propDesc = decision.getExposeAsProperty(); - if (propDesc != null && !(introspData.get(propDesc.getName()) instanceof PropertyDescriptor)) { + if (propDesc != null && !(introspData.get(propDesc.getName()) instanceof FastPropertyDescriptor)) { addPropertyDescriptorToClassIntrospectionData( introspData, propDesc, clazz, accessibleMethods); } @@ -359,7 +358,7 @@ class ClassIntrospector { // Already overloaded method - add new overload ((OverloadedMethods) previous).addMethod(method); } else if (decision.getMethodShadowsProperty() - || !(previous instanceof PropertyDescriptor)) { + || !(previous instanceof FastPropertyDescriptor)) { // Simple method (this far) introspData.put(methodKey, method); Class<?>[] replaced = getArgTypesByMethod(introspData).put(method, @@ -377,6 +376,7 @@ class ClassIntrospector { } // end if (exposureLevel < EXPOSE_PROPERTIES_ONLY) } + // TODO [FM3] As we ignore indexed property getters in FM3, this might could be simplified. /** * Very similar to {@link BeanInfo#getPropertyDescriptors()}, but can deal with Java 8 default methods too. */ @@ -647,44 +647,9 @@ class ClassIntrospector { private void addPropertyDescriptorToClassIntrospectionData(Map<Object, Object> introspData, PropertyDescriptor pd, Class<?> clazz, Map<MethodSignature, List<Method>> accessibleMethods) { - if (pd instanceof IndexedPropertyDescriptor) { - IndexedPropertyDescriptor ipd = - (IndexedPropertyDescriptor) pd; - Method readMethod = ipd.getIndexedReadMethod(); - Method publicReadMethod = getMatchingAccessibleMethod(readMethod, accessibleMethods); - if (publicReadMethod != null && isAllowedToExpose(publicReadMethod)) { - try { - if (readMethod != publicReadMethod) { - ipd = new IndexedPropertyDescriptor( - ipd.getName(), ipd.getReadMethod(), - null, publicReadMethod, - null); - } - introspData.put(ipd.getName(), ipd); - getArgTypesByMethod(introspData).put(publicReadMethod, publicReadMethod.getParameterTypes()); - } catch (IntrospectionException e) { - LOG.warn("Failed creating a publicly-accessible property descriptor " - + "for {} indexed property {}, read method {}", - clazz.getName(), pd.getName(), publicReadMethod, - e); - } - } - } else { - Method readMethod = pd.getReadMethod(); - Method publicReadMethod = getMatchingAccessibleMethod(readMethod, accessibleMethods); - if (publicReadMethod != null && isAllowedToExpose(publicReadMethod)) { - try { - if (readMethod != publicReadMethod) { - pd = new PropertyDescriptor(pd.getName(), publicReadMethod, null); - } - introspData.put(pd.getName(), pd); - } catch (IntrospectionException e) { - LOG.warn("Failed creating a publicly-accessible property descriptor " - + "for {} property {}, read method {}", - clazz.getName(), pd.getName(), publicReadMethod, - e); - } - } + Method readMethod = getMatchingAccessibleMethod(pd.getReadMethod(), accessibleMethods); + if (readMethod != null && isAllowedToExpose(readMethod)) { + introspData.put(pd.getName(), new FastPropertyDescriptor(readMethod)); } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef4674e8/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FastPropertyDescriptor.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FastPropertyDescriptor.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FastPropertyDescriptor.java new file mode 100644 index 0000000..1019ba9 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FastPropertyDescriptor.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.freemarker.core.model.impl; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; + +/** + * Used instead of {@link PropertyDescriptor}, because the methods of that are synchronized. + * Note that if a property has no read method (a non-indexed an one), then we don't treat is as a property. + */ +final class FastPropertyDescriptor { + private final Method readMethod; + + public FastPropertyDescriptor(Method readMethod) { + this.readMethod = readMethod; + } + + public Method getReadMethod() { + return readMethod; + } + +}
