    001// Copyright 2006, 2007 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007//     http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.ioc.internal;
    016
    017import org.apache.tapestry5.commons.ObjectCreator;
    018import org.apache.tapestry5.ioc.def.ServiceDef;
    019import org.slf4j.Logger;
    020
    021/**
    022 * Decorator for {@link org.apache.tapestry5.commons.ObjectCreator} that ensures the service is only created once. This
    023 * detects a situation where the service builder for a service directly or indirectly invokes methods on the service
    024 * itself. This would show up as a second call up the ServiceCreator stack injected into the proxy, potentially leading
    025 * to endless recursion. We try to identify that recursion and produce a useable exception report.
    026 */
    027public class RecursiveServiceCreationCheckWrapper implements ObjectCreator
    028{
    029    private final ServiceDef serviceDef;
    030
    031    private final ObjectCreator delegate;
    032
    033    private final Logger logger;
    034
    035    private boolean locked;
    036
    037    public RecursiveServiceCreationCheckWrapper(ServiceDef serviceDef, ObjectCreator delegate,
    038                                                Logger logger)
    039    {
    040        this.serviceDef = serviceDef;
    041        this.delegate = delegate;
    042        this.logger = logger;
    043    }
    044
    045    /**
    046     * We could make this method synchronized, but in the context of creating a service for a proxy, it will already be
    047     * synchronized (inside the proxy).
    048     */
    049    @Override
    050    public Object createObject()
    051    {
    052        if (locked)
    053            throw new IllegalStateException(IOCMessages.recursiveServiceBuild(serviceDef));
    054
    055        // Set the lock, to ensure that recursive service construction fails.
    056
    057        locked = true;
    058
    059        try
    060        {
    061            return delegate.createObject();
    062        }
    063        catch (RuntimeException ex)
    064        {
    065            logger.error(IOCMessages.serviceConstructionFailed(serviceDef, ex), ex);
    066
    067            // Release the lock on failure; the service is now in an unknown state, but we may
    068            // be able to continue from here.
    069
    070            locked = false;
    071
    072            throw ex;
    073        }
    074
    075    }
    076}