org.eclipse.che.ide.examples.CheFluxLiveEditExtension.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.che.ide.examples.CheFluxLiveEditExtension.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2016 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.ide.examples;

import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.user.client.Timer;
import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.machine.gwt.client.MachineManager;
import org.eclipse.che.api.machine.gwt.client.MachineServiceClient;
import org.eclipse.che.api.machine.shared.dto.MachineProcessDto;
import org.eclipse.che.api.machine.shared.dto.event.MachineProcessEvent;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.extension.Extension;
import org.eclipse.che.ide.extension.machine.client.command.CommandManager;
import org.eclipse.che.ide.extension.machine.client.command.valueproviders.CommandPropertyValueProvider;
import org.eclipse.che.ide.extension.machine.client.command.valueproviders.CommandPropertyValueProviderRegistry;
import org.eclipse.che.ide.jseditor.client.document.Document;
import org.eclipse.che.ide.jseditor.client.document.DocumentHandle;
import org.eclipse.che.ide.jseditor.client.events.DocumentChangeEvent;
import org.eclipse.che.ide.jseditor.client.events.DocumentChangeHandler;
import org.eclipse.che.ide.jseditor.client.events.DocumentReadyEvent;
import org.eclipse.che.ide.jseditor.client.events.DocumentReadyHandler;
import org.eclipse.che.ide.project.event.ProjectExplorerLoadedEvent;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.eclipse.che.ide.socketio.Consumer;
import org.eclipse.che.ide.socketio.SocketIOOverlay;
import org.eclipse.che.ide.socketio.SocketIOResources;
import org.eclipse.che.ide.socketio.SocketOverlay;
import org.eclipse.che.ide.util.loging.Log;
import org.eclipse.che.ide.websocket.MessageBus;
import org.eclipse.che.ide.websocket.MessageBusProvider;
import org.eclipse.che.ide.websocket.WebSocketException;
import org.eclipse.che.ide.websocket.events.MessageHandler;
import org.eclipse.che.ide.websocket.rest.SubscriptionHandler;
import org.eclipse.che.ide.websocket.rest.Unmarshallable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Extension(title = "Che Flux extension", version = "1.0.0")
public class CheFluxLiveEditExtension {

    private Map<String, Document> liveDocuments = new HashMap<String, Document>();

    private SocketOverlay socket;

    private boolean isUpdatingModel = false;

    private MessageBus messageBus;

    private CommandManager commandManager;

    private CommandPropertyValueProviderRegistry commandPropertyValueProviderRegistry;

    @Inject
    public CheFluxLiveEditExtension(final MessageBusProvider messageBusProvider, final EventBus eventBus,
            final MachineManager machineManager, final MachineServiceClient machineServiceClient,
            final DtoUnmarshallerFactory dtoUnmarshallerFactory, final AppContext appContext,
            final CommandManager commandManager,
            final CommandPropertyValueProviderRegistry commandPropertyValueProviderRegistry) {
        this.commandManager = commandManager;
        this.messageBus = messageBusProvider.getMessageBus();
        this.commandPropertyValueProviderRegistry = commandPropertyValueProviderRegistry;

        injectSocketIO();

        eventBus.addHandler(ProjectExplorerLoadedEvent.getType(),
                new ProjectExplorerLoadedEvent.ProjectExplorerLoadedHandler() {

                    @Override
                    public void onProjectsLoaded(ProjectExplorerLoadedEvent event) {

                        String machineId = appContext.getDevMachineId();
                        Promise<List<MachineProcessDto>> processesPromise = machineServiceClient
                                .getProcesses(machineId);
                        processesPromise.then(new Operation<List<MachineProcessDto>>() {
                            @Override
                            public void apply(final List<MachineProcessDto> descriptors) throws OperationException {

                                if (descriptors.isEmpty()) {
                                    return;
                                }
                                Log.info(getClass(), "machine processes");
                                for (MachineProcessDto machineProcessDto : descriptors) {
                                    Log.info(getClass(), " - " + machineProcessDto);
                                    if (connectIfFluxMicroservice(machineProcessDto)) {
                                        break;
                                    }
                                }
                            }

                        });

                    }
                });

        String machineId = appContext.getDevMachineId();
        final Unmarshallable<MachineProcessEvent> unmarshaller = dtoUnmarshallerFactory
                .newWSUnmarshaller(MachineProcessEvent.class);
        final String processStateChannel = "machine:process:" + machineId;
        final MessageHandler handler = new SubscriptionHandler<MachineProcessEvent>(unmarshaller) {
            @Override
            protected void onMessageReceived(MachineProcessEvent result) {
                Log.info(getClass(), "process event" + result);

                if (MachineProcessEvent.EventType.STARTED.equals(result.getEventType())) {
                    final int processId = result.getProcessId();
                    machineServiceClient.getProcesses(appContext.getDevMachineId())
                            .then(new Operation<List<MachineProcessDto>>() {
                                @Override
                                public void apply(List<MachineProcessDto> descriptors) throws OperationException {
                                    if (descriptors.isEmpty()) {
                                        return;
                                    }

                                    for (final MachineProcessDto machineProcessDto : descriptors) {
                                        if (machineProcessDto.getPid() == processId) {
                                            Log.info(getClass(), "Started Process" + machineProcessDto);
                                            new Timer() {
                                                @Override
                                                public void run() {
                                                    if (connectIfFluxMicroservice(machineProcessDto)) {
                                                        // break;
                                                    }
                                                }
                                            }.schedule(8000);
                                            return;
                                        }
                                    }
                                }

                            });
                }
            }

            @Override
            protected void onErrorReceived(Throwable exception) {
                Log.error(getClass(), exception);
            }
        };
        wsSubscribe(processStateChannel, handler);

        eventBus.addHandler(DocumentReadyEvent.TYPE, new DocumentReadyHandler() {
            @Override
            public void onDocumentReady(DocumentReadyEvent event) {
                liveDocuments.put(event.getDocument().getFile().getPath(), event.getDocument());

                final DocumentHandle documentHandle = event.getDocument().getDocumentHandle();

                documentHandle.getDocEventBus().addHandler(DocumentChangeEvent.TYPE, new DocumentChangeHandler() {
                    @Override
                    public void onDocumentChange(DocumentChangeEvent event) {
                        if (socket != null) {
                            // full path start with /, so substring
                            String fullPath = event.getDocument().getDocument().getFile().getPath().substring(1);
                            String project = fullPath.substring(0, fullPath.indexOf('/'));
                            String resource = fullPath.substring(fullPath.indexOf('/') + 1);
                            String text = JsonUtils.escapeValue(event.getText());
                            String json = "{"//
                                    + "\"username\":\"USER\","//
                                    + "\"project\":\"" + project + "\","//
                                    + "\"resource\":\"" + resource + "\"," //
                                    + "\"offset\":" + event.getOffset() + "," //
                                    + "\"removedCharCount\":" + event.getRemoveCharCount() + "," //
                                    + "\"addedCharacters\": " + text //
                                    + "}";
                            if (isUpdatingModel) {
                                return;
                            }
                            socket.emit("liveResourceChanged", JsonUtils.unsafeEval(json));
                        }
                    }
                });
            }
        });

    }

    private void injectSocketIO() {
        SocketIOResources ioresources = GWT.create(SocketIOResources.class);
        ScriptInjector.fromString(ioresources.socketIo().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject();
    }

    private boolean connectIfFluxMicroservice(MachineProcessDto descriptor) {
        if (descriptor == null) {
            return false;
        }
        if ("flux".equals(descriptor.getName())) {
            String urlToSubstitute = "http://${server.port.3000}";
            if (commandPropertyValueProviderRegistry == null) {
                return false;
            }
            substituteAndConnect(urlToSubstitute);
            return true;
        }

        return false;

    }

    int retryConnectToFlux = 5;

    protected void connectToFlux(final String url) {

        final SocketIOOverlay io = getSocketIO();
        Log.info(getClass(), "connecting to " + url);

        socket = io.connect(url);
        socket.on("error", new Runnable() {

            @Override
            public void run() {
                Log.info(getClass(), "error connecting to " + url);
            }
        });

        socket.on("liveResourceChanged", new Consumer<FluxResourceChangedEventDataOverlay>() {
            @Override
            public void accept(FluxResourceChangedEventDataOverlay event) {
                Document document = liveDocuments.get("/" + event.getProject() + "/" + event.getResource());
                if (document == null) {
                    return;
                }
                String addedCharacters = event.getAddedCharacters();
                isUpdatingModel = true;
                document.replace(event.getOffset(), event.getRemovedCharCount(), addedCharacters);
                //    public void replace(int offset, int length, String text) {
                //   this.editorOverlay.setText(text, offset, offset + length);
                isUpdatingModel = false;
            }
        });

        socket.emit("connectToChannel", JsonUtils.safeEval("{\"channel\" : \"USER\"}"));

    }

    public static native SocketIOOverlay getSocketIO()/*-{
                                                      return $wnd.io;
                                                      }-*/;

    private void wsSubscribe(String wsChannel, MessageHandler handler) {
        try {
            messageBus.subscribe(wsChannel, handler);
        } catch (WebSocketException e) {
            Log.error(getClass(), e);
        }
    }

    int trySubstitude = 10;

    public void substituteAndConnect(final String commandLine) {
        try {
            String cmdLine = commandLine;
            List<CommandPropertyValueProvider> providers = commandPropertyValueProviderRegistry.getProviders();

            for (CommandPropertyValueProvider provider : providers) {
                cmdLine = cmdLine.replace(provider.getKey(), provider.getValue());
            }

            connectToFlux(cmdLine);
            return;
        } catch (Exception e) {
            Log.info(getClass(), e, "retrying " + trySubstitude--);
            if (trySubstitude > 0) {
                new Timer() {
                    @Override
                    public void run() {
                        substituteAndConnect(commandLine);
                    }
                }.schedule(1000);
                return;
            }
            throw e;
        }
    }

}