org.nuxeo.apidoc.browse.Distribution.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.apidoc.browse.Distribution.java

Source

/*
 * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Thierry Delprat
 */
package org.nuxeo.apidoc.browse;

import static org.nuxeo.apidoc.snapshot.DistributionSnapshot.PROP_RELEASED;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.sql.Date;
import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.naming.NamingException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.apidoc.documentation.DocumentationService;
import org.nuxeo.apidoc.export.ArchiveFile;
import org.nuxeo.apidoc.listener.AttributesExtractorStater;
import org.nuxeo.apidoc.snapshot.DistributionSnapshot;
import org.nuxeo.apidoc.snapshot.DistributionSnapshotDesc;
import org.nuxeo.apidoc.snapshot.SnapshotFilter;
import org.nuxeo.apidoc.snapshot.SnapshotManager;
import org.nuxeo.apidoc.snapshot.SnapshotManagerComponent;
import org.nuxeo.apidoc.snapshot.SnapshotResolverHelper;
import org.nuxeo.apidoc.worker.ExtractXmlAttributesWorker;
import org.nuxeo.common.Environment;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.IterableQueryResult;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.query.QueryFilter;
import org.nuxeo.ecm.core.query.sql.NXQL;
import org.nuxeo.ecm.core.work.api.Work;
import org.nuxeo.ecm.core.work.api.WorkManager;
import org.nuxeo.ecm.webengine.forms.FormData;
import org.nuxeo.ecm.webengine.model.Resource;
import org.nuxeo.ecm.webengine.model.WebObject;
import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException;
import org.nuxeo.ecm.webengine.model.impl.ModuleRoot;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.transaction.TransactionHelper;

@Path("/distribution")
// needed for 5.4.1
@WebObject(type = "distribution")
public class Distribution extends ModuleRoot {

    public static final String DIST_ID = "distId";

    protected static final Log log = LogFactory.getLog(Distribution.class);

    protected static final Pattern VERSION_REGEX = Pattern.compile("^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-.*)?",
            Pattern.CASE_INSENSITIVE);

    // handle errors
    @Override
    public Object handleError(WebApplicationException e) {
        if (e instanceof WebResourceNotFoundException) {
            return Response.status(404).entity(getTemplate("error/error_404.ftl")).type("text/html").build();
        } else {
            return super.handleError(e);
        }
    }

    protected SnapshotManager getSnapshotManager() {
        return Framework.getLocalService(SnapshotManager.class);
    }

    public String getNavigationPoint() {
        String currentUrl = getContext().getURL();
        String navPoint = "somewhere";

        if (currentUrl.contains("/listBundles")) {
            navPoint = "listBundles";
        } else if (currentUrl.contains("/listSeamComponents")) {
            navPoint = "listSeamComponents";
        } else if (currentUrl.contains("/viewSeamComponent")) {
            navPoint = "viewSeamComponent";
        } else if (currentUrl.contains("/listComponents")) {
            navPoint = "listComponents";
        } else if (currentUrl.contains("/listServices")) {
            navPoint = "listServices";
        } else if (currentUrl.contains("/listExtensionPoints")) {
            navPoint = "listExtensionPoints";
        } else if (currentUrl.contains("/listContributions")) {
            navPoint = "listContributions";
        } else if (currentUrl.contains("/listBundleGroups")) {
            navPoint = "listBundleGroups";
        } else if (currentUrl.contains("/viewBundleGroup")) {
            navPoint = "viewBundleGroup";
        } else if (currentUrl.contains("/viewComponent")) {
            navPoint = "viewComponent";
        } else if (currentUrl.contains("/viewService")) {
            navPoint = "viewService";
        } else if (currentUrl.contains("/viewExtensionPoint")) {
            navPoint = "viewExtensionPoint";
        } else if (currentUrl.contains("/viewContribution")) {
            navPoint = "viewContribution";
        } else if (currentUrl.contains("/viewBundle")) {
            navPoint = "viewBundle";
        } else if (currentUrl.contains("/listOperations")) {
            navPoint = "listOperations";
        } else if (currentUrl.contains("/viewOperation")) {
            navPoint = "viewOperation";
        } else if (currentUrl.contains("/doc")) {
            navPoint = "documentation";
        }
        return navPoint;
    }

    @GET
    @Produces("text/html")
    public Object doGet() {
        return getView("index").arg("hideNav", Boolean.TRUE);
    }

    @Path("latest")
    public Resource getLatest() {
        List<DistributionSnapshot> snaps = listPersistedDistributions();
        Optional<DistributionSnapshot> distribution = snaps.stream()
                .filter(snap -> snap.getName().toLowerCase().startsWith("nuxeo platform")).findFirst();

        String latest = "current";
        if (distribution.isPresent()) {
            latest = distribution.get().getKey();
        }
        return ctx.newObject("redirectWO", "latest", latest);
    }

    @Path("{distributionId}")
    public Resource viewDistribution(@PathParam("distributionId") String distributionId) {
        if (distributionId == null || "".equals(distributionId)) {
            return this;
        }
        String orgDistributionId = distributionId;
        Boolean embeddedMode = Boolean.FALSE;
        if ("adm".equals(distributionId)) {
            embeddedMode = Boolean.TRUE;
        } else {
            List<DistributionSnapshot> snaps = getSnapshotManager().listPersistentSnapshots((ctx.getCoreSession()));
            snaps.add(getSnapshotManager().getRuntimeSnapshot());
            distributionId = SnapshotResolverHelper.findBestMatch(snaps, distributionId);
        }
        if (distributionId == null || "".equals(distributionId)) {
            distributionId = "current";
        }

        if (!orgDistributionId.equals(distributionId)) {
            return ctx.newObject("redirectWO", orgDistributionId, distributionId);
        }

        ctx.setProperty("embeddedMode", embeddedMode);
        ctx.setProperty("distribution", getSnapshotManager().getSnapshot(distributionId, ctx.getCoreSession()));
        ctx.setProperty(DIST_ID, distributionId);
        return ctx.newObject("apibrowser", distributionId, embeddedMode);
    }

    public List<DistributionSnapshotDesc> getAvailableDistributions() {
        return getSnapshotManager().getAvailableDistributions(ctx.getCoreSession());
    }

    public String getRuntimeDistributionName() {
        return SnapshotManagerComponent.RUNTIME;
    }

    public DistributionSnapshot getRuntimeDistribution() {
        return getSnapshotManager().getRuntimeSnapshot();
    }

    public List<DistributionSnapshot> listPersistedDistributions() {
        SnapshotManager sm = getSnapshotManager();
        return sm.listPersistentSnapshots(ctx.getCoreSession()).stream().sorted((o1, o2) -> {
            Matcher m1 = VERSION_REGEX.matcher(o1.getVersion());
            Matcher m2 = VERSION_REGEX.matcher(o2.getVersion());

            if (m1.matches() && m2.matches()) {
                for (int i = 0; i < 3; i++) {
                    String s1 = m1.group(i + 1);
                    int c1 = s1 != null ? Integer.parseInt(s1) : 0;
                    String s2 = m2.group(i + 1);
                    int c2 = s2 != null ? Integer.parseInt(s2) : 0;

                    if (c1 != c2 || i == 2) {
                        return Integer.compare(c2, c1);
                    }
                }
            }
            log.info(String.format("Comparing version using String between %s - %s", o1.getVersion(),
                    o2.getVersion()));
            return o2.getVersion().compareTo(o1.getVersion());
        }).filter(s -> !s.isHidden()).collect(Collectors.toList());
    }

    public Map<String, DistributionSnapshot> getPersistedDistributions() {
        return getSnapshotManager().getPersistentSnapshots(ctx.getCoreSession());
    }

    public DistributionSnapshot getCurrentDistribution() {
        String distId = (String) ctx.getProperty(DIST_ID);
        DistributionSnapshot currentDistribution = (DistributionSnapshot) ctx.getProperty("currentDistribution");
        if (currentDistribution == null || !currentDistribution.getKey().equals(distId)) {
            currentDistribution = getSnapshotManager().getSnapshot(distId, ctx.getCoreSession());
            ctx.setProperty("currentDistribution", currentDistribution);
        }
        return currentDistribution;
    }

    @POST
    @Path("save")
    @Produces("text/html")
    public Object doSave() throws NamingException, NotSupportedException, SystemException, RollbackException,
            HeuristicMixedException, HeuristicRollbackException, ParseException {
        if (!canAddDocumentation()) {
            return null;
        }
        FormData formData = getContext().getForm();
        String distribLabel = formData.getString("name");

        log.info("Start Snapshot...");
        boolean startedTx = false;
        UserTransaction tx = TransactionHelper.lookupUserTransaction();
        if (tx != null && !TransactionHelper.isTransactionActiveOrMarkedRollback()) {
            tx.begin();
            startedTx = true;
        }

        Map<String, Serializable> otherProperties = readFormData(formData);
        try {
            getSnapshotManager().persistRuntimeSnapshot(getContext().getCoreSession(), distribLabel,
                    otherProperties);

        } catch (NuxeoException e) {
            log.error("Error during storage", e);
            if (tx != null) {
                tx.rollback();
            }
            return getView("savedKO").arg("message", e.getMessage());
        }
        log.info("Snapshot saved.");
        if (tx != null && startedTx) {
            tx.commit();
        }

        String redirectUrl = getContext().getBaseURL() + getPath();
        log.debug("Path => " + redirectUrl);
        return getView("saved");
    }

    protected Map<String, Serializable> readFormData(FormData formData) {
        Map<String, Serializable> properties = new HashMap<>();

        // Release date
        String released = formData.getString("released");
        if (StringUtils.isNotBlank(released)) {
            LocalDate date = LocalDate.parse(released);
            Instant instant = date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
            properties.put(PROP_RELEASED, Date.from(instant));
        }

        return properties;
    }

    @POST
    @Path("saveExtended")
    @Produces("text/html")
    public Object doSaveExtended() throws NamingException, NotSupportedException, SystemException,
            SecurityException, RollbackException, HeuristicMixedException, HeuristicRollbackException {
        if (!canAddDocumentation()) {
            return null;
        }

        FormData formData = getContext().getForm();

        String distribLabel = formData.getString("name");
        String bundleList = formData.getString("bundles");
        String pkgList = formData.getString("packages");
        SnapshotFilter filter = new SnapshotFilter(distribLabel);

        if (bundleList != null) {
            String[] bundles = bundleList.split("\n");
            for (String bundleId : bundles) {
                filter.addBundlePrefix(bundleId);
            }
        }

        if (pkgList != null) {
            String[] packages = pkgList.split("\\r?\\n");
            for (String pkg : packages) {
                filter.addPackagesPrefix(pkg);
            }
        }

        Map<String, Serializable> otherProperties = readFormData(formData);

        log.info("Start Snapshot...");
        boolean startedTx = false;
        UserTransaction tx = TransactionHelper.lookupUserTransaction();
        if (tx != null && !TransactionHelper.isTransactionActiveOrMarkedRollback()) {
            tx.begin();
            startedTx = true;
        }
        try {
            getSnapshotManager().persistRuntimeSnapshot(getContext().getCoreSession(), distribLabel,
                    otherProperties, filter);
        } catch (NuxeoException e) {
            log.error("Error during storage", e);
            if (tx != null) {
                tx.rollback();
            }
            return getView("savedKO").arg("message", e.getMessage());
        }
        log.info("Snapshot saved.");
        if (tx != null && startedTx) {
            tx.commit();
        }
        return getView("saved");
    }

    public String getDocumentationInfo() {
        DocumentationService ds = Framework.getService(DocumentationService.class);
        return ds.getDocumentationStats(getContext().getCoreSession());
    }

    protected File getExportTmpFile() {
        File tmpFile = new File(Environment.getDefault().getTemp(), "export.zip");
        if (tmpFile.exists()) {
            tmpFile.delete();
        }
        tmpFile.deleteOnExit();
        return tmpFile;
    }

    @GET
    @Path("downloadDoc")
    public Response downloadDoc() throws IOException {
        DocumentationService ds = Framework.getService(DocumentationService.class);
        File tmp = getExportTmpFile();
        tmp.createNewFile();
        OutputStream out = new FileOutputStream(tmp);
        ds.exportDocumentation(getContext().getCoreSession(), out);
        out.flush();
        out.close();
        ArchiveFile aFile = new ArchiveFile(tmp.getAbsolutePath());
        return Response.ok(aFile).header("Content-Disposition", "attachment;filename=" + "nuxeo-documentation.zip")
                .type("application/zip").build();
    }

    @GET
    @Path("download/{distributionId}")
    public Response downloadDistrib(@PathParam("distributionId") String distribId) throws IOException {
        File tmp = getExportTmpFile();
        tmp.createNewFile();
        OutputStream out = new FileOutputStream(tmp);
        getSnapshotManager().exportSnapshot(getContext().getCoreSession(), distribId, out);
        out.close();
        String fName = "nuxeo-distribution-" + distribId + ".zip";
        fName = fName.replace(" ", "_");
        ArchiveFile aFile = new ArchiveFile(tmp.getAbsolutePath());
        return Response.ok(aFile).header("Content-Disposition", "attachment;filename=" + fName)
                .type("application/zip").build();
    }

    /**
     * Use to allow authorized users to upload distribution even in site mode
     *
     * @since 8.3
     */
    @GET
    @Path("_admin")
    public Object getForms() {
        NuxeoPrincipal principal = (NuxeoPrincipal) getContext().getPrincipal();
        if (SecurityHelper.canEditDocumentation(principal)) {
            return getView("forms").arg("hideNav", Boolean.TRUE);
        } else {
            return Response.status(401).build();
        }
    }

    @POST
    @Path("uploadDistrib")
    @Produces("text/html")
    public Object uploadDistrib() throws IOException {
        if (!canAddDocumentation()) {
            return null;
        }
        Blob blob = getContext().getForm().getFirstBlob();

        getSnapshotManager().importSnapshot(getContext().getCoreSession(), blob.getStream());
        getSnapshotManager().readPersistentSnapshots(getContext().getCoreSession());

        return getView("index");
    }

    @POST
    @Path("uploadDistribTmp")
    @Produces("text/html")
    public Object uploadDistribTmp() throws IOException {
        if (!canAddDocumentation()) {
            return null;
        }
        Blob blob = getContext().getForm().getFirstBlob();
        if (blob == null || blob.getLength() == 0) {
            return null;
        }
        DocumentModel snap = getSnapshotManager().importTmpSnapshot(getContext().getCoreSession(),
                blob.getStream());
        if (snap == null) {
            log.error("Unable to import archive");
            return null;
        }
        DistributionSnapshot snapObject = snap.getAdapter(DistributionSnapshot.class);
        return getView("uploadEdit").arg("tmpSnap", snap).arg("snapObject", snapObject);
    }

    @POST
    @Path("uploadDistribTmpValid")
    @Produces("text/html")
    public Object uploadDistribTmpValid() {
        if (!canAddDocumentation()) {
            return null;
        }

        FormData formData = getContext().getForm();
        String name = formData.getString("name");
        String version = formData.getString("version");
        String pathSegment = formData.getString("pathSegment");
        String title = formData.getString("title");

        getSnapshotManager().validateImportedSnapshot(getContext().getCoreSession(), name, version, pathSegment,
                title);
        getSnapshotManager().readPersistentSnapshots(getContext().getCoreSession());
        return getView("importDone");
    }

    @POST
    @Path("uploadDoc")
    @Produces("text/html")
    public Object uploadDoc() throws IOException {
        if (!canAddDocumentation()) {
            return null;
        }

        Blob blob = getContext().getForm().getFirstBlob();
        if (blob == null || blob.getLength() == 0) {
            return null;
        }

        DocumentationService ds = Framework.getService(DocumentationService.class);
        ds.importDocumentation(getContext().getCoreSession(), blob.getStream());

        log.info("Documents imported.");

        return getView("docImportDone");
    }

    @GET
    @Path("_reindex")
    @Produces("text/plain")
    public Object reindex() {
        NuxeoPrincipal nxPrincipal = (NuxeoPrincipal) getContext().getPrincipal();
        if (!nxPrincipal.isAdministrator()) {
            return Response.status(404).build();
        }

        CoreSession coreSession = getContext().getCoreSession();
        String query = String.format(
                "SELECT ecm:uuid FROM Document WHERE ecm:primaryType in ('%s') AND ecm:isProxy = 0 AND ecm:currentLifeCycleState <> 'deleted'",
                StringUtils.join(AttributesExtractorStater.DOC_TYPES, "','"));

        try (IterableQueryResult it = coreSession.queryAndFetch(query, NXQL.NXQL, QueryFilter.EMPTY);) {
            for (Map<String, Serializable> map : it) {
                String id = (String) map.get(NXQL.ECM_UUID);
                Work work = new ExtractXmlAttributesWorker(coreSession.getRepositoryName(), nxPrincipal.getName(),
                        id);
                Framework.getLocalService(WorkManager.class).schedule(work);
            }
        }

        return Response.ok().build();
    }

    public boolean isEmbeddedMode() {
        Boolean embed = (Boolean) getContext().getProperty("embeddedMode", Boolean.FALSE);
        return embed != null && embed;
    }

    public boolean isEditor() {
        if (isEmbeddedMode() || isSiteMode()) {
            return false;
        }
        NuxeoPrincipal principal = (NuxeoPrincipal) getContext().getPrincipal();
        return SecurityHelper.canEditDocumentation(principal);
    }

    public boolean canAddDocumentation() {
        NuxeoPrincipal principal = (NuxeoPrincipal) getContext().getPrincipal();
        return !isEmbeddedMode() && SecurityHelper.canEditDocumentation(principal);
    }

    public static boolean showCurrentDistribution() {
        return !(Framework.isBooleanPropertyTrue("org.nuxeo.apidoc.hide.current.distribution") || isSiteMode());
    }

    public static boolean showSeamComponent() {
        return !(Framework.isBooleanPropertyTrue("org.nuxeo.apidoc.hide.seam.components") || isSiteMode());
    }

    public static boolean isSiteMode() {
        return Framework.isBooleanPropertyTrue("org.nuxeo.apidoc.site.mode");
    }
}