Search in sources :

Example 1 with PerThreadValue

use of org.apache.tapestry5.ioc.services.PerThreadValue in project tapestry-5 by apache.

the class ParameterWorker method createComputedParameterConduit.

@SuppressWarnings("all")
private ComputedValue<FieldConduit<Object>> createComputedParameterConduit(final String parameterName, final String fieldTypeName, final Parameter annotation, final MethodHandle defaultMethodHandle) {
    boolean primitive = PlasticUtils.isPrimitive(fieldTypeName);
    final boolean allowNull = annotation.allowNull() && !primitive;
    return new ComputedValue<FieldConduit<Object>>() {

        public ParameterConduit get(InstanceContext context) {
            final InternalComponentResources icr = context.get(InternalComponentResources.class);
            final Class fieldType = classCache.forName(fieldTypeName);
            final PerThreadValue<ParameterState> stateValue = perThreadManager.createValue();
            return new ParameterConduit() {

                // Default value for parameter, computed *once* at
                // page load time.
                private Object defaultValue = classCache.defaultValueForType(fieldTypeName);

                private Binding parameterBinding;

                boolean loaded = false;

                private boolean invariant = false;

                {
                    // Inform the ComponentResources about the parameter conduit, so it can be
                    // shared with mixins.
                    icr.setParameterConduit(parameterName, this);
                    icr.getPageLifecycleCallbackHub().addPageLoadedCallback(new Runnable() {

                        public void run() {
                            load();
                        }
                    });
                }

                private ParameterState getState() {
                    ParameterState state = stateValue.get();
                    if (state == null) {
                        state = new ParameterState();
                        state.value = defaultValue;
                        stateValue.set(state);
                    }
                    return state;
                }

                private boolean isLoaded() {
                    return loaded;
                }

                public void set(Object instance, InstanceContext context, Object newValue) {
                    ParameterState state = getState();
                    if (!loaded) {
                        state.value = newValue;
                        defaultValue = newValue;
                        return;
                    }
                    // This will catch read-only or unbound parameters.
                    writeToBinding(newValue);
                    state.value = newValue;
                    // If caching is enabled for the parameter (the typical case) and the
                    // component is currently rendering, then the result
                    // can be cached in this ParameterConduit (until the component finishes
                    // rendering).
                    state.cached = annotation.cache() && icr.isRendering();
                }

                private Object readFromBinding() {
                    Object result;
                    try {
                        Object boundValue = parameterBinding.get();
                        result = typeCoercer.coerce(boundValue, fieldType);
                    } catch (RuntimeException ex) {
                        throw new TapestryException(String.format("Failure reading parameter '%s' of component %s: %s", parameterName, icr.getCompleteId(), ExceptionUtils.toMessage(ex)), parameterBinding, ex);
                    }
                    if (result == null && !allowNull) {
                        throw new TapestryException(String.format("Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.", parameterName, icr.getCompleteId()), parameterBinding, null);
                    }
                    return result;
                }

                private void writeToBinding(Object newValue) {
                    if (parameterBinding == null) {
                        return;
                    }
                    try {
                        Object coerced = typeCoercer.coerce(newValue, parameterBinding.getBindingType());
                        parameterBinding.set(coerced);
                    } catch (RuntimeException ex) {
                        throw new TapestryException(String.format("Failure writing parameter '%s' of component %s: %s", parameterName, icr.getCompleteId(), ExceptionUtils.toMessage(ex)), icr, ex);
                    }
                }

                public void reset() {
                    if (!invariant) {
                        getState().reset(defaultValue);
                    }
                }

                public void load() {
                    if (logger.isDebugEnabled()) {
                        logger.debug("{} loading parameter {}", icr.getCompleteId(), parameterName);
                    }
                    if (!icr.isBound(parameterName)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("{} parameter {} not yet bound", icr.getCompleteId(), parameterName);
                        }
                        // Otherwise, construct a default binding, or use one provided from
                        // the component.
                        Binding binding = getDefaultBindingForParameter();
                        if (logger.isDebugEnabled()) {
                            logger.debug("{} parameter {} bound to default {}", icr.getCompleteId(), parameterName, binding);
                        }
                        if (binding != null) {
                            icr.bindParameter(parameterName, binding);
                        }
                    }
                    parameterBinding = icr.getBinding(parameterName);
                    loaded = true;
                    invariant = parameterBinding != null && parameterBinding.isInvariant();
                    getState().value = defaultValue;
                }

                public boolean isBound() {
                    return parameterBinding != null;
                }

                public Object get(Object instance, InstanceContext context) {
                    if (!isLoaded()) {
                        return defaultValue;
                    }
                    ParameterState state = getState();
                    if (state.cached || !isBound()) {
                        return state.value;
                    }
                    // Read the parameter's binding and cast it to the
                    // field's type.
                    Object result = readFromBinding();
                    if (invariant || (annotation.cache() && icr.isRendering())) {
                        state.value = result;
                        state.cached = true;
                    }
                    return result;
                }

                private Binding getDefaultBindingForParameter() {
                    if (InternalUtils.isNonBlank(annotation.value())) {
                        return bindingSource.newBinding("default " + parameterName, icr, annotation.defaultPrefix(), annotation.value());
                    }
                    if (annotation.autoconnect()) {
                        return defaultProvider.defaultBinding(parameterName, icr);
                    }
                    // Invoke the default method and install any value or Binding returned there.
                    invokeDefaultMethod();
                    return parameterBinding;
                }

                private void invokeDefaultMethod() {
                    if (defaultMethodHandle == null) {
                        return;
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("{} invoking method {} to obtain default for parameter {}", icr.getCompleteId(), defaultMethodHandle, parameterName);
                    }
                    MethodInvocationResult result = defaultMethodHandle.invoke(icr.getComponent());
                    result.rethrow();
                    Object defaultValue = result.getReturnValue();
                    if (defaultValue == null) {
                        return;
                    }
                    if (defaultValue instanceof Binding) {
                        parameterBinding = (Binding) defaultValue;
                        return;
                    }
                    parameterBinding = new LiteralBinding(null, "default " + parameterName, defaultValue);
                }
            };
        }
    };
}
Also used : LiteralBinding(org.apache.tapestry5.internal.bindings.LiteralBinding) Binding(org.apache.tapestry5.Binding) LiteralBinding(org.apache.tapestry5.internal.bindings.LiteralBinding) InternalComponentResources(org.apache.tapestry5.internal.InternalComponentResources) TapestryException(org.apache.tapestry5.commons.internal.util.TapestryException)

Example 2 with PerThreadValue

use of org.apache.tapestry5.ioc.services.PerThreadValue in project tapestry-5 by apache.

the class TapestryModule method provideTransformWorkers.

/**
 * Adds a number of standard component class transform workers:
 * <dl>
 * <dt>Parameter</dt>
 * <dd>Identifies parameters based on the {@link org.apache.tapestry5.annotations.Parameter} annotation</dd>
 * <dt>BindParameter</dt>
 * <dd>Support for the {@link BindParameter} annotation</dd>
 * <dt>Property</dt>
 * <dd>Generates accessor methods if {@link org.apache.tapestry5.annotations.Property} annotation is present</dd>
 * <dt>Import</dt>
 * <dd>Supports the {@link Import} annotation</dd>
 * <dt>UnclaimedField</dt>
 * <dd>Manages unclaimed fields, storing their value in a {@link PerThreadValue}</dd>
 * <dt>OnEvent</dt>
 * <dd>Handle the @OnEvent annotation, and related naming convention</dd>
 * <dt>RenderCommand</dt>
 * <dd>Ensures all components also implement {@link org.apache.tapestry5.runtime.RenderCommand}</dd>
 * <dt>SupportsInformalParameters</dt>
 * <dd>Checks for the annotation</dd>
 * <dt>RenderPhase</dt>
 * <dd>Link in render phase methods</dd>
 * <dt>Retain</dt>
 * <dd>Allows fields to retain their values between requests</dd>
 * <dt>Meta</dt>
 * <dd>Checks for meta data annotations and adds it to the component model</dd>
 * <dt>PageActivationContext</dt> <dd>Support for {@link PageActivationContext} annotation</dd>
 * <dt>DiscardAfter</dt> <dd>Support for {@link DiscardAfter} method annotation </dd>
 * <dt>MixinAfter</dt> <dd>Support for the {@link MixinAfter} mixin class annotation</dd>
 * <dt>PageReset</dt>
 * <dd>Checks for the {@link PageReset} annotation</dd>
 * <dt>Mixin</dt>
 * <dd>Adds a mixin as part of a component's implementation</dd>
 * <dt>Cached</dt>
 * <dd>Checks for the {@link org.apache.tapestry5.annotations.Cached} annotation</dd>
 * <dt>ActivationRequestParameter</dt>
 * <dd>Support for the {@link ActivationRequestParameter} annotation</dd>
 * <dt>PageLoaded, PageAttached, PageDetached</dt>
 * <dd>Support for annotations {@link PageLoaded}, {@link PageAttached}, {@link PageDetached}</dd>
 * <dt>InjectService</dt>
 * <dd>Handles the {@link org.apache.tapestry5.ioc.annotations.InjectService} annotation</dd>
 * <dt>Component</dt>
 * <dd>Defines embedded components based on the {@link org.apache.tapestry5.annotations.Component} annotation</dd>
 * <dt>Environment</dt>
 * <dd>Allows fields to contain values extracted from the {@link org.apache.tapestry5.services.Environment} service</dd>
 * <dt>ApplicationState</dt>
 * <dd>Converts fields that reference application state objects</dd>
 * <dt>Persist</dt>
 * <dd>Allows fields to store their their value persistently between requests via {@link Persist}</dd>
 * <dt>SessionAttribute</dt>
 * <dd>Support for the {@link SessionAttribute}</dd>
 * <dt>Log</dt>
 * <dd>Checks for the {@link org.apache.tapestry5.annotations.Log} annotation</dd>
 * <dt>HeartbeatDeferred
 * <dd>Support for the {@link HeartbeatDeferred} annotation, which defers method invocation to the end of the {@link Heartbeat}
 * <dt>Inject</dt>
 * <dd>Used with the {@link org.apache.tapestry5.ioc.annotations.Inject} annotation, when a value is supplied</dd>
 * <dt>Operation</dt> <dd>Support for the {@link Operation} method annotation</dd>
 * </dl>
 */
@Contribute(ComponentClassTransformWorker2.class)
@Primary
public static void provideTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2> configuration, MetaWorker metaWorker, ComponentClassResolver resolver) {
    configuration.add("Property", new PropertyWorker());
    // Order this one pretty early:
    configuration.addInstance("Operation", OperationWorker.class);
    configuration.add("RenderCommand", new RenderCommandWorker());
    configuration.addInstance("OnEvent", OnEventWorker.class);
    configuration.add("MixinAfter", new MixinAfterWorker());
    // These must come after Property, since they actually delete fields
    // that may still have the annotation
    configuration.addInstance("ApplicationState", ApplicationStateWorker.class);
    configuration.addInstance("Environment", EnvironmentalWorker.class);
    configuration.add("Component", new ComponentWorker(resolver));
    configuration.add("Mixin", new MixinWorker(resolver));
    configuration.addInstance("InjectPage", InjectPageWorker.class);
    configuration.addInstance("InjectComponent", InjectComponentWorker.class);
    configuration.addInstance("InjectContainer", InjectContainerWorker.class);
    // Default values for parameters are often some form of injection, so
    // make sure that Parameter fields are processed after injections.
    configuration.addInstance("Parameter", ParameterWorker.class);
    // bind parameter should always go after parameter to make sure all
    // parameters have been properly setup.
    configuration.addInstance("BindParameter", BindParameterWorker.class);
    configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
    configuration.addInstance("RenderPhase", RenderPhaseMethodWorker.class);
    // Import advises methods, usually render phase methods, so it must come after RenderPhase.
    configuration.addInstance("Import", ImportWorker.class);
    configuration.add("Meta", metaWorker.getWorker());
    configuration.add("Retain", new RetainWorker());
    configuration.add("PageActivationContext", new PageActivationContextWorker());
    configuration.addInstance("ActivationRequestParameter", ActivationRequestParameterWorker.class);
    configuration.addInstance("Cached", CachedWorker.class);
    configuration.addInstance("DiscardAfter", DiscardAfterWorker.class);
    add(configuration, PageLoaded.class, TransformConstants.CONTAINING_PAGE_DID_LOAD_DESCRIPTION);
    add(configuration, PageAttached.class, TransformConstants.CONTAINING_PAGE_DID_ATTACH_DESCRIPTION);
    add(configuration, PageDetached.class, TransformConstants.CONTAINING_PAGE_DID_DETACH_DESCRIPTION);
    configuration.addInstance("PageReset", PageResetAnnotationWorker.class);
    configuration.addInstance("InjectService", InjectServiceWorker.class);
    configuration.addInstance("Inject", InjectWorker.class);
    configuration.addInstance("Persist", PersistWorker.class);
    configuration.addInstance("SessionAttribute", SessionAttributeWorker.class);
    configuration.addInstance("Log", LogWorker.class);
    configuration.addInstance("HeartbeatDeferred", HeartbeatDeferredWorker.class);
    // This one is always last. Any additional private fields that aren't
    // annotated will
    // be converted to clear out at the end of the request.
    configuration.addInstance("UnclaimedField", UnclaimedFieldWorker.class, "after:*");
}
Also used : PageActivationContextWorker(org.apache.tapestry5.internal.transform.PageActivationContextWorker) PropertyWorker(org.apache.tapestry5.internal.transform.PropertyWorker) MixinWorker(org.apache.tapestry5.internal.transform.MixinWorker) RetainWorker(org.apache.tapestry5.internal.transform.RetainWorker) ComponentWorker(org.apache.tapestry5.internal.transform.ComponentWorker) InjectComponentWorker(org.apache.tapestry5.internal.transform.InjectComponentWorker) RenderCommandWorker(org.apache.tapestry5.internal.transform.RenderCommandWorker) SupportsInformalParametersWorker(org.apache.tapestry5.internal.transform.SupportsInformalParametersWorker) MixinAfterWorker(org.apache.tapestry5.internal.transform.MixinAfterWorker) Primary(org.apache.tapestry5.ioc.annotations.Primary) Contribute(org.apache.tapestry5.ioc.annotations.Contribute)

Example 3 with PerThreadValue

use of org.apache.tapestry5.ioc.services.PerThreadValue in project tapestry-5 by apache.

the class CachedWorker method adviseMethod.

private void adviseMethod(PlasticClass plasticClass, PlasticMethod method) {
    // Every instance of the clas srequires its own per-thread value. This handles the case of multiple
    // pages containing the component, or the same page containing the component multiple times.
    PlasticField cacheField = plasticClass.introduceField(PerThreadValue.class, "cache$" + method.getDescription().methodName);
    cacheField.injectComputed(new ComputedValue<PerThreadValue>() {

        public PerThreadValue get(InstanceContext context) {
            // Each instance will get a new PerThreadValue
            return perThreadManager.createValue();
        }
    });
    Cached annotation = method.getAnnotation(Cached.class);
    MethodResultCacheFactory factory = createFactory(plasticClass, annotation.watch(), method);
    MethodAdvice advice = createAdvice(cacheField, factory);
    method.addAdvice(advice);
}
Also used : Cached(org.apache.tapestry5.annotations.Cached) PerThreadValue(org.apache.tapestry5.ioc.services.PerThreadValue)

Aggregations

Binding (org.apache.tapestry5.Binding)1 Cached (org.apache.tapestry5.annotations.Cached)1 TapestryException (org.apache.tapestry5.commons.internal.util.TapestryException)1 InternalComponentResources (org.apache.tapestry5.internal.InternalComponentResources)1 LiteralBinding (org.apache.tapestry5.internal.bindings.LiteralBinding)1 ComponentWorker (org.apache.tapestry5.internal.transform.ComponentWorker)1 InjectComponentWorker (org.apache.tapestry5.internal.transform.InjectComponentWorker)1 MixinAfterWorker (org.apache.tapestry5.internal.transform.MixinAfterWorker)1 MixinWorker (org.apache.tapestry5.internal.transform.MixinWorker)1 PageActivationContextWorker (org.apache.tapestry5.internal.transform.PageActivationContextWorker)1 PropertyWorker (org.apache.tapestry5.internal.transform.PropertyWorker)1 RenderCommandWorker (org.apache.tapestry5.internal.transform.RenderCommandWorker)1 RetainWorker (org.apache.tapestry5.internal.transform.RetainWorker)1 SupportsInformalParametersWorker (org.apache.tapestry5.internal.transform.SupportsInformalParametersWorker)1 Contribute (org.apache.tapestry5.ioc.annotations.Contribute)1 Primary (org.apache.tapestry5.ioc.annotations.Primary)1 PerThreadValue (org.apache.tapestry5.ioc.services.PerThreadValue)1