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

com.paremus.dosgi.net.client.BeginStreamingInvocation Maven / Gradle / Ivy

Go to download

An implementation of OSGi Remote Service Admin, enhanced to provide support for OSGi Promises and streaming using OSGi PushStreams.

The newest version!
/*-
 * #%L
 * com.paremus.dosgi.net
 * %%
 * Copyright (C) 2016 - 2019 Paremus Ltd
 * %%
 * Licensed under the Fair Source License, Version 0.9 (the "License");
 * 
 * See the NOTICE.txt file distributed with this work for additional 
 * information regarding copyright ownership. You may not use this file 
 * except in compliance with the License. For usage restrictions see the 
 * LICENSE.txt file distributed with this work
 * #L%
 */
package com.paremus.dosgi.net.client;

import static com.paremus.dosgi.net.client.ClientMessageType.STREAMING_RESPONSE_OPEN;
import static org.osgi.framework.ServiceException.REMOTE;

import java.util.UUID;
import java.util.function.Consumer;

import org.osgi.framework.ServiceException;

import com.paremus.dosgi.net.serialize.Serializer;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

public class BeginStreamingInvocation extends AbstractClientInvocationWithResult {
	
	private final EventExecutor executor; 
	private final Consumer dataConsumer;
	private final Consumer closeConsumer;

	private final Future close;

	
	public BeginStreamingInvocation(UUID serviceId, int callId, Serializer serializer, 
			EventExecutor executor, Consumer dataConsumer, 
			Consumer closeConsumer, Future close) {
		super(STREAMING_RESPONSE_OPEN, serviceId, callId, serializer);
		this.executor = executor;
		this.dataConsumer = dataConsumer;
		this.closeConsumer = closeConsumer;
		this.close = close;
	}

	public Consumer getDataConsumer() {
		return dataConsumer;
	}

	public Consumer getCloseConsumer() {
		return closeConsumer;
	}

	@Override
	public long getTimeout() {
		return 0;
	}

	@Override
	public void fail(Throwable t) {
		if(executor.inEventLoop()) {
			internalError(t);
		} else {
			try {
				executor.execute(() -> internalError(t));
			} catch (Exception e) {
				streamError(t, e);
			}
		}
	}

	private void internalError(Throwable t) {
		Exception toSend = t == null ? null : t instanceof Exception ? (Exception) t :
			new ServiceException("An incompatible Exception type was receieved", ServiceException.REMOTE, t);
		closeConsumer.accept(toSend);
	}

	private void streamError(Throwable t, Exception e) {
		e.addSuppressed(t);
		closeConsumer.accept(new ServiceException("An error occurred with the data stream", 
				ServiceException.REMOTE, e));
	}

	@Override
	public void fail(ByteBuf b) throws Exception {
		
		Throwable o;
		try {
			o = (Throwable) getSerializer().deserializeReturn(b);
		} catch (Exception e) {
			o = new ServiceException(
					"Failed to deserialize the remote exception value", ServiceException.REMOTE, e);
		}
		
		fail(o);
	}
	
	@Override
	public void data(ByteBuf b) throws Exception {
		try {
			internalData(getSerializer().deserializeReturn(b));
		} catch (Exception e) {
			internalError(new ServiceException(
					"Failed to deserialize the remote return value", ServiceException.REMOTE, e));
			return;
		}
	}

	private void internalData(Object o) {
		if(executor.inEventLoop()) {
			dataConsumer.accept(o);
		} else {
			try {
				executor.execute(() -> dataConsumer.accept(o));
			} catch (Exception e) {
				internalError(new ServiceException(
						"Failed to deserialize the remote return value", ServiceException.REMOTE, e));
			}
		}
	}

	@Override
	public void addCompletionListener(GenericFutureListener> listener) {
		close.addListener(listener);
	}

	@Override
	public void write(ByteBuf buffer, ChannelPromise promise) {
		writeHeader(buffer);
		writeLength(buffer);
		promise.addListener(f -> {
				if(!f.isSuccess()) {
					closeConsumer.accept(new ServiceException("Unable to open the remote stream " +
							getServiceId() + " due to a communications failure" , REMOTE, f.cause()));
				}
			});
	}
}