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

The newest version!
/***
 * OW2 FraSCAti Tinfi
 * Copyright (C) 2011-2021 Inria, Univ. Lille
 *
 * 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 scala.collection.mutable.HashMap
import org.oasisopen.sca.ServiceReference
import org.oasisopen.sca.annotation.Property
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.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.objectweb.fractal.juliac.commons.ipf.InjectionPoint

/**
 * 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 getPropertyValue( name: String ) : Object = {

		val ip = ccmd.props.get(name)
		if( ip == null ) {
			// No such property. Do nothing.
			return null
		}

		val contents = sm.getFcCurrentContents
		if( contents.length ==  0 ) {
			return null
		}

		val value = ip.get(contents(0))
		for( i <- 1 until contents.length ) {
			val content = contents(i)
			val v = ip.get(content)
			if( v != value ) {
				return null
			}
		}

		return 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 - 2024 Weber Informatics LLC | Privacy Policy