/* * Copyright 2013 Atteo. * * 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.atteo.moonshine.services.internal; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import javax.management.MBeanRegistration; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.atteo.classindex.ClassIndex; import org.atteo.moonshine.reflection.ReflectionUtils; import org.atteo.moonshine.services.EmptyImplementation; import org.atteo.moonshine.services.Service; import org.atteo.moonshine.services.ServiceInfo; import org.atteo.moonshine.services.ServiceMXBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Strings; import com.google.inject.Module; import com.google.inject.spi.Element; public class ServiceWrapper implements ServiceInfo, ServiceMXBean, MBeanRegistration { private final Logger logger = LoggerFactory.getLogger("Moonshine"); private final String name; private final Service service; private final List<Dependency> dependencies = new ArrayList<>(); private final AtomicReference<Status> status = new AtomicReference<>(Status.CREATED); private List<com.google.inject.spi.Element> elements; private boolean singleton = false; private final boolean configureImplemented; private final boolean startImplemented; private final boolean stopImplemented; private final boolean closeImplemented; public ServiceWrapper(Service service) { this.service = service; this.name = getServiceName(service); this.configureImplemented = isImplemented(service.getClass(), "configure"); this.startImplemented = isImplemented(service.getClass(), "start"); this.stopImplemented = isImplemented(service.getClass(), "stop"); this.closeImplemented = isImplemented(service.getClass(), "close"); } public void addDependency(ServiceWrapper service, Class<? extends Annotation> annotation) { dependencies.add(new Dependency(service, annotation)); } public List<Dependency> getDependencies() { return dependencies; } public Service getService() { return service; } @Override public String getName() { return name; } public void setElements(List<Element> elements) { this.elements = elements; } public Status getStatus() { return status.get(); } @Override public List<Element> getElements() { return elements; } private static String getServiceName(Service service) { StringBuilder builder = new StringBuilder(); if (service.getId() != null) { builder.append("\""); builder.append(service.getId()); builder.append("\" "); } String className = service.getClass().getSimpleName(); if (Strings.isNullOrEmpty(className)) { // for anonymous inner class getSimpleName() returns "" className = service.getClass().getName(); } builder.append(className); String summary = ClassIndex.getClassSummary(service.getClass()); if (summary != null) { builder.append(" ("); builder.append(summary); builder.append(")"); } return builder.toString(); } public ObjectName getObjectName() { try { Hashtable<String, String> keys = new Hashtable<>(); keys.put("type", service.getClass().getName()); if (service.getId() != null) { keys.put("id", service.getId()); } else if (!singleton){ keys.put("hashCode", Integer.toHexString(System.identityHashCode(service))); } return ObjectName.getInstance(Service.class.getPackage().getName(), keys); } catch (MalformedObjectNameException ex) { throw new RuntimeException(ex); } } private static boolean isImplemented(Class<?> klass, String methodName) { Method method = ReflectionUtils.findMethod(klass, methodName); return method.getAnnotation(EmptyImplementation.class) == null; } public boolean isSingleton() { return singleton; } public void setSingleton(boolean singleton) { this.singleton = singleton; } private void changeState(Status expect, Status update) { if (!status.compareAndSet(expect, update)) { throw new IllegalStateException("Cannot configure service, not in " + expect + " state"); } } public Module configure() { changeState(Status.CREATED, Status.CONFIGURING); if (logger.isInfoEnabled() && configureImplemented) { logger.info("Configuring: {}", getName()); } Module module = service.configure(); status.set(Status.READY); return module; } @Override public void start() { changeState(Status.READY, Status.STARTING); if (logger.isInfoEnabled() && startImplemented) { logger.info("Starting: {}", getName()); } service.start(); status.set(Status.STARTED); } @Override public void stop() { if (!status.compareAndSet(Status.STARTED, Status.STOPPING)) { return; } if (logger.isInfoEnabled() && stopImplemented) { logger.info("Stopping: {}", getName()); } try { service.stop(); } finally { status.set(Status.READY); } } public void close() { if (!status.compareAndSet(Status.READY, Status.CLOSING)) { return; } if (logger.isInfoEnabled() && closeImplemented) { logger.info("Closing: {}", getName()); } try { service.close(); } finally { status.set(Status.CLOSED); } } @Override public ObjectName preRegister(MBeanServer mbs, ObjectName on) throws Exception { return getObjectName(); } @Override public void postRegister(Boolean bln) { } @Override public void preDeregister() throws Exception { } @Override public void postDeregister() { } @Override public String toString() { return name; } public static class Dependency { private final ServiceWrapper service; private final Class<? extends Annotation> annotation; public Dependency(ServiceWrapper service, Class<? extends Annotation> annotation) { this.service = service; this.annotation = annotation; } public Class<? extends Annotation> getAnnotation() { return annotation; } public ServiceWrapper getService() { return service; } } public static enum Status { CREATED, CONFIGURING, READY, STARTING, STARTED, STOPPING, CLOSING, CLOSED } }