# ApplicationManagerService.java

    /*
     * Copyright (c) 2010, Rickard Öberg. All Rights Reserved.
     *
     * 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.qi4j.library.jmx;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;
    import javax.management.MBeanOperationInfo;
    import javax.management.MBeanServer;
    import javax.management.ObjectName;
    import javax.management.modelmbean.ModelMBeanOperationInfo;
    import javax.management.modelmbean.RequiredModelMBean;
    import org.qi4j.api.activation.Activation;
    import org.qi4j.api.activation.ActivatorAdapter;
    import org.qi4j.api.activation.Activators;
    import org.qi4j.api.composite.ModelDescriptor;
    import org.qi4j.api.injection.scope.Service;
    import org.qi4j.api.injection.scope.Structure;
    import org.qi4j.api.mixin.Mixins;
    import org.qi4j.api.service.ImportedServiceDescriptor;
    import org.qi4j.api.service.ServiceComposite;
    import org.qi4j.api.service.ServiceDescriptor;
    import org.qi4j.api.service.ServiceReference;
    import org.qi4j.api.structure.Application;
    import org.qi4j.api.structure.Layer;
    import org.qi4j.api.structure.LayerDescriptor;
    import org.qi4j.api.structure.Module;
    import org.qi4j.api.structure.ModuleDescriptor;
    import org.qi4j.functional.HierarchicalVisitorAdapter;

    import static org.qi4j.api.service.qualifier.ServiceQualifier.withId;
    import static org.qi4j.functional.Iterables.filter;
    import static org.qi4j.functional.Iterables.first;

    /**
     * Expose the Zest app as a "tree" of MBeans.
     *
     * Other services should reuse the object names and create
     * nodes under the ones created here. For example:
     * <pre>
     * Zest:application=MyApp,layer=Application,module=MyModule,class=Service,service=MyService
     * </pre>
     * is exported by this service, so another exporter showing some aspect related to this service should
     * use this as base for the ObjectName, and add their own properties. Example:
     * <pre>
     * Zest:application=MyApp,layer=Application,module=MyModule,class=Service,service=MyService,name=Configuration
     * </pre>
     * Use the following snippet to find the ObjectName of a service with a given identity:
     * <pre>
     * ObjectName serviceName = Qi4jMBeans.findService(mbeanServer, applicationName, serviceId);
     * </pre>
     */
    @Mixins( ApplicationManagerService.Mixin.class )
    @Activators( ApplicationManagerService.Activator.class )
    public interface ApplicationManagerService
        extends ServiceComposite
    {

        void exportApplicationStructure()
            throws Exception;

        void unexportApplicationStructure()
            throws Exception;

        class Activator
            extends ActivatorAdapter<ServiceReference<ApplicationManagerService>>
        {

            @Override
            public void afterActivation( ServiceReference<ApplicationManagerService> activated )
                throws Exception
            {
                activated.get().exportApplicationStructure();
            }

            @Override
            public void beforePassivation( ServiceReference<ApplicationManagerService> passivating )
                throws Exception
            {
                passivating.get().unexportApplicationStructure();
            }
        }

        abstract class Mixin
            implements ApplicationManagerService
        {
            @Service
            public MBeanServer server;

            @Structure
            public Application application;

            private List<ObjectName> mbeans = new ArrayList<>();

            @Override
            public void exportApplicationStructure()
                throws Exception
            {
                application.descriptor().accept( new HierarchicalVisitorAdapter<Object, Object, Exception>()
                {
                    Layer layer;
                    Module module;
                    Stack<ObjectName> names = new Stack<>();

                    @Override
                    public boolean visitEnter( Object visited )
                        throws Exception
                    {
                        if( visited instanceof LayerDescriptor )
                        {
                            LayerDescriptor layerDescriptor = (LayerDescriptor) visited;
                            layer = application.findLayer( layerDescriptor.name() );

                            LayerBean layerBean = new LayerBean( layer, layerDescriptor );
                            ObjectName objectName = new ObjectName( "Zest:application=" + application.name() + ",layer=" + layer
                                .name() );
                            names.push( objectName );

                            RequiredModelMBean mbean = new ModelMBeanBuilder( objectName, layerDescriptor.name(), LayerBean.class
                                .getName() ).
                                attribute( "uses", "Layer usages", String.class.getName(), "Other layers that this layer uses", "getUses", null )
                                .operation( "restart", "Restart layer", String.class.getName(), MBeanOperationInfo.ACTION_INFO )
                                .newModelMBean();

                            mbean.setManagedResource( layerBean, "ObjectReference" );
                            server.registerMBean( mbean, objectName );
                            mbeans.add( objectName );
                        }
                        else if( visited instanceof ModuleDescriptor )
                        {
                            ModuleDescriptor moduleDescriptor = (ModuleDescriptor) visited;
                            module = application.findModule( layer.name(), moduleDescriptor.name() );
                            ObjectName objectName = new ObjectName( names.peek()
                                                                        .toString() + ",module=" + moduleDescriptor.name() );
                            names.push( objectName );
                            RequiredModelMBean mbean = new ModelMBeanBuilder( objectName, moduleDescriptor.name(), moduleDescriptor
                                .getClass()
                                .getName() ).
                                attribute( "name", "Module name", String.class.getName(), "Name of module", "name", null ).
                                newModelMBean();

                            mbean.setManagedResource( moduleDescriptor, "ObjectReference" );

                            server.registerMBean( mbean, objectName );
                            mbeans.add( objectName );
                        }
                        else if( visited instanceof ServiceDescriptor )
                        {
                            ServiceDescriptor serviceDescriptor = (ServiceDescriptor) visited;
                            ObjectName objectName = new ObjectName( names.peek()
                                                                        .toString() + ",class=Service,service=" + serviceDescriptor
                                                                        .identity() );
                            RequiredModelMBean mbean = new ModelMBeanBuilder( objectName, serviceDescriptor.identity(), ServiceBean.class
                                .getName() ).
                                attribute( "Id", "Service id", String.class.getName(), "Id of service", "getId", null ).
                                attribute( "Visibility", "Service visibility", String.class.getName(), "Visibility of service", "getVisibility", null )
                                .
                                    attribute( "Type", "Service type", String.class.getName(), "Type of service", "getType", null )
                                .
                                    attribute( "Active", "Service is active", Boolean.class.getName(), "Service is active", "isActive", null )
                                .
                                    attribute( "Available", "Service is available", Boolean.class.getName(), "Service is available", "isAvailable", null )
                                .
                                    operation( "restart", "Restart service", String.class.getName(), ModelMBeanOperationInfo.ACTION_INFO )
                                .
                                    newModelMBean();

                            mbean.setManagedResource( new ServiceBean( serviceDescriptor, module ), "ObjectReference" );

                            server.registerMBean( mbean, objectName );
                            mbeans.add( objectName );
                        }
                        else if( visited instanceof ImportedServiceDescriptor )
                        {
                            ImportedServiceDescriptor importedServiceDescriptor = (ImportedServiceDescriptor) visited;
                            ObjectName objectName = new ObjectName( names.peek()
                                                                        .toString() + ",class=Imported service,importedservice=" + importedServiceDescriptor
                                                                        .identity() );
                            RequiredModelMBean mbean = new ModelMBeanBuilder( objectName, importedServiceDescriptor.identity(), ImportedServiceBean.class
                                .getName() ).
                                attribute( "Id", "Service id", String.class.getName(), "Id of service", "getId", null ).
                                attribute( "Visibility", "Service visibility", String.class.getName(), "Visibility of service", "getVisibility", null )
                                .
                                    attribute( "Type", "Service type", String.class.getName(), "Type of imported service", "getType", null )
                                .
                                    newModelMBean();

                            mbean.setManagedResource( new ImportedServiceBean( importedServiceDescriptor ), "ObjectReference" );

                            server.registerMBean( mbean, objectName );
                            mbeans.add( objectName );
                        }

                        return !( visited instanceof ModelDescriptor );
                    }

                    @Override
                    public boolean visitLeave( Object visited )
                        throws Exception
                    {
                        if( visited instanceof ModuleDescriptor || visited instanceof LayerDescriptor )
                        {
                            names.pop();
                        }

                        return true;
                    }
                } );
            }

            @Override
            public void unexportApplicationStructure()
                throws Exception
            {
                for( ObjectName mbean : mbeans )
                {
                    server.unregisterMBean( mbean );
                }
            }
        }

        class LayerBean
        {
            private final Layer layer;
            private final LayerDescriptor layerDescriptor;
            private String uses;

            public LayerBean( Layer layer, LayerDescriptor layerDescriptor )
            {
                this.layer = layer;
                this.layerDescriptor = layerDescriptor;

                uses = "Uses: ";
                for( LayerDescriptor usedLayer : layerDescriptor.usedLayers().layers() )
                {
                    uses += usedLayer.name() + " ";
                }
            }

            public String getUses()
            {
                return uses;
            }

            public String restart()
                throws Exception
            {
                try
                {
                    layer.passivate();
                    layer.activate();
                    return "Restarted layer";
                }
                catch( Exception e )
                {
                    return "Could not restart layer:" + e.getMessage();
                }
            }
        }

        class ServiceBean
        {
            private final ServiceDescriptor serviceDescriptor;
            private final Module module;

            public ServiceBean( ServiceDescriptor serviceDescriptor, Module module )
            {
                this.serviceDescriptor = serviceDescriptor;
                this.module = module;
            }

            public String getId()
            {
                return serviceDescriptor.identity();
            }

            public String getVisibility()
            {
                return serviceDescriptor.visibility().name();
            }

            public String getType()
            {
                Class<?> first = first( serviceDescriptor.types() );
                if( first == null )
                {
                    return null;
                }
                return first.getName();
            }

            public boolean isActive()
            {
                Class<?> mainType = first( serviceDescriptor.types() );
                ServiceReference<?> first = first( filter( withId( serviceDescriptor.identity() ),
                                                           module.findServices( mainType ) )
                );
                return first != null && first.isActive();
            }

            public boolean isAvailable()
            {
                Class<?> mainType = first( serviceDescriptor.types() );
                ServiceReference<?> first = first( filter( withId( serviceDescriptor.identity() ),
                                                           module.findServices( mainType ) )
                );
                return first != null && first.isAvailable();
            }

            public String restart()
            {
                Iterable<?> services = module.findServices( first( serviceDescriptor.types() ) );
                ServiceReference<?> serviceRef = (ServiceReference) first( filter( withId( serviceDescriptor.identity() ),
                                                                                   services )
                );
                if( serviceRef != null )
                {
                    try
                    {
                        ( (Activation) serviceRef ).passivate();
                        ( (Activation) serviceRef ).activate();
                        return "Restarted service";
                    }
                    catch( Exception e )
                    {
                        return "Could not restart service:" + e.getMessage();
                    }
                }
                else
                {
                    return "Could not find service";
                }
            }
        }

        class ImportedServiceBean
        {
            private final ImportedServiceDescriptor serviceDescriptor;

            public ImportedServiceBean( ImportedServiceDescriptor serviceDescriptor )
            {
                this.serviceDescriptor = serviceDescriptor;
            }

            public String getId()
            {
                return serviceDescriptor.identity();
            }

            public String getVisibility()
            {
                return serviceDescriptor.visibility().name();
            }

            public String getType()
            {
                Class<?> mainType = first( serviceDescriptor.types() );
                if( mainType == null )
                {
                    return null;
                }
                return mainType.getName();
            }
        }
    }