com.codenvy.plugin.contribution.client.steps.GenerateReviewFactoryStep.java Source code

Java tutorial

Introduction

Here is the source code for com.codenvy.plugin.contribution.client.steps.GenerateReviewFactoryStep.java

Source

/*******************************************************************************
 * Copyright (c) 2014-2015 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 com.codenvy.plugin.contribution.client.steps;

import com.codenvy.plugin.contribution.client.ContributeMessages;
import com.codenvy.plugin.contribution.client.jso.Blob;
import com.codenvy.plugin.contribution.client.jso.FormData;
import com.codenvy.plugin.contribution.client.jso.JsBlob;
import com.codenvy.plugin.contribution.client.utils.FactoryHelper;
import com.codenvy.plugin.contribution.client.utils.NotificationHelper;
import com.codenvy.plugin.contribution.vcs.client.hosting.VcsHostingService;
import com.codenvy.plugin.contribution.vcs.client.hosting.VcsHostingServiceProvider;
import com.google.gwt.http.client.Header;
import com.google.gwt.http.client.Response;
import com.google.gwt.i18n.client.Messages;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;

import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.factory.dto.Factory;
import org.eclipse.che.api.project.shared.dto.ImportSourceDescriptor;
import org.eclipse.che.api.project.shared.dto.RunnerSource;
import org.eclipse.che.api.project.shared.dto.Source;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.app.CurrentProject;
import org.eclipse.che.ide.commons.exception.ServerException;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.rest.AsyncRequestCallback;
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.eclipse.che.ide.rest.HTTPMethod;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.Map;

import static com.codenvy.plugin.contribution.client.steps.events.StepEvent.Step.GENERATE_REVIEW_FACTORY;
import static com.codenvy.plugin.contribution.projecttype.shared.ContributionProjectTypeConstants.CONTRIBUTE_MODE_VARIABLE_NAME;
import static com.codenvy.plugin.contribution.projecttype.shared.ContributionProjectTypeConstants.CONTRIBUTE_VARIABLE_NAME;
import static com.codenvy.plugin.contribution.projecttype.shared.ContributionProjectTypeConstants.PULL_REQUEST_ID_VARIABLE_NAME;
import static java.util.Arrays.asList;
import static org.eclipse.che.api.project.shared.Constants.VCS_PROVIDER_NAME;
import static org.eclipse.che.ide.MimeType.APPLICATION_JSON;
import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT;

/**
 * Generates a factory for the contribution reviewer.
 */
public class GenerateReviewFactoryStep implements Step {
    private final Step addReviewFactoryLinkStep;
    private final ContributeMessages messages;
    private final ApiUrlTemplate apiTemplate;
    private final DtoFactory dtoFactory;
    private final AsyncRequestFactory asyncRequestFactory;
    private final DtoUnmarshallerFactory dtoUnmarshallerFactory;
    private final AppContext appContext;
    private final VcsHostingServiceProvider vcsHostingServiceProvider;
    private final NotificationHelper notificationHelper;

    @Inject
    public GenerateReviewFactoryStep(@Nonnull final AddReviewFactoryLinkStep addReviewFactoryLinkStep,
            @Nonnull final ApiUrlTemplate apiUrlTemplate, @Nonnull final ContributeMessages messages,
            @Nonnull final DtoFactory dtoFactory, @Nonnull final DtoUnmarshallerFactory dtoUnmarshallerFactory,
            @Nonnull final AsyncRequestFactory asyncRequestFactory, @Nonnull final AppContext appContext,
            @Nonnull final VcsHostingServiceProvider vcsHostingServiceProvider,
            @Nonnull final NotificationHelper notificationHelper) {
        this.addReviewFactoryLinkStep = addReviewFactoryLinkStep;
        this.apiTemplate = apiUrlTemplate;
        this.messages = messages;
        this.asyncRequestFactory = asyncRequestFactory;
        this.dtoUnmarshallerFactory = dtoUnmarshallerFactory;
        this.dtoFactory = dtoFactory;
        this.appContext = appContext;
        this.vcsHostingServiceProvider = vcsHostingServiceProvider;
        this.notificationHelper = notificationHelper;
    }

    /**
     * Sends the request, passing the form data as content.
     *
     * @param xhr
     *         the request
     * @param formData
     *         the form data
     * @return true if the request was sent correctly - Note: doesn't mean the request will be successful
     */
    private static native boolean sendFormData(XMLHttpRequest xhr, FormData formData) /*-{
                                                                                      try {
                                                                                      xhr.send(formData);
                                                                                      return true;
                                                                                      } catch (e) {
                                                                                      return false;
                                                                                      }
                                                                                      }-*/;

    @Override
    public void execute(@Nonnull final ContributorWorkflow workflow) {
        createFactory(workflow.getContext(), new AsyncCallback<Factory>() {
            @Override
            public void onSuccess(final Factory factory) {
                // find factory URL inside factory
                workflow.getContext().setReviewFactoryUrl(FactoryHelper.getCreateProjectRelUrl(factory));
                workflow.fireStepDoneEvent(GENERATE_REVIEW_FACTORY);
                workflow.setStep(addReviewFactoryLinkStep);
                workflow.executeStep();
            }

            @Override
            public void onFailure(final Throwable exception) {
                notificationHelper.showWarning(messages.stepGenerateReviewFactoryErrorCreateFactory());
                workflow.fireStepDoneEvent(GENERATE_REVIEW_FACTORY);
                workflow.setStep(addReviewFactoryLinkStep);
                workflow.executeStep();
            }
        });
    }

    private void createFactory(final Context context, final AsyncCallback<Factory> callback) {
        getFactory(context, new AsyncCallback<Factory>() {
            @Override
            public void onSuccess(final Factory factory) {
                final String factoryJson = dtoFactory.toJson(factory);
                final FormData formData = FormData.create();

                final Blob blob = JsBlob.create(factoryJson);
                formData.append("factoryUrl", blob);
                saveFactory(formData, callback);
            }

            @Override
            public void onFailure(final Throwable exception) {
                callback.onFailure(exception);
            }
        });
    }

    private void getFactory(final Context context, final AsyncCallback<Factory> callback) {
        exportProject(new AsyncCallback<Factory>() {
            @Override
            public void onSuccess(final Factory factory) {
                getSource(context, new AsyncCallback<Source>() {
                    @Override
                    public void onFailure(final Throwable exception) {
                        callback.onFailure(exception);
                    }

                    @Override
                    public void onSuccess(final Source source) {
                        factory.setSource(source);

                        // project must be public to be shared
                        factory.getProject().setVisibility("public");

                        // new factory is not a 'contribute workflow factory'
                        factory.getProject().getAttributes().remove(CONTRIBUTE_VARIABLE_NAME);

                        // new factory is in a review mode
                        factory.getProject().getAttributes().put(CONTRIBUTE_MODE_VARIABLE_NAME, asList("review"));

                        // remember the related pull request id
                        factory.getProject().getAttributes().put(PULL_REQUEST_ID_VARIABLE_NAME, asList("notUsed"));

                        callback.onSuccess(factory);
                    }
                });
            }

            @Override
            public void onFailure(final Throwable exception) {
                callback.onFailure(exception);
            }
        });
    }

    private void exportProject(final AsyncCallback<Factory> callback) {
        final String workspaceId = appContext.getWorkspace().getId();

        final CurrentProject currentProject = appContext.getCurrentProject();
        if (currentProject != null) {
            final String projectName = currentProject.getRootProject().getName();
            final String requestUrl = apiTemplate.getFactoryJson(workspaceId, projectName);

            asyncRequestFactory.createGetRequest(requestUrl).header(ACCEPT, APPLICATION_JSON)
                    .send(new AsyncRequestCallback<Factory>(dtoUnmarshallerFactory.newUnmarshaller(Factory.class)) {

                        @Override
                        protected void onSuccess(final Factory result) {
                            callback.onSuccess(result);
                        }

                        @Override
                        protected void onFailure(final Throwable exception) {
                            callback.onFailure(exception);
                        }
                    });
        }
    }

    private void getSource(final Context context, final AsyncCallback<Source> callback) {
        vcsHostingServiceProvider.getVcsHostingService(new AsyncCallback<VcsHostingService>() {
            @Override
            public void onFailure(final Throwable exception) {
                callback.onFailure(exception);
            }

            @Override
            public void onSuccess(final VcsHostingService vcsHostingService) {
                final Source source = dtoFactory.createDto(Source.class);
                final ImportSourceDescriptor importSourceDescriptor = dtoFactory
                        .createDto(ImportSourceDescriptor.class);

                final String forkRepoUrl = vcsHostingService.makeSSHRemoteUrl(context.getHostUserLogin(),
                        context.getForkedRepositoryName());
                importSourceDescriptor.setLocation(forkRepoUrl);

                final String vcsType = context.getProject().getAttributes().get(VCS_PROVIDER_NAME).get(0);
                importSourceDescriptor.setType(vcsType);

                importSourceDescriptor.setParameters(new HashMap<String, String>());

                // keep some origin factory settings
                if (appContext.getFactory() != null) {
                    final Factory originFactory = appContext.getFactory();

                    final String keepDirectory = originFactory.getSource().getProject().getParameters()
                            .get("keepDirectory");
                    if (keepDirectory != null) {
                        importSourceDescriptor.getParameters().put("keepDirectory", keepDirectory);
                    }

                    final Map<String, RunnerSource> runners = originFactory.getSource().getRunners();
                    if (runners != null && !runners.isEmpty()) {
                        source.setRunners(runners);
                    }
                }

                // keep VCS information
                importSourceDescriptor.getParameters().put("keepVcs", "true");

                // Use the contribution branch
                importSourceDescriptor.getParameters().put("branch", context.getWorkBranchName());

                callback.onSuccess(source.withProject(importSourceDescriptor));
            }
        });
    }

    private void saveFactory(final FormData formData, final AsyncCallback<Factory> callback) {
        final String requestUrl = apiTemplate.saveFactory();

        final XMLHttpRequest xhr = XMLHttpRequest.create();
        xhr.open(HTTPMethod.POST, requestUrl);
        xhr.setRequestHeader(ACCEPT, APPLICATION_JSON);
        xhr.setOnReadyStateChange(new ReadyStateChangeHandler() {

            @Override
            public void onReadyStateChange(final XMLHttpRequest request) {
                if (request.getReadyState() == XMLHttpRequest.DONE) {
                    if (request.getStatus() == Response.SC_OK) {
                        request.clearOnReadyStateChange();
                        final String payLoad = request.getResponseText();
                        final Factory createdFactory = dtoFactory.createDtoFromJson(payLoad, Factory.class);

                        if (createdFactory.getId() == null || createdFactory.getId().isEmpty()) {
                            final ServiceError error = dtoFactory.createDtoFromJson(payLoad, ServiceError.class);
                            callback.onFailure(new Exception(error.getMessage()));
                        } else {
                            callback.onSuccess(createdFactory);
                        }
                    } else {
                        final Response response = new ResponseImpl(request);
                        callback.onFailure(new ServerException(response));
                    }
                }

            }
        });

        if (!sendFormData(xhr, formData)) {
            callback.onFailure(new Exception("Could not call service"));
        }
    }

    /**
     * Template for building api urls.
     */
    interface ApiUrlTemplate extends Messages {
        /**
         * Returns a 'getFactoryJson' call URL.
         *
         * @param workspaceId
         *         the workspace id
         * @param projectName
         *         the project name
         * @return the call URL
         */
        @DefaultMessage("/api/factory/{0}/{1}")
        String getFactoryJson(String workspaceId, String projectName);

        /**
         * Returns a 'getFactoryJson'/saveFactory call URL.
         *
         * @return the call URL
         */
        @DefaultMessage("/api/factory")
        String saveFactory();
    }

    /**
     * Concrete {@link Response}, {@link #getHeaders()} copied from GWT internal.
     */
    private class ResponseImpl extends Response {
        private final XMLHttpRequest request;

        ResponseImpl(final XMLHttpRequest request) {
            this.request = request;
        }

        @Override
        public String getText() {
            return request.getResponseText();
        }

        @Override
        public String getStatusText() {
            return request.getStatusText();
        }

        @Override
        public int getStatusCode() {
            return request.getStatus();
        }

        @Override
        public String getHeadersAsString() {
            return request.getAllResponseHeaders();
        }

        @Override
        public Header[] getHeaders() {
            final String allHeaders = request.getAllResponseHeaders();
            final String[] unparsedHeaders = allHeaders.split("\n");
            final Header[] parsedHeaders = new Header[unparsedHeaders.length];

            for (int i = 0, n = unparsedHeaders.length; i < n; ++i) {
                final String unparsedHeader = unparsedHeaders[i];

                if (unparsedHeader.length() == 0) {
                    continue;
                }

                final int endOfNameIdx = unparsedHeader.indexOf(':');
                if (endOfNameIdx < 0) {
                    continue;
                }

                final String name = unparsedHeader.substring(0, endOfNameIdx).trim();
                final String value = unparsedHeader.substring(endOfNameIdx + 1).trim();
                final Header header = new Header() {
                    @Override
                    public String getName() {
                        return name;
                    }

                    @Override
                    public String getValue() {
                        return value;
                    }

                    @Override
                    public String toString() {
                        return name + " : " + value;
                    }
                };

                parsedHeaders[i] = header;
            }

            return parsedHeaders;
        }

        @Override
        public String getHeader(final String header) {
            return request.getResponseHeader(header);
        }
    }
}