package com.framsticks.hosting; import static com.framsticks.util.lang.Strings.assureNotEmpty; import com.framsticks.communication.*; import com.framsticks.communication.queries.ApplicationRequest; import com.framsticks.communication.queries.CallRequest; import com.framsticks.communication.queries.GetRequest; import com.framsticks.communication.queries.InfoRequest; import com.framsticks.communication.queries.RegisterRequest; import com.framsticks.communication.queries.SetRequest; import com.framsticks.core.LocalTree; import com.framsticks.core.Tree; import com.framsticks.core.Path; import com.framsticks.params.*; import com.framsticks.params.types.EventParam; import com.framsticks.params.types.ProcedureParam; import com.framsticks.parsers.Savers; import com.framsticks.util.FramsticksException; import com.framsticks.util.Misc; import com.framsticks.util.dispatching.AbstractJoinable; import com.framsticks.util.dispatching.Dispatching; import com.framsticks.util.dispatching.ExceptionResultHandler; import com.framsticks.util.dispatching.FutureHandler; import com.framsticks.util.dispatching.Joinable; import com.framsticks.util.dispatching.JoinableParent; import com.framsticks.util.dispatching.JoinableState; import com.framsticks.util.lang.FlagsUtil; import com.framsticks.util.lang.Strings; import static com.framsticks.core.TreeOperations.*; import static com.framsticks.params.AccessOperations.*; import java.net.Socket; /** * @author Piotr Sniegowski */ public class ClientAtServer extends AbstractJoinable implements RequestHandler, JoinableParent, ExceptionResultHandler { protected final Server server; protected final Tree contentTree; protected final Object treeRootObject; protected final ServerSideManagedConnection connection; protected final Cli cliObject; protected final LocalTree rootTree; protected final FramsClass rootFramsClass; protected final Object root; protected final String contentPrefix; public ClientAtServer(Server server, Socket socket) { this.server = server; this.contentTree = server.hosted; this.connection = new ServerSideManagedConnection(socket, this); treeRootObject = contentTree.getAssignedRoot().getObject(); Misc.throwIfNull(treeRootObject); cliObject = new Cli(this); rootTree = new LocalTree(); rootTree.setName(server.getName() + " root tree"); // rootTree.setDispatcher(new AtOnceDispatcher()); rootTree.setDispatcher(server.getHosted().getDispatcher()); assert rootTree.getDispatcher() != null; final FramsClass framsClass = bindAccess(contentTree.getAssignedRoot()).getFramsClass(); final String id = Strings.uncapitalize(framsClass.getName()); contentPrefix = "/" + id; final String rootFramsClassId = id + "Root"; rootFramsClass = FramsClass.build() .idAndName(rootFramsClassId) .param(Param.build().id(id).name(framsClass.getName()).type("o " + framsClass.getId())) .param(Param.build().id("cli").name("CLI").type("o Cli")) .param(Param.build().id("system").name("Operating system").type("s").flags(ParamFlags.READONLY)) .param(Param.build().id("user").name("User name").type("s").flags(ParamFlags.READONLY)) .finish(); // rootTree.putInfoIntoCache(rootFramsClass); rootTree.getRegistry().putFramsClass(rootFramsClass); rootTree.getRegistry().registerAndBuild(Cli.class); rootTree.getRegistry().registerAndBuild(CliEvent.class); Access access = new PropertiesAccess(rootFramsClass); root = createAccessee(rootTree, access); access.select(root); access.set(id, treeRootObject); access.set("cli", cliObject); access.set("system", System.getProperties().getProperty("os.name") + " " + System.getProperties().getProperty("os.version") + " " + System.getProperties().getProperty("os.arch")); access.set("user", System.getProperties().getProperty("user.name")); rootTree.assignRootParam(access.buildParam(new ParamBuilder()).name(rootFramsClassId).finish(CompositeParam.class)); rootTree.assignRootObject(root); } @Override public String getName() { return connection + " to " + server; } @Override public void handle(final ApplicationRequest request, final ServerSideResponseFuture responseCallback) { assureNotEmpty(request.getPath()); if (request.getPath().startsWith(contentPrefix)) { String p = request.getPath().substring(contentPrefix.length()); request.path(p.equals("") ? "/" : p); handleInTree(contentTree, request, responseCallback, contentPrefix); return; } handleInTree(rootTree, request, responseCallback, ""); } public static File printToFile(String path, Access access) { ListSink sink = new ListSink(); save(access, sink); return new File(path, new ListSource(sink.getOut())); } protected void handleInTree(final Tree tree, final ApplicationRequest request, final ServerSideResponseFuture responseCallback, final String usedPrefix) { tryGet(tree, request.getActualPath(), new FutureHandler(responseCallback) { @Override protected void result(final Path path) { if (!path.getTextual().equals(request.getActualPath())) { throw new FramsticksException().msg("invalid path").arg("path", request.getActualPath()); } // final Access access = tree.prepareAccess(path); final Access access = bindAccess(path); if (request instanceof GetRequest) { Object result = path.getTopObject(); if (result != access.getSelected()) { throw new FramsticksException().msg("mismatch objects during fetch").arg("path", path); } responseCallback.pass(new Response(true, "", File.single(printToFile(path.getTextual(), access)))); return; } if (request instanceof SetRequest) { SetRequest setRequest = (SetRequest) request; tree.set(path, access.getFramsClass().getParamEntry(setRequest.getField(), PrimitiveParam.class), setRequest.getValue(), new FutureHandler(responseCallback) { @Override protected void result(Integer flag) { responseCallback.pass(new Response(true, FlagsUtil.write(SetStateFlags.class, flag, null), null)); } }); return; } if (request instanceof CallRequest) { final CallRequest callRequest = (CallRequest) request; tree.call(path, access.getFramsClass().getParamEntry(callRequest.getProcedure(), ProcedureParam.class), callRequest.getArguments().toArray(), Object.class, new FutureHandler(responseCallback) { @Override protected void result(Object result) { ListSink sink = new ListSink(); sink.print("Result:").breakLine(); sink.print("value:"); sink.print("["); if (result != null) { sink.print(result); } sink.print("]"); responseCallback.pass(new Response(true, "", File.single(new File("", new ListSource(sink.getOut()))))); } }); return; } if (request instanceof InfoRequest) { FramsClass framsClass = getInfo(path); if (framsClass == null) { throw new FramsticksException().msg("info should be available"); } responseCallback.pass(new Response(true, null, File.single(new File(path.getTextual(), new ListSource(Savers.saveFramsClass(new ListSink(), framsClass).getOut()))))); return; } if (request instanceof RegisterRequest) { RegisterRequest register = (RegisterRequest) request; cliObject.addListener(path, access.getFramsClass().getParamEntry(register.getEventName(), EventParam.class), usedPrefix, responseCallback); return; } throw new FramsticksException().msg("invalid request type: " + request.getCommand()); } }); } @Override protected void joinableStart() { Dispatching.use(connection, this); Dispatching.use(rootTree, this); } @Override protected void joinableInterrupt() { Dispatching.drop(rootTree, this); Dispatching.drop(connection, this); } @Override protected void joinableFinish() { } @Override protected void joinableJoin() throws InterruptedException { Dispatching.join(connection); Dispatching.join(rootTree); } @Override public void childChangedState(Joinable joinable, JoinableState state) { proceedToState(state); } @Override public void handle(FramsticksException exception) { contentTree.handle(exception); } /** * @return the tree */ public Tree getTree() { return contentTree; } }