001// Copyright 2007, 2008, 2009, 2010, 2011 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.*; 018import org.apache.tapestry5.ioc.annotations.EagerLoad; 019import org.apache.tapestry5.ioc.annotations.Marker; 020import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration; 021import org.apache.tapestry5.ioc.annotations.Scope; 022import org.apache.tapestry5.ioc.def.ServiceDef; 023import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 024import org.apache.tapestry5.ioc.internal.util.InternalUtils; 025import org.apache.tapestry5.ioc.internal.util.OneShotLock; 026import org.apache.tapestry5.ioc.services.PlasticProxyFactory; 027 028import java.lang.annotation.Annotation; 029import java.lang.reflect.Constructor; 030import java.lang.reflect.Method; 031import java.util.Arrays; 032import java.util.Set; 033 034@SuppressWarnings("all") 035public class ServiceBinderImpl implements ServiceBinder, ServiceBindingOptions 036{ 037 private final OneShotLock lock = new OneShotLock(); 038 039 private final Method bindMethod; 040 041 private final ServiceDefAccumulator accumulator; 042 043 private PlasticProxyFactory proxyFactory; 044 045 private final Set<Class> defaultMarkers; 046 047 private final boolean moduleDefaultPreventDecoration; 048 049 public ServiceBinderImpl(ServiceDefAccumulator accumulator, Method bindMethod, PlasticProxyFactory proxyFactory, 050 Set<Class> defaultMarkers, boolean moduleDefaultPreventDecoration) 051 { 052 this.accumulator = accumulator; 053 this.bindMethod = bindMethod; 054 this.proxyFactory = proxyFactory; 055 this.defaultMarkers = defaultMarkers; 056 this.moduleDefaultPreventDecoration = moduleDefaultPreventDecoration; 057 058 clear(); 059 } 060 061 private String serviceId; 062 063 private Class serviceInterface; 064 065 private Class serviceImplementation; 066 067 private final Set<Class> markers = CollectionFactory.newSet(); 068 069 private ObjectCreatorSource source; 070 071 private boolean eagerLoad; 072 073 private String scope; 074 075 private boolean preventDecoration; 076 077 private boolean preventReloading; 078 079 public void finish() 080 { 081 lock.lock(); 082 083 flush(); 084 } 085 086 protected void flush() 087 { 088 if (serviceInterface == null) 089 return; 090 091 // source will be null when the implementation class is provided; non-null when using 092 // a ServiceBuilder callback 093 094 if (source == null) 095 source = createObjectCreatorSourceFromImplementationClass(); 096 097 // Combine service-specific markers with those inherited form the module. 098 Set<Class> markers = CollectionFactory.newSet(defaultMarkers); 099 markers.addAll(this.markers); 100 101 ServiceDef serviceDef = new ServiceDefImpl(serviceInterface, serviceImplementation, serviceId, markers, scope, 102 eagerLoad, preventDecoration, source); 103 104 accumulator.addServiceDef(serviceDef); 105 106 clear(); 107 } 108 109 private void clear() 110 { 111 serviceId = null; 112 serviceInterface = null; 113 serviceImplementation = null; 114 source = null; 115 this.markers.clear(); 116 eagerLoad = false; 117 scope = null; 118 preventDecoration = moduleDefaultPreventDecoration; 119 preventReloading = false; 120 } 121 122 private ObjectCreatorSource createObjectCreatorSourceFromImplementationClass() 123 { 124 if (InternalUtils.SERVICE_CLASS_RELOADING_ENABLED && !preventReloading && isProxiable() && reloadableScope() 125 && InternalUtils.isLocalFile(serviceImplementation)) 126 return createReloadableConstructorBasedObjectCreatorSource(); 127 128 return createStandardConstructorBasedObjectCreatorSource(); 129 } 130 131 private boolean isProxiable() 132 { 133 return serviceInterface.isInterface(); 134 } 135 136 private boolean reloadableScope() 137 { 138 return scope.equalsIgnoreCase(ScopeConstants.DEFAULT); 139 } 140 141 private ObjectCreatorSource createStandardConstructorBasedObjectCreatorSource() 142 { 143 final Constructor constructor = InternalUtils.findAutobuildConstructor(serviceImplementation); 144 145 if (constructor == null) 146 throw new RuntimeException(IOCMessages.noConstructor(serviceImplementation, serviceId)); 147 148 return new ObjectCreatorSource() 149 { 150 @Override 151 public ObjectCreator constructCreator(ServiceBuilderResources resources) 152 { 153 return new ConstructorServiceCreator(resources, getDescription(), constructor); 154 } 155 156 @Override 157 public String getDescription() 158 { 159 return String.format("%s via %s", proxyFactory.getConstructorLocation(constructor), 160 proxyFactory.getMethodLocation(bindMethod)); 161 } 162 }; 163 } 164 165 private ObjectCreatorSource createReloadableConstructorBasedObjectCreatorSource() 166 { 167 return new ReloadableObjectCreatorSource(proxyFactory, bindMethod, serviceInterface, serviceImplementation, 168 eagerLoad); 169 } 170 171 @Override 172 @SuppressWarnings("unchecked") 173 public <T> ServiceBindingOptions bind(Class<T> serviceClass) 174 { 175 if (serviceClass.isInterface()) 176 { 177 try 178 { 179 String expectedImplName = serviceClass.getName() + "Impl"; 180 181 ClassLoader classLoader = proxyFactory.getClassLoader(); 182 183 Class<T> implementationClass = (Class<T>) classLoader.loadClass(expectedImplName); 184 185 if (!implementationClass.isInterface() && serviceClass.isAssignableFrom(implementationClass)) 186 { 187 return bind( 188 serviceClass, implementationClass); 189 } 190 throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceClass)); 191 } catch (ClassNotFoundException ex) 192 { 193 throw new RuntimeException(String.format("Could not find default implementation class %sImpl. Please provide this class, or bind the service interface to a specific implementation class.", 194 serviceClass.getName())); 195 } 196 } 197 198 return bind(serviceClass, serviceClass); 199 } 200 201 @Override 202 public <T> ServiceBindingOptions bind(Class<T> serviceInterface, final ServiceBuilder<T> builder) 203 { 204 assert serviceInterface != null; 205 assert builder != null; 206 lock.check(); 207 208 flush(); 209 210 this.serviceInterface = serviceInterface; 211 this.scope = ScopeConstants.DEFAULT; 212 213 serviceId = serviceInterface.getSimpleName(); 214 215 this.source = new ObjectCreatorSource() 216 { 217 @Override 218 public ObjectCreator constructCreator(final ServiceBuilderResources resources) 219 { 220 return new ObjectCreator() 221 { 222 @Override 223 public Object createObject() 224 { 225 return builder.buildService(resources); 226 } 227 }; 228 } 229 230 @Override 231 public String getDescription() 232 { 233 return proxyFactory.getMethodLocation(bindMethod).toString(); 234 } 235 }; 236 237 return this; 238 } 239 240 @Override 241 public <T> ServiceBindingOptions bind(Class<T> serviceInterface, Class<? extends T> serviceImplementation) 242 { 243 assert serviceInterface != null; 244 assert serviceImplementation != null; 245 lock.check(); 246 247 flush(); 248 249 this.serviceInterface = serviceInterface; 250 251 this.serviceImplementation = serviceImplementation; 252 253 // Set defaults for the other properties. 254 255 eagerLoad = serviceImplementation.getAnnotation(EagerLoad.class) != null; 256 257 serviceId = InternalUtils.getServiceId(serviceImplementation); 258 259 if (serviceId == null) 260 { 261 serviceId = serviceInterface.getSimpleName(); 262 } 263 264 Scope scope = serviceImplementation.getAnnotation(Scope.class); 265 266 this.scope = scope != null ? scope.value() : ScopeConstants.DEFAULT; 267 268 Marker marker = serviceImplementation.getAnnotation(Marker.class); 269 270 if (marker != null) 271 { 272 InternalUtils.validateMarkerAnnotations(marker.value()); 273 markers.addAll(Arrays.asList(marker.value())); 274 } 275 276 preventDecoration |= serviceImplementation.getAnnotation(PreventServiceDecoration.class) != null; 277 278 return this; 279 } 280 281 @Override 282 public ServiceBindingOptions eagerLoad() 283 { 284 lock.check(); 285 286 eagerLoad = true; 287 288 return this; 289 } 290 291 @Override 292 public ServiceBindingOptions preventDecoration() 293 { 294 lock.check(); 295 296 preventDecoration = true; 297 298 return this; 299 } 300 301 @Override 302 public ServiceBindingOptions preventReloading() 303 { 304 lock.check(); 305 306 preventReloading = true; 307 308 return this; 309 } 310 311 @Override 312 public ServiceBindingOptions withId(String id) 313 { 314 assert InternalUtils.isNonBlank(id); 315 lock.check(); 316 317 serviceId = id; 318 319 return this; 320 } 321 322 @Override 323 public ServiceBindingOptions withSimpleId() 324 { 325 if (serviceImplementation == null) 326 { 327 throw new IllegalArgumentException("No defined implementation class to generate simple id from."); 328 } 329 330 return withId(serviceImplementation.getSimpleName()); 331 } 332 333 @Override 334 public ServiceBindingOptions scope(String scope) 335 { 336 assert InternalUtils.isNonBlank(scope); 337 lock.check(); 338 339 this.scope = scope; 340 341 return this; 342 } 343 344 @Override 345 public ServiceBindingOptions withMarker(Class<? extends Annotation>... marker) 346 { 347 lock.check(); 348 349 InternalUtils.validateMarkerAnnotations(marker); 350 351 markers.addAll(Arrays.asList(marker)); 352 353 return this; 354 } 355}