@Override public IFile createFile(String path) { File file = new File(context.absPath(path)); String name = file.getName(); String parentPath = file.getParent(); try { VfsUtil.createDirectories(parentPath); } catch (IOException e) { Flog.error("Create directories error %s", e); context.errorMessage("The Floobits plugin was unable to create directories for file."); return null; } VirtualFile parent = LocalFileSystem.getInstance().findFileByPath(parentPath); if (parent == null) { Flog.error("Virtual file is null? %s", parentPath); return null; } VirtualFile newFile; try { newFile = parent.findOrCreateChildData(context, name); } catch (Throwable e) { Flog.error("Create file error %s", e); context.errorMessage( String.format("The Floobits plugin was unable to create a file: %s.", path)); return null; } return new FileImpl(newFile); }
@Override public void _on_data(String name, JsonObject obj) { Flog.info("on_data %s %s", obj, name); if (!name.equals("create_user")) { return; } FloorcJson floorcJson = FloorcJson.getFloorcJsonFromSettings(); HashMap<String, String> auth_host = floorcJson.auth.get(host); if (floorcJson.auth == null) { floorcJson.auth = new HashMap<String, HashMap<String, String>>(); } if (auth_host == null) { auth_host = new HashMap<String, String>(); floorcJson.auth.put(host, auth_host); } for (Map.Entry<String, JsonElement> thing : obj.entrySet()) { String key = thing.getKey(); if (key.equals("name")) { continue; } auth_host.put(key, thing.getValue().getAsString()); } PersistentJson p = PersistentJson.getInstance(); Settings.write(context, floorcJson); p.auto_generated_account = true; p.disable_account_creation = true; p.save(); context.statusMessage( String.format( "Successfully created new Floobits account with username %s. " + "You can now share a project or join a workspace.", auth_host.get("username"))); Flog.info("All setup"); context.shutdown(); }
public void write() { if (!isPopulated()) { Flog.warn("Unable to write %s because it's not populated yet.", path); return; } VirtualFile virtualFile = getVirtualFile(); if (virtualFile == null) { virtualFile = createFile(); if (virtualFile == null) { context.errorMessage("The Floobits plugin was unable to write to a file."); return; } } Document d = Buf.getDocumentForVirtualFile(virtualFile); if (d != null) { try { Listener.flooDisable(); d.setReadOnly(false); d.setText(buf); } finally { Listener.flooEnable(); } return; } Flog.warn("Tried to write to null document: %s", path); try { virtualFile.setBinaryContent(buf.getBytes()); } catch (IOException e) { Flog.warn(e); context.errorMessage("The Floobits plugin was unable to write to a file."); } }
public void write(Serializable obj) { // TODO: threading issue. lock channel if (channel == null) { Flog.error("not writing because no channel"); return; } String data = new Gson().toJson(obj); if (channel == null) { Flog.error("Lost connection? Not writing because no channel. Also, race condition!"); return; } channel.writeAndFlush(data + "\n"); }
public void read() { VirtualFile virtualFile = this.getVirtualFile(); if (virtualFile == null) { Flog.warn("Can't get virtual file to read from disk %s", this); return; } Document d = Buf.getDocumentForVirtualFile(virtualFile); if (d == null) { Flog.warn("Can't get document to read from disk %s", this); return; } this.buf = d.getText(); this.md5 = DigestUtils.md5Hex(this.buf); }
@Override public IFile createDirectories(String path) { VirtualFile directory = null; try { directory = VfsUtil.createDirectories(path); } catch (IOException e) { Flog.error(e); } if (directory == null) { Flog.warn("Failed to create directories %s %s", path); return null; } return new FileImpl(directory); }
public void send_patch(VirtualFile virtualFile) { Document d = Buf.getDocumentForVirtualFile(virtualFile); if (d == null) { Flog.warn("Can't get document to read from disk for sending patch %s", path); return; } send_patch(d.getText()); }
protected void reconnect() { if (retries <= 0) { Flog.log("Giving up!"); context.shutdown(); return; } delay = Math.min(10000, Math.round((float) 1.5 * delay)); Flog.log("Connection lost. Reconnecting in %sms", delay); context.setTimeout( delay, new Runnable() { @Override public void run() { Flog.log("Attempting to reconnect."); connect(); } }); }
public void shutdown() { retries = -1; if (channel != null) { try { channel.disconnect(); channel.close(); } catch (Throwable e) { Flog.error(e); } channel = null; } }
@Override public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { JsonObject obj = (JsonObject) new JsonParser().parse(msg); JsonElement name = obj.get("name"); if (name == null) { Flog.warn("No name for receive, ignoring"); return; } String requestName = name.getAsString(); retries = MAX_RETRIES; delay = INITIAL_RECONNECT_DELAY; handler.on_data(requestName, obj); }
protected void connect() { if (retries <= 0) { Flog.error("I give up connecting."); return; } retries -= 1; FlooUrl flooUrl = handler.getUrl(); final String host; final int port; if (flooUrl.host.equals(Constants.floobitsDomain) && retries % 4 == 0) { host = Constants.OUTBOUND_FILTER_PROXY_HOST; port = Constants.OUTBOUND_FILTER_PROXY_PORT; } else { host = flooUrl.host; port = flooUrl.port; } if (channel == null) { _connect(host, port); return; } try { channel .close() .addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { channel = null; _connect(host, port); } }); } catch (Throwable e) { Flog.error(e); reconnect(); } }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (retries < 0) { return; } if (cause instanceof ConnectException) { Flog.error("Failed to connect: " + cause.getMessage()); return; } if (cause instanceof IOException) { Flog.error(cause); return; } if (cause instanceof TooLongFrameException) { Flog.error(String.format("Took too long: %s", cause.getMessage())); return; } // if (cause instanceof SSLHandshakeException) { // Flog.error(String.format("SSL Handshake failed: %s", cause.getMessage())); // return; // } API.uploadCrash(handler, context, cause); }
protected void _connect(String host, int port) { Bootstrap b = new Bootstrap(); if (!context.addGroup(b)) { Flog.warn("no loopgroup, will not reconnect"); return; } b.channel(NioSocketChannel.class); b.option(ChannelOption.SO_KEEPALIVE, true); b.option(ChannelOption.TCP_NODELAY, true); b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15 * 1000); b.handler(new FlooChannelInitializer(this)); try { ChannelFuture connect = b.connect(host, port); channel = connect.channel(); } catch (RejectedExecutionException e) { context.errorMessage("Can not connect to floobits!"); context.shutdown(); } catch (Throwable e) { Flog.error(e); reconnect(); } }
public void send_patch(String current) { String before_md5; String textPatch; String after_md5; String previous = buf; before_md5 = md5; after_md5 = DigestUtils.md5Hex(current); LinkedList<diff_match_patch.Patch> patches = dmp.patch_make(previous, current); textPatch = dmp.patch_toText(patches); set(current, after_md5); if (before_md5.equals(after_md5)) { Flog.log("Not patching %s because no change.", path); return; } outbound.patch(textPatch, before_md5, this); }
@Override public IDoc getDocument(IFile virtualFile) { if (virtualFile == null) { return null; } Document document; try { document = FileDocumentManager.getInstance().getDocument(((FileImpl) virtualFile).virtualFile); } catch (RuntimeException e) { // We've seen an java.io.EOFException here before. Flog.error(e); return null; } if (document == null) { return null; } return new DocImpl(context, document); }
@Override public void on_connect() { Flog.error("Connected."); conn.setRetries(-1); conn.write(new NewAccount()); }
@Override public void channelUnregistered(final ChannelHandlerContext ctx) { Flog.log("Disconnected from %s", ctx.channel().remoteAddress()); reconnect(); }
@Override public void channelInactive(final ChannelHandlerContext ctx) throws Exception { Flog.log("Channel is now inactive."); }
@Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { Flog.log("%s", evt.toString()); }
public void patch(final FlooPatch res) { final TextBuf b = this; Flog.info("Got _on_patch"); String text; String md5FromDoc; final Document d; String oldText = buf; VirtualFile virtualFile = b.getVirtualFile(); if (virtualFile == null) { Flog.warn("VirtualFile is null, no idea what do do. Aborting everything %s", this); getBuf(); return; } d = Buf.getDocumentForVirtualFile(virtualFile); if (d == null) { Flog.warn("Document not found for %s", virtualFile); getBuf(); return; } String viewText; if (virtualFile.exists()) { viewText = d.getText(); if (viewText.equals(oldText)) { b.forced_patch = false; } else if (!b.forced_patch) { b.forced_patch = true; oldText = viewText; b.send_patch(viewText); Flog.warn("Sending force patch for %s. this is dangerous!", b.path); } } else { viewText = oldText; } b.cancelTimeout(); String md5Before = DigestUtils.md5Hex(viewText); if (!md5Before.equals(res.md5_before)) { Flog.warn("starting md5s don't match for %s. this is dangerous!", b.path); } List<diff_match_patch.Patch> patches = dmp.patch_fromText(res.patch); final Object[] results = dmp.patch_apply((LinkedList<diff_match_patch.Patch>) patches, oldText); final String patchedContents = (String) results[0]; final boolean[] patchesClean = (boolean[]) results[1]; final FlooPatchPosition[] positions = (FlooPatchPosition[]) results[2]; for (boolean clean : patchesClean) { if (!clean) { Flog.log("Patch not clean for %s. Sending get_buf and setting readonly.", d); getBuf(); return; } } // XXX: If patchedContents have carriage returns this will be a problem: String md5After = DigestUtils.md5Hex(patchedContents); if (!md5After.equals(res.md5_after)) { Flog.info("MD5 after mismatch (ours %s remote %s)", md5After, res.md5_after); } if (!d.isWritable()) { d.setReadOnly(false); } if (!ReadonlyStatusHandler.ensureDocumentWritable(context.project, d)) { Flog.info("Document: %s is not writable.", d); return; } final Editor[] editors = EditorFactory.getInstance().getEditors(d, context.project); final HashMap<ScrollingModel, Integer[]> original = new HashMap<ScrollingModel, Integer[]>(); for (Editor editor : editors) { if (editor.isDisposed()) { continue; } ScrollingModel scrollingModel = editor.getScrollingModel(); original.put( scrollingModel, new Integer[] { scrollingModel.getHorizontalScrollOffset(), scrollingModel.getVerticalScrollOffset() }); } for (FlooPatchPosition flooPatchPosition : positions) { int start = Math.max(0, flooPatchPosition.start); int end_ld = Math.max(start + flooPatchPosition.end, start); end_ld = Math.min(end_ld, d.getTextLength()); String contents = NEW_LINE.matcher(flooPatchPosition.text).replaceAll("\n"); Throwable e = null; try { Listener.flooDisable(); d.replaceString(start, end_ld, contents); } catch (Throwable exception) { e = exception; } finally { Listener.flooEnable(); } if (e != null) { Flog.warn(e); getBuf(); return; } } text = d.getText(); md5FromDoc = DigestUtils.md5Hex(text); if (!md5FromDoc.equals(res.md5_after)) { Flog.info("md5FromDoc mismatch (ours %s remote %s)", md5FromDoc, res.md5_after); b.setGetBufTimeout(); } for (Map.Entry<ScrollingModel, Integer[]> entry : original.entrySet()) { ScrollingModel model = entry.getKey(); Integer[] offsets = entry.getValue(); model.scrollHorizontally(offsets[0]); model.scrollVertically(offsets[1]); } b.set(text, md5FromDoc); Flog.log("Patched %s", res.path); }
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Flog.log("Connected to %s", ctx.channel().remoteAddress()); handler.on_connect(); }