package model.osagaia; import java.io.Serializable; import java.io.InputStream; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import platform.servicesregister.ServiceClosedException; import model.interfaces.control.IBusinessComponent; import util.streams.samples.Sample; import model.StopBCException; import java.util.TimerTask; import java.util.Timer; import util.Parameters; import model.interfaces.inputoutput.InputListener; import platform.servicesregister.ServicesRegisterManager; import platform.ClassManager.JarRessources; import platform.context.ContextManager; import platform.context.ContextInformation; // Classe de base permettant d'ecrire les composants metier /** * Abstract class from which BCs inherits.<br> * This class manages the life cycle of the BC and * offers a method to read a sample on the input stream * and a method to write a sample on the output stream.<br><br> * The BC designer had to write the method <b>run_BC</b> * and the method <b>levelStateQoS</b> which returns the actual QoS level of the BC (0 to 1)<br> * <br><br><br> * The method run_CM looks like this:<br> * public void run_CM() throws StopCMException, InterruptedException {<br> *      // Initis to do after a migration<br> *      while (isRunning()) {<br> *         // treatment that uses:<br> *         // Sample readSample() to read in the input streams *         // or register listeners to inputs<br> *         // and writeSample(Sample) to write to the output stream<br> *         // these methods can raise a StopCMException exception<br> *     }<br> * }<br> * <br> * The BC designer can write the method <b>init</b> called when the BC is started * and the method <b>destroy</b> called when the BC is stopped.<br><br> * In order to define oner's own samples it is necessary to create * a class inherited from <b>util.streams.samples.Sample</b> which had to be serializable. * * @author Dalmau */ public abstract class BCModel implements IBusinessComponent, Runnable, Serializable { private static final long serialVersionUID = 64240020300000001L; // pour serialisation transient private Thread current = null; // Thread accueillant le CM /** * Thread that manages the listeners associated to this BC */ transient protected InputListenersManager listenerManager; transient private InputUnit[] iu = null; // UE transient private OutputUnit ou = null; // US transient private ControlUnit uc = null; // UC transient private String monNom; // nom symbolique du composant transient private BCContainer container; // conteneur du CM /** * Indicates if the BC is running */ transient protected boolean BCRunning; // indique si le CM est en cours d'execution transient private boolean BCAllowedToRun; // indique si le CM peut continuer ee s'executer transient private boolean runStopped; // indique si le CM a ete arrete pendant son run transient private boolean listenerManagerRunning; // indique si le listener manager est lance transient private InputListener[] listeListeners; // ecouteyrs d'entrees // Proprietes serialisees private boolean serialized; // indicateur de CM serialise ou cree private String[] listeFiltres; // filtres associes aux entrees /** * Constuction with no parameters */ public BCModel() { current = null; iu = null; ou = null; container = null; serialized = false; BCRunning = false; } //Methodes qui doivent etre reecrites par le developpeur de CM /** * Returns the QoS level of the BC * * @return the QoS level of the BC */ abstract public float levelStateQoS(); /** * Infinite loop controled by a call to <b>isRunning</b> method. * The BC reads on the input streams, performs a treatment then writes results to the output stream. * * @throws StopBCException when the BC is stopped by the platform * @throws java.lang.InterruptedException * Exceptions used to stop the BC not to be caught */ // rele du CM (boucle infinie) abstract public void run_BC() throws StopBCException, InterruptedException; // Methodes qui peuvent etre reecrites par le concepteur de CM /** * Called when the platform starts the BC * @throws StopBCException when the BC is stopped by the platform * @throws InterruptedException when the BC is stopped by the platform */ public void init() throws StopBCException, InterruptedException {} // appelee e la creation du CM /** * Called when the platform stops the BC * @throws StopBCException when the BC is stopped by the platform * @throws InterruptedException when the BC is stopped by the platform */ public void destroy() throws StopBCException, InterruptedException {} // appelee e la suppression du CM /** * The BC calls this method if all its work is done into listeners * @throws InterruptedException when the BC is stopped by the platform */ protected final synchronized void idle() throws InterruptedException { wait(); } // Methodes utilisees par les CM pour les E/S avec les connecteurs // L'exception StopCMException ne doit pas etre recuperee par le CM // Elle est utilisee par la PF pour arreter le CM (voir run ci-dessous) // designe la classe des echantillons d'entree /** * Define the name of the class of samples to be read by the BC in the input stream.<br>. * When <b>readInInputUnit</b> is called without parameter, it is assumed that * the BC reads samples assignment-compatible with the class defined by this method. * If the sample actually present in the connector * is not assignment-compatible with this class it is discarded and * the Input Unit waits for another one.<br> * This method is usefull for BCs that reads allways the same class of samples: * Giving this class name by this method allows to use the <b>readSample</b> without parameter. * If this method is not called it is assumed that BC accept any <b>Sample</b> class * assignment-compatible kind of samples.<BR> * The class defined by this method is also used by the method <b>isSampleAvailableOnInput</b>. * That means that this methods returne true only if a sample of this class is available.<br> * * @param numero number of the Input Unit to which the filter is associated * @param className name of the class used as input filter for this Input Unit */ public void setInputClassFilter(int numero, String className) { if (numero < iu.length) { if (iu[numero] != null) { try { iu[numero].setInputClassFilter(className); listeFiltres[numero] = className; } catch (ClassNotFoundException cnfe) { try { iu[numero].setInputClassFilter(Sample.class.getName()); listeFiltres[numero] = Sample.class.getName(); } catch (ClassNotFoundException impossible) {} } } } } /** * Removes the filter on the class of samples to be read by the BC in the input stream.<br>. * The BC will now accept any <b>Sample</b> class * assignment-compatible kind of samples.<BR> * * @param numero number of the Input Unit from which the filter is removed * */ public void removeInputClassFilter(int numero) { if (numero < iu.length) { if (iu[numero] != null) { iu[numero].removeInputClassFilter(); listeFiltres[numero] = ""; } } } /** * Waits for a sample of the class defined by the <b>setInputClassFilter</b> method in one input stream. * This method suspends the BC until a sample assignment-compatible with this class is available.<br> * The name of the class of samples to be read is the one previously defined by the * <b>setInputClassFilter</b> method. If the sample actually present in the connector * is not assignment-compatible with this class it is discarded and * the Input Unit waits for another one. * * @param numero number of the Input Unit to read in * @return the sample read * @throws StopBCException when the BC is stopped by the platform */ final public Sample readSample(int numero) throws StopBCException { // lire un echantillon dans l'UE if (numero >= iu.length)throw new StopBCException("BC tries to read in an unknown input stream"); if (iu[numero] != null) { Sample ech = iu[numero].readInInputUnit(); ech.setInputNumber(numero); return ech; } else throw new StopBCException("BC tries to read in an unknown input stream"); } /** * Waits for a sample of a given class in one input stream. * The requester is locked on a semaphore until a sample is available.<br> * If the sample actually present in the connector is not assignment-compatible * with the class given in parameter it is discarded and the Input Unit waits for another one. * * @param numero number of the Input Unit to read in * @param className name of the class of sample to be read. If the sample actually present * in the connector is not assignment-compatible with this class it is discarded and * the Input Unit waits for another one. * @return the sample read * @throws StopBCException when the BC is stopped by the platform * @throws ClassNotFoundException If the class name in parameter could not be found */ final public Sample readSample(int numero, String className) throws StopBCException , ClassNotFoundException { // lire un echantillon dans l'UE if (numero >= iu.length)throw new StopBCException("BC tries to read in an unknown input stream"); if (iu[numero] != null) { Sample ech = iu[numero].readInInputUnit(className); ech.setInputNumber(numero); return ech; } else throw new StopBCException("BC tries to read in an unknown input stream"); } /** * Waits for a sample in one input stream. This sample needs to be assignment-compatible * with the filter associated to this input stream. * The requester is locked on a semaphore until a sample is available.<br> * The number of the stream on which the returned sample has been read is returned * by the method <b>getInputNumber</b> of the returned sample. * @return the sample read * @throws StopBCException when the BC is stopped by the platform */ final public Sample readFirstAvailableSample() throws StopBCException { return uc.getFirstInput(); } /** * Test if a sample is available on one input stream.<br> * This method uses the class of samples defined by <b>setInputClassFilter</b>. * It returns true only if a sample of this class is available on the given input stream. * * @param numero number of the Input Unit to read in * @return true if a sample of the class defined by defined by <b>setInputClassFilter</b> is available.<br> * A true return value means that a of this class is in the stream. But not that it is the first one in this input stream. * * @throws StopBCException when the BC is stopped by the platform */ final public boolean isSampleAvailableOnInput(int numero) throws StopBCException { // Regerder si un echantillon est disponible dans l'UE if (numero >= iu.length)throw new StopBCException("BC tries to read in an unknown input stream"); if (iu[numero] != null) return iu[numero].isInputAvailable(); else throw new StopBCException("BC tries to read in an unknown input stream"); } /** * Writes a sample to all output streams * * @param sample the sample to write * @throws StopBCException when the BC is stopped by the platform */ final public void writeSample(Sample sample) throws StopBCException { // ecrire un echantillon dans une US if (ou != null) ou.writeInOutputUnit(sample); } // Methodes liees au cycle de vie du CM ne peuvent pas etre surchargees /** * Method called the BC at each loop of treatment. * This method allows to stop the BC by the platform<br> * * @return true if the BC can go on running * @throws StopBCException when the BC is stopped by the platform */ final public boolean isRunning() throws StopBCException { if (BCAllowedToRun) { Thread.yield(); return true; } else throw new StopBCException("Stopped by time out"); } /** * Adds an input listener to an input stream. * @param number number of the input stream * @param listener listener to associate to this input stream */ public final void addInputListener(int number, InputListener listener) { if (iu[number] != null) { if (!listenerManagerRunning) { // lancer le manager si ca n'a pas ete deja fait listenerManagerRunning = true; listenerManager.start(); } iu[number].addInputListener(listener); listeListeners[number] = listener; } } /** * Removes an input listener fromo an input stream. * @param number number of the input stream */ public final void removeInputListener(int number) { if (iu[number] != null) { iu[number].removeInputListener(); listeListeners[number] = null; } } /** * Returns an Input stream that gives acces to a resource * @param name name of the resource * @return the Input stream that gives acces to this resource */ public final InputStream getResourceAsStream(String name) { InputStream retour; JarRessources ressources = new JarRessources(container.getClassLoader().getJarFileName()); try { retour = ressources.getRessourceAsStream("classes/"+name); } catch (IOException ioe) { retour = null; } if (retour == null) { retour = getClass().getResourceAsStream(name); } return retour; } /** * Returns a resource as a byte array * @param name name of the resource * @return this resource as a byte array * @throws IOException when the resource can't be read */ public final byte[] getResourceAsByteArray(String name) throws IOException { BufferedInputStream lect = new BufferedInputStream(getResourceAsStream(name)); ByteArrayOutputStream octs = new ByteArrayOutputStream(); byte[] lu = new byte[8*1024]; int cpt; while ((cpt = lect.read(lu, 0, lu.length)) != -1) { octs.write(lu, 0, cpt); } octs.flush(); return octs.toByteArray(); } /** * * This method returns the symbolic name of the BC<br> * * @return symbolic name of the BC */ final public String getName() { return monNom; } /** * Method called by the platform to run the BC<br> * First calls the init method of the BC (done only if the BC has not been serialized)<br> * Then calls the run_CM method of the BC<br> * Before the BC is stopped by the platform, the destroy method is called */ final public void run() { // methode d'execution d'un CM // lancement du CM appel de init (si non migre) puis de run // pour terminer appel de destroy BCRunning = true; // le CM est lance BCAllowedToRun = true; // Le CM a le droit de continuer runStopped = false; // la methode run_BC du CM n'a pas ete arretee par la PF if (!serialized) { // si le CM n'a pas ete migre on execute init try { init(); } // init du CM catch (StopBCException scme) { // arret dans une E/S pendant l'init terminate_CM(); // debloquer la PF si elle est en attente dans join return; } catch (InterruptedException scme) { // arret brutal pendant l'init terminate_CM(); // debloquer la PF si elle est en attente dans join return; } } try { // L'init a ete fait maintenant ou avant la migration run_BC(); // boucle de travail du CM } catch (InterruptedException scme) { // arret brutal runStopped = true; // la methode run_BC du CM a ete arretee par la PF CM_terminatedByPF(); // terminaison du CM (appel de destroy) } catch (StopBCException scme) { // arret dans une E/S runStopped = true; // la methode run_BC du CM a ete arretee par la PF cleanException(); // pour eliminer une InterruptedException qui aurait ete levee CM_terminatedByPF(); // terminaison du CM (appel de destroy) } if (!runStopped) { // Si le Run s'est termine sans exception CM_terminatedByPF(); // terminaison du CM (appel de destroy) } } final private void cleanException() { // annule une InterruptException recue trop tot try { Thread.sleep(0); } // pour recuperer l'exception catch (InterruptedException ie) { /* l'exception est annulee */ } } /** * Normal terminaison of a BC: calls destroy then definitly stops the BC. */ final public void CM_terminatedByPF() { try { destroy(); } // terminaison du CM (appel de destroy) catch (StopBCException sbcid) { } catch (InterruptedException iid) { } terminate_CM(); // debloquer la PF si elle est en attente dans join } final private synchronized void terminate_CM() { BCRunning = false; notifyAll(); // debloquer la PF si elle est dans join } /** * Depose a listener to be run by the listener Manager.<br> * The listener manager is a thread that execute the <b>performSample</B> of a listener. * It manages a list of listeners to run and runs them. * * @param toRead Osagaia Input Unit on which the listener will read a sample */ final public void deposeListener(InputUnit toRead) { listenerManager.deposeListener(toRead); // demander son lancement } /** * Starts the BC (called by the Osagaia's UC when started).<br> * This method starts the listener manager thread ans the BC thread. */ public void start() { // appelee par l'UC pour lancer le CM if (!BCRunning) { listenerManager = new InputListenersManager(this); listenerManager.setPriority(Thread.NORM_PRIORITY-1); listenerManagerRunning = false; // le listener manager ne sera lance que si on enregistre un listener current = new Thread(this); current.setPriority(Thread.NORM_PRIORITY-1); current.start(); } } /** * Method called by the platform to stop the BC<br> * This method throws an InterrutedException to the BC * in order to stop it if it is waiting (sleep or wait methods).<br> * The listener manager thread is also stopped. */ final public void stop() { // appelee par l'UC pour arereter le CM BCAllowedToRun = false; listenerManager.stopThread(); listenerManager.interrupt(); current.interrupt(); // debloquer le CM s'il est bloque par sleep ou wait } /** * Waits for the BC and the listener manager to terminate. * A maximum wait time is defined if <b>Parameters</b>.<br> * After this delai, if the BC is not yet terminated, the platform set the BC's prioriry * to the minimal priority and stops waiting. */ final synchronized public void join() { // Attente de terminaison du CM if ((BCRunning) || (listenerManager.isRunning())) { // le CM est encore en marche Timer delai = new Timer(); // timer pour eviter d'attendre trop longtemps delai.schedule(new WaitForJoin(), Parameters.MAXIMAL_WAIT_FOR_CM); while ((BCRunning) || (listenerManager.isRunning())) {// attendre la fin du CM try { wait(); } // semaphore debloque par l'arret du CM // ou la fin du delai d'attente de cet arret catch (InterruptedException ie) { BCRunning = false; } } delai.cancel(); // arreter le timer current = null; listenerManager = null; } } /** * Associate Inuput and Ouptus Units of the container to the BC * @param inputUnit array of the Input Units of the container * @param outputUnit Output Unit of the container */ final public void setInputOutputUnits(InputUnit[] inputUnit, OutputUnit outputUnit) { // association du CM e son UE et son US this.iu = (InputUnit[])inputUnit; listeListeners = new InputListener[iu.length]; if (!serialized) listeFiltres = new String[iu.length]; for (int i=0; i<iu.length; i++) { listeListeners[i] = null; if (!serialized) listeFiltres[i] = ""; } this.ou = (OutputUnit)outputUnit; } /** * Associate the Control Unit of the container to the BC * @param cu Control Unit of the container */ final public void setControlUnit(ControlUnit cu) { uc = cu; } /** * Sets the symbolic name of the BC<br> * @param name symbolic name of the BC */ final public void setName(String name) { monNom = name; } /** * Returns the QoS of the BC<br> * If the BC is not running returns -1 * * @return QoS level of the BC (0 to 1) */ final public float readQoS() { if (BCRunning) return levelStateQoS(); else return -1f; } /** * Reassociate all serialized input class filters to the input streams of the BC */ public final void associateFilters() { for (int i=0; i<iu.length; i++) { if (listeFiltres[i].length() != 0) setInputClassFilter(i, listeFiltres[i]); } } // Classe lancee par un timer pour permettre e la methode join qui attend // la terminaison du CM de l'arreter brutalement s'il ne se termine // pas dans un delai suffsamment court (defini dans Utils) private class WaitForJoin extends TimerTask { public void run() { terminate_CM(); if (current != null) current.setPriority(Thread.MIN_PRIORITY); if (listenerManager != null) listenerManager.setPriority(Thread.MIN_PRIORITY); try { ContextManager pf = (ContextManager)(ServicesRegisterManager.lookForService(Parameters.CONTEXT_MANAGER)); pf.signalEvent(new ContextInformation("Osagaia container "+monNom+": Can't stop BC")); } catch (ServiceClosedException sce) { System.err.println("Osagaia Container "+monNom+" can't get acces to the platform service"); } } } /** * This method is used to indicate that this BC has been serialized */ public void setSerialized() { serialized = true; } /** * Associates the container to the BC * @param cont the container of the BC */ public void setContainer(BCContainer cont) { container = cont; } }