org.entcore.archive.controllers.ArchiveController.java Source code

Java tutorial

Introduction

Here is the source code for org.entcore.archive.controllers.ArchiveController.java

Source

/* Copyright  "Open Digital Education", 2014
 *
 * This program is published by "Open Digital Education".
 * You must indicate the name of the software and the company in any production /contribution
 * using the software and indicate on the home page of the software industry in question,
 * "powered by Open Digital Education" with a reference to the website: https://opendigitaleducation.com/.
 *
 * This program is free software, licensed under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, version 3 of the License.
 *
 * You can redistribute this application and/or modify it since you respect the terms of the GNU Affero General Public License.
 * If you modify the source code and then use this modified source code in your creation, you must make available the source code of your modifications.
 *
 * You should have received a copy of the GNU Affero General Public License along with the software.
 * If not, please see : <http://www.gnu.org/licenses/>. Full compliance requires reading the terms of this license and following its directives.
    
 *
 */

package org.entcore.archive.controllers;

import fr.wseduc.bus.BusAddress;
import fr.wseduc.rs.Get;
import fr.wseduc.rs.Post;
import fr.wseduc.security.ActionType;
import fr.wseduc.security.SecuredAction;
import fr.wseduc.webutils.Either;
import fr.wseduc.webutils.I18n;
import fr.wseduc.webutils.email.EmailSender;
import fr.wseduc.webutils.http.BaseController;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.shareddata.LocalMap;
import org.entcore.archive.Archive;
import org.entcore.archive.services.ExportService;
import org.entcore.archive.services.impl.FileSystemExportService;
import org.entcore.common.email.EmailFactory;
import org.entcore.common.events.EventStore;
import org.entcore.common.events.EventStoreFactory;
import org.entcore.common.notification.TimelineHelper;
import org.entcore.common.storage.Storage;
import org.entcore.common.storage.StorageFactory;
import org.entcore.common.user.UserInfos;
import org.entcore.common.user.UserUtils;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.spi.cluster.ClusterManager;
import org.entcore.common.utils.MapFactory;
import org.vertx.java.core.http.RouteMatcher;

import java.util.*;

public class ArchiveController extends BaseController {

    private ExportService exportService;
    private EventStore eventStore;
    private Storage storage;

    private enum ArchiveEvent {
        ACCESS
    }

    @Override
    public void init(Vertx vertx, final JsonObject config, RouteMatcher rm,
            Map<String, fr.wseduc.webutils.security.SecuredAction> securedActions) {
        super.init(vertx, config, rm, securedActions);
        String exportPath = config.getString("export-path", System.getProperty("java.io.tmpdir"));
        Set<String> expectedExports = new HashSet<>();
        final JsonArray e = config.getJsonArray("expected-exports");
        for (Object o : e) {
            if (o instanceof String) {
                expectedExports.add((String) o);
            }
        }
        LocalMap<Object, Object> server = vertx.sharedData().getLocalMap("server");
        Boolean cluster = (Boolean) server.get("cluster");
        final Map<String, Long> userExport = MapFactory.getSyncClusterMap(Archive.ARCHIVES, vertx);
        EmailFactory emailFactory = new EmailFactory(vertx, config);
        EmailSender notification = config.getBoolean("send.export.email", false) ? emailFactory.getSender() : null;
        storage = new StorageFactory(vertx, config).getStorage();
        exportService = new FileSystemExportService(vertx.fileSystem(), eb, exportPath, expectedExports,
                notification, storage, userExport, new TimelineHelper(vertx, eb, config));
        eventStore = EventStoreFactory.getFactory().getEventStore(Archive.class.getSimpleName());
        Long periodicUserClear = config.getLong("periodicUserClear");
        if (periodicUserClear != null) {
            vertx.setPeriodic(periodicUserClear, new Handler<Long>() {
                @Override
                public void handle(Long event) {
                    final long limit = System.currentTimeMillis() - config.getLong("userClearDelay", 3600000l);
                    Set<Map.Entry<String, Long>> entries = new HashSet<>(userExport.entrySet());
                    for (Map.Entry<String, Long> e : entries) {
                        if (e.getValue() == null || e.getValue() < limit) {
                            userExport.remove(e.getKey());
                        }
                    }
                }
            });
        }
    }

    @Get("")
    @SecuredAction("archive.view")
    public void view(HttpServerRequest request) {
        renderView(request);
        eventStore.createAndStoreEvent(ArchiveEvent.ACCESS.name(), request);
    }

    @Post("/export")
    @SecuredAction("archive.export")
    public void export(final HttpServerRequest request) {
        UserUtils.getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    exportService.export(user, I18n.acceptLanguage(request), request,
                            new Handler<Either<String, String>>() {
                                @Override
                                public void handle(Either<String, String> event) {
                                    if (event.isRight()) {
                                        renderJson(request, new JsonObject().put("message", "export.in.progress")
                                                .put("exportId", event.right().getValue()));
                                    } else {
                                        badRequest(request, event.left().getValue());
                                    }
                                }
                            });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Get("/export/:exportId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    public void downloadExport(final HttpServerRequest request) {
        final String exportId = request.params().get("exportId");
        exportService.waitingExport(exportId, new Handler<Boolean>() {
            @Override
            public void handle(Boolean event) {
                if (Boolean.TRUE.equals(event)) {
                    log.debug("waiting export true");
                    final String address = "export." + exportId;
                    final MessageConsumer<JsonObject> consumer = eb.consumer(address);
                    final Handler<Message<JsonObject>> downloadHandler = new Handler<Message<JsonObject>>() {
                        @Override
                        public void handle(Message<JsonObject> event) {
                            String path = event.body().getString("destZip");
                            if ("ok".equals(event.body().getString("status")) && path != null) {
                                log.debug("Download export " + exportId);
                                event.reply(new JsonObject().put("status", "ok"));
                                downloadExport(request, exportId);
                            } else {
                                event.reply(new JsonObject().put("status", "error"));
                                renderError(request, event.body());
                            }
                            consumer.unregister();
                        }
                    };
                    request.response().closeHandler(new Handler<Void>() {
                        @Override
                        public void handle(Void event) {
                            consumer.unregister();
                            if (log.isDebugEnabled()) {
                                log.debug("Unregister handler : " + address);
                            }
                        }
                    });
                    consumer.handler(downloadHandler);
                } else {
                    log.debug("waiting export false");
                    downloadExport(request, exportId);
                }
            }
        });
    }

    private void downloadExport(final HttpServerRequest request, final String exportId) {
        exportService.setDownloadInProgress(exportId);
        storage.sendFile(exportId, exportId + ".zip", request, false, null, new Handler<AsyncResult<Void>>() {
            @Override
            public void handle(AsyncResult<Void> event) {
                if (event.succeeded() && request.response().getStatusCode() == 200) {
                    exportService.deleteExport(exportId);
                } else if (!request.response().ended()) {
                    notFound(request);
                }
            }
        });
    }

    @BusAddress("entcore.export")
    public void export(Message<JsonObject> message) {
        String action = message.body().getString("action", "");
        switch (action) {
        case "exported":
            exportService.exported(message.body().getString("exportId"), message.body().getString("status"),
                    message.body().getString("locale", "fr"),
                    message.body().getString("host", config.getString("host", "")));
            break;
        default:
            log.error("Archive : invalid action " + action);
        }
    }

}