org.dawnsci.marketplace.ui.editors.OverviewPage.java Source code

Java tutorial

Introduction

Here is the source code for org.dawnsci.marketplace.ui.editors.OverviewPage.java

Source

/*****************************************************************************
 * Copyright (c) 2016 Diamond Light Source Ltd.
 * 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:
 *    Torkild U. Resheim - initial API and implementation
 ****************************************************************************/
package org.dawnsci.marketplace.ui.editors;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.dawnsci.marketplace.Iu;
import org.dawnsci.marketplace.Node;
import org.dawnsci.marketplace.core.MarketplaceSerializer;
import org.dawnsci.marketplace.ui.Activator;
import org.dawnsci.marketplace.ui.Credentials;
import org.dawnsci.marketplace.ui.LoginDialog;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.emf.common.util.EList;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.internal.build.site.QualifierReplacer;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.PDECoreMessages;
import org.eclipse.pde.internal.core.exports.FeatureExportInfo;
import org.eclipse.pde.internal.core.exports.FeatureExportOperation;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.eclipse.pde.internal.ui.PDEPluginImages;
import org.eclipse.pde.internal.ui.PDEUIMessages;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Link;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.IMessage;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.progress.IProgressConstants;

@SuppressWarnings("restriction")
public class OverviewPage extends FormPage {

    private static final String MPC = "MPC";

    private static final String X_CSRF_TOKEN = "X-CSRF-TOKEN";

    private Node solution;

    protected class AntErrorDialog extends MessageDialog {
        private File fLogLocation;

        public AntErrorDialog(File logLocation) {
            super(PlatformUI.getWorkbench().getDisplay().getActiveShell(),
                    PDECoreMessages.FeatureBasedExportOperation_ProblemDuringExport, null, null,
                    MessageDialog.ERROR, new String[] { IDialogConstants.OK_LABEL }, 0);
            fLogLocation = logLocation;
        }

        protected Control createMessageArea(Composite composite) {
            Link link = new Link(composite, SWT.WRAP);
            try {
                link.setText(NLS.bind(PDEUIMessages.PluginExportWizard_Ant_errors_during_export_logs_generated,
                        "<a>" + fLogLocation.getCanonicalPath() + "</a>")); //$NON-NLS-1$ //$NON-NLS-2$
            } catch (IOException e) {
                Activator.log(e);
            }
            GridData data = new GridData();
            data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
            link.setLayoutData(data);
            link.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {
                    try {
                        Program.launch(fLogLocation.getCanonicalPath());
                    } catch (IOException ex) {
                        Activator.log(ex);
                    }
                }
            });
            return link;
        }
    }

    public OverviewPage(FormEditor editor, String id, String title, Node solution) {
        super(editor, id, title);
        this.solution = solution;
    };

    public OverviewPage(String id, String title) {
        super(id, title);
    }

    private void createActions(IToolBarManager manager) {
        Action publishAction = new Action("Publish to DAWN marketplace", Action.AS_PUSH_BUTTON) {

            @Override
            public ImageDescriptor getImageDescriptor() {
                return Activator.getImageDescriptor("icons/publish.png");
            }

            @Override
            public void run() {
                OverviewPage.this.getManagedForm().getMessageManager().removeAllMessages();
                try {
                    scheduleExportJob();
                } catch (IOException e) {
                    OverviewPage.this.getManagedForm().getMessageManager().addMessage(MPC, e.getMessage(), null,
                            IMessage.ERROR);
                }
            }

        };
        manager.add(publishAction);
        manager.update(true);
    }

    @Override
    protected void createFormContent(IManagedForm managedForm) {
        FormToolkit toolkit = managedForm.getToolkit();
        ScrolledForm form = managedForm.getForm();
        createActions(form.getToolBarManager());
        form.setText("Overview");
        Composite body = form.getBody();
        toolkit.decorateFormHeading(form.getForm());
        toolkit.paintBordersFor(body);

        body.setLayout(new GridLayout(2, false));

        DetailsSection detailsSection = new DetailsSection(managedForm.getForm().getBody(), toolkit,
                Section.TITLE_BAR);
        detailsSection.initialize(managedForm);
        GridData gdDetails = new GridData(GridData.FILL, GridData.FILL, true, true);
        gdDetails.verticalSpan = 2;
        gdDetails.verticalAlignment = SWT.BEGINNING;
        gdDetails.widthHint = 300;
        detailsSection.getSection().setLayoutData(gdDetails);
        detailsSection.setFormInput(solution);

        FeatureSection featureSection = new FeatureSection(managedForm.getForm().getBody(), toolkit,
                Section.TITLE_BAR);
        featureSection.initialize(managedForm);
        GridData gdFeature = new GridData(GridData.FILL, GridData.FILL, true, false);
        featureSection.getSection().setLayoutData(gdFeature);
        featureSection.setFormInput(solution);

        ShortDescriptionSection descriptionSection = new ShortDescriptionSection(managedForm.getForm().getBody(),
                toolkit, Section.TITLE_BAR);
        descriptionSection.initialize(managedForm);
        GridData gdDescription = new GridData(GridData.FILL, GridData.FILL, true, true);
        descriptionSection.getSection().setLayoutData(gdDescription);
        descriptionSection.setFormInput(solution);

        ImagesSection imagesSection = new ImagesSection(managedForm.getForm().getBody(), toolkit,
                Section.TITLE_BAR);
        imagesSection.initialize(managedForm);
        GridData gdImages = new GridData(GridData.FILL, GridData.FILL, true, true);
        gdImages.verticalAlignment = SWT.BEGINNING;
        gdImages.horizontalSpan = 2;
        imagesSection.getSection().setLayoutData(gdImages);
        imagesSection.setFormInput(solution);
    }

    private String getCsrfToken(CloseableHttpClient client) throws ClientProtocolException, IOException {
        String url = getMarketplaceUrl();

        HttpHead head = new HttpHead(url + "token");
        head.addHeader(X_CSRF_TOKEN, "fetch");
        CloseableHttpResponse response = client.execute(head);

        EntityUtils.consume(response.getEntity());
        String token = null;
        Header[] headers = response.getHeaders(X_CSRF_TOKEN);
        for (Header header : headers) {
            token = header.getValue();
        }
        return token;
    }

    private String getMarketplaceUrl() {
        String url = System.getProperty("org.eclipse.epp.internal.mpc.core.service.DefaultMarketplaceService.url");
        // XXX: Switch to using Spring REST instead of Jersey to avoid this kludge?
        if (url.endsWith("mpc")) {
            url = url.substring(0, url.length() - 3);
        }
        return url;
    }

    private void addMessage(int type, String text) {
        Display.getDefault().asyncExec(new Runnable() {

            @Override
            public void run() {
                OverviewPage.this.getManagedForm().getMessageManager().addMessage(MPC, text, null, type);
            }
        });

    }

    /**
     * Signs in to the marketplace server using its authentication form.
     * 
     * @throws ClientProtocolException
     * @throws IOException
     * @throws UnsupportedEncodingException
     * @throws StorageException 
     */
    private boolean signIn(CloseableHttpClient client)
            throws ClientProtocolException, IOException, UnsupportedEncodingException, StorageException {

        OverviewPage.this.getManagedForm().getMessageManager().removeMessage(MPC);

        String token = getCsrfToken(client);
        String url = getMarketplaceUrl();

        ISecurePreferences root = SecurePreferencesFactory.getDefault().node("org.dawnsci.marketplace");
        ISecurePreferences node = root.node(URLEncoder.encode(url, "utf-8"));

        String user = node.get("username", "");
        String password = node.get("password", "");

        HttpPost httpPost = new HttpPost(url + "signin/authenticate");
        httpPost.addHeader(X_CSRF_TOKEN, token);

        List<NameValuePair> params = new ArrayList<NameValuePair>(3);
        params.add(new BasicNameValuePair("_csrf", token));
        params.add(new BasicNameValuePair("username", user));
        params.add(new BasicNameValuePair("password", password));

        httpPost.setEntity(new UrlEncodedFormEntity(params));

        HttpResponse response = client.execute(httpPost);
        int statusCode = response.getStatusLine().getStatusCode();
        String location = response.getFirstHeader("Location").getValue();

        if (statusCode == 302 && !location.contains("bad_credentials")) {
            addMessage(IMessage.INFORMATION, "Signed into the marketplace service");
            return true;
        } else {
            addMessage(IMessage.ERROR, "Could not sign in to the marketplace service. Please verify credentials.");
            requestCredentials(node, user, password);
            return false;
        }
    }

    private void requestCredentials(final ISecurePreferences node, String user, String password) {
        Credentials credentials = new Credentials(user, password);
        IStatus status = new Status(IStatus.ERROR, "org.dawnsci.marketplace.ui",
                "Could not sign in to the marketplace service. Please verify credentials and try again.");
        LoginDialog ld = new LoginDialog(this.getSite().getShell(), credentials, status);
        this.getSite().getShell().getDisplay().asyncExec(new Runnable() {

            @Override
            public void run() {
                if (ld.open() == 0) {
                    try {
                        node.put("username", credentials.getUsername(), false);
                        node.put("password", credentials.getPassword(), true);
                    } catch (StorageException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    protected Object[] getFeatures() {
        List<String> identifiers = new ArrayList<>();
        EList<Iu> ius = solution.getIus().getItems();
        for (Iterator<Iu> iterator = ius.iterator(); iterator.hasNext();) {
            Iu tag = iterator.next();
            String id = tag.getMixed().getValue(0).toString();
            identifiers.add(id);
        }
        IFeatureModel[] allModels = PDECore.getDefault().getFeatureModelManager().getModels();
        ArrayList<IFeatureModel> newModels = new ArrayList<IFeatureModel>();
        for (int i = 0; i < allModels.length; i++) {
            if (identifiers.contains(allModels[i].getFeature().getId())) {
                newModels.add(allModels[i]);
            }
        }
        return newModels.toArray(new IFeatureModel[0]);
    }

    protected void scheduleExportJob() throws IOException {

        Path folder = Files.createTempDirectory("eclipse-export", new FileAttribute<?>[0]);
        final FeatureExportInfo info = new FeatureExportInfo();
        info.toDirectory = false; // in order to install from the repository
        info.useJarFormat = true;
        info.exportSource = false;
        info.exportSourceBundle = false;
        info.allowBinaryCycles = true;
        info.useWorkspaceCompiledClasses = false;
        info.destinationDirectory = folder.toString();
        info.zipFileName = "p2-repo.zip";
        info.items = getFeatures();
        info.signingInfo = null; //
        info.exportMetadata = true;
        info.qualifier = QualifierReplacer.getDateQualifier();

        final FeatureExportOperation job = new FeatureExportOperation(info, PDEUIMessages.FeatureExportJob_name);
        job.setUser(true);
        job.setRule(ResourcesPlugin.getWorkspace().getRoot());
        job.setProperty(IProgressConstants.ICON_PROPERTY, PDEPluginImages.DESC_PLUGIN_OBJ);

        // listen to job changes, we'll upload stuff when the building has been done 
        job.addJobChangeListener(new JobChangeAdapter() {
            public void done(IJobChangeEvent event) {
                if (job.hasAntErrors()) {
                    // if there were errors when running the ant scripts, inform
                    // the user where the logs can be found.
                    final File logLocation = new File(info.destinationDirectory, "logs.zip"); //$NON-NLS-1$
                    if (logLocation.exists()) {
                        PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                            public void run() {
                                AntErrorDialog dialog = new AntErrorDialog(logLocation);
                                dialog.open();
                            }
                        });
                    }
                } else if (event.getResult().isOK()) {
                    // can publish
                    try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
                        // sign in and upload items
                        if (signIn(client)) {
                            uploadForm(client);
                            // upload the images
                            if (solution.getScreenshot() != null) {
                                uploadFile(client, Paths.get(solution.getScreenshot()), "upload-screenshot");
                            }
                            if (solution.getImage() != null) {
                                uploadFile(client, Paths.get(solution.getImage()), "upload-image");
                            }
                            // upload the repository
                            uploadFile(client, folder.resolve("p2-repo.zip"), "upload-p2repo");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        addMessage(IMessage.ERROR, e.getMessage());
                    }

                }
            }

        });
        job.schedule();
    }

    protected void uploadFile(CloseableHttpClient client, Path path, String segment)
            throws ClientProtocolException, IOException {
        String token = getCsrfToken(client);
        String url = getMarketplaceUrl();

        // upload the p2-repository file
        HttpPost httpPost = new HttpPost(url + "/" + segment);
        httpPost.addHeader(X_CSRF_TOKEN, token);
        HttpEntity file = MultipartEntityBuilder.create().addBinaryBody("file", path.toFile())
                .addTextBody("id", solution.getId().toString()).build();
        httpPost.setEntity(file);
        HttpResponse response = client.execute(httpPost);
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            String result = EntityUtils.toString(response.getEntity());
            // obtain some key values from the server version
            // and update the local instance
            Node node = MarketplaceSerializer.deSerializeSolution(result);
            solution.setChanged(node.getChanged());
            addMessage(IMessage.INFORMATION, "File " + path.getFileName() + " uploaded");
        } else {
            String reasonPhrase = response.getStatusLine().getReasonPhrase();
            addMessage(IMessage.ERROR, reasonPhrase);
        }
    }

    private void uploadForm(CloseableHttpClient client)
            throws ClientProtocolException, IOException, UnsupportedEncodingException {
        OverviewPage.this.getManagedForm().getMessageManager().removeMessage(MPC);

        String token = getCsrfToken(client);
        String url = getMarketplaceUrl();

        // upload the form
        HttpPost httpPost = new HttpPost(url + "/upload");
        httpPost.addHeader(X_CSRF_TOKEN, token);
        HttpEntity entity = new ByteArrayEntity(MarketplaceSerializer.serialize(solution).getBytes("UTF-8"));
        httpPost.setEntity(entity);
        HttpResponse response = client.execute(httpPost);
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            String result = EntityUtils.toString(response.getEntity());
            // obtain some key values from the server version
            // and update the local instance
            Node node = MarketplaceSerializer.deSerializeSolution(result);
            solution.setId(node.getId());
            solution.setChanged(node.getChanged());
            solution.setCreated(node.getCreated());
            addMessage(IMessage.INFORMATION, "Solution uploaded");
        } else {
            String reasonPhrase = response.getStatusLine().getReasonPhrase();
            addMessage(IMessage.ERROR, reasonPhrase);
        }
    }
}