/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.view.rest; import java.net.URI; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.ws.rs.core.UriBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.engine.resource.EngineResourceReference; import com.opengamma.id.UniqueIdentifiable; import com.opengamma.util.rest.FudgeRestClient; /** * Remote implementation of {@link EngineResourceReference}. * * @param <T> the type of resource */ public abstract class RemoteEngineResourceReference<T extends UniqueIdentifiable> implements EngineResourceReference<T> { private static final Logger s_logger = LoggerFactory.getLogger(RemoteEngineResourceReference.class); private final URI _baseUri; private final FudgeRestClient _client; private final ScheduledFuture<?> _scheduledHeartbeat; private final AtomicBoolean _isReleased = new AtomicBoolean(false); public RemoteEngineResourceReference(URI baseUri, ScheduledExecutorService scheduler) { this(baseUri, scheduler, FudgeRestClient.create()); } public RemoteEngineResourceReference(URI baseUri, ScheduledExecutorService scheduler, FudgeRestClient client) { _baseUri = baseUri; _client = client; _scheduledHeartbeat = scheduler.scheduleAtFixedRate(new HeartbeaterTask(client, _baseUri), DataEngineResourceManagerResource.REFERENCE_LEASE_MILLIS / 2, DataEngineResourceManagerResource.REFERENCE_LEASE_MILLIS / 2, TimeUnit.MILLISECONDS); } protected FudgeRestClient getClient() { return _client; } private void releaseImpl() { s_logger.debug("Releasing {}", this); try { getClient().accessFudge(_baseUri).delete(); s_logger.debug("Remote for {}", this); } finally { stopHeartbeating(); } } @Override protected void finalize() throws Throwable { if (_isReleased.getAndSet(true) == false) { s_logger.warn("{} has open reference at garbage collection time", this); releaseImpl(); } super.finalize(); } @Override public T get() { if (_isReleased.get()) { throw new IllegalStateException("The view cycle reference has been released"); } URI uri = UriBuilder.fromUri(_baseUri).path(DataEngineResourceReferenceResource.PATH_RESOURCE).build(); return getRemoteResource(uri); } protected abstract T getRemoteResource(URI baseUri); @Override public void release() { if (_isReleased.getAndSet(true)) { s_logger.warn("{} already released", this); return; } releaseImpl(); } /** * For testing */ public void stopHeartbeating() { s_logger.debug("Stopping heartbeating of {}", this); _scheduledHeartbeat.cancel(true); } private static final class HeartbeaterTask implements Runnable { private final FudgeRestClient _client; private final URI _baseUri; public HeartbeaterTask(final FudgeRestClient client, final URI baseUri) { _client = client; _baseUri = baseUri; } @Override public void run() { try { _client.accessFudge(_baseUri).post(); } catch (Exception e) { s_logger.warn("Failed to heartbeat view cycle reference", e); } } } @Override public String toString() { return getClass().getSimpleName() + "[" + _baseUri + "]"; } }