/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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.asakusafw.runtime.core.util;
import java.io.IOException;
/**
* An abstract super class of shared object holder for operator classes.
* Clients can inherit this class and implement
* {@link #initialValue()} to prepare shared value.
* <p>
* Example:
* </p>
<pre><code>
// operator class
public abstract class SomeOperator {
// shared value container
static final Shared<Hoge> SHARED = new Shared<Hoge>() {
@Override protected Hoge initialValue() throws IOException {
// initializes shared value
Hoge result = ...;
return result;
}
}
// operator
@Update
public void some(Foo foo) {
// obtains shared value
Hoge hoge = SHARED.get();
...
}
}
</code></pre>
* @param <T> the value type
* @since 0.7.3
*/
public abstract class Shared<T> {
private volatile T shared;
private boolean initialized;
/**
* Returns the shared value.
* If this container is not {@link #isInitialzed() initialized} yet,
* this will prepare an initial value and returns it.
* @return the shared value
* @throws Shared.InitializationException if failed to initialize the shared value
*/
public final T get() {
T cached = shared;
if (cached != null) {
return cached;
}
synchronized (this) {
if (initialized == false) {
try {
set(initialValue());
} catch (IOException e) {
throw new InitializationException("failed to initialize shared value", e);
}
}
return shared;
}
}
/**
* Discards the current shared value.
* After this is invoked, {@link #isInitialzed()} will return {@code false} until
* this container is re-initialized.
* If this does not hold a shared value, this does nothing.
*/
public final void remove() {
synchronized (this) {
this.shared = null;
this.initialized = false;
}
}
/**
* Manually sets a shared value.
* The {@link #get()} method will return the value until {@link #remove()} is invoked.
* This forcibly replaces the shared value even if this container is already initialized.
* @param value the shared value
* @see #isInitialzed()
*/
public final void set(T value) {
synchronized (this) {
this.shared = value;
this.initialized = true;
}
}
/**
* Returns whether this container is initialized or not.
* @return {@code true} if this is initialized, or {@code false} otherwise
*/
public final boolean isInitialzed() {
synchronized (this) {
return this.initialized;
}
}
/**
* Returns the initial shared value.
* @return the shared value
* @throws IOException if initialization was failed
*/
protected abstract T initialValue() throws IOException;
/**
* Represents an exception occurred while {@link Shared#initialValue() initializing} shared values.
* @since 0.7.3
*/
public static final class InitializationException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
* @param message the exception message
* @param cause the original cause
*/
public InitializationException(String message, Throwable cause) {
super(message, cause);
}
}
}