You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Leif Mortenson <le...@tanukisoftware.com> on 2003/06/29 17:42:41 UTC

[Fortress] Cyclic loops

I have been playing around with JMF off and on for a few years. This
weekend I attempted to upgrade a project that I have had on the back
burner for a few months to the latest and greatest Fortress and am
running into a problem.

I have defined a series of Media Processors, and Capture Devices, each
of which implement a TJMFDataSourceProvider interface. I have set up
the Processor components so that their configurations each specify the
name another TJMFDataSourceProvider data source provider. This worked
great because I could easily chain together a series of data source
providers
from the config file without any special coding.

capture -> sharpen -> detect-motion -> output

The problem that I am now running into is because all of components
implement the TJMFDataSourceProvider role and the processor
components also depend on other TJMFDataSourceProvider implementations,
fortress will throw a CyclicDependencyException because the cyclic checks
are based on role rather than the actual component instances.

I can't think of any way that Fortress could handle this any better because
the actual instance is only known at run time from within the component as
its configure method is running. I just wanted to get the thought of the
possibility of applications out there in case anyone has any ideas.

I was able to get my application working by removing the dependency
declaration from the processor components that was causing the cyclic
error. The processors then look up the components anyway. Things work
now, but I don't really like it as the loop is still there. If the
container ever
gets stricter about what components it allows to be returned for a given
component, this would break again.

Cheers,
Leif


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Berin Loritsch <bl...@apache.org>.
Anton Tagunov wrote:
> Hello Leif!
> 
> LM> The problem that I am now running into is because all of components
> LM> implement the TJMFDataSourceProvider role and the processor
> LM> components also depend on other TJMFDataSourceProvider implementations,
> LM> fortress will throw a CyclicDependencyException because the cyclic checks
> LM> are based on role rather than the actual component instances.
> 
> LM> I can't think of any way that Fortress could handle this any better because
> LM> the actual instance is only known at run time from within the component as
> LM> its configure method is running.
> 
> Honestly me neither.
> 
> 7)
> 
> This section is a sort of "RT" :-)
> 
> Are the loops evil after all?

They very well can be.  In this case, it seems to be benign.  The closest
analogy I can give is one of "tumors".  Not all tumors are cancerous, but
they they _might_ be.  A component that forces the container into a real
cyclic dependency loop will never be able to be initialized because it will
never get out of the initialization stage.  Component A will require Component
B wich requires Component C which requires Component A will continue that
initialization cycle until it is forcibly broken.

When the Component "A"s are really Component Acapture and Component Ascan
then those are not really cyclic.  THey are two different components.


> Can there be a legitimate application where two components need
> to lookup() each other?
> 
> Why not?
> 
> The real trouble will happen if the components
> invoke each other during the "warmup" peirod.
> 
> ("warmup" = contextualize, configure, service, initialize, start)

If they are looked up at that time then the problem still exists--and
that seems to be a common practice.


-- 

"They that give up essential liberty to obtain a little temporary safety
  deserve neither liberty nor safety."
                 - Benjamin Franklin




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re[2]: [Fortress] Cyclic loops

Posted by Anton Tagunov <at...@mail.cnt.ru>.
Hello Leif!

0)

AT>(I hope my poor knowledge of terminology has not stopped me from
AT>being understandable here :)

LM> Not at all.  Your English is much better than my Russian. :-)  I can 
LM> relate when using my Japanese though :-)
It was not English, I do not know Phoenix terminology so I was
afraid that the terms I used instead will not be understood.
BTW, how are you getting on your katakana? :-)

1)

AT>Are the loops evil after all?
AT>
AT>Can there be a legitimate application where two components need
AT>to lookup() each other?
AT>
AT>The real trouble will happen if the components
AT>invoke each other during the "warmup" peirod.

LM> Actually, I don't think it should be up to the component to worry about
LM> when it can actually make use of components that have been obtained via
LM> a lookup. So I think it is good that the Container makes sure that components
LM> do no declare any loops.

Hmm... unfortunately this causes us to go into tricks like the one
you described with CA, CB, CD, CD

And this won't work at all for all the containers that enforce
the "you get only what you are allowed to" principle.

2)

LM> It should be possible to create a lookup counter that tracks the number
LM> of times that a component.  The counter would then be checked before a
LM> component was disposed and the counter would wait up to a timeout limit
LM> before disposing the component.  The container would prevent any new
LM> references to the component from being looked up once the shutdown
LM> process was initiated.

AFAIK

* ECM had this counter
* for Fortress a more advanced scheme was discussed
  that would build dependency graph

  Oh, dear what will it do if it detects loops there...

LM> Thoughts?

3)

BTW, look at what peter@realityforge.org has proposed
for Phoenix (my prev. mail had this in section 8)

Imagine we have components A, B, C.

We declare dependencies

   C -> A
   C -> B
   
Now, component C does

C::start() // sorry for C++ syntax :)
{
    m_a = m_sm.lookup( A );
    m_b = m_sm.lookup( B );

    m_a.takeB( m_b );

    // this far it has been what Peter has proposed, now
    // let's go one step further

    m_b.takeA( m_a );
}

then to make sure the order of decommission is correct we
might even do

C::stop()
{
    m_a.drop( m_b );
    m_b.drop( m_a );

    m_sm.release( m_a ); m_a = null;
    m_sm.release( m_b ); m_b = null;
}

Oh, dear. This shows that there is a way to deceive any
mechanism tracking dependencies and even not be hit by
a brick on your head.

But I do not like this at all.
In fact if A and B are not sigletones this will work
only if any requests to A and B arrive from or via C.

(Plz ask if I'm not verbose enough here, I want to
be understood.)

The funnies thing about it all is that peter@realityforge.org
offers a similar thing (wiring component B to A via component C)
as a perfectly legitimate approach for Phoenix.

It's option 3 in his mail.
(For Phoenix it is a little less bad as
with Phoenix everyone is a singletone.)

4)

But as I said I do not like very much to deceive anybody.
It would be nicer if not component C but regular container
mechanisms allowed us such component composition.

But I'm not sure haw viable it is.

Component C in this example has some intimate knowledge
of A and B and that's why the example works.

A general container does not this initmate knowledge,
unless we find a way to stuff it into the configuration,
which I do not see yet.

-Anton


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Leif Mortenson <le...@tanukisoftware.com>.

Anton Tagunov wrote:

>3)
>The issue may be partially resolved by allowed a component to
>declare a dependency on its own role. (Probably this has already
>been done and that's how excalibur-store compiles fortress
>meta-data, but then why are you having troubles?)
>
>In fact it is quite reasonable to allow a component to look up
>its own role and not consider it a cyclic dependency. If I want
>to get myself via a lookup() why not allow me to do this?
>
Hmmm.  I think that a loop is a loop.  Roles may be fine.  But a component
instance should not be able to lookup itself as the returned instance would
not yet have been initialized.

>4.2)
>(I hope my poor knowledge of terminology has not stopped me from
>being understandable here :)
>
Not at all.  Your English is much better than my Russian. :-)  I can 
relate when
using my Japanese though :-)

>7)
>
>This section is a sort of "RT" :-)
>
>Are the loops evil after all?
>
>Can there be a legitimate application where two components need
>to lookup() each other?
>
>Why not?
>
>The real trouble will happen if the components
>invoke each other during the "warmup" peirod.
>
>("warmup" = contextualize, configure, service, initialize, start)
>
>But if the components do not make any real calls on each other
>until after "warmup", then its all ok!
>
Actually, I don't think it should be up to the component to worry about 
when it can
actually make use of components that have been obtained via a lookup. 
 So I think
it is good that the Container makes sure that components do no declare any
loops.

The question is how to handle lookups whose lifecycles are managed by the
component.  This is how I have handled systems that must contain actual 
loops.
For example. If you have 4 components CA, CB, CC, and CD.  You can declare
that CB depends on CA, CC depends on CB, and CD depends on CA.  Fortress
will correctly load CA then CB, then CC, and finally CD.  The problem is 
that
CA also needs to call methods in CD.  The declaration that CA depends on CD
will not work because this would create a loop that would prevent the 
container
from being able to guarantee that all components are initialized before 
being
looked up.

I have worked around this by wrapping the call to lookup CD in CA in a 
lookup
release block.  This means that the lookup and release must be done for each
call,  But if I make sure that CD can not be disposed while a call is 
being made

CD cd = (CD)sm.lookup( CD.ROLE );
try
{
    cd.doSomething();
}
finally
{
    sm.release( cd );
    cd = null;
}

Even this method is not perfect on its own if doSomething takes a while to
execute.  Fortress does not wait until all looked up references have been
released before disposing a component.  It simple guarantees that the
components will be disposed in the correct order.  Meaning
CD->CC->CB->CA in this case.

The problem is that the above will dispose CD while its doSomething method
is being called by CA.  This can lead to NPE's if CD's dispose method is
called before doSomething has completed.

This is a hack, but I have worked around this by implementing a call 
counter in
all methods of CD that are exposed.
public void doSomething()
{
    synchronized(this)
    {
        m_calls++;
    }
    try
    {
        // Actually do something
    }
    finally
    {
        synchronized(this)
        {
            m_calls--;
            this.notifyall();
        }
    }
}

Then in the components dispose method, wait until the m_calls counter
has dropped to 0.   This will work most of the time, but there is still a
slight chance that the thread from CA will lookup the CD component just
before it is disposed and the dispose method will be called before the
doSomething() is actually called.   This is only an issue at application
shutdown, so it was an acceptable workaround at the time.

It would be nice if the container handled this kind of thing correctly.

It should be possible to create a lookup counter that tracks the number
of times that a component.  The counter would then be checked before a
component was disposed and the counter would wait up to a timeout limit
before disposing the component.  The container would prevent any new
references to the component from being looked up once the shutdown
process was initiated.

Thoughts?

Cheers,
Leif


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Leif Mortenson <le...@tanukisoftware.com>.
Anton Tagunov wrote:

>BL> When the Component "A"s are really Component Acapture and Component Ascan
>
>Acapture, Ascan??
>What's this? :)
>
Those were just example components from my JMF application.
The Interface(Role) is "A", and "Acapture" and "Ascan" are implementations
of "A".

Cheers,
Leif



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re[2]: [Fortress] Cyclic loops

Posted by Anton Tagunov <at...@mail.cnt.ru>.
Hello Berin! Glad to see you again! :-))

AT> Are the loops evil after all?

BL> When the Component "A"s are really Component Acapture and Component Ascan

Acapture, Ascan??
What's this? :)


BL> then those are not really cyclic.  THey are two different components.

-Anton


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Berin Loritsch <bl...@apache.org>.
Anton Tagunov wrote:
> Hello Leif!
> 
> LM> The problem that I am now running into is because all of components
> LM> implement the TJMFDataSourceProvider role and the processor
> LM> components also depend on other TJMFDataSourceProvider implementations,
> LM> fortress will throw a CyclicDependencyException because the cyclic checks
> LM> are based on role rather than the actual component instances.
> 
> LM> I can't think of any way that Fortress could handle this any better because
> LM> the actual instance is only known at run time from within the component as
> LM> its configure method is running.
> 
> Honestly me neither.
> 
> 7)
> 
> This section is a sort of "RT" :-)
> 
> Are the loops evil after all?

They very well can be.  In this case, it seems to be benign.  The closest
analogy I can give is one of "tumors".  Not all tumors are cancerous, but
they they _might_ be.  A component that forces the container into a real
cyclic dependency loop will never be able to be initialized because it will
never get out of the initialization stage.  Component A will require Component
B wich requires Component C which requires Component A will continue that
initialization cycle until it is forcibly broken.

When the Component "A"s are really Component Acapture and Component Ascan
then those are not really cyclic.  THey are two different components.


> Can there be a legitimate application where two components need
> to lookup() each other?
> 
> Why not?
> 
> The real trouble will happen if the components
> invoke each other during the "warmup" peirod.
> 
> ("warmup" = contextualize, configure, service, initialize, start)

If they are looked up at that time then the problem still exists--and
that seems to be a common practice.


-- 

"They that give up essential liberty to obtain a little temporary safety
  deserve neither liberty nor safety."
                 - Benjamin Franklin


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Anton Tagunov <at...@mail.cnt.ru>.
Hello Leif!

LM> The problem that I am now running into is because all of components
LM> implement the TJMFDataSourceProvider role and the processor
LM> components also depend on other TJMFDataSourceProvider implementations,
LM> fortress will throw a CyclicDependencyException because the cyclic checks
LM> are based on role rather than the actual component instances.

LM> I can't think of any way that Fortress could handle this any better because
LM> the actual instance is only known at run time from within the component as
LM> its configure method is running.

Honestly me neither.

1)

In fact the issue has shown up before (that time with Merlin),
some guy was also building a system where components were
to chain heavily, something a bit cocoon like, I do not recall
precisely. Under Merlin it was even worse, as it does enforce
the "you can get only what you have been allowed to get"
restriction.

Don't remember what recommendations did Steve finally give. Steve?

2)

The same trouble used to exist in the excalibur-store package.
It even did failed to compile Fortress meta data
because a component was declaring a dependency
on its own role. Would be interesting to see how this has
been resolved.

3)

The issue may be partially resolved by allowed a component to
declare a dependency on its own role. (Probably this has already
been done and that's how excalibur-store compiles fortress
meta-data, but then why are you having troubles?)

In fact it is quite reasonable to allow a component to look up
its own role and not consider it a cyclic dependency. If I want
to get myself via a lookup() why not allow me to do this?

But this resolves only the trivial loop: a component depending
on its own role. A "loop" consisting of two roles will not
be resolved with this.

4)

Initially Berin was saying that it will be possible in the future
to consider allowing dependency declarations of the form

    "my.cool.Processor/hintA"

but this rises the following reservations:

4.1)

Since Fortress has no assembly now this is problematic: we will have
to put this info "...../hintA" into the source file, that is specify
at compile time. On the other hand the real hints (aka id-s) are
specified as the user writes .xconf. And id clashes for instance
may make this mechanism unworkable.

4.2)

Even for containers that have assembly (and with fortress if it gains
one in the future) this moves the name of the processor we chain with
from the component configuration where it naturally belongs and is
easy to write and understand to the section that describes dependencies.

(I hope my poor knowledge of terminology has not stopped me from
being understandable here :)

5)

I believe yet none of the containers supports the most dynamic case
when the id of the component we need is not known at configure time
at all. (E.g. comes some _very_ weird way from the configuration).

6)

If users start using this "hack" - not declaring a dependency but
lookup()-ing the component anyway than future introduction of the
"you can get only what you have been allowed to get"
restriction to Fortress will become a compatibility breakage.
*deep sigh*

7)

This section is a sort of "RT" :-)

Are the loops evil after all?

Can there be a legitimate application where two components need
to lookup() each other?

Why not?

The real trouble will happen if the components
invoke each other during the "warmup" peirod.

("warmup" = contextualize, configure, service, initialize, start)

But if the components do not make any real calls on each other
until after "warmup", then its all ok!

Oh, yes, they also must make no calls on each other in the
"cooldown" ( stop, dispose ) period and they must be decomissioned
together..

8) Compare this all to the mail by Niclas Hedhman <ni...@hedhman.org>
with subject "[Phoenix] Design issue about wiring" in this list.
It's about just the same in Phoenix. The issue is common!

peter@realityforge.org ;-)
answer is interesting to read

I'm afraid I do not understand what his solution 2) means, it reads as

PRF> 2. Use a listener to collect all the blocks that implement
PRF> the Source interface and then register them with the listeners
PRF> in applicationStarted() method.

I guess it is something Phoenix specific.

Hmm.. is it really about putting the code not into a component
but into something that sort of "plugs inside the container"
(the listener and the applicationStarted() method)
and has less access restrictions then a component?

This looks to me like inventing another sort of components
that have less restrictions since the regular components have
too many. Hey, aren't we overcomplicating our design?

- Anton


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Leif Mortenson <le...@tanukisoftware.com>.

Berin Loritsch wrote:

>Leif Mortenson wrote:
>  
>
>One way around it is to specify the longer form of ${ROLE}/${type}
>which for the above could be translated to:
>
>org.leif.mortenson.TJMFDataSourceProvider/capture
>org.leif.mortenson.TJMFDataSourceProvider/sharpen
>org.leif.mortenson.TJMFDataSourceProvider/detect-motion
>org.leif.mortenson.TJMFDataSourceProvider/output
>
Where would these be specified?  I am already using this syntax when doing
the lookup.  But the cyclical dependency is being flagged at container 
startup.
Are you suggesting configuring these in the @avalon.dependency meta
definitions?  If so, that would not really work as the chaining order 
would be
not be configurable without rebuilding the application.

What about moving the dependency checks to the lookup phase rather than
the container initialization phase.  Have not thought it out enough to 
say whether
or not that is even possible...  The dependency tree needs to be worked 
out to
decide on a startup order, but maybe we should ignore loops at that 
point and
then let them be detected as components are being initialized on demand.
If the container pushed each component onto a thread local stack as it was
being initialized, it would just be a matter of checking to see whether 
or not the
component class is already in the stack at the start of initialization.  
If it is, then
we have a loop.

Cheers,
Leif




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Re: [Fortress] Cyclic loops

Posted by Berin Loritsch <bl...@apache.org>.
Leif Mortenson wrote:
> I have been playing around with JMF off and on for a few years. This
> weekend I attempted to upgrade a project that I have had on the back
> burner for a few months to the latest and greatest Fortress and am
> running into a problem.
> 
> I have defined a series of Media Processors, and Capture Devices, each
> of which implement a TJMFDataSourceProvider interface. I have set up
> the Processor components so that their configurations each specify the
> name another TJMFDataSourceProvider data source provider. This worked
> great because I could easily chain together a series of data source
> providers
> from the config file without any special coding.
> 
> capture -> sharpen -> detect-motion -> output
> 
> The problem that I am now running into is because all of components
> implement the TJMFDataSourceProvider role and the processor
> components also depend on other TJMFDataSourceProvider implementations,
> fortress will throw a CyclicDependencyException because the cyclic checks
> are based on role rather than the actual component instances.

Hmmm.  This can be a problem as Cocoon has the same type of setup.
The issue of course is one of assembly--or ensuring that component
implementations are set correctly.

One way around it is to specify the longer form of ${ROLE}/${type}
which for the above could be translated to:

org.leif.mortenson.TJMFDataSourceProvider/capture
org.leif.mortenson.TJMFDataSourceProvider/sharpen
org.leif.mortenson.TJMFDataSourceProvider/detect-motion
org.leif.mortenson.TJMFDataSourceProvider/output

It would work, technically speaking.  It's not the best, but
for the short term it is OK.

Even better would be to turn off the cyclic dependency checking
with an option for meta-data generation.  At runtime when there
is proper assembly information available we can ensure that any
one path is not cyclic.

> I can't think of any way that Fortress could handle this any better because
> the actual instance is only known at run time from within the component as
> its configure method is running. I just wanted to get the thought of the
> possibility of applications out there in case anyone has any ideas.

WOuld the above help?


-- 

"They that give up essential liberty to obtain a little temporary safety
 deserve neither liberty nor safety."
                - Benjamin Franklin


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org