In 2.3.27 release process: Merge branch '2.3-gae' into 2.3
diff --git a/README.md b/README.md
index f191947..0337f86 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,12 @@
you are using an optional feature that's certainly because your
application already uses the related library.
+Attention: If you upgrade to OpenJDK 9 or later, and you are using
+XPath queries in templates, you will need to add Apache Xalan as a
+dependency, as freemarker.ext.dom can't use the XPath support
+included in OpenJDK anymore. It's not needed on Oracle Java 9,
+or if FreeMarker is configured to use Jaxen for XPath.
+
The minimum required Java version is currently Java SE 5. (The presence
of a later version may be detected on runtime and utilized by
FreeMarker.)
diff --git a/src/main/java/freemarker/template/utility/ClassUtil.java b/src/main/java/freemarker/template/utility/ClassUtil.java
index 6b262f5..66944fe 100644
--- a/src/main/java/freemarker/template/utility/ClassUtil.java
+++ b/src/main/java/freemarker/template/utility/ClassUtil.java
@@ -25,7 +25,6 @@
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
-import java.util.zip.ZipException;
import freemarker.core.Environment;
import freemarker.core.Macro;
@@ -387,7 +386,10 @@
/**
* Very similar to {@link Class#getResourceAsStream(String)}, but throws {@link IOException} instead of returning
* {@code null} if {@code optional} is {@code false}, and attempts to work around "IllegalStateException: zip file
- * closed" and related issues (caused by bugs outside of FreeMarker).
+ * closed" and similar {@code sun.net.www.protocol.jar.JarURLConnection}-related glitches. These are caused by bugs
+ * outside of FreeMarker. Note that in cases where the JAR resource becomes broken concurrently, similar errors can
+ * still occur later when the {@link InputStream} is read ({@link #loadProperties(Class, String)} works that
+ * around as well).
*
* @return If {@code optional} is {@code false}, it's never {@code null}, otherwise {@code null} indicates that the
* resource doesn't exist.
@@ -400,6 +402,7 @@
throws IOException {
InputStream ins;
try {
+ // This is how we did this earlier. May uses some JarURLConnection caches, which leads to the problems.
ins = baseClass.getResourceAsStream(resource);
} catch (Exception e) {
// Workaround for "IllegalStateException: zip file closed", and other related exceptions. This happens due
@@ -421,6 +424,7 @@
*/
public static InputStream getReasourceAsStream(ClassLoader classLoader, String resource, boolean optional)
throws IOException {
+ // See source commends in the other overload of this method.
InputStream ins;
try {
ins = classLoader.getResourceAsStream(resource);
@@ -437,7 +441,7 @@
/**
* Loads a class loader resource into a {@link Properties}; tries to work around "zip file closed" and related
- * errors.
+ * {@code sun.net.www.protocol.jar.JarURLConnection} glitches.
*
* @since 2.3.27
*/
@@ -447,6 +451,7 @@
InputStream ins = null;
try {
try {
+ // This is how we did this earlier. May uses some JarURLConnection caches, which leads to the problems.
ins = baseClass.getResourceAsStream(resource);
} catch (Exception e) {
throw new MaybeZipFileClosedException();
@@ -456,6 +461,13 @@
props.load(ins);
} catch (Exception e) {
throw new MaybeZipFileClosedException();
+ } finally {
+ try {
+ ins.close();
+ } catch (Exception e) {
+ // Do nothing to suppress "ZipFile closed" and related exceptions.
+ }
+ ins = null;
}
} catch (MaybeZipFileClosedException e) {
// Workaround for "zip file closed" exception, and other related exceptions. This happens due to bugs
@@ -468,8 +480,8 @@
if (ins != null) {
try {
ins.close();
- } catch (ZipException e) {
- // Do nothing to suppress "ZipFile closed" exceptions.
+ } catch (Exception e) {
+ // Do nothing to suppress "ZipFile closed" and related exceptions.
}
}
}
@@ -484,6 +496,7 @@
}
}
+ /** Used internally to work around some JarURLConnection glitches */
private static class MaybeZipFileClosedException extends Exception {
//
}
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 5aba725..1375135 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -4035,6 +4035,31 @@
}</programlisting>
</section>
+ <section xml:id="dgui_template_exp_comment">
+ <title>Comments in expressions</title>
+
+ <indexterm>
+ <primary>comment</primary>
+ </indexterm>
+
+ <para>Expression may contain comments anywhere where they can
+ contain ignored white-space (<link
+ linkend="dgui_template_exp_whitespace">see above</link>). Comments
+ look like <literal><#-- ... --></literal> or as <literal>[#--
+ ... --]</literal>. Example:</para>
+
+ <programlisting role="template"><#assign x <#-- A comment --> = 123 <#-- A comment -->>
+<#function f(x <#-- A comment -->, y <#-- A comment -->)>
+ <#return <#-- A comment --> 1 <#-- A comment -->>
+</#function>
+<#assign someHash = {
+ "foo": 123, <#-- A comment -->
+ "bar": x <#-- A comment --> + 1,
+ <#-- A comment -->
+ "baaz": f(1 <#-- A comment -->, 2 <#-- A comment -->)
+} <#-- A comment -->></programlisting>
+ </section>
+
<section xml:id="dgui_template_exp_precedence">
<title>Operator precedence</title>
@@ -27191,19 +27216,20 @@
</listitem>
<listitem>
- <para>Bug fixed: <literal>BeansWrapper</literal> and
- <literal>DefaultObjectWrapper</literal>, starting from Java 8,
- when the same JavaBeans property has both non-indexed read
- method (like <literal>String[] getFoos()</literal>) and indexed
- read method (like <literal>String getFoos(int index)</literal>),
- has mistakenly used the indexed read method to access the
- property. This is a problem because then the array size was
- unknown, and thus the property has suddenly become unlistable on
- Java 8 (that is, <literal><#list myObject.foos as
- foo></literal> fails). To enable the fix (where it will use
- the non-indexed read method), you should to increase the value
- of the <literal>incompatibleImprovements</literal> constructor
- argument of the used <literal>DefaultObjectWrapper</literal> or
+ <para>Bug fixed: Starting from Java 8, when the same JavaBeans
+ property has both non-indexed read method (like
+ <literal>String[] getFoos()</literal>) and indexed read method
+ (like <literal>String getFoos(int index)</literal>),
+ <literal>BeansWrapper</literal> and
+ <literal>DefaultObjectWrapper</literal> have mistakenly used the
+ indexed read method to access the property. This is a problem
+ because then the array size was unknown, and thus the property
+ has suddenly become unlistable on Java 8 (that is,
+ <literal><#list myObject.foos as foo></literal> fails). To
+ enable the fix (where it will use the non-indexed read method),
+ you should increase the value of the
+ <literal>incompatibleImprovements</literal> constructor argument
+ of the used <literal>DefaultObjectWrapper</literal> or
<literal>BeansWrapper</literal> to 2.3.27. Note that if you
leave the <literal>object_wrapper</literal> setting of the
<literal>Configuration</literal> on its default, it's enough to
@@ -27223,6 +27249,22 @@
</listitem>
<listitem>
+ <para>Bug fixed (affects Java 8 and later): Regardless of the
+ value of the <literal>preferIndexedReadMethod</literal> setting
+ (see previous point), if one of the indexed read method and the
+ non-indexed read method is inaccessible (i.e., it's declared in
+ a non-public type, and wasn't inherited by a public type), while
+ the other read method is accessible, we will use the accessible
+ one. Earlier, if there was an indexed read method but it was
+ inaccessible, we have given up, and that bean property wasn't
+ visible. Such properties will now be visible again, just as
+ before Java 8. (Before Java 8
+ <literal>java.beans.Inrospector</literal> has only exposed the
+ non-indexed read method in this case, so we didn't have this
+ problem.)</para>
+ </listitem>
+
+ <listitem>
<para>Bug fixed: On OpenJDK 9 (but not on earlier versions, nor
on Oracle Java 9 (tested with <quote>build 9+181</quote>)), when
you try to use the DOM-based XML support
@@ -27232,7 +27274,7 @@
<literal>IllegalAccessError</literal> because <quote>java.xml
does not export com.sun.org.apache.xml.internal.utils</quote>.
Note that while the exception is not thrown anymore in 2.3.27,
- FreeMarker can't use the XPath support included in Open JDK 9,
+ FreeMarker can't use the XPath support included in OpenJDK 9,
and so templates that try to use XPath expressions (like
<literal>doc['//foo']</literal>) will still fail, <link
linkend="xgui_imperative_learn_xpath">unless 3rd party XPath
diff --git a/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java b/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
index 917b2b3..c3ff772 100644
--- a/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
+++ b/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
@@ -22,12 +22,14 @@
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import java.lang.reflect.Modifier;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import freemarker.core._JavaVersions;
import freemarker.template.Configuration;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateHashModel;
@@ -36,6 +38,7 @@
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
+import freemarker.template.Version;
import freemarker.template.utility.Constants;
@RunWith(JUnit4.class)
@@ -95,6 +98,27 @@
assertEquals(2, ((TemplateSequenceModel) fooTM).size());
}
}
+
+ @Test
+ public void java8InaccessibleIndexedAccessibleNonIndexedReadMethodTest() throws TemplateModelException {
+ assertTrue("This test case must be ran on Java 8 or later", _JavaVersions.JAVA_8 != null);
+ assertFalse(Modifier.isPublic(BeanWithInaccessibleIndexedProperty.class.getModifiers()));
+
+ for (Version ici : new Version[] { Configuration.VERSION_2_3_26, Configuration.VERSION_2_3_27 }) {
+ BeansWrapper bw = new BeansWrapper(ici);
+ TemplateHashModel beanTM = (TemplateHashModel) bw.wrap(new BeanWithInaccessibleIndexedProperty());
+ TemplateModel fooTM = beanTM.get("foo");
+
+ assertThat(fooTM, instanceOf(TemplateSequenceModel.class));
+ assertEquals("b",
+ ((TemplateScalarModel) ((TemplateSequenceModel) fooTM).get(1)).getAsString());
+ // Even with 2.3.26, where the indexed reader was preferred, as it's inaccessible, we use the normal reader:
+ assertEquals(2, ((TemplateSequenceModel) fooTM).size());
+
+ TemplateModel barTM = beanTM.get("bar");
+ assertNull(barTM); // all read methods inaccessible
+ }
+ }
public static class BeanWithBothIndexedAndArrayProperty {
@@ -110,4 +134,28 @@
}
+ public interface HasFoo {
+ String[] getFoo();
+ }
+
+ // Note: This class is deliberately not public
+ static class BeanWithInaccessibleIndexedProperty implements HasFoo {
+
+ private final static String[] FOO = new String[] { "a", "b" };
+
+ public String getFoo(int index) {
+ return FOO[index];
+ }
+
+ // This will be accessible
+ public String[] getFoo() {
+ return FOO;
+ }
+
+ public String getBar(int index) {
+ return FOO[index];
+ }
+
+ }
+
}