blob: 2bd4e3046ab33b91b4cb34551cb3beb48712f64d [file] [log] [blame]
// Copyright 2007, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.ioc.internal;
import org.apache.tapestry5.ioc.*;
import org.apache.tapestry5.ioc.annotations.EagerLoad;
import org.apache.tapestry5.ioc.annotations.Marker;
import org.apache.tapestry5.ioc.annotations.Scope;
import org.apache.tapestry5.ioc.def.ServiceDef;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;
import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.OneShotLock;
import org.apache.tapestry5.ioc.services.ClassFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;
public class ServiceBinderImpl implements ServiceBinder, ServiceBindingOptions
{
private final OneShotLock lock = new OneShotLock();
private final Method bindMethod;
private final ServiceDefAccumulator accumulator;
private final ClassFactory classFactory;
private final Set<Class> defaultMarkers;
public ServiceBinderImpl(ServiceDefAccumulator accumulator, Method bindMethod,
ClassFactory classFactory,
Set<Class> defaultMarkers)
{
this.accumulator = accumulator;
this.bindMethod = bindMethod;
this.classFactory = classFactory;
this.defaultMarkers = defaultMarkers;
}
private String serviceId;
private Class serviceInterface;
private Class serviceImplementation;
private final Set<Class> markers = CollectionFactory.newSet();
private ObjectCreatorSource source;
private boolean eagerLoad;
private String scope;
public void finish()
{
lock.lock();
flush();
}
protected void flush()
{
if (serviceInterface == null) return;
// source will be null when the implementation class is provided; non-null when using
// a ServiceBuilder callback
if (source == null)
source = createObjectCreatorSourceFromImplementationClass();
// Combine service-specific markers with those inherited form the module.
Set<Class> markers = CollectionFactory.newSet(defaultMarkers);
markers.addAll(this.markers);
ServiceDef serviceDef = new ServiceDefImpl(serviceInterface, serviceId, markers, scope, eagerLoad, source);
accumulator.addServiceDef(serviceDef);
serviceId = null;
serviceInterface = null;
serviceImplementation = null;
source = null;
this.markers.clear();
eagerLoad = false;
scope = null;
}
private ObjectCreatorSource createObjectCreatorSourceFromImplementationClass()
{
final Constructor constructor = InternalUtils.findAutobuildConstructor(serviceImplementation);
if (constructor == null)
throw new RuntimeException(IOCMessages.noConstructor(serviceImplementation, serviceId));
return new ObjectCreatorSource()
{
public ObjectCreator constructCreator(ServiceBuilderResources resources)
{
return new ConstructorServiceCreator(resources, getDescription(), constructor);
}
public String getDescription()
{
return String.format("%s via %s",
classFactory.getConstructorLocation(constructor),
classFactory.getMethodLocation(bindMethod));
}
};
}
public <T> ServiceBindingOptions bind(Class<T> serviceClass)
{
if (serviceClass.isInterface())
{
try
{
Class<T> implementationClass = (Class<T>) Class.forName(serviceClass.getName() + "Impl");
if (!implementationClass.isInterface() && serviceClass.isAssignableFrom(implementationClass))
{
return bind(serviceClass, implementationClass);
}
throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceClass));
}
catch (ClassNotFoundException ex)
{
throw new RuntimeException(IOCMessages.noConventionServiceImplementationFound(serviceClass));
}
}
return bind(serviceClass, serviceClass);
}
public <T> ServiceBindingOptions bind(Class<T> serviceInterface, final ServiceBuilder<T> builder)
{
Defense.notNull(serviceInterface, "serviceInterface");
Defense.notNull(builder, "builder");
lock.check();
flush();
this.serviceInterface = serviceInterface;
this.scope = IOCConstants.DEFAULT_SCOPE;
serviceId = serviceInterface.getSimpleName();
this.source = new ObjectCreatorSource()
{
public ObjectCreator constructCreator(final ServiceBuilderResources resources)
{
return new ObjectCreator()
{
public Object createObject()
{
return builder.buildService(resources);
}
};
}
public String getDescription()
{
return classFactory.getMethodLocation(bindMethod).toString();
}
};
return this;
}
public <T> ServiceBindingOptions bind(Class<T> serviceInterface, Class<? extends T> serviceImplementation)
{
notNull(serviceInterface, "serviceIterface");
notNull(serviceImplementation, "serviceImplementation");
lock.check();
flush();
this.serviceInterface = serviceInterface;
this.serviceImplementation = serviceImplementation;
// Set defaults for the other properties.
eagerLoad = serviceImplementation.getAnnotation(EagerLoad.class) != null;
serviceId = serviceInterface.getSimpleName();
Scope scope = serviceImplementation.getAnnotation(Scope.class);
this.scope = scope != null ? scope.value() : IOCConstants.DEFAULT_SCOPE;
Marker marker = serviceImplementation.getAnnotation(Marker.class);
if (marker != null)
{
InternalUtils.validateMarkerAnnotations(marker.value());
markers.addAll(Arrays.asList(marker.value()));
}
return this;
}
public ServiceBindingOptions eagerLoad()
{
lock.check();
eagerLoad = true;
return this;
}
public ServiceBindingOptions withId(String id)
{
notBlank(id, "id");
lock.check();
serviceId = id;
return this;
}
public ServiceBindingOptions scope(String scope)
{
notBlank(scope, "scope");
lock.check();
this.scope = scope;
return this;
}
public <T extends Annotation> ServiceBindingOptions withMarker(Class<T>... marker)
{
lock.check();
InternalUtils.validateMarkerAnnotations(marker);
markers.addAll(Arrays.asList(marker));
return this;
}
}