/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.metamodel.util;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents a lazy loaded reference.
*
* @param <E>
*/
public abstract class LazyRef<E> implements Ref<E> {
private static final Logger logger = LoggerFactory.getLogger(LazyRef.class);
private final AtomicBoolean _fetched = new AtomicBoolean(false);
private volatile Throwable _error;
private E _object;
@Override
public final E get() {
if (!_fetched.get()) {
synchronized (_fetched) {
if (!_fetched.get()) {
try {
_object = fetch();
} catch (Throwable t) {
_error = t;
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
logger.warn("Failed to fetch value: " + this + ". Reporting error.", t);
} finally {
_fetched.set(true);
}
}
}
}
return _object;
}
protected abstract E fetch() throws Throwable;
/**
* Gets whether the lazy loaded reference has been loaded or not.
*
* @return a boolean indicating whether or not the reference has been loaded
* or not
*/
public boolean isFetched() {
return _fetched.get();
}
/**
* Gets any error that occurred while fetching the lazy loaded value.
*
* @return
*/
public Throwable getError() {
return _error;
}
/**
* Requests an asynchronous load of the lazy reference. If not already
* loaded, this will cause another thread to load the reference, typically
* to make it immediately available for later evaluation.
*
* @param errorAction
* an optional error action to invoke if an exception is thrown
* during loading of the reference.
*/
public void requestLoad(final Action<Throwable> errorAction) {
if (!isFetched()) {
ExecutorService executorService = SharedExecutorService.get();
executorService.submit(new Runnable() {
@Override
public void run() {
try {
get();
} catch (RuntimeException e) {
// do nothing, the exception will be handled via _error
// below
}
if (_error != null && errorAction != null) {
try {
errorAction.run(_error);
} catch (Exception e) {
logger.error("Error handling action failed!", e);
}
}
}
});
}
}
/**
* Requests an asynchronous load of the lazy reference. If not already
* loaded, this will cause another thread to load the reference, typically
* to make it immediately available for later evaluation.
*/
public void requestLoad() {
requestLoad(null);
}
}