Skip to content

Commit

Permalink
Merge pull request #1609 from rsksmart/closable-ctx
Browse files Browse the repository at this point in the history
Make RskContext class Closeable and dispose its resources on close
  • Loading branch information
Vovchyk authored Oct 19, 2021
2 parents 8a3cdb6 + 52d4b79 commit 2cd0778
Show file tree
Hide file tree
Showing 28 changed files with 1,517 additions and 563 deletions.
77 changes: 72 additions & 5 deletions rskj-core/src/main/java/co/rsk/FullNodeRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,72 @@

import co.rsk.config.InternalService;
import co.rsk.config.RskSystemProperties;
import co.rsk.util.ExecState;
import co.rsk.util.SystemUtils;
import com.google.common.annotations.VisibleForTesting;
import org.ethereum.net.eth.EthVersion;
import org.ethereum.util.BuildInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class FullNodeRunner implements NodeRunner {
private static Logger logger = LoggerFactory.getLogger("fullnoderunner");

private static final Logger logger = LoggerFactory.getLogger("fullnoderunner");

private final NodeContext nodeContext;
private final List<InternalService> internalServices;
private final RskSystemProperties rskSystemProperties;
private final BuildInfo buildInfo;

private volatile ExecState state = ExecState.CREATED;

public FullNodeRunner(
NodeContext nodeContext,
List<InternalService> internalServices,
RskSystemProperties rskSystemProperties,
BuildInfo buildInfo) {
this.nodeContext = nodeContext;
this.internalServices = Collections.unmodifiableList(internalServices);
this.rskSystemProperties = rskSystemProperties;
this.buildInfo = buildInfo;
}

@VisibleForTesting
ExecState getState() {
return state;
}

/**
* This method starts internal services.
*
* If some internal service throws an exception while starting, then {@link InternalService#stop()} method will be
* called on each already started service.
*
* Note that this method is not idempotent, which means that calling this method on a running node will throw an exception.
*
* @throws IllegalStateException - if this node is already running.
* @throws IllegalStateException - if this node's context is closed.
*/
@Override
public void run() {
public synchronized void run() throws Exception {
if (state == ExecState.RUNNING) {
throw new IllegalStateException("The node is already running");
}

if (nodeContext.isClosed()) {
throw new IllegalStateException("Node Context is closed. Consider creating a brand new RskContext");
}

if (state == ExecState.FINISHED) {
throw new IllegalStateException("The node is stopped and cannot run again. Consider creating a brand new RskContext");
}

logger.info("Starting RSK");

logger.info(
Expand All @@ -61,8 +99,20 @@ public void run() {
SystemUtils.printSystemInfo(logger);
}

for (InternalService internalService : internalServices) {
internalService.start();
ArrayList<InternalService> startedServices = new ArrayList<>(internalServices.size());
InternalService curService = null;
try {
for (InternalService internalService : internalServices) {
curService = internalService;
internalService.start();
startedServices.add(internalService);
}
} catch (RuntimeException e) {
logger.error("{} failed to start. Stopping already started services...", Optional.ofNullable(curService).map(Object::getClass).map(Class::getSimpleName), e);

startedServices.forEach(InternalService::stop);

throw e;
}

if (logger.isInfoEnabled()) {
Expand All @@ -71,10 +121,27 @@ public void run() {
}

logger.info("done");

state = ExecState.RUNNING;
}

/**
* This method stops internal services in reverse order that they were started.
*
* It has no effect, if a node has not yet been started.
*
* Note that this method is idempotent, which means that calling this method more than once does not have any
* visible side effect.
*/
@Override
public void stop() {
public synchronized void stop() {
if (state != ExecState.RUNNING) {
logger.warn("The node is not running. Ignoring");
return;
}

state = ExecState.FINISHED;

logger.info("Shutting down RSK node");

for (int i = internalServices.size() - 1; i >= 0; i--) {
Expand Down
31 changes: 31 additions & 0 deletions rskj-core/src/main/java/co/rsk/NodeContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* This file is part of RskJ
* Copyright (C) 2021 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.rsk;

/**
* This interface extends {@link AutoCloseable} interface, so that implementations should properly release their resources
* when {@link AutoCloseable#close()} is triggered.
*
* Note that implementers of this interface are encouraged to make their close method idempotent.
*/
public interface NodeContext extends AutoCloseable {
/**
* Returns {@code true} if this context is already closed, otherwise - {@code false}.
*/
boolean isClosed();
}
Loading

0 comments on commit 2cd0778

Please sign in to comment.