All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.regnosys.rosetta.ide.server.RosettaRequestManager Maven / Gradle / Ivy

/*
 * Copyright 2024 REGnosys
 *
 * 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.regnosys.rosetta.ide.server;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.regnosys.rosetta.cache.IRequestScopedCache;

import org.eclipse.xtext.ide.server.concurrent.AbstractRequest;
import org.eclipse.xtext.ide.server.concurrent.RequestManager;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.service.OperationCanceledManager;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.xbase.lib.Functions.Function0;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Functions.Function2;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.*;

/**
 * A request manager that will time out after a configurable amount of seconds.
 * It can be configured through an environment variable.
 */
@Singleton
public class RosettaRequestManager extends RequestManager {
	public static String TIMEOUT_ENV_NAME = "ROSETTA_LANGUAGE_SERVER_REQUEST_TIMEOUT";
		
	private final Duration timeout;
	private final ScheduledExecutorService scheduler =
	        Executors.newScheduledThreadPool(
	                1,
	                new ThreadFactoryBuilder()
	                        .setDaemon(true)
	                        .setNameFormat("rosetta-language-server-request-timeout-%d")
	                        .build());
	private final IRequestScopedCache requestCache;

	/*
	 * TODO: contribute to Xtext
	 * The code that uses this list fixes a memory leak in the RequestManager and should be contributed
	 * back to the Xtext project then removed from here
	 */
	/* @ProtectedForTesting */
	protected List> removableRequestList = new CopyOnWriteArrayList<>();

	@Inject
	public RosettaRequestManager(ExecutorService parallel, OperationCanceledManager operationCanceledManager, IResourceServiceProvider.Registry serviceProviderRegistry) {
		super(parallel, operationCanceledManager);
		
		String rawTimeout = System.getenv(TIMEOUT_ENV_NAME);
		if (rawTimeout != null) {
			this.timeout = Duration.ofSeconds(Long.parseLong(rawTimeout));
		} else {
			this.timeout = null;
		}
		
		Object serviceProvider = serviceProviderRegistry.getExtensionToFactoryMap().get("rosetta");
		if (serviceProvider instanceof IResourceServiceProvider) {
			this.requestCache = ((IResourceServiceProvider) serviceProvider).get(IRequestScopedCache.class);
		} else {
			this.requestCache = null;
		}
	}

	@Override
	protected  CompletableFuture submit(AbstractRequest request) {
		addRequest(request);
		submitRequest(request);
		return request.get().whenComplete((result, error) -> removableRequestList.remove(request));
	}

	@Override
	protected void addRequest(AbstractRequest request) {
		removableRequestList.add(request);
	}

	@Override
	protected CompletableFuture cancel() {
		List> localRequests = removableRequestList;
		removableRequestList = new CopyOnWriteArrayList<>();
		CompletableFuture[] cfs = new CompletableFuture[localRequests.size()];
		for (int i = 0, max = localRequests.size(); i < max; i++) {
			AbstractRequest request = localRequests.get(i);
			request.cancel();
			cfs[i] = request.get();
		}
		CompletableFuture cancelAll = CompletableFuture.allOf(cfs);
		if (requestCache != null) {
			cancelAll = cancelAll.thenRun(() -> requestCache.clear());
		}
		return cancelAll;
	}
	
	@Override
	public  CompletableFuture runRead(Function1 cancellable) {
		return super.runRead((cancelIndicator) -> {		    
		    try {
		    	if (timeout == null) {
		    		return cancellable.apply(cancelIndicator);
		    	}
				return CompletableFuture.supplyAsync(
						() -> cancellable.apply(cancelIndicator),
						scheduler
					).get(timeout.toMillis(), TimeUnit.MILLISECONDS);
			} catch (Exception ex) {
				if (ex instanceof RuntimeException) {
					throw (RuntimeException)ex;
				}
				throw new RuntimeException(ex);
			}
		});
	}

	@Override
	public  CompletableFuture runWrite(
			Function0 nonCancellable,
			Function2 cancellable) {
		return super.runWrite(nonCancellable, (cancelIndicator, intermediate) -> {		    
		    try {
		    	if (timeout == null) {
		    		return cancellable.apply(cancelIndicator, intermediate);
		    	}
				return CompletableFuture.supplyAsync(
						() -> cancellable.apply(cancelIndicator, intermediate),
						scheduler
					).get(timeout.toMillis(), TimeUnit.MILLISECONDS);
			} catch (Exception ex) {
				if (ex instanceof RuntimeException) {
					throw (RuntimeException)ex;
				}
				throw new RuntimeException(ex);
			}
		});
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy