/***************************************************************************
* Copyright (c) 2013 VMware, Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package com.vmware.vhadoop.util;
/* ThreadLocalCompoundStatus is a mechanism for stashing a Root CompoundStatus object in a thread
* The idea is that any class wanting to add status to a thread's execution can do so if it has
* a reference to the ThreadLocalCompoundStatus. It can simply call get().addStatus(x)
* The significant advantage to this is that CompoundStatuses do not have to be passed around through signatures
* and for re-entrant single instance classes, there is a guarantee that the right status will be updated
* As further protection against memory leaks, the capability has been added for the ThreadLocalCompoundStatus
* to just return a dummy object unless the current thread explicitly called initialize(). That calling thread
* is then responsible for explicitly calling remove() once the call stack has returned. The other advantage
* of this is that code using the CompoundStatus does not need to be full of null checks. It will either get
* a useless dummy or something useful.
*/
public class ThreadLocalCompoundStatus extends ThreadLocal<CompoundStatus> {
class InitializedState {
boolean _initialized;
}
ThreadLocal<InitializedState> _initializedState = new ThreadLocal<InitializedState>() {
@Override
protected InitializedState initialValue() {
return new InitializedState();
};
};
/* Ensures that only a single caller can initialize the ThreadLocal which is the same caller that removes it
* This means that any code can call get() and if it's running in a thread that hasn't initialized a CompoundStatus
* the code gets a dummy CompoundStatus to avoid a NPE, but a memory leak cannot occur */
public CompoundStatus initialize() {
InitializedState threadLocalIS = _initializedState.get();
threadLocalIS._initialized = true;
return super.get(); /* Returns the result of initialValue() and then that same object, until remove() is called */
}
@Override
protected CompoundStatus initialValue() {
/* Return the root CompoundStatus for a thread operation. Any number of compound statuses can be added to this one */
return new CompoundStatus("ROOT");
}
@Override
public CompoundStatus get() {
/* If this thread has not explicitly initialized its TLCS, it should not be allowed to create a ThreadLocal
* Instead it gets a useless dummy */
InitializedState threadLocalIS = _initializedState.get();
if (threadLocalIS._initialized == false) {
_initializedState.remove();
return new CompoundStatus("UNINITIALIZED DUMMY");
}
return super.get();
}
@Override
public void remove() {
/* Any thread that has called initialize(), must follow it up with a call to remove() once the operation is completed */
super.remove();
_initializedState.remove();
}
}