| /*=============================================================================# |
| # Copyright (c) 2009, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.rj.servi; |
| |
| import java.rmi.Remote; |
| import java.rmi.RemoteException; |
| import java.rmi.server.Unreferenced; |
| |
| import org.apache.commons.pool2.PooledObject; |
| import org.apache.commons.pool2.impl.DefaultPooledObject; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| import org.eclipse.statet.rj.RjException; |
| import org.eclipse.statet.rj.servi.pool.PoolNodeObject; |
| import org.eclipse.statet.rj.servi.pool.PoolNodeState; |
| |
| |
| @NonNullByDefault |
| public class APool2NodeHandler extends NodeHandler implements PoolNodeObject, |
| RServiImpl.PoolRef, Unreferenced { |
| |
| |
| static final long safeNanos(final long nanos) { |
| return (nanos != 0) ? nanos : 1; |
| } |
| |
| static final long evictNanos(final long timeoutMillis) { |
| long nanos= System.nanoTime(); |
| if (timeoutMillis > 0) { |
| nanos+= timeoutMillis * 1_000_000L; |
| } |
| return safeNanos(nanos); |
| } |
| |
| |
| private final APool2 pool; |
| private final DefaultPooledObject<APool2NodeHandler> p; |
| private volatile long evict; |
| |
| final Stats.NodeEntry stats= new Stats.NodeEntry(); |
| |
| @Nullable Remote thisRemote; |
| |
| private volatile long accessId; |
| |
| |
| public APool2NodeHandler(final APool2 pool) { |
| this.pool= pool; |
| this.p= new DefaultPooledObject<>(this); |
| } |
| |
| |
| @Override |
| public NodeHandler getNodeHandler() { |
| return this; |
| } |
| |
| PooledObject<APool2NodeHandler> getPooledObject() { |
| return this.p; |
| } |
| |
| @Override |
| public long getCreationTime() { |
| return this.p.getCreateTime(); |
| } |
| |
| @Override |
| public long getAllocatedCount() { |
| return this.p.getBorrowedCount(); |
| } |
| |
| @Override |
| public long getAllocatedDuration() { |
| return this.p.getActiveTimeMillis(); |
| } |
| |
| @Override |
| public PoolNodeState getState() { |
| if (this.node == null) { |
| switch (this.p.getState()) { |
| case IDLE: |
| return PoolNodeState.INITIALIZING; |
| case INVALID: |
| return PoolNodeState.DISPOSED; |
| default: |
| break; |
| } |
| } |
| |
| switch(this.p.getState()) { |
| case IDLE: |
| return PoolNodeState.IDLING; |
| case ALLOCATED: |
| return PoolNodeState.ALLOCATED; |
| default: |
| return PoolNodeState.CHECKING; |
| } |
| } |
| |
| @Override |
| public long getStateTime() { |
| switch (getState()) { |
| case INITIALIZING: |
| return getStartTime(); |
| case ALLOCATED: |
| return this.p.getLastBorrowTime(); |
| case CHECKING: |
| case IDLING: |
| return this.p.getLastReturnTime(); |
| case DISPOSED: |
| return getShutdownTime(); |
| default: |
| return -1; |
| } |
| } |
| |
| |
| @Override |
| void bindClient(final String name, final String host) throws RemoteException { |
| super.bindClient(name, host); |
| synchronized (this) { |
| this.accessId= this.p.getBorrowedCount(); |
| } |
| } |
| |
| void invalidateClient() { |
| this.accessId= -1L; |
| setClientLabel(null); |
| } |
| |
| @Override |
| void unbindClient() throws RemoteException { |
| synchronized (this) { |
| invalidateClient(); |
| } |
| super.unbindClient(); |
| } |
| |
| @Override |
| public void evict(final long timeout) { |
| doEvict(evictNanos(timeout), timeout == 0); |
| } |
| |
| void doEvict(final long nanos, final boolean direct) { |
| synchronized (this.p) { |
| if (this.evict == 0 || this.evict > nanos) { |
| this.evict= safeNanos(nanos); |
| } |
| } |
| |
| if (direct) { |
| try { |
| this.pool.invalidateObject(this); |
| } |
| catch (final Exception e) {} |
| } |
| } |
| |
| boolean isEvictRequested(final long nanos) { |
| final long evict= this.evict; |
| return (evict != 0 |
| && (nanos == 0 || nanos >= evict) ); |
| } |
| |
| long getAccessId() { |
| final long accessId= this.accessId; |
| if (accessId == -1L) { |
| throw new IllegalAccessError(); |
| } |
| return accessId; |
| } |
| |
| @Override |
| public void returnObject(final long accessId) throws RjException { |
| try { |
| synchronized (this) { |
| if (this.accessId != accessId) { |
| throw new IllegalStateException("Access id no longer valid."); |
| } |
| invalidateClient(); |
| } |
| this.pool.returnObject(this); |
| } |
| catch (final Exception e) { |
| Utils.logError("An unexpected error occurred when returning RServi instance.", e); |
| throw new RjException("An unexpected error occurred when closing RServi instance. See server log for detail."); |
| } |
| } |
| |
| @Override |
| public void unreferenced() { |
| final PoolNodeState state= getState(); |
| synchronized (this) { |
| if (state != PoolNodeState.ALLOCATED || this.accessId == -1L) { |
| return; |
| } |
| invalidateClient(); |
| } |
| Utils.logInfo("The RServi instance is lent and unreferenced. It will be returned now."); |
| try { |
| this.pool.returnObject(this); |
| } |
| catch (final Exception e) { |
| Utils.logError("An unexpected error occurred when returning RServi instance.", e); |
| } |
| } |
| |
| } |