    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
    015    package org.apache.tapestry5.ioc.internal;
    016
    017    import org.apache.tapestry5.ioc.ObjectCreator;
    018    import org.apache.tapestry5.ioc.def.ServiceDef;
    019    import 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     */
    027    public 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    }