You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

轻量Groovy与JSF(JSF2/PrimeFaces)集成方案咨询

Lightweight Groovy + JSF Integration for Dynamic GroovyObjects

Great question! Since you're sticking to a TomEE/JEE/JSF core and want to avoid the heavy Grails-style stack, here's a practical, low-overhead approach tailored to your enterprise custom extension use case:

Core Problem to Solve

JSF relies on JavaBean conventions (getters/setters) and EL expression resolution to access object properties. Dynamic GroovyObjects (with runtime-added properties) don't fit this pattern out of the box, so we need to bridge EL to Groovy's dynamic property system.

Step 1: Add Groovy Dependency

First, include Groovy in your project (no Grails required). For Maven:

<dependency>
  <groupId>org.codehaus.groovy</groupId>
  <artifactId>groovy-all</artifactId>
  <version>3.0.17</version> <!-- Use latest stable version -->
  <scope>compile</scope>
</dependency>

Step 2: Custom EL PropertyResolver for GroovyObjects

Create a custom EL resolver to let JSF's EL engine handle Groovy's dynamic properties. This resolver will intercept property access for GroovyObject instances and use Groovy's native getProperty()/setProperty() methods:

package com.yourcompany.jsf.el;

import groovy.lang.GroovyObject;
import javax.el.PropertyResolver;
import javax.el.ELContext;

public class GroovyELPropertyResolver extends PropertyResolver {
    private final PropertyResolver delegate;

    public GroovyELPropertyResolver(PropertyResolver delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        if (base instanceof GroovyObject && property instanceof String) {
            context.setPropertyResolved(true);
            return ((GroovyObject) base).getProperty((String) property);
        }
        return delegate.getValue(context, base, property);
    }

    @Override
    public void setValue(ELContext context, Object base, Object property, Object value) {
        if (base instanceof GroovyObject && property instanceof String) {
            context.setPropertyResolved(true);
            ((GroovyObject) base).setProperty((String) property, value);
            return;
        }
        delegate.setValue(context, base, property, value);
    }

    // Delegate all other PropertyResolver methods to the default resolver
    @Override
    public Class<?> getType(ELContext context, Object base, Object property) {
        return delegate.getType(context, base, property);
    }

    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        return delegate.isReadOnly(context, base, property);
    }

    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        return delegate.getCommonPropertyType(context, base);
    }

    @Override
    public Iterator<String> getPropertyNames(ELContext context, Object base) {
        return delegate.getPropertyNames(context, base);
    }
}

Register this resolver in your faces-config.xml to activate it:

<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2">
    <application>
        <el-resolver>com.yourcompany.jsf.el.GroovyELPropertyResolver</el-resolver>
    </application>
</faces-config>

Step 3: Use GroovyObjects as JSF Beans

You can use Groovy's built-in Expando (a ready-to-use GroovyObject) or create custom Groovy classes for your dynamic entities. Here's how to expose them via CDI (TomEE supports CDI out of the box):

Example 1: Expando as a Managed Bean

package com.yourcompany.beans;

import groovy.util.Expando;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import java.io.Serializable;

@Named("dynamicEntityHolder")
@SessionScoped
class DynamicEntityHolder implements Serializable {
    // A dynamic Groovy object with runtime-addable properties
    Expando customerEntity = new Expando()

    // Method to add custom properties (for customer-specific extensions)
    void addCustomProperty(String name, Object value) {
        customerEntity[name] = value
    }

    // Helper to get all property names (for dynamic table columns)
    List<String> getPropertyNames() {
        customerEntity.properties.keySet().findAll { it != 'class' && it != 'metaClass' } as List
    }
}

Example 2: Custom GroovyObject Implementation

For more control, create a custom Groovy class that implements GroovyObject:

package com.yourcompany.entities;

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named("customGroovyEntity")
@RequestScoped
class CustomGroovyEntity implements GroovyObject {
    private final Map<String, Object> dynamicProps = [:]
    private final GroovyObject delegate = new Expando()

    @Override
    Object getProperty(String propertyName) {
        dynamicProps.getOrDefault(propertyName, delegate.getProperty(propertyName))
    }

    @Override
    void setProperty(String propertyName, Object newValue) {
        dynamicProps.put(propertyName, newValue)
    }

    @Override
    MetaClass getMetaClass() {
        delegate.getMetaClass()
    }

    @Override
    void setMetaClass(MetaClass metaClass) {
        delegate.setMetaClass(metaClass)
    }

    @Override
    Object invokeMethod(String name, Object args) {
        delegate.invokeMethod(name, args)
    }
}

Step 4: Render Dynamic Properties in JSF Components

Now you can directly access Groovy's dynamic properties in JSF EL. For a data table with dynamic columns (perfect for custom customer extensions):

<h:dataTable value="#{customerList}" var="customer" border="1">
    <!-- Dynamic columns based on Groovy object properties -->
    <c:forEach items="#{dynamicEntityHolder.propertyNames}" var="propName">
        <h:column>
            <f:facet name="header">#{propName}</f:facet>
            #{customer[propName]}
        </h:column>
    </c:forEach>
</h:dataTable>

Key Advantages for Your Use Case

  • Lightweight: No Grails dependencies—just core Groovy and JSF/EL extensions.
  • Flexible: Perfect for customer-specific extensions where you need runtime-addable properties.
  • Native Integration: Works seamlessly with TomEE's JSF/CDI stack.

内容的提问来源于stack exchange,提问作者Jacek Cz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:52:12