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    }