/* * Copyright 1996-2011 Niclas Hedhman. * * 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.alarm; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.qi4j.api.entity.EntityBuilder; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.query.Query; import org.qi4j.api.query.QueryBuilder; import org.qi4j.api.query.QueryBuilderFactory; import org.qi4j.api.service.ServiceReference; import org.qi4j.api.unitofwork.UnitOfWork; import org.qi4j.api.unitofwork.UnitOfWorkFactory; import org.qi4j.api.value.ValueBuilder; import org.qi4j.api.value.ValueBuilderFactory; /** * Defines the AlarmSystem interface. * * <p> * The <code>AlarmSystem</code> is a central registry/handler for all * <code>AlarmPoint</code> objects. By registering <code>AlarmListener</code>s * to the AlarmSystem, objects are able to 'ignore' the fact that there * are many <code>AlarmPoint</code> objects in the system. * </p> * <p> * Also, new attributes registered with the AlarmSystem will propagate into * all existing and future <code>AlarmPoint</code>s, whereas attributes at <code> * AlarmPoint</code> level is individual to an <code>AlarmPoint</code>. * </p> * <p> * Many different AlarmModels can co-exist in the same application. * In fact, every AlarmPoint can have its own AlarmModel, and the AlarmModel * can be changed in runtime, for unrivaled flexibility. However, typically * the AlarmModel is set in the AlarmSystem only, and all Alarms will * use the default model. * </p> * <p>The default alarm model is a service set by during assembly; * </p> * <pre><code> * public void assemble( ModuleAssembly module ) * throws AssemblyException * { * module.addServices( AlarmSystemService.class ); * module.addServices( StandardAlarmModelService.class ); * } * </code></pre> * * @author Niclas Hedhman */ @Mixins( AlarmSystemService.AlarmSystemMixin.class ) public interface AlarmSystem { /** * Returns all the AlarmModels that has been installed. * * @return all the AlarmModels that has been installed. */ List<AlarmModelDescriptor> alarmModels(); /** * Returns the default AlarmModel. * * @return the default AlarmModel in this AlarmSystem. */ AlarmModel defaultAlarmModel(); /** * Returns a list of all Alarms registered to the service. * <p> * The returned Collection may not be modified in any way. The * implementation is free to return a clone, but not required to do * so, and may decide to terminate if the collection is modified. * </p> * * @return a list of all Alarms registered to the service. */ Query<AlarmPoint> alarmList(); /** * Creates an AlarmPoint with the default AlarmModel. * * @param name the name of the AlarmPoint to be created. * * @param category The category the created AlarmPoint should belong to. * * @return the created AlarmPoint with the given name using the default AlarmModel. */ AlarmPoint createAlarm( String name, AlarmCategory category ); /** * Register AlarmListener to recieve <code>AlarmEvents</code> from all * <code>Alarms</code> managed by this <code>AlarmSystem</code>. * * @param listener the global listener to be added. */ void addAlarmListener( AlarmListener listener ); /** * Remove the <code>AlarmListener</code> from the <code>AlarmSystem</code>. * * @param listener the global listener to be removed. */ void removeAlarmListener( AlarmListener listener ); /** * Returns an immmutable list of all AlarmListeners registered to the service. * * @return a list of all AlarmListeners registered to the service. */ List<AlarmListener> alarmListeners(); /** * AlarmSystem implementation. */ class AlarmSystemMixin implements AlarmSystem { @Service private Iterable<ServiceReference<AlarmModel>> models; private final CopyOnWriteArrayList<AlarmListener> alarmListeners; @Structure private UnitOfWorkFactory uowf; @Structure private ValueBuilderFactory vbf; @Structure private QueryBuilderFactory qbf; public AlarmSystemMixin() { alarmListeners = new CopyOnWriteArrayList<AlarmListener>(); } /** * Returns all the AlarmModels that has been installed. */ @Override public List<AlarmModelDescriptor> alarmModels() { List<AlarmModelDescriptor> descriptors = new ArrayList<AlarmModelDescriptor>(); for( ServiceReference<AlarmModel> model : models ) { descriptors.add( model.metaInfo( AlarmModelDescriptor.class ) ); } return descriptors; } /** * Returns the default AlarmModel. */ @Override public AlarmModel defaultAlarmModel() { AlarmModelDescriptor defaultDefault = null; for( AlarmModelDescriptor descriptor : alarmModels() ) { if( descriptor.isDefaultModel() ) { return alarmModel( descriptor ); } defaultDefault = descriptor; } return alarmModel( defaultDefault ); } private AlarmModel alarmModel( AlarmModelDescriptor descriptor ) { for( ServiceReference<AlarmModel> model : models ) { if( model.metaInfo( AlarmModelDescriptor.class ).equals( descriptor ) ) { return model.get(); } } return null; } /** * Creates an AlarmPoint with the default AlarmModel. * @param name The system name of the AlarmPoint. * @param category The AlarmPoint Category the created alarm should belong to. */ @Override public AlarmPoint createAlarm( String name, AlarmCategory category ) { UnitOfWork uow = uowf.currentUnitOfWork(); EntityBuilder<AlarmPoint> builder = uow.newEntityBuilder( AlarmPoint.class ); builder.instance().category().set( category ); AlarmPoint.AlarmState state = builder.instanceFor( AlarmPoint.AlarmState.class ); state.systemName().set( name ); state.currentStatus().set( createStatus( AlarmPoint.STATUS_NORMAL ) ); return builder.newInstance(); } private AlarmStatus createStatus( String status ) { ValueBuilder<AlarmStatus> builder = vbf.newValueBuilder( AlarmStatus.class ); AlarmStatus.State statePrototype = builder.prototypeFor( AlarmStatus.State.class ); statePrototype.name().set( status ); statePrototype.creationDate().set( new Date() ); return builder.newInstance(); } /** * Register AlarmListener to recieve <code>AlarmEvents</code> from all * <code>Alarms</code> managed by this <code>AlarmSystem</code>. */ @Override public void addAlarmListener( AlarmListener listener ) { alarmListeners.add( listener ); } /** * Remove the <code>AlarmListener</code> from the <code>AlarmSystem</code>. */ @Override public void removeAlarmListener( AlarmListener listener ) { alarmListeners.remove( listener ); } /** * Returns a list of all Alarms registered to the service. */ @Override public Query<AlarmPoint> alarmList() { UnitOfWork uow = uowf.currentUnitOfWork(); QueryBuilder<AlarmPoint> builder = qbf.newQueryBuilder( AlarmPoint.class ); return uow.newQuery( builder ); } @Override public List<AlarmListener> alarmListeners() { synchronized( alarmListeners ) { return alarmListeners; } } public void alarmFired( AlarmEvent event ) { Iterator list; //noinspection SynchronizeOnNonFinalField synchronized( alarmListeners ) { list = alarmListeners.iterator(); } while( list.hasNext() ) { AlarmListener listener = (AlarmListener) list.next(); try { listener.alarmFired( event ); } catch( Exception e ) { // TODO: Utilize a logger system instead. System.err.println( "Exception in AlarmListener: " + listener ); e.printStackTrace(); } } } } }