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
}
}