Merge pull request #94 from astappiev/patch-2
fix: Incorrect Last-Modified header for resources
diff --git a/api/pom.xml b/api/pom.xml
index e243ed1..7673f97 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -68,6 +68,32 @@
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/*/README.md</exclude>
+ <exclude>**/*/MANIFEST.MF</exclude>
+ <exclude>.git</exclude>
+ <exclude>.gitignore</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>src/main/resources/META-INF/licenses/*-LICENSE.txt</exclude>
+ <exclude>src/main/resources/META-INF/licenses/*-LICENSE.TXT</exclude>
+ <exclude>src/main/resources/META-INF/services/**</exclude>
+ <exclude>DEPENDENCIES</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
<!-- compilation and compression of our javascripts -->
<plugin>
<artifactId>myfaces-javascript-plugin</artifactId>
diff --git a/assembly/pom.xml b/assembly/pom.xml
index caef2bf..fd23968 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -37,8 +37,8 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>dependency-maven-plugin</artifactId>
+ <version>1.0</version>
<executions>
-
<execution>
<id>copy-javadoc</id>
<phase>package</phase>
diff --git a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java
index 3ebd16f..e2797e6 100644
--- a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java
+++ b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java
@@ -68,7 +68,6 @@
import io.quarkus.arc.deployment.BeanRegistrarBuildItem;
import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem;
import io.quarkus.arc.deployment.ContextRegistrarBuildItem;
-import io.quarkus.arc.processor.ContextRegistrar;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
@@ -173,7 +172,7 @@
FaceletsResourceResolver.class.getName(),
FlowDefinition.class.getName()
};
-
+
private static final String[] FACTORIES =
{
FactoryFinder.APPLICATION_FACTORY,
@@ -232,29 +231,24 @@
@BuildStep
void buildCdiScopes(BuildProducer<ContextRegistrarBuildItem> contextRegistrar) throws IOException
{
- contextRegistrar.produce(new ContextRegistrarBuildItem(new ContextRegistrar()
- {
- @Override
- public void register(ContextRegistrar.RegistrationContext registrationContext)
- {
- registrationContext.configure(ViewScoped.class)
- .normal()
- .contextClass(QuarkusViewScopeContext.class)
- .done();
- registrationContext.configure(FacesScoped.class)
- .normal()
- .contextClass(QuarkusFacesScopeContext.class)
- .done();
- registrationContext.configure(ViewTransientScoped.class)
- .normal()
- .contextClass(QuarkusViewTransientScopeContext.class)
- .done();
- registrationContext.configure(FlowScoped.class)
- .normal()
- .contextClass(QuarkusFlowScopedContext.class)
- .done();
- }
- }));
+ contextRegistrar.produce(new ContextRegistrarBuildItem(registrationContext -> {
+ registrationContext.configure(ViewScoped.class)
+ .normal()
+ .contextClass(QuarkusViewScopeContext.class)
+ .done();
+ registrationContext.configure(FacesScoped.class)
+ .normal()
+ .contextClass(QuarkusFacesScopeContext.class)
+ .done();
+ registrationContext.configure(ViewTransientScoped.class)
+ .normal()
+ .contextClass(QuarkusViewTransientScopeContext.class)
+ .done();
+ registrationContext.configure(FlowScoped.class)
+ .normal()
+ .contextClass(QuarkusFlowScopedContext.class)
+ .done();
+ }, ViewScoped.class, FacesScoped.class, ViewTransientScoped.class, FlowScoped.class));
}
@BuildStep
diff --git a/extensions/quarkus/pom.xml b/extensions/quarkus/pom.xml
index 2d0e6c1..4dfe0f7 100644
--- a/extensions/quarkus/pom.xml
+++ b/extensions/quarkus/pom.xml
@@ -43,7 +43,7 @@
</modules>
<properties>
- <quarkus.version>1.2.1.Final</quarkus.version>
+ <quarkus.version>1.3.1.Final</quarkus.version>
</properties>
<build>
@@ -55,6 +55,29 @@
<excludes>**/*$$accessor.java</excludes>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/*/README.md</exclude>
+ <exclude>**/*/MANIFEST.MF</exclude>
+ <exclude>.git</exclude>
+ <exclude>.gitignore</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>src/main/resources/META-INF/services/**</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/extensions/quarkus/showcase/pom.xml b/extensions/quarkus/showcase/pom.xml
index 2d98fcc..0651ea9 100644
--- a/extensions/quarkus/showcase/pom.xml
+++ b/extensions/quarkus/showcase/pom.xml
@@ -25,8 +25,8 @@
<name>Apache MyFaces Core 2.3-next - Extensions - Quarkus - Showcase</name>
<properties>
- <quarkus.version>1.2.1.Final</quarkus.version>
- <myfaces.version>2.3-next-SNAPSHOT</myfaces.version>
+ <quarkus.version>1.3.1.Final</quarkus.version>
+ <myfaces.version>2.3-next-M2</myfaces.version>
<failsafe.version>2.22.0</failsafe.version>
<surefire.version>2.22.0</surefire.version>
@@ -70,6 +70,12 @@
<version>2.0.1.Final</version>
</dependency>
+ <dependency> <!-- workaround for https://github.com/quarkusio/quarkus/issues/7359 -->
+ <groupId>com.fasterxml.woodstox</groupId>
+ <artifactId>woodstox-core</artifactId>
+ <version>5.0.2</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
@@ -83,6 +89,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>4.5.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.6.2</version>
diff --git a/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesNativeIT.java b/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesNativeIT.java
index 5674136..4ec83b9 100644
--- a/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesNativeIT.java
+++ b/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesNativeIT.java
@@ -1,3 +1,21 @@
+/*
+ * 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.myfaces.core.extensions.quarkus.showcase;
import io.quarkus.test.junit.NativeImageTest;
diff --git a/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesShowcaseTest.java b/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesShowcaseTest.java
index d4fe663..0f595bc 100644
--- a/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesShowcaseTest.java
+++ b/extensions/quarkus/showcase/src/test/java/org/apache/myfaces/core/extensions/quarkus/showcase/QuarkusMyFacesShowcaseTest.java
@@ -1,3 +1,21 @@
+/*
+ * 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.myfaces.core.extensions.quarkus.showcase;
import com.gargoylesoftware.htmlunit.BrowserVersion;
diff --git a/impl/src/main/java/org/apache/myfaces/component/validate/WholeBeanValidator.java b/impl/src/main/java/org/apache/myfaces/component/validate/WholeBeanValidator.java
index 9b52958..d55f115 100644
--- a/impl/src/main/java/org/apache/myfaces/component/validate/WholeBeanValidator.java
+++ b/impl/src/main/java/org/apache/myfaces/component/validate/WholeBeanValidator.java
@@ -364,7 +364,7 @@
/**
* Set the Bean Validation validation groups.
* @param validationGroups The validation groups String, separated by
- * {@link BeanValidator#VALIDATION_GROUPS_DELIMITER}.
+ * {@link javax.faces.validator.BeanValidator#VALIDATION_GROUPS_DELIMITER}.
*/
public void setValidationGroups(ValidateWholeBeanComponent component, final String validationGroups)
{
diff --git a/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java b/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
index 5b72d9c..bf663fe 100755
--- a/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
+++ b/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
@@ -18,6 +18,7 @@
*/
package org.apache.myfaces.config;
+import java.lang.invoke.MethodHandles;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.application.ProjectStage;
@@ -788,12 +789,12 @@
/**
* Defines if the clientbehavior scripts are passed as string or function to the jsf.util.chain.
- * "As string" is actually the default behavior of both MyFaces (until 3.0) and Mojarra.
+ * "As string" is actually the default behavior of both MyFaces (until 2.3-next) and Mojarra.
* "As function" is quite usefull for CSP as no string needs to be evaluated as function.
*
* Our jsf.util.chain supports both of course.
*/
- @JSFWebConfigParam(name="org.apache.myfaces.RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING", since="3.0", defaultValue = "false")
+ @JSFWebConfigParam(name="org.apache.myfaces.RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING", since="2.3-next", defaultValue = "false")
public static final String RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING = "org.apache.myfaces.RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING";
public static final boolean RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING_DEFAULT = false;
@@ -805,9 +806,18 @@
* rendering if a view is not transient and server side state saving is in use.
*/
@JSFWebConfigParam(since="2.3.6", defaultValue="false", expectedValues="true,false")
- protected static final String ALWAYS_FORCE_SESSION_CREATION =
+ public static final String ALWAYS_FORCE_SESSION_CREATION =
"org.apache.myfaces.ALWAYS_FORCE_SESSION_CREATION";
- public final static boolean ALWAYS_FORCE_SESSION_CREATION_DEFAULT = false;
+ protected final static boolean ALWAYS_FORCE_SESSION_CREATION_DEFAULT = false;
+
+ /**
+ * Defines if MethodHandles and LambdaMetafactory instead of Reflection should be used for getter/setter.
+ */
+ @JSFWebConfigParam(since="2.3-next", defaultValue="true", expectedValues="true,false", tags="performance")
+ public static final String USE_METHOD_HANDLES =
+ "org.apache.myfaces.USE_METHOD_HANDLES";
+ protected final static boolean USE_METHOD_HANDLES_DEFAULT = true;
+
// we need it, applicationImpl not ready probably
private ProjectStage projectStage = ProjectStage.Production;
@@ -888,6 +898,7 @@
private int websocketMaxConnections = WEBSOCKET_MAX_CONNECTIONS_DEFAULT;
private boolean renderClientBehaviorScriptsAsString = RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING_DEFAULT;
private boolean alwaysForceSessionCreation = ALWAYS_FORCE_SESSION_CREATION_DEFAULT;
+ private boolean useMethodHandles = USE_METHOD_HANDLES_DEFAULT;
private static final boolean MYFACES_IMPL_AVAILABLE;
private static final boolean RI_IMPL_AVAILABLE;
@@ -1303,6 +1314,21 @@
cfg.alwaysForceSessionCreation = getBoolean(extCtx, ALWAYS_FORCE_SESSION_CREATION,
ALWAYS_FORCE_SESSION_CREATION_DEFAULT);
+ cfg.useMethodHandles = getBoolean(extCtx, USE_METHOD_HANDLES,
+ USE_METHOD_HANDLES_DEFAULT);
+ if (cfg.useMethodHandles)
+ {
+ try
+ {
+ MethodHandles.class.getMethod("privateLookupIn", Class.class,
+ MethodHandles.Lookup.class);
+ }
+ catch (NoSuchMethodException e)
+ {
+ cfg.useMethodHandles = false;
+ }
+ }
+
return cfg;
}
@@ -1762,5 +1788,10 @@
{
return alwaysForceSessionCreation;
}
+
+ public boolean isUseMethodHandles()
+ {
+ return useMethodHandles;
+ }
}
diff --git a/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java b/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java
index f294922..5bb2804 100644
--- a/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java
+++ b/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java
@@ -46,6 +46,7 @@
import org.apache.myfaces.el.resolver.ScopedAttributeResolver;
import org.apache.myfaces.el.resolver.implicitobject.ImplicitObjectResolver;
import org.apache.myfaces.config.MyfacesConfig;
+import org.apache.myfaces.el.resolver.MethodHandleBeanELResolver;
import org.apache.myfaces.util.lang.ClassUtils;
/**
@@ -139,8 +140,15 @@
list.add(new MapELResolver());
list.add(new ListELResolver());
list.add(new ArrayELResolver());
- list.add(new BeanELResolver());
-
+ if (myfacesConfig.isUseMethodHandles())
+ {
+ list.add(new MethodHandleBeanELResolver());
+ }
+ else
+ {
+ list.add(new BeanELResolver());
+ }
+
// give the user a chance to sort the resolvers
sortELResolvers(list, Scope.Faces);
diff --git a/impl/src/main/java/org/apache/myfaces/el/resolver/FacesCompositeELResolver.java b/impl/src/main/java/org/apache/myfaces/el/resolver/FacesCompositeELResolver.java
index 5442eac..2f09a88 100644
--- a/impl/src/main/java/org/apache/myfaces/el/resolver/FacesCompositeELResolver.java
+++ b/impl/src/main/java/org/apache/myfaces/el/resolver/FacesCompositeELResolver.java
@@ -30,9 +30,6 @@
* This composite el resolver will be used at the top level resolver for faces
* ({@link javax.faces.application.Application#getELResolver()})
* and jsp (the one we add with {@link javax.servlet.jsp.JspApplicationContext#addELResolver(javax.el.ELResolver)}.
- * It keeps track of its scope to let the variable resolver {@link org.apache.myfaces.el.VariableResolverImpl}
- * know in which scope it is executed. This is
- * necessarry to call either the faces or the jsp resolver head.
* </p>
* <p>
* This implementation does nothing if there is no actual faces context. This is necessarry since we registered our
diff --git a/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java b/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
new file mode 100644
index 0000000..6588d0e
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
@@ -0,0 +1,137 @@
+/*
+ * 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.myfaces.el.resolver;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.el.BeanELResolver;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.PropertyNotWritableException;
+import org.apache.myfaces.util.lang.MethodHandleUtils;
+
+public class MethodHandleBeanELResolver extends BeanELResolver
+{
+ private final ConcurrentHashMap<String, Map<String, MethodHandleUtils.LambdaPropertyDescriptor>> cache;
+
+ public MethodHandleBeanELResolver()
+ {
+ cache = new ConcurrentHashMap<>(1000);
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property)
+ {
+ Objects.requireNonNull(context);
+ if (base == null || property == null)
+ {
+ return null;
+ }
+
+ context.setPropertyResolved(base, property);
+
+ return getPropertyDescriptor(base, property).getPropertyType();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object getValue(ELContext context, Object base, Object property)
+ {
+ Objects.requireNonNull(context);
+ if (base == null || property == null)
+ {
+ return null;
+ }
+
+ context.setPropertyResolved(base, property);
+
+ try
+ {
+ return getPropertyDescriptor(base, property).getReadFunction().apply(base);
+ }
+ catch (Exception e)
+ {
+ throw new ELException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value)
+ {
+ Objects.requireNonNull(context);
+ if (base == null || property == null)
+ {
+ return;
+ }
+
+ context.setPropertyResolved(base, property);
+
+ MethodHandleUtils.LambdaPropertyDescriptor propertyDescriptor = getPropertyDescriptor(base, property);
+ if (propertyDescriptor.getWriteFunction() == null)
+ {
+ throw new PropertyNotWritableException("Property \"" + (String) property
+ + "\" in \"" + base.getClass().getName() + "\" is not writable!");
+ }
+
+ try
+ {
+ propertyDescriptor.getWriteFunction().accept(base, value);
+ }
+ catch (Exception e)
+ {
+ throw new ELException(e);
+ }
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property)
+ {
+ Objects.requireNonNull(context);
+ if (base == null || property == null)
+ {
+ return false;
+ }
+
+ context.setPropertyResolved(base, property);
+
+ return getPropertyDescriptor(base, property).getWriteFunction() == null;
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base)
+ {
+ if (base != null)
+ {
+ return Object.class;
+ }
+
+ return null;
+ }
+
+ protected MethodHandleUtils.LambdaPropertyDescriptor getPropertyDescriptor(Object base, Object property)
+ {
+ Map<String, MethodHandleUtils.LambdaPropertyDescriptor> beanCache = cache.computeIfAbsent(
+ base.getClass().getName(), k -> MethodHandleUtils.getLambdaPropertyDescriptors(base.getClass()));
+ return beanCache.get((String) property);
+ }
+
+}
diff --git a/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java b/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java
index c3cf527..cd0214f 100644
--- a/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java
+++ b/impl/src/main/java/org/apache/myfaces/util/ExternalSpecifications.java
@@ -50,7 +50,7 @@
{
try
{
- available = (ClassUtils.classForName("javax.validation.Validation") != null);
+ available = ClassUtils.classForName("javax.validation.Validation") != null;
}
catch(ClassNotFoundException e)
{
diff --git a/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java b/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java
new file mode 100644
index 0000000..7152823
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java
@@ -0,0 +1,244 @@
+/*
+ * 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.myfaces.util.lang;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.ObjDoubleConsumer;
+import java.util.function.ObjIntConsumer;
+import java.util.function.ObjLongConsumer;
+import javax.el.ELException;
+
+public class MethodHandleUtils
+{
+ private static Method privateLookupIn;
+
+ static
+ {
+ try
+ {
+ privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class,
+ MethodHandles.Lookup.class);
+ }
+ catch (Exception e)
+ {
+ }
+ }
+
+ public static class LambdaPropertyDescriptor
+ {
+ private PropertyDescriptor wrapped;
+ private Function<Object, Object> readFunction;
+ private BiConsumer<Object, Object> writeFunction;
+
+ public PropertyDescriptor getWrapped()
+ {
+ return wrapped;
+ }
+
+ public Class<?> getPropertyType()
+ {
+ return wrapped.getPropertyType();
+ }
+
+ public Function<Object, Object> getReadFunction()
+ {
+ return readFunction;
+ }
+
+ public BiConsumer<Object, Object> getWriteFunction()
+ {
+ return writeFunction;
+ }
+ }
+
+ public static Map<String, LambdaPropertyDescriptor> getLambdaPropertyDescriptors(Class<?> target)
+ {
+ try
+ {
+ PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(target).getPropertyDescriptors();
+ if (propertyDescriptors == null || propertyDescriptors.length == 0)
+ {
+ return Collections.emptyMap();
+ }
+
+ HashMap<String, LambdaPropertyDescriptor> properties = new HashMap<>(propertyDescriptors.length);
+
+ MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupIn.invoke(null, target,
+ MethodHandles.lookup());
+
+ for (PropertyDescriptor pd : Introspector.getBeanInfo(target).getPropertyDescriptors())
+ {
+ LambdaPropertyDescriptor lpd = new LambdaPropertyDescriptor();
+ lpd.wrapped = pd;
+
+ Method getter = pd.getReadMethod();
+ if (getter != null)
+ {
+ MethodHandle getterHandle = lookup.unreflect(getter);
+ CallSite getterCallSite = LambdaMetafactory.metafactory(lookup,
+ "apply",
+ MethodType.methodType(Function.class),
+ MethodType.methodType(Object.class, Object.class),
+ getterHandle,
+ getterHandle.type());
+ lpd.readFunction = (Function) getterCallSite.getTarget().invokeExact();
+ }
+
+ Method setter = pd.getWriteMethod();
+ if (setter != null)
+ {
+ MethodHandle setterHandle = lookup.unreflect(setter);
+ lpd.writeFunction = createSetter(lookup, lpd, setterHandle);
+ }
+
+ properties.put(pd.getName(), lpd);
+ }
+
+ return properties;
+ }
+ catch (Throwable e)
+ {
+ throw new ELException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static BiConsumer createSetter(MethodHandles.Lookup lookup, LambdaPropertyDescriptor propertyInfo,
+ MethodHandle setterHandle)
+ throws LambdaConversionException, Throwable
+ {
+ Class<?> propertyType = propertyInfo.getPropertyType();
+ // special handling for primitives required, see https://dzone.com/articles/setters-method-handles-and-java-11
+ if (propertyType.isPrimitive())
+ {
+ if (propertyType == double.class)
+ {
+ ObjDoubleConsumer consumer = (ObjDoubleConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjDoubleConsumer.class, double.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (double) b);
+ }
+ else if (propertyType == int.class)
+ {
+ ObjIntConsumer consumer = (ObjIntConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjIntConsumer.class, int.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (int) b);
+ }
+ else if (propertyType == long.class)
+ {
+ ObjLongConsumer consumer = (ObjLongConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjLongConsumer.class, long.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (long) b);
+ }
+ else if (propertyType == float.class)
+ {
+ ObjFloatConsumer consumer = (ObjFloatConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjFloatConsumer.class, float.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (float) b);
+ }
+ else if (propertyType == byte.class)
+ {
+ ObjByteConsumer consumer = (ObjByteConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjByteConsumer.class, byte.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (byte) b);
+ }
+ else if (propertyType == char.class)
+ {
+ ObjCharConsumer consumer = (ObjCharConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjCharConsumer.class, char.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (char) b);
+ }
+ else if (propertyType == short.class)
+ {
+ ObjShortConsumer consumer = (ObjShortConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjShortConsumer.class, short.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (short) b);
+ }
+ else if (propertyType == boolean.class)
+ {
+ ObjBooleanConsumer consumer = (ObjBooleanConsumer) createSetterCallSite(
+ lookup, setterHandle, ObjBooleanConsumer.class, boolean.class).getTarget().invokeExact();
+ return (a, b) -> consumer.accept(a, (boolean) b);
+ }
+ else
+ {
+ throw new RuntimeException("Type is not supported yet: " + propertyType.getName());
+ }
+ }
+ else
+ {
+ return (BiConsumer) createSetterCallSite(lookup, setterHandle, BiConsumer.class, Object.class).getTarget()
+ .invokeExact();
+ }
+ }
+
+ protected static CallSite createSetterCallSite(MethodHandles.Lookup lookup, MethodHandle setter,
+ Class<?> interfaceType, Class<?> valueType)
+ throws LambdaConversionException
+ {
+ return LambdaMetafactory.metafactory(lookup,
+ "accept",
+ MethodType.methodType(interfaceType),
+ MethodType.methodType(void.class, Object.class, valueType),
+ setter,
+ setter.type());
+ }
+
+ @FunctionalInterface
+ public interface ObjFloatConsumer<T extends Object>
+ {
+ public void accept(T t, float i);
+ }
+
+ @FunctionalInterface
+ public interface ObjByteConsumer<T extends Object>
+ {
+ public void accept(T t, byte i);
+ }
+
+ @FunctionalInterface
+ public interface ObjCharConsumer<T extends Object>
+ {
+ public void accept(T t, char i);
+ }
+
+ @FunctionalInterface
+ public interface ObjShortConsumer<T extends Object>
+ {
+ public void accept(T t, short i);
+ }
+
+ @FunctionalInterface
+ public interface ObjBooleanConsumer<T extends Object>
+ {
+ public void accept(T t, boolean i);
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java
index f33708e..5493e7a 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java
@@ -20,6 +20,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.function.BiConsumer;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.MetaRule;
@@ -40,18 +41,38 @@
@Override
public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget meta)
{
- Method m = meta.getWriteMethod(name);
-
- // if the property is writable
- if (m != null)
+ if (meta instanceof MethodHandleMetadataTargetImpl)
{
- if (attribute.isLiteral())
+ BiConsumer<Object, Object> f = ((MethodHandleMetadataTargetImpl) meta).getWriteFunction(name);
+
+ // if the property is writable
+ if (f != null)
{
- return new LiteralPropertyMetadata(m, attribute);
+ if (attribute.isLiteral())
+ {
+ return new LiteralPropertyMetadata(meta.getPropertyType(name), f, attribute);
+ }
+ else
+ {
+ return new DynamicPropertyMetadata(meta.getPropertyType(name), f, attribute);
+ }
}
- else
+ }
+ else
+ {
+ Method m = meta.getWriteMethod(name);
+
+ // if the property is writable
+ if (m != null)
{
- return new DynamicPropertyMetadata(m, attribute);
+ if (attribute.isLiteral())
+ {
+ return new LiteralPropertyMetadata(meta.getPropertyType(name), m, attribute);
+ }
+ else
+ {
+ return new DynamicPropertyMetadata(meta.getPropertyType(name), m, attribute);
+ }
}
}
@@ -60,27 +81,53 @@
final static class LiteralPropertyMetadata extends Metadata
{
+ private final Class<?> propertyType;
private final Method method;
+ private final BiConsumer<Object, Object> function;
private final TagAttribute attribute;
- private Object[] value;
+ private Object value;
+ private Object[] valueArgs;
- public LiteralPropertyMetadata(Method method, TagAttribute attribute)
+ public LiteralPropertyMetadata(Class<?> propertyType, Method method, TagAttribute attribute)
{
+ this.propertyType = propertyType;
this.method = method;
+ this.function = null;
+ this.attribute = attribute;
+ }
+
+ public LiteralPropertyMetadata(Class<?> propertyType, BiConsumer<Object, Object> function,
+ TagAttribute attribute)
+ {
+ this.propertyType = propertyType;
+ this.method = null;
+ this.function = function;
this.attribute = attribute;
}
@Override
public void applyMetadata(FaceletContext ctx, Object instance)
{
- if (value == null)
- {
- String str = this.attribute.getValue();
- value = new Object[] { ctx.getExpressionFactory().coerceToType(str, method.getParameterTypes()[0]) };
- }
try
{
- method.invoke(instance, this.value);
+ if (method != null)
+ {
+ if (valueArgs == null)
+ {
+ String str = this.attribute.getValue();
+ valueArgs = new Object[] { ctx.getExpressionFactory().coerceToType(str, propertyType) };
+ }
+ method.invoke(instance, valueArgs);
+ }
+ else if (function != null)
+ {
+ if (value == null)
+ {
+ String str = this.attribute.getValue();
+ value = ctx.getExpressionFactory().coerceToType(str, propertyType);
+ }
+ function.accept(instance, value);
+ }
}
catch (InvocationTargetException e)
{
@@ -96,23 +143,41 @@
final static class DynamicPropertyMetadata extends Metadata
{
+ private final Class<?> propertyType;
private final Method method;
+ private final BiConsumer<Object, Object> function;
private final TagAttribute attribute;
- private final Class<?> type;
- public DynamicPropertyMetadata(Method method, TagAttribute attribute)
+ public DynamicPropertyMetadata(Class<?> propertyType, Method method, TagAttribute attribute)
{
+ this.propertyType = propertyType;
this.method = method;
- this.type = method.getParameterTypes()[0];
+ this.function = null;
this.attribute = attribute;
}
+ public DynamicPropertyMetadata(Class<?> propertyType, BiConsumer<Object, Object> function,
+ TagAttribute attribute)
+ {
+ this.propertyType = propertyType;
+ this.method = null;
+ this.function = function;
+ this.attribute = attribute;
+ }
+
@Override
public void applyMetadata(FaceletContext ctx, Object instance)
{
try
{
- method.invoke(instance, new Object[] { attribute.getObject(ctx, type) });
+ if (method != null)
+ {
+ method.invoke(instance, new Object[] { attribute.getObject(ctx, propertyType) });
+ }
+ else if (function != null)
+ {
+ function.accept(instance, attribute.getObject(ctx, propertyType));
+ }
}
catch (InvocationTargetException e)
{
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java
index 5405b39..e5f7988 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java
@@ -34,6 +34,7 @@
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.apache.myfaces.config.MyfacesConfig;
import org.apache.myfaces.util.lang.Assert;
import org.apache.myfaces.view.facelets.PassthroughRule;
import org.apache.myfaces.view.facelets.tag.jsf.PassThroughLibrary;
@@ -324,7 +325,14 @@
{
try
{
- meta = new MetadataTargetImpl(_type);
+ if (MyfacesConfig.getCurrentInstance().isUseMethodHandles())
+ {
+ meta = new MethodHandleMetadataTargetImpl(_type);
+ }
+ else
+ {
+ meta = new MetadataTargetImpl(_type);
+ }
}
catch (IntrospectionException e)
{
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodHandleMetadataTargetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodHandleMetadataTargetImpl.java
new file mode 100644
index 0000000..f810fc9
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodHandleMetadataTargetImpl.java
@@ -0,0 +1,115 @@
+/*
+ * 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.myfaces.view.facelets.tag;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import javax.faces.view.facelets.MetadataTarget;
+import org.apache.myfaces.util.lang.MethodHandleUtils;
+
+public class MethodHandleMetadataTargetImpl extends MetadataTarget
+{
+ private final Map<String, MethodHandleUtils.LambdaPropertyDescriptor> propertyDescriptors;
+ private final Class<?> type;
+
+ public MethodHandleMetadataTargetImpl(Class<?> type) throws IntrospectionException
+ {
+ this.type = type;
+ this.propertyDescriptors = MethodHandleUtils.getLambdaPropertyDescriptors(type);
+ }
+
+ @Override
+ public PropertyDescriptor getProperty(String name)
+ {
+ MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);
+ if (lpd == null)
+ {
+ return null;
+ }
+
+ return lpd.getWrapped();
+ }
+
+ @Override
+ public Class<?> getPropertyType(String name)
+ {
+ MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);
+ if (lpd == null)
+ {
+ return null;
+ }
+
+ return lpd.getPropertyType();
+ }
+
+ @Override
+ public Method getReadMethod(String name)
+ {
+ throw new UnsupportedOperationException("Not supported!");
+ }
+
+ @Override
+ public Class<?> getTargetClass()
+ {
+ return type;
+ }
+
+ @Override
+ public Method getWriteMethod(String name)
+ {
+ throw new UnsupportedOperationException("Not supported!");
+ }
+
+ @Override
+ public boolean isTargetInstanceOf(Class type)
+ {
+ return type.isAssignableFrom(type);
+ }
+
+ public MethodHandleUtils.LambdaPropertyDescriptor getLambdaProperty(String name)
+ {
+ return propertyDescriptors.get(name);
+ }
+
+ public Function<Object, Object> getReadFunction(String name)
+ {
+ MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);
+ if (lpd == null)
+ {
+ return null;
+ }
+
+ return lpd.getReadFunction();
+ }
+
+ public BiConsumer<Object, Object> getWriteFunction(String name)
+ {
+ MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);
+ if (lpd == null)
+ {
+ return null;
+ }
+
+ return lpd.getWriteFunction();
+ }
+}
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodRule.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodRule.java
index b76abd5..4b9db45 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodRule.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodRule.java
@@ -20,6 +20,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.function.BiConsumer;
import javax.el.MethodExpression;
import javax.faces.view.facelets.FaceletContext;
@@ -71,36 +72,55 @@
private class MethodExpressionMetadata extends Metadata
{
- private final Method _method;
- private final TagAttribute _attribute;
- private Class<?>[] _paramList;
- private Class<?> _returnType;
+ private final Method method;
+ private final BiConsumer<Object, Object> function;
+ private final TagAttribute attribute;
+ private Class<?>[] paramList;
+ private Class<?> returnType;
public MethodExpressionMetadata(Method method, TagAttribute attribute, Class<?> returnType,
- Class<?>[] paramList)
+ Class<?>[] paramList)
{
- _method = method;
- _attribute = attribute;
- _paramList = paramList;
- _returnType = returnType;
+ this.method = method;
+ this.function = null;
+ this.attribute = attribute;
+ this.paramList = paramList;
+ this.returnType = returnType;
+ }
+
+ public MethodExpressionMetadata(BiConsumer<Object, Object> function, TagAttribute attribute,
+ Class<?> returnType, Class<?>[] paramList)
+ {
+ this.method = null;
+ this.function = function;
+ this.attribute = attribute;
+ this.paramList = paramList;
+ this.returnType = returnType;
}
@Override
public void applyMetadata(FaceletContext ctx, Object instance)
{
- MethodExpression expr = _attribute.getMethodExpression(ctx, _returnType, _paramList);
+ MethodExpression expr = attribute.getMethodExpression(ctx, returnType, paramList);
try
{
- _method.invoke(instance, new Object[] { expr });
+ if (method != null)
+ {
+ method.invoke(instance, new Object[] { expr });
+ }
+ else if (function != null)
+ {
+ function.accept(instance, expr);
+ }
}
catch (InvocationTargetException e)
{
- throw new TagAttributeException(_attribute, e.getCause());
+ throw new TagAttributeException(attribute, e.getCause());
}
catch (Exception e)
{
- throw new TagAttributeException(_attribute, e);
+ throw new TagAttributeException(attribute, e);
}
}
}
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentRule.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentRule.java
index df641a8..b4ece12 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentRule.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeComponentRule.java
@@ -27,6 +27,7 @@
import javax.faces.view.facelets.Metadata;
import javax.faces.view.facelets.MetadataTarget;
import javax.faces.view.facelets.TagAttribute;
+import org.apache.myfaces.view.facelets.tag.MethodHandleMetadataTargetImpl;
/**
* Copy of org.apache.myfaces.view.facelets.tag.jsf.ComponentRule
@@ -132,6 +133,27 @@
return new ValueExpressionMetadata(name, type, attribute);
}
+ else if (meta instanceof MethodHandleMetadataTargetImpl)
+ {
+ if (((MethodHandleMetadataTargetImpl) meta).getWriteFunction(name) == null)
+ {
+ Class<?> type = meta.getPropertyType(name);
+ if (type == null)
+ {
+ if (((MethodHandleMetadataTargetImpl) meta).getLambdaProperty(name) == null)
+ {
+ // this was an attribute literal, but not property
+ warnAttr(attribute, meta.getTargetClass(), name);
+ }
+
+ return new LiteralAttributeMetadata(name, attribute.getValue());
+ }
+ else
+ {
+ return new TypedLiteralAttributeMetadata(name, type, attribute);
+ }
+ }
+ }
else if (meta.getWriteMethod(name) == null)
{
Class<?> type = meta.getPropertyType(name);
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java
index 4195297..5d4d3ed 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java
@@ -38,7 +38,9 @@
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.apache.myfaces.config.MyfacesConfig;
import org.apache.myfaces.util.lang.Assert;
+import org.apache.myfaces.view.facelets.tag.MethodHandleMetadataTargetImpl;
import org.apache.myfaces.view.facelets.tag.NullMetadata;
public class CompositeMetaRulesetImpl extends MetaRuleset
@@ -213,7 +215,14 @@
{
try
{
- meta = new MetadataTargetImpl(_type);
+ if (MyfacesConfig.getCurrentInstance().isUseMethodHandles())
+ {
+ meta = new MethodHandleMetadataTargetImpl(_type);
+ }
+ else
+ {
+ meta = new MetadataTargetImpl(_type);
+ }
}
catch (IntrospectionException e)
{
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetadataTargetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetadataTargetImpl.java
index 6b8c548..c775f4c 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetadataTargetImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetadataTargetImpl.java
@@ -40,32 +40,29 @@
*/
final class CompositeMetadataTargetImpl extends MetadataTarget
{
- private final Map<String, PropertyDescriptor> _pd;
-
- private final MetadataTarget _delegate;
-
- private final BeanInfo _beanInfo;
+ private final Map<String, PropertyDescriptor> descriptors;
+ private final MetadataTarget delegate;
+ private final BeanInfo beanInfo;
public CompositeMetadataTargetImpl(MetadataTarget delegate, BeanInfo beanInfo) throws IntrospectionException
{
- _delegate = delegate;
- _beanInfo = beanInfo;
+ this.delegate = delegate;
+ this.beanInfo = beanInfo;
+ this.descriptors = new HashMap<>();
- _pd = new HashMap<String, PropertyDescriptor>();
-
- for (PropertyDescriptor descriptor : _beanInfo.getPropertyDescriptors())
+ for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors())
{
- _pd.put(descriptor.getName(), descriptor);
+ this.descriptors.put(descriptor.getName(), descriptor);
}
}
@Override
public PropertyDescriptor getProperty(String name)
{
- PropertyDescriptor pd = _delegate.getProperty(name);
+ PropertyDescriptor pd = delegate.getProperty(name);
if (pd == null)
{
- pd = _pd.get(name);
+ pd = descriptors.get(name);
}
return pd;
}
@@ -79,7 +76,7 @@
Object type = pd.getValue("type");
if (type != null)
{
- type = ((ValueExpression)type).getValue(FacesContext.getCurrentInstance().getELContext());
+ type = ((ValueExpression) type).getValue(FacesContext.getCurrentInstance().getELContext());
if (type instanceof String)
{
try
@@ -114,7 +111,7 @@
@Override
public Class<?> getTargetClass()
{
- return _delegate.getTargetClass();
+ return delegate.getTargetClass();
}
@Override
@@ -132,6 +129,6 @@
@Override
public boolean isTargetInstanceOf(Class type)
{
- return _delegate.isTargetInstanceOf(type);
+ return delegate.isTargetInstanceOf(type);
}
}
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentRule.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentRule.java
index d39937e..461c4c5 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentRule.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentRule.java
@@ -27,6 +27,7 @@
import javax.faces.view.facelets.Metadata;
import javax.faces.view.facelets.MetadataTarget;
import javax.faces.view.facelets.TagAttribute;
+import org.apache.myfaces.view.facelets.tag.MethodHandleMetadataTargetImpl;
/**
*
@@ -99,9 +100,18 @@
return new ValueExpressionMetadata(name, type, attribute);
}
+ else if (meta instanceof MethodHandleMetadataTargetImpl)
+ {
+ if (((MethodHandleMetadataTargetImpl) meta).getWriteFunction(name) == null)
+ {
+ // this was an attribute literal, but not property
+ warnAttr(attribute, meta.getTargetClass(), name);
+
+ return new LiteralAttributeMetadata(name, attribute.getValue());
+ }
+ }
else if (meta.getWriteMethod(name) == null)
{
-
// this was an attribute literal, but not property
warnAttr(attribute, meta.getTargetClass(), name);