All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.frascati.tinfi.control.content.SCABasicContentControllerTrait.scala Maven / Gradle / Ivy

/***
 * OW2 FraSCAti Tinfi
 * Copyright (C) 2011-2013 Inria, Univ. Lille 1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: [email protected]
 *
 * Author: Lionel Seinturier
 */

package org.ow2.frascati.tinfi.control.content

import org.oasisopen.sca.RequestContext
import org.oasisopen.sca.ServiceReference
import org.oasisopen.sca.annotation.Property
import org.ow2.frascati.tinfi.control.content.scope.ScopeManager
import org.objectweb.fractal.api.Interface
import org.objectweb.fractal.api.NoSuchInterfaceException
import org.objectweb.fractal.api.`type`.ComponentType
import org.objectweb.fractal.api.`type`.InterfaceType
import org.objectweb.fractal.julia.BasicControllerTrait
import org.objectweb.fractal.julia.ComponentInterface
import org.objectweb.fractal.julia.InitializationContext
import org.objectweb.fractal.julia.Interceptor
import org.objectweb.fractal.julia.UseComponentTrait
import org.ow2.frascati.tinfi.TinfiComponentOutInterface
import org.ow2.frascati.tinfi.api.control.ContentInstantiationException
import org.ow2.frascati.tinfi.control.content.scope.CompositeScopeManager
import org.ow2.frascati.tinfi.control.content.scope.ScopeManager
import org.ow2.frascati.tinfi.control.content.scope.StatelessScopeManager
import org.ow2.frascati.tinfi.oasis.ServiceReferenceImpl
import org.ow2.frascati.tinfi.osoa.ConversationScopeManager
import org.ow2.frascati.tinfi.osoa.RequestScopeManager
import org.ow2.frascati.tinfi.reflect.InjectionPoint
import org.ow2.frascati.tinfi.reflect.InjectionPointMap
import scala.collection.mutable.HashMap

/**
 * Trait implementing the functionalities of the {@link
 * SCAExtendedContentController} interface.
 * 
 * @author Lionel 
 * @since 1.4.5
 */
trait SCABasicContentControllerTrait extends BasicControllerTrait
with UseComponentTrait with SCAExtendedContentControllerTrait {

    /**
     * The metadata for the content class associated to the component controlled
     * by the current controller.
     */
    private var ccmd: ContentClassMetaData = null
    
    /**
     * The scope manager for the component controlled by the current controller.
     */
    private var sm: ScopeManager = null

    /**
     * The properties declared by the content class associated with this
     * controller. The index is the property name and the value is the property
     * type.
     */
    private var props = new scala.collection.mutable.HashMap[String,Class[_]]

    
    // -------------------------------------------------------------------------
    // Implementation of the Controller interface
    // -------------------------------------------------------------------------
    
    override def initFcController( ic: InitializationContext ) = {
        super.initFcController(ic)
        val c = ic.content.asInstanceOf[Class[_]]
        innerSetFcContentClass(c)
    }
  
    
    // -------------------------------------------------------------------------
    // Implementation of the SCAContentController interface
    // -------------------------------------------------------------------------
    
    override def setFcContentClass( c: Class[_] ) = innerSetFcContentClass(c)
    override def getFcContentClass : Class[_] = return ccmd.fcContentClass
    override def getFcContent : Object = return sm.getFcContent
    override def setFcContent( content: Object ) = sm.setFcContent(content)

    
    // -------------------------------------------------------------------------
    // Implementation of the SCAExtendedContentController interface
    // -------------------------------------------------------------------------

    override def releaseFcContent( content: Object, isEndMethod: Boolean ) =
        sm.releaseFcContent(content,isEndMethod)
    
    override def eagerInit = {
    	if( ccmd.eagerinit ) {
    	  
	        if( ! sm.isInstanceOf[CompositeScopeManager] ) {
	            val msg =
	                "Eager initialization is meaningless in scopes other than "+
	                "COMPOSITE (scope is: "+ccmd.scope+")"
	            throw new ContentInstantiationException(msg)
	        }
	        
	        // Retrieve the content to trigger its initialization
	        sm.getFcContent()
	        }        
    }
    
    override def start = {
        if( ccmd.startMethod != null ) {
            val contents = sm.getFcCurrentContents
            contents.foreach( content => {
                ccmd.startMethod.invoke(content)
            })
        }
    }

    override def stop = {
        if( ccmd.stopMethod != null ) {
            val contents = sm.getFcCurrentContents
            contents.foreach( content => {
                ccmd.stopMethod.invoke(content)
            })
        }
    }
    
    override def containsPropertyName( name: String ) : Boolean = {
        val b = props.contains(name)
        return b
    }
    
    override def getPropertyNames : Array[String] = {
        val propnames = props.keys
        return propnames.toArray
    }
    
    override def getPropertyType( name: String ) : Class[_] = {
       val typ = props(name)
       return typ
    }
    
    override def setPropertyValue( name: String, value: Object ) = {
        val ip = ccmd.props.get(name)
        if( ip != null ) {
            // If null, no such property, do nothing
            val contents = sm.getFcCurrentContents
            contents.foreach( content => {
                ip.set(content,value)
            })
        }
    }

    override def setReferenceValue( name: String, value: ServiceReference[_] ) = {

        val ip = getInjectionPoint(name)
        if( ip != null ) {
          
    		/*
    		 * If ip is null, there is no injection point. This may be the case
    		 * for references which are injected with the @Constructor
    		 * annotation. See for example ConstructorClientImpl. Do nothing.
    		 */
          
            val iptype = ip.getType
            val contents = sm.getFcCurrentContents
            contents.foreach( content => {
              
    		    if( classOf[java.util.List[_]].isAssignableFrom(iptype) ) {
    		      
    		    	// SCA style collection reference
    			
    		        val srs = ip.get(content).asInstanceOf[java.util.List[ServiceReference[_]]]
	    			
	    			/*
	    			 * If a service reference has already been recorded for the same
	    			 * name, we don't need to add it again. The binding may have
	    			 * changed but in this case, this is the delegate field of the
	    			 * component output interceptor which has been updated. The
	    			 * service reference object which is injected in the injection
	    			 * point does not need to be changed. The chain service
	    			 * reference > component interface > component interceptor is
	    			 * still valid.
	    			 */
    		        var found = false
    		        for( i <- 0 until srs.size ) {
    		            val sri = srs.get(i).asInstanceOf[ServiceReferenceImpl[_]]
    		            val o = sri._getDelegate
    		            val itf = o.asInstanceOf[Interface]
    		            val itfname =  itf.getFcItfName
					    if( itfname.equals(name) ) {
						    found = true
					    }
    		        }
    		        if( !found ) {
    		        	srs.add(value);
    		        }
	    			
    		        /*
	    			 * Re-inject the reference of the list even if this is not
	    			 * needed. This follows a discussion with Philippe on 7
	    			 * March 2013 that needs a way to be notified that something
	    			 * has changed. Reinjecting has the side-effect of invoking
	    			 * the setter (in the case the injection point corresponds
	    			 * to asetter/getter), and enables to be notified that
	    			 * something has changed in the list of references.
	    			 */
    		        ip.set(content,srs)
    		    }
    		    else if( classOf[java.util.Map[_,_]].isAssignableFrom(iptype) ) {
    			
    		    	// Fraclet style collection reference
    		    	val srs = ip.get(content).asInstanceOf[java.util.Map[String,ServiceReference[_]]]
    		    	srs.put(name,value)
    		    	
	    			// Same comment as for SCA style collection references
	    			ip.set(content,srs)
    		    }
    		    else {
    			    // Singleton reference
    			   ip.set(content,value)
    		}
            })
        }
    }

    override def unsetReferenceValue( name: String ) = {
        
        val ip = getInjectionPoint(name)
        if( ip != null ) {
          
    		/*
    		 * If ip is null, there is no injection point. This may be the case
    		 * for references which are injected with the @Constructor
    		 * annotation. See for example ConstructorClientImpl. Do nothing.
    		 */
          
            val iptype = ip.getType
            val contents = sm.getFcCurrentContents
            contents.foreach( content => {
              
    		   if( classOf[java.util.List[_]].isAssignableFrom(iptype) ) {
    		     
    			   // SCA style collection reference
    			
    		       val srs = ip.get(content).asInstanceOf[java.util.List[ServiceReference[_]]]
    		       var toBeRemoved: ServiceReference[_] = null
    		       for( i <- 0 until srs.size ) {
    		           val sri = srs.get(i).asInstanceOf[ServiceReferenceImpl[_]]
    		           val o = sri._getDelegate
    		           val itf = o.asInstanceOf[Interface]
    		           val itfname =  itf.getFcItfName
					   if( itfname.equals(name) ) {
						   toBeRemoved = sri
					   }
    		       }    			
    			   srs.remove(toBeRemoved)
    			   	    			
    		       /*
	    			* Re-inject the reference of the list even if this is not
	    		    * needed. This follows a discussion with Philippe on 7
	    			* March 2013 that needs a way to be notified that something
	    			* has changed. Reinjecting has the side-effect of invoking
	    			* the setter (in the case the injection point corresponds
	    			* to asetter/getter), and enables to be notified that
	    			* something has changed in the list of references.
	    			*/
    		       ip.set(content,srs)
    		    }
    		    else if( classOf[java.util.Map[_,_]].isAssignableFrom(iptype) ) {
    			
    			    // Fraclet style collection reference
    			    val srs = ip.get(content).asInstanceOf[java.util.Map[String,ServiceReference[_]]]
    			    srs.remove(name)
    		    	
	    			// Same comment as for SCA style collection references
	    			ip.set(content,srs)
    		    }
    		    else {
    			    // Singleton reference
    		        ip.set(content,null)
    		    }
            })
        }
    }
    
    
    // -------------------------------------------------------------------------
    // Implementation specific
    // -------------------------------------------------------------------------

    /**
     * Declare the content class which should be used.
     * 
     * This operation can only be performed if the component is stopped.
     * This method is synchronized to prevent inconsistent concurrent changes.
     * 
     * @since 1.0
     */
    private def innerSetFcContentClass( c: Class[_] ) = {
        synchronized {
        
	        /*
	         * Content class meta data and scope manager retrievals.
	         */
	        ccmd = ContentClassMetaData.get(c)
	        
	        val scope = if(ccmd.scope==null) null else ccmd.scope.toUpperCase()
	        if( scope == null || scope.equals("STATELESS") ) {
	            sm = new StatelessScopeManager(weaveableC,ccmd);
	        }
	        else if( scope.equals("REQUEST") ) {
	            sm = new RequestScopeManager(weaveableC,ccmd)
	        }
	        else if( scope.equals("COMPOSITE") ) {
	            sm = new CompositeScopeManager(weaveableC,ccmd)
	        }
	        else if( scope.equals("CONVERSATION") ) {
	            sm = new ConversationScopeManager(weaveableC,ccmd)
	        }
	        else {
	            val msg = "Unsupported scope: "+ccmd.scope
	            throw new IllegalContentClassMetaData(msg)
	        }
	        
	        /*
	         * Initialize property names and types.
	         * 
	         * Firstly, search in the @Property annotated elements (fields and
	         * setters.)
	         */
	        props = new HashMap[String,Class[_]]
	        val ipm = ccmd.props
	        // TODO find a better way for iterating on a java.util.Collection
	        val ipnames = ipm.keySet.toArray(new Array[String](ipm.size))
	        ipnames.foreach( ipname => {
	            val ip = ipm.get(ipname)
	            val typ = ip.getType
	            props += ipname -> typ
	        })
        
	        /*
	         * Secondly, search in the @Property annotated parameters of the
	         * @Constructor annotated constructor.
	         */
	        val ctr = ccmd.constructorAnnotatedElement
	        if( ctr != null ) {
                        
	            val ptypes = ctr.getParameterTypes
	            val psannots = ctr.getParameterAnnotations
	            for( i <- 0 until ptypes.length ) {
	            	val ptype = ptypes(i)
	            	val pannots = psannots(i)
	            	pannots.foreach( pannot => {
	                    if( pannot.isInstanceOf[Property] ) {
	                        val prop = pannot.asInstanceOf[Property]
	                        val propname = prop.name
	                        props += propname -> ptype
	                    }
	            	})
	            }
			}
        }
    }
    
    private def getInjectionPoint( name: String ) : InjectionPoint = {

    	/*
    	 * Do not mimic setPropertyValue which iterates on ccmd.props and
    	 * iterate on ccmd.refs. The reason is that one may find the case where
    	 * the component defines e.g. an opt singleton interface and an opts
    	 * collection interfaces. When searching e.g. for opts0 both injection
    	 * point will match and one may ends up with opt instead of opts. 
    	 */
        val ct = weaveableC.getFcType.asInstanceOf[ComponentType]
    	var it : InterfaceType = null
    	try {
    	    it = ct.getFcInterfaceType(name)
    	}
        catch {
          case nsie: NoSuchInterfaceException =>
    		  // No such interface, neither singleton, nor collection
    		  return null
        }
        val itname = it.getFcItfName
    	
    	/*
    	 * For singleton interfaces, itname equals name.
    	 * For collection interfaces, itname equals the prefix of the collection
    	 * for name.
    	 */
        val ip = ccmd.refs.get(itname)
        return ip
    }
    
    /**
     * Return a {@link ServiceReference} corresponding to the specified client
     * interface or null if the client interface is not bound.
     * 
     * @since 1.4.5
     */
    protected def getServiceReference( clientItf: Object ) : Object = {
        val itf = clientItf.asInstanceOf[ComponentInterface]
        val outInterceptor = itf.getFcItfImpl().asInstanceOf[Interceptor]
        val delegate = outInterceptor.getFcItfDelegate()
        if( delegate == null ) {
        	/*
        	 * Unbound client interface. At this point this means that this is
        	 * an optional interface. Mandatory unbound interfaces are detected
        	 * when starting the component.
        	 */
        	return null
        }
        val tcoi = clientItf.asInstanceOf[TinfiComponentOutInterface[_]]
        val sr = tcoi.getServiceReference
        return sr
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy