/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you 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 com.ning.billing.beatrix.lifecycle;
import com.google.common.base.Supplier;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.ning.billing.lifecycle.KillbillService;
import com.ning.billing.lifecycle.LifecycleHandlerType;
import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel.Sequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
public class Lifecycle {
private final static Logger log = LoggerFactory.getLogger(Lifecycle.class);
private final SetMultimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> handlersByLevel;
private final ServiceFinder serviceFinder;
private final Injector injector;
@Inject
public Lifecycle(Injector injector) {
this.serviceFinder = new ServiceFinder(Lifecycle.class.getClassLoader());
this.handlersByLevel = Multimaps.newSetMultimap(new ConcurrentHashMap<LifecycleLevel, Collection<LifecycleHandler<? extends KillbillService>>>(),
new Supplier<Set<LifecycleHandler<? extends KillbillService>>>() {
@Override
public Set<LifecycleHandler<? extends KillbillService>> get() {
return new CopyOnWriteArraySet<LifecycleHandler<? extends KillbillService>>();
}
});
this.injector = injector;
init();
}
public void init() {
Set<? extends KillbillService> services = findServices();
Iterator<? extends KillbillService> it = services.iterator();
while (it.hasNext()) {
handlersByLevel.putAll(findAllHandlers(it.next()));
}
}
public void fireStartupSequencePriorEventRegistration() {
fireSequence(Sequence.STARTUP_PRE_EVENT_REGISTRATION);
}
public void fireStartupSequencePostEventRegistration() {
fireSequence(Sequence.STARTUP_POST_EVENT_REGISTRATION);
}
public void fireShutdownSequencePriorEventUnRegistration() {
fireSequence(Sequence.SHUTDOWN_PRE_EVENT_UNREGISTRATION);
}
public void fireShutdownSequencePostEventUnRegistration() {
fireSequence(Sequence.SHUTDOWN_POST_EVENT_UNREGISTRATION);
}
private void fireSequence(Sequence seq) {
List<LifecycleLevel> levels = LifecycleLevel.getLevelsForSequence(seq);
for (LifecycleLevel cur : levels) {
doFireStage(cur);
}
}
private void doFireStage(LifecycleLevel level) {
log.info("Killbill lifecycle firing stage {}", level);
Set<LifecycleHandler<? extends KillbillService>> handlers = handlersByLevel.get(level);
for (LifecycleHandler<? extends KillbillService> cur : handlers) {
try {
Method method = cur.getMethod();
KillbillService target = cur.getTarget();
log.info("Killbill lifecycle calling handler {} for service {}", cur.getMethod().getName(), target.getName());
method.invoke(target);
} catch (Exception e) {
logWarn("Killbill lifecycle failed to invoke lifecycle handler", e);
}
}
}
private Set<? extends KillbillService> findServices() {
Set<KillbillService> result = new HashSet<KillbillService>();
Set<Class<? extends KillbillService>> services = serviceFinder.getServices();
for (Class<? extends KillbillService> cur : services) {
log.debug("Found service {}", cur.getName());
try {
KillbillService instance = injector.getInstance(cur);
log.debug("got instance {}", instance.getName());
result.add(instance);
} catch (Exception e) {
logWarn("Failed to inject " + cur.getName(), e);
}
}
return result;
}
// Used to disable valid injection failure from unit tests
protected void logWarn(String msg, Exception e) {
log.warn(msg, e);
}
public Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> findAllHandlers(KillbillService service) {
Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> methodsInService = HashMultimap.create();
Class<? extends KillbillService> clazz = service.getClass();
for (Method method : clazz.getMethods()) {
LifecycleHandlerType annotation = method.getAnnotation(LifecycleHandlerType.class);
if (annotation != null) {
LifecycleLevel level = annotation.value();
LifecycleHandler<? extends KillbillService> handler = new LifecycleHandler<KillbillService>(service, method);
methodsInService.put(level, handler);
}
}
return methodsInService;
}
private final class LifecycleHandler<T> {
private final T target;
private final Method method;
public LifecycleHandler(T target, Method method) {
this.target = target;
this.method = method;
}
public T getTarget() {
return target;
}
public Method getMethod() {
return method;
}
}
}