[Design of Embedded JBoss] - Re: External Contribution to AS Embedded
by pgmjsd
"ALRubinger" wrote :
| Great code in general. I appreciated your commitment to documentation and cleanup/centralization as you go along.
|
| In ripping jboss-bootstrap out of the Application Server, I've been especially sensitive to violation of scope and extending a component's concerns outside its domain. In this case, startup/shutdown of bootstraps is provided by impl-base, and the attached patch addresses test cases in impl-mc. This makes it more of a compliance/acceptance test where impl-mc validates it's getting a good impl-base.
|
I wasn't sure what each module was for. I just looked for a test that started up 'bootstraps' inside a 'server'. Got it... impl-mc is for the MC specific stuff like parsing XML and whatnot, impl-base is for basic lifecycle sequence.
"ALRubinger" wrote :
| So I've attached an alternate way of tackling the issue in "JBBOOT-56.v2.ALR.patch" on the JIRA. Have a look and let me know what you think.
|
Downloaded the patch, here's what I think:
* I had to move the files in the patch because the patch was based on a subdirectory. I'll have to see if IDEA can make patches with a directory different from the base of the project. I'm using boostrap/trunk as the base.
* Now I understand what 'a Bootstrap' is, sort of. Partial understanding of the module structure.
* I'm not sure about the "package per jira issue" convention. But hey... if that's how you wanna roll.
"ALRubinger" wrote :
| Some misc minor notes:
|
| * You don't need AtomicInteger in LifecycleOrderPojo (as you hint); this is all single-threaded.
| * We need to get you set up w/ JBoss licence headers and preferably with a formatter (if you're in Eclipse I have code templates).
|
Okay. Wasn't sure about AtomicInteger. I have multi-threading on the brain.
I have eclipse, but I prefer IDEA. I've already made templates for the license and such for IDEA.
"ALRubinger" wrote :
| I'm impressed that you've picked up the Bootstrap Server API and MC jboss-beans.xml deployments so quickly. This was a good, working patch you've provided. So let's just address the scope issue and move forward on something else? ;)
|
Yes, now that I understand everything (yeah, right). ;)
"ALRubinger" wrote :
| Outstanding questions:
|
| * Do you have an account on jboss.org? This enables posting to the forums, JIRA assignments, etc.
| * Have you yet signed a contributor's agreement? Once done I can commit on your behalf, and after a few patches, get you commit access.
|
| http://jboss.org/contributor
|
I'm forum/wiki/JIRA enabled. But I haven't signed a contributor agreement in a while. I pre-date the shiny new contributor sign up web application. ;)
View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4238841#4238841
Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4238841
16 years, 9 months
[Design the new POJO MicroContainer] - Re: ClassPool for JBoss Reflection
by flavia.rainone@jboss.com
Kabir,
I have taken a very good look on all class pools, and I'm still having a hard time to figure out a few things.
Starting from javassist.ClassPool (http://www.docjar.com/html/api/javassist/ClassPool.java.html):
ClassPool allows you to retrieve a CtClass representing already existing classes through get and through getCtClass:
- getCtClass adds support to arrays and calls get
- get delegates to get0, plus increment some CtClass getCounter
- get0 uses childFirstLookup to determine whether it should try to retrieve the class from the parent domain before or after trying to retrieve itself. When trying to retrieve itself,it checks on whether the class is cached, if not, creates a new one by calling createCtClass, plus caches it.
Note that the caching of get0 is enabled or disabled according to the cache parameters. If cache is false, the ClassPool won't cache the result of createCtClass. Given get0 is an internal method, I've taken a look at all callers to see when is cache false and when is it true. It is false only when it is called by getAndRename, which IMO makes sense, given it is just a shortcut for creating a new class, so you don't want to cache the original one with a new name. So, we can assume that get0 will cache classes whenever needed.
Now, taking a look at ScopedClassPool:
http://www.docjar.com/html/api/javassist/scopedpool/ScopedClassPool.java....
- it extends ClassPool
- adds a very good functionality: soften. This method allows that classes be moved from the hard reference classes cache to the softcache, that keeps soft references, allowing them to be gc'ed. This functionality is used by JBoss AOP to mark all classes that were not instrumented, thus preventing memory leak of non-instrumented CtClasses (the idea is that if they need to be created again, there won't be inconsistencies as no instrumentation has taken place). We could do the same for JBoss Reflection.
- cacheCtClass: complementing the soften functionality, this CP overwrites the cacheCtClass method, delegating to the superclass only if the class is being created dynamicly (i.e., it is a new class, that was not in the classpath), and adding it to softcache otherwise.
- adds a ScopedClassPoolRepository, that contains several pools.
- overwrites internal getCached method, so that whenever you need a cached class it will also look into the classes of the other class pools
- adds a getCachedLocally, which just looks for classes at the "classes" cache (defined at the super class ClassPool) and at the "softcache' as well.
- overwrites the toClass method, so that it uses the getClassLoader0 (there is a commentary justifying why it is that way), plus it calls lockInCache, which I didn't understand why. (1) ***
- added lockInCache, which is just a shortcut for calling super.cacheCtClass(c.getName(), c, false);
- added a close() method, that removes the cp from the repository
- added a getLocally method. This method basically removes a class from the softcache, creates it and caches it at the classes collection. I imagine that this method removes the class from the softcache for garanteeing consistency, so that there won't be two different versions of the same ctClass: one in the softcache and another one in the classes cache.
I figured that this ScopedClassPool and its repository were created to deal with UnifiedClassLoaders (let me know if I'm wrong). But now, the one million dollar question (2): why is it that getLocally was added to ScopedClassPool? Shouldn't ScopedClassPool had overwitten get0 instead? I see that this method is almost the same as the get0, except for two differences: it removes the class from the softcache (a much needed feature whenever a ct class is created) and it doesn't use childLookupFirst to delegate to the parent class loader. The result is that, IMO, by creating getLocally instead of overwriting get0, there are two ways of retrieving a ctclass, and the "official" way doesn't even handle the softcache stuff, creating an inconsistency. A way of solving but keeping getLocally would be overriding createCtClass like this:
softcache.remove(classname); super.createCtClass(...);
For the record, JBoss AOP never uses the official methods get and getCtClass... it uses the getLocally instead, thus avoiding the inconsistency mentioned. (3)
Now, we go to AOPClassPool:
- it extends ScopedClassPool
- getLocally: this method is overriden, but the strange thing is that the body of this method is a copy of the body of the super class, excepts for a few logging calls and except for teh fact that it replaces an invocation to ClassPool.cacheCtClass with a call to lockInCache, but they are equivalent. (4)
- getCached: the AOPClassPool overrides the ScopedClassPool.getCached method, thus overriding the single point of ScopedClassPool that uses the ScopedClassPoolRepository to delegate ctclass retrieval to other CPs of the same repository.
- there are several other details that I don't think are useful for my questions.
So, why does AOPClassPool extends ScopedClassPool if it doesn't uses the ScopedClassPoolRepository for retrieval of classes? (5)
Now, going to BaseClassPool (subclass of AOPClassPool), I see that it overrites createCtClass. It adds a call to lockInCache before calling super.createCtClass method. This generates another inconsistency for the get methods of ClassPool. Take a look at a part of get0 implementation:
491 clazz = createCtClass(classname, useCache);
| 492 if (clazz != null) {
| 493 // clazz.getName() != classname if classname is "[L<name>;".
| 494 if (useCache)
| 495 cacheCtClass(clazz.getName(), clazz, false);
| 496
| 497 return clazz;
| 498 }
As you can see, get0 adds the class to the cache by calling cacheCtClass after calling createCtClass. As I mentioned previously, cacheCtClass is overriden by ScopedClassPool, and adds the class to classes cache only if it is not generated dynamic. By adding the lockInCache call to the createCtClass method at BaseClassPool, you are generating a (6):
- memory leak if the class was generated dynamically. The class will be added to both softcache (by Scoped.cacheCtClass) and to classes (by BaseClassPool.cacheCtClass), thus avoiding it from being gc'ed.
- or a redundancy if the class was not generated dynamically, as the Scoped.cacheCtClass will already add the class to the classes cache.
Now, taking a look at NonDelegatingClassPool (subclass of BaseClassPool), I see that it overrides createCtClass, recreating the behaviour we had originally in the ClassPool. So, why is it a subclass of ScopedClassPool in the first place? (7)
(8) Plus, taking a look at DelegatingClassPool (subclass of BaseClassPool), I see it overrides the get0 method, but this method is never invoked by JBoss AOP if it uses getLocally. So, are the ClassPool.get/ClassPool.getCtClass being used from inside AS? If they are not, I don't see how are the new functionalities of the new ClassPools being used inside AS. If they are, then I see a point in overriding get0, but there is an inconsistency given that JBoss AOP uses the getLocally method, defined at AOPClass superclass.
So, Kabir, I'm obviously missing several links that will help me understand the class pools. So, please, comment on the numbered remarks and questions, and let me know what is it I'm missing here :)
*** I mean, when you do toClass, the class is already in the cache by now. Plus, what if somebody is doing a toClass with a class that is in the softcache? The lockInCache call will force the class being added as a hard reference to the "classes" cache, thus ruining the soften feature.
View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4238816#4238816
Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4238816
16 years, 9 months
[Design the new POJO MicroContainer] - Re: Parallel deployments
by kabir.khan@jboss.com
I have something working now, and will try to break it with more tests. Before describing what I am doing, here is an outline of what the controller does in pseudocode.
* install() and change() both obtain the writeLock, update the ctx's required state and end up in resolveContexts(boolean)
| void resolveContexts(boolean trace){
| boolean resolved = true;
| while (resolved){
| for (state : each controller state registered in controller){
| if (resolveContexts(state, state+1, trace){
| resolved = true;
| break;
| }
| }
| }
| }
|
| boolean resolveContexts(ControllerState fromState, ControllerState toState, boolean trace){
| contexts = all contexts in the fromState that have not yet reached the required state, with dependencies satisfied to move to toState
| for (ctx : contexts){
| attempt to record the ctx as "installing"
| if already installing (i.e. by another thread){
| continue;
| try{
| incrementState(ctx, trace);
| }finally{
| unrecord the ctx as installing
| }
| }
| }
|
| boolean incrementState(ControllerContext ctx, boolean trace){
| confirm state ctx is in and which we want to move it to
| try{
| unlockWriteLock();
| install(ctx, fromState, toState); //This invokes the context actions, i.e. the create(), start() methods
| move the context to the appropriate state in the controller
| invoke callbacks etc.
| }finally{
| lockWriteLock();
| if (install caused error){
| record ctx as failed in ctx and controller
| return false;
| }
| }
| return true;
| }
|
So basically, the call to resolveContexts(boolean) tries to increment the state of all contexts that can be moved on. If any contexts can be moved on, and succeed resolveContexts(boolean) starts again and goes on until no more contexts can be moved further.
I have added a wrapper around incrementState() which checks if the ctx is ASYNCHRONOUS. If not, it continues as before. If ASYNCHRONOUS, I record it as belonging to the executor, and then invoke a Runnable task using the executor.
The "installing" check in resolveContexts, has been upated to skip contexts belonging to the executor to avoid the asynchronous context to be picked up by the wrong thread.
The runnable task looks something like:
| class InstallControllerContextTask implements Runnable
| {
| ControllerContext context;
| boolean trace;
|
| public InstallControllerContextTask(ControllerContext context, boolean trace)
| {
| this.context = context;
| this.trace = trace;
| }
|
| public void run()
| {
| lockWrite();
| //associate context with my thread
| try
| {
| //Move the given context as far through the states towards context.requiredState() as possible
| boolean stateChanged = installMyContext();
|
| //The given context had its state changed, now install anybody who might have been dependent on it
| if (stateChanged)
| {
| resolveContexts(trace);
| }
| }
| finally
| {
| //remove association between my context and thread
| unlockWrite();
| }
| }
| }
|
I am currently testing that:
* N asynchronous contexts with no dependencies are getting installed in their own threads and that they can all be in the same lifecycle at the same time
* If an asynchronous context is installed with a dependency on a normal context, the asynchronous context executes in its own thread and stops when the unsatisfied dependency is needed. The dependency is then deployed, and once satisfied the controller picks up the asynchronous context again, and installs that further. Due to the wrapper around incrementState() the asynchronous context is installed in an executor thread again.
* If a normal context is installed with a dependency on an asynchronous context, the normal context is installed in the calling thread and stops when the unsatisfied dependency is needed. The dependency is then deployed, in an executor thread, and once installed the call to resolveContexts() at the end of the Runnable task installs the normal context. The install of the normal context after the wait for its dependencies happens in the executor thread.
I will add more tests on Monday.
View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4238813#4238813
Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4238813
16 years, 9 months
[Design of Messaging on JBoss (Messaging/JBoss)] - Server-side browser refactoring
by jmesnil
I'm working on https://jira.jboss.org/jira/browse/JBMESSAGING-1437 to have the browser
iterates on the queue rather than working on a snapshot copy of the queue.
First steps was to use LinkedBlockingDeque to provide an iterator on PriorityLinkedList which allows for
concurrent traversals and mutations
Next step is to refactor the server-side to have the browser works on the iterator
How does a browser differ from a simple consumer?
2/ the queue will deliver all messages to the browser but never removed the corresponding references
3/ the queue must not deliver messages to the browser through its distribution policy
Delivering messages to browsers and to regular consumers are two different code paths that can
not occur in the same loop
When a consumer is added to the queue, the queue could check if it is a browser or not.
If it is a browser, it is *not* added to the Distributor.
For now there is a single DeliverRunner for the queue (-> regular code path)
We could add other Runnable for every browser with their own iterators.
When the session is prompted to deliver, we must check if the consumer prompting for delivery is
a browser or not.
if it is a browser, a Runnable will be created with its own iterator and executed. It will deliver all ref to the browser *without removing the ref from the iterator*
if it is not a browser, regular code path applies
When a consumer is removed, we check if there is a Runnable associated to it and stop it if necessary
wdyt?
View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4238786#4238786
Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4238786
16 years, 9 months