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.ioc.ObjectCreator;
018import org.apache.tapestry5.ioc.def.ServiceDef;
019import org.slf4j.Logger;
020
021/**
022 * Decorator for {@link org.apache.tapestry5.ioc.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    public Object createObject()
050    {
051        if (locked)
052            throw new IllegalStateException(IOCMessages.recursiveServiceBuild(serviceDef));
053
054        // Set the lock, to ensure that recursive service construction fails.
055
056        locked = true;
057
058        try
059        {
060            return delegate.createObject();
061        }
062        catch (RuntimeException ex)
063        {
064            logger.error(IOCMessages.serviceConstructionFailed(serviceDef, ex), ex);
065
066            // Release the lock on failure; the service is now in an unknown state, but we may
067            // be able to continue from here.
068
069            locked = false;
070
071            throw ex;
072        }
073
074    }
075}