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 }