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    @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}