| /* |
| * Copyright (c) 2011, Rickard Öberg. All Rights Reserved. |
| * Copyright (c) 2012, Paul Merlin. 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.sql.jmx; |
| |
| import java.lang.reflect.AccessibleObject; |
| import java.util.ArrayList; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import javax.management.Attribute; |
| import javax.management.AttributeList; |
| import javax.management.AttributeNotFoundException; |
| import javax.management.DynamicMBean; |
| import javax.management.InstanceAlreadyExistsException; |
| import javax.management.InvalidAttributeValueException; |
| import javax.management.MBeanAttributeInfo; |
| import javax.management.MBeanException; |
| import javax.management.MBeanInfo; |
| import javax.management.MBeanOperationInfo; |
| import javax.management.MBeanParameterInfo; |
| import javax.management.MBeanRegistrationException; |
| import javax.management.MBeanServer; |
| import javax.management.MalformedObjectNameException; |
| import javax.management.NotCompliantMBeanException; |
| import javax.management.ObjectName; |
| import javax.management.ReflectionException; |
| import javax.sql.DataSource; |
| import org.qi4j.api.activation.Activation; |
| import org.qi4j.api.activation.ActivatorAdapter; |
| import org.qi4j.api.activation.Activators; |
| import org.qi4j.api.association.AssociationStateHolder; |
| import org.qi4j.api.entity.EntityComposite; |
| import org.qi4j.api.entity.EntityDescriptor; |
| import org.qi4j.api.injection.scope.Service; |
| import org.qi4j.api.injection.scope.Structure; |
| import org.qi4j.api.mixin.Mixins; |
| import org.qi4j.api.property.Property; |
| import org.qi4j.api.property.PropertyDescriptor; |
| import org.qi4j.api.service.ServiceComposite; |
| import org.qi4j.api.service.ServiceImporter; |
| import org.qi4j.api.service.ServiceReference; |
| import org.qi4j.api.structure.Application; |
| import org.qi4j.api.structure.Module; |
| import org.qi4j.api.unitofwork.UnitOfWork; |
| import org.qi4j.api.unitofwork.UnitOfWorkCompletionException; |
| import org.qi4j.library.sql.datasource.DataSourceConfiguration; |
| import org.qi4j.spi.Qi4jSPI; |
| |
| /** |
| * Expose DataSourceConfiguration through JMX. |
| * Allow configurations to be edited, and the services to be restarted. |
| */ |
| @Mixins( DataSourceConfigurationManagerService.Mixin.class ) |
| @Activators( DataSourceConfigurationManagerService.Activator.class ) |
| public interface DataSourceConfigurationManagerService |
| extends ServiceComposite |
| { |
| |
| void exportDataSources() |
| throws Exception; |
| |
| void unexportDataSources() |
| throws Exception; |
| |
| class Activator |
| extends ActivatorAdapter<ServiceReference<DataSourceConfigurationManagerService>> |
| { |
| |
| @Override |
| public void afterActivation( ServiceReference<DataSourceConfigurationManagerService> activated ) |
| throws Exception |
| { |
| activated.get().exportDataSources(); |
| } |
| |
| @Override |
| public void beforePassivation( ServiceReference<DataSourceConfigurationManagerService> passivating ) |
| throws Exception |
| { |
| passivating.get().unexportDataSources(); |
| } |
| |
| } |
| |
| abstract class Mixin |
| implements DataSourceConfigurationManagerService |
| { |
| |
| @Structure |
| Module module; |
| |
| @Service |
| MBeanServer server; |
| |
| @Structure |
| Qi4jSPI spi; |
| |
| @Structure |
| Application application; |
| |
| @Service |
| Iterable<ServiceReference<DataSource>> dataSources; |
| |
| @Service |
| ServiceReference<ServiceImporter<DataSource>> dataSourceService; |
| |
| private List<ObjectName> configurationNames = new ArrayList<ObjectName>(); |
| |
| @Override |
| public void exportDataSources() |
| throws MalformedObjectNameException, MBeanRegistrationException, InstanceAlreadyExistsException, NotCompliantMBeanException |
| { |
| for ( ServiceReference<DataSource> dataSource : dataSources ) { |
| String name = dataSource.identity(); |
| Module module = ( Module ) spi.moduleOf( dataSource ); |
| EntityDescriptor descriptor = module.entityDescriptor( DataSourceConfiguration.class.getName() ); |
| List<MBeanAttributeInfo> attributes = new ArrayList<MBeanAttributeInfo>(); |
| Map<String, AccessibleObject> properties = new LinkedHashMap<String, AccessibleObject>(); |
| for ( PropertyDescriptor persistentProperty : descriptor.state().properties() ) { |
| if ( !persistentProperty.isImmutable() ) { |
| String propertyName = persistentProperty.qualifiedName().name(); |
| String type = persistentProperty.valueType().mainType().getName(); |
| attributes.add( new MBeanAttributeInfo( propertyName, type, propertyName, true, true, type.equals( "java.lang.Boolean" ) ) ); |
| properties.put( propertyName, persistentProperty.accessor() ); |
| } |
| } |
| |
| List<MBeanOperationInfo> operations = new ArrayList<MBeanOperationInfo>(); |
| operations.add( new MBeanOperationInfo( "restart", "Restart DataSource", new MBeanParameterInfo[ 0 ], "void", MBeanOperationInfo.ACTION_INFO ) ); |
| |
| MBeanInfo mbeanInfo = new MBeanInfo( DataSourceConfiguration.class.getName(), name, attributes.toArray( new MBeanAttributeInfo[ attributes.size() ] ), null, operations.toArray( new MBeanOperationInfo[ operations.size() ] ), null ); |
| Object mbean = new ConfigurableDataSource( dataSourceService, mbeanInfo, name, properties ); |
| ObjectName configurableDataSourceName = new ObjectName( "Zest:application=" + application.name() + ",class=Datasource,name=" + name ); |
| server.registerMBean( mbean, configurableDataSourceName ); |
| configurationNames.add( configurableDataSourceName ); |
| } |
| } |
| |
| @Override |
| public void unexportDataSources() |
| throws Exception |
| { |
| for ( ObjectName configurableServiceName : configurationNames ) { |
| server.unregisterMBean( configurableServiceName ); |
| } |
| } |
| |
| abstract class EditableConfiguration |
| implements DynamicMBean |
| { |
| |
| MBeanInfo info; |
| |
| String identity; |
| |
| Map<String, AccessibleObject> propertyNames; |
| |
| EditableConfiguration( MBeanInfo info, String identity, Map<String, AccessibleObject> propertyNames ) |
| { |
| this.info = info; |
| this.identity = identity; |
| this.propertyNames = propertyNames; |
| } |
| |
| @Override |
| public Object getAttribute( String name ) |
| throws AttributeNotFoundException, MBeanException, ReflectionException |
| { |
| UnitOfWork uow = module.newUnitOfWork(); |
| try { |
| EntityComposite configuration = uow.get( EntityComposite.class, identity ); |
| AssociationStateHolder state = spi.stateOf( configuration ); |
| AccessibleObject accessor = propertyNames.get( name ); |
| Property<Object> property = state.propertyFor( accessor ); |
| return property.get(); |
| } catch ( Exception ex ) { |
| throw new ReflectionException( ex, "Could not get attribute " + name ); |
| } finally { |
| uow.discard(); |
| } |
| } |
| |
| @Override |
| public void setAttribute( Attribute attribute ) |
| throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException |
| { |
| UnitOfWork uow = module.newUnitOfWork(); |
| try { |
| EntityComposite configuration = uow.get( EntityComposite.class, identity ); |
| AssociationStateHolder state = spi.stateOf( configuration ); |
| AccessibleObject accessor = propertyNames.get( attribute.getName() ); |
| Property<Object> property = state.propertyFor( accessor ); |
| property.set( attribute.getValue() ); |
| try { |
| uow.complete(); |
| } catch ( UnitOfWorkCompletionException e ) { |
| throw new ReflectionException( e ); |
| } |
| } finally { |
| uow.discard(); |
| } |
| } |
| |
| @Override |
| public AttributeList getAttributes( String[] names ) |
| { |
| AttributeList list = new AttributeList(); |
| for ( String name : names ) { |
| try { |
| Object value = getAttribute( name ); |
| list.add( new Attribute( name, value ) ); |
| } catch ( AttributeNotFoundException e ) { |
| e.printStackTrace(); |
| } catch ( MBeanException e ) { |
| e.printStackTrace(); |
| } catch ( ReflectionException e ) { |
| e.printStackTrace(); |
| } |
| } |
| |
| return list; |
| } |
| |
| @Override |
| public AttributeList setAttributes( AttributeList attributeList ) |
| { |
| AttributeList list = new AttributeList(); |
| for ( int i = 0; i < list.size(); i++ ) { |
| Attribute attribute = ( Attribute ) list.get( i ); |
| |
| try { |
| setAttribute( attribute ); |
| list.add( attribute ); |
| } catch ( AttributeNotFoundException e ) { |
| e.printStackTrace(); |
| } catch ( InvalidAttributeValueException e ) { |
| e.printStackTrace(); |
| } catch ( MBeanException e ) { |
| e.printStackTrace(); |
| } catch ( ReflectionException e ) { |
| e.printStackTrace(); |
| } |
| } |
| |
| return list; |
| } |
| |
| @Override |
| public MBeanInfo getMBeanInfo() |
| { |
| return info; |
| } |
| |
| } |
| |
| class ConfigurableDataSource |
| extends EditableConfiguration |
| { |
| |
| private ServiceReference<ServiceImporter<DataSource>> service; |
| |
| ConfigurableDataSource( ServiceReference<ServiceImporter<DataSource>> service, MBeanInfo info, String identity, Map<String, AccessibleObject> propertyNames ) |
| { |
| super( info, identity, propertyNames ); |
| this.service = service; |
| } |
| |
| @Override |
| public Object invoke( String s, Object[] objects, String[] strings ) |
| throws MBeanException, ReflectionException |
| { |
| if ( s.equals( "restart" ) ) { |
| try { |
| // Refresh and restart |
| if ( service.isActive() ) { |
| ( ( Activation ) service ).passivate(); |
| ( ( Activation ) service ).activate(); |
| } |
| |
| return "Restarted DataSource"; |
| } catch ( Exception e ) { |
| return "Could not restart DataSource:" + e.getMessage(); |
| } |
| } |
| |
| return "Unknown operation"; |
| } |
| |
| } |
| |
| } |
| |
| } |