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

com.github.gkutiel.flip.web.Remote Maven / Gradle / Ivy

package com.github.gkutiel.flip.web;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;

import ch.lambdaj.Lambda;

import com.github.gkutiel.flip.AFlip;
import com.github.gkutiel.flip.AFlip.Hook;
import com.github.gkutiel.flip.utils.Utils;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;

public class Remote extends ServletHolder {
	public interface Doorman {
		boolean allow(Req req, String key);
		void bye(Req req, String key);
		void hi(Req req, String key, T remote);
	}

	@SuppressWarnings("unused")
	private static class Cb {
		final String name;
		final Object[] args;

		public Cb(final String name, final Object[] args) {
			this.name = name;
			this.args = args;
		}
	}

	private class FlipSocket implements WebSocket {
		private T remote;
		private Connection con;
		private String key;
		private Req req;
		private Doorman doorman;

		@SuppressWarnings("unchecked")
		public FlipSocket(final Req req, final String key, final Doorman doorman) {
			this.req = req;
			this.key = key;
			this.doorman = doorman;
			remote = (T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { c }, new InvocationHandler() {

				@Override
				public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
					if (method.getDeclaringClass().equals(c)) {
						con.sendMessage(GSON.toJson(new Cb(method.getName(), args)));
						return null;
					}
					return method.invoke(FlipSocket.this, args);
				}
			});
		}

		@Override
		public void onClose(final int closeCode, final String message) {
			remotes.remove(key, remote);
			doorman.bye(req, key);
		}

		@Override
		public void onOpen(final Connection con) {
			this.con = con;
			remotes.put(key, remote);
			doorman.hi(req, key, remote);
		}

	}

	private static final String _JS = Utils.Resource.load("_.js");

	static {
		AFlip.hook(new Hook() {

			@Override
			public void hook(final AFlip flip) {
				flip.bind(new ServletHolder(new HttpServlet() {
					@Override
					protected void doGet(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
						final ServletOutputStream out = res.getOutputStream();
						out.print(_JS);
						out.close();
					}

				}), "/_.js");
			}
		});
	}

	private static final Gson GSON = new Gson();

	public static  Remote bind(final Class remote, final String path) {
		final Remote r = new Remote<>(remote);
		AFlip.hook(new Hook() {

			@Override
			public void hook(final AFlip flip) {
				flip.bind(r, path);
			}
		});
		return r;
	}

	public static  Remote bind(final Class remote, final String path, final Doorman doorman) {
		final Remote r = new Remote<>(remote, doorman);
		AFlip.hook(new Hook() {

			@Override
			public void hook(final AFlip flip) {
				flip.bind(r, path);
			}
		});
		return r;
	}

	private final Multimap remotes = HashMultimap.create();

	private Class c;

	private Remote(final Class c) {
		this(c, new Doorman() {

			@Override
			public boolean allow(final Req req, final String key) {
				return true;
			}

			@Override
			public void bye(final Req req, final String key) {}

			@Override
			public void hi(final Req req, final String key, final T remote) {}
		});
	}

	private Remote(final Class remote, final Doorman doorman) {
		this.c = remote;
		setServlet(new WebSocketServlet() {

			@Override
			public WebSocket doWebSocketConnect(final HttpServletRequest req, final String key) {
				final Req r = HttpUtils.req(req);
				if (doorman.allow(r, key)) return new FlipSocket(r, key, doorman);
				return null;
			}
		});
	}

	public T at() {
		return at(".");
	}

	public T at(final String key) {
		return Lambda.forEach(remotes.get(key), c);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy