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

org.gwtproject.rpc.websockets.shared.impl.AbstractEndpointImpl Maven / Gradle / Ivy

There is a newer version: 1.0-alpha-8
Show newest version
/*
 * #%L
 * gwt-websockets-api
 * %%
 * Copyright (C) 2011 - 2018 Vertispan LLC
 * %%
 * 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.
 * #L%
 */
package org.gwtproject.rpc.websockets.shared.impl;

import com.google.gwt.user.client.rpc.SerializationException;
import org.gwtproject.rpc.serialization.api.SerializationStreamReader;
import org.gwtproject.rpc.serialization.api.SerializationStreamWriter;
import org.gwtproject.rpc.serialization.api.TypeSerializer;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Base class for use in implementing any kind of endpoint, simplifying the code required to have in
 * generated concrete implementations.
 */
public abstract class AbstractEndpointImpl {
	/**
	 * Interface describing the constructor of any concrete subclass for use in wrapping these instances.
	 */
	@FunctionalInterface
	public interface EndpointImplConstructor {
		 E create(
				Function writerFactory,
				Consumer send,
				BiConsumer, TypeSerializer> onMessage
		);
	}

	private final Function writerFactory;
	private final Consumer send;

	private final TypeSerializer serializer;

	// count starts at 1, leaving zero for remote methods
	private int nextCallbackId = 1;
	private Map> callbacks = new HashMap<>();


	protected  AbstractEndpointImpl(
			Function writerFactory,
			Consumer send,
			TypeSerializer serializer,
			BiConsumer, TypeSerializer> onMessage) {
		this.writerFactory = (Function) writerFactory;
		this.send = (Consumer) send;
		this.serializer = serializer;
		onMessage.accept(this::__onMessage, serializer);
	}

	/**
	 * Push any errors to the local (non-generated) endpoint implementation.
	 */
	protected abstract void __onError(Throwable ex);

	/**
	 * Invoke the remote invoked method on the local (non-generated) endpoint implementation.
	 */
	protected abstract void __invoke(int recipient, SerializationStreamReader reader) throws SerializationException;

	public void __onMessage(SerializationStreamReader reader) {
		try {
			int recipient = reader.readInt();
			if (recipient >= 0) {
				__invoke(recipient, reader);
			} else {
				ReadingCallback callback = callbacks.get(-recipient);
				callback.handle(reader);
			}
		} catch (SerializationException ex) {
			__onError(ex);
		}
	}

	private SerializationStreamWriter __startCall() {
		return writerFactory.apply(serializer);
	}
	private void __endCall(SerializationStreamWriter writer) {
		send.accept(writer);
	}

	/**
	 * Easy lambda to let generated classes run code which might throw within __send()
	 */
	protected interface Send {
		void send(SerializationStreamWriter writer) throws SerializationException;
	}

	protected void __send(int recipient, Send s) {
		SerializationStreamWriter writer = __startCall();
		try {
			writer.writeInt(recipient);
			s.send(writer);
			__endCall(writer);
		} catch (SerializationException e) {
			__onError(e);
			throw new RuntimeException(e);
		}
	}
	protected void __send(int recipient, Send s, ReadingCallback callback) {
		SerializationStreamWriter writer = __startCall();
		try {
			writer.writeInt(recipient);

			// add the callbackId to the message to send so the remote end knows it will need a callback
			// object when handling the rest of the body
			int callbackId = nextCallbackId++;
			writer.writeInt(callbackId);
			s.send(writer);

			__endCall(writer);
			//only after we've successfully sent, register the callback
			callbacks.put(callbackId, callback);
		} catch (SerializationException e) {
			//TODO report? can't actually pass to Callback.onFailure, since it might expect something else
			throw new RuntimeException(e);
		}
	}

	protected static abstract class ReadingCallback {
		public final void handle(SerializationStreamReader reader) throws SerializationException {
			boolean success = reader.readBoolean();
			if (success) {
				success(reader);
			} else {
				failure(reader);
			}
		}
		public abstract void success(SerializationStreamReader reader) throws org.gwtproject.rpc.serialization.api.SerializationException;
		public abstract void failure(SerializationStreamReader reader) throws org.gwtproject.rpc.serialization.api.SerializationException;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy