dk.dma.msinm.legacy.msi.service.LegacyMsiImportService.java Source code

Java tutorial

Introduction

Here is the source code for dk.dma.msinm.legacy.msi.service.LegacyMsiImportService.java

Source

/* Copyright (c) 2011 Danish Maritime Authority
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
package dk.dma.msinm.legacy.msi.service;

import dk.dma.msinm.common.MsiNmApp;
import dk.dma.msinm.common.db.Sql;
import dk.dma.msinm.common.sequence.DefaultSequence;
import dk.dma.msinm.common.sequence.Sequence;
import dk.dma.msinm.common.sequence.Sequences;
import dk.dma.msinm.common.service.BaseService;
import dk.dma.msinm.common.settings.DefaultSetting;
import dk.dma.msinm.common.settings.Setting;
import dk.dma.msinm.common.settings.Settings;
import dk.dma.msinm.common.settings.SettingsEntity;
import dk.dma.msinm.common.util.TextUtils;
import dk.dma.msinm.common.util.TimeUtils;
import dk.dma.msinm.legacy.msi.model.LegacyMessage;
import dk.dma.msinm.model.Area;
import dk.dma.msinm.model.Category;
import dk.dma.msinm.model.Location;
import dk.dma.msinm.model.Message;
import dk.dma.msinm.model.MessageDesc;
import dk.dma.msinm.model.Point;
import dk.dma.msinm.model.Priority;
import dk.dma.msinm.model.SeriesIdType;
import dk.dma.msinm.model.SeriesIdentifier;
import dk.dma.msinm.model.Status;
import dk.dma.msinm.model.Type;
import dk.dma.msinm.service.MessageService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * Imports data from a local db dump of the Danish MSI database
 */
@Stateless
public class LegacyMsiImportService extends BaseService {

    static final int LIMIT = 1000; // Import at most 1000 MSI at a time

    /**
     * Defines whether the import is active or not.
     * By default, the import is not active
     */
    public static final Setting LEGACY_MSI_ACTIVE = new DefaultSetting("legacyMsiImportActive", "false");

    /**
     * Registers the start date of the legacy message import.
     * By default, pick the first day of the year
     */
    public static final Setting LEGACY_MSI_START_DATE = new DefaultSetting("legacyMsiImportStartDate",
            String.valueOf(getJanFirstThisYear().getTime()));

    /**
     * Registers the last update date of the legacy messages that has been processed.
     * By default, pick the first day of the year
     */
    public static final Setting LEGACY_MSI_LAST_UPDATE = new DefaultSetting("legacyMsiImportLastUpdate",
            String.valueOf(getJanFirstThisYear().getTime()));

    @Inject
    Logger log;

    @Inject
    LegacyDatabase legacyDatabase;

    @Inject
    MsiNmApp app;

    @Inject
    MessageService messageService;

    @Inject
    LegacyMessageService legacyMessageService;

    @Inject
    Sequences sequences;

    @Inject
    @Sql("/sql/legacy_msi_data.sql")
    String legacyMsiDataSql;

    @Inject
    @Sql("/sql/all_legacy_msi.sql")
    String allLegacyMsiSql;

    @Inject
    Settings settings;

    /**
     * Import all MSI warnings
     * @return the imported/updated MSI warnings
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public List<LegacyMessage> importAllMsiWarnings() {

        // Check if the integration is active
        if (!settings.getBoolean(LEGACY_MSI_ACTIVE)) {
            return new ArrayList<>();
        }

        // Import the legacy MSI from the last registered update time until now
        // Note that at most LIMIT messages are processed
        Date lastRegisteredUpdateDate = settings.getDate(LEGACY_MSI_LAST_UPDATE);
        Date now = new Date();
        List<LegacyMessage> result = new ArrayList<>();
        Date lastUpdate = importMsi(result, allLegacyMsiSql, "all", lastRegisteredUpdateDate, now);

        // And register the last update time
        if (lastUpdate != null) {
            settings.updateSetting(new SettingsEntity(LEGACY_MSI_LAST_UPDATE.getSettingName(),
                    String.valueOf(lastUpdate.getTime())));
        }

        return result;
    }

    /**
     * Import active MSI warnings
     * @param sql the sql for fetching IDS
     * @return the last updated date
     */
    public Date importMsi(List<LegacyMessage> result, String sql, String type, Date... dataParams) {
        log.debug("Start importing at most " + LIMIT + " " + type + " legacy MSI warnings from local DB");

        Date lastUpdate = null;
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            conn = legacyDatabase.getConnection();

            stmt = conn.prepareStatement(sql);

            // Set the parameters, which must consist of a set of data parameters,
            // and lastly a limit parameter
            for (int x = 0; x < dataParams.length; x++) {
                stmt.setTimestamp(x + 1, new Timestamp(dataParams[x].getTime()));
            }
            stmt.setInt(dataParams.length + 1, LIMIT);

            log.debug("Executing SQL\n" + sql);
            long t0 = System.currentTimeMillis();

            // Fetch ID's of active MSI
            List<Integer> ids = new ArrayList<>();
            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                ids.add(rs.getInt("id"));
            }
            rs.close();
            log.debug(String.format("Fetched %d ID's for %s legacy MSI in %d ms", ids.size(), type,
                    System.currentTimeMillis() - t0));

            // Import the MSI's
            if (ids.size() > 0) {
                lastUpdate = importMsi(ids, conn, result);
            }

        } catch (Exception ex) {
            log.error("Failed fetching active legacy MSI messages from database ", ex);
        } finally {
            try {
                stmt.close();
            } catch (Exception ex) {
            }
            try {
                conn.close();
            } catch (Exception ex) {
            }
        }

        return lastUpdate;
    }

    /**
     * Import the legacy MSI with the given ID's
     * @param ids the ID's of the MSI to import
     * @param conn the DB connection
     * @return the result
     */
    private Date importMsi(List<Integer> ids, Connection conn, List<LegacyMessage> result) {
        log.debug("Start importing at most " + LIMIT + " legacy MSI warnings from local DB");
        long t0 = System.currentTimeMillis();

        Date lastUpdate = null;
        Statement stmt = null;
        try {
            stmt = conn.createStatement();

            String sql = legacyMsiDataSql.replace(":ids", StringUtils.join(ids, ","));

            log.debug("Executing SQL\n" + sql);
            ResultSet rs = stmt.executeQuery(sql);

            Integer skipId = null;
            LegacyMessage legacyMessage = null;
            while (rs.next()) {
                Integer id = getInt(rs, "id");
                Integer messageId = getInt(rs, "messageId");
                Boolean statusDraft = getBoolean(rs, "statusDraft");
                String navtexNo = getString(rs, "navtexNo");
                String descriptionEn = getString(rs, "description_en");
                String descriptionDa = getString(rs, "description_da");
                String title = getString(rs, "title");
                Date validFrom = getDate(rs, "validFrom");
                Date validTo = getDate(rs, "validTo");
                Date created = getDate(rs, "created");
                Date updated = getDate(rs, "updated");
                Date deleted = getDate(rs, "deleted");
                Integer version = getInt(rs, "version");
                String priority = getString(rs, "priority");
                String messageType = getString(rs, "messageType");
                String category1En = getString(rs, "category1_en");
                String category1Da = getString(rs, "category1_da");
                String category2En = getString(rs, "category2_en");
                String category2Da = getString(rs, "category2_da");
                String area1En = getString(rs, "area1_en");
                String area1Da = getString(rs, "area1_da");
                String area2En = getString(rs, "area2_en");
                String area2Da = getString(rs, "area2_da");
                String area3En = getString(rs, "area3_en");
                String area3Da = getString(rs, "area3_da");
                String locationType = getString(rs, "locationType");
                Integer pointIndex = getInt(rs, "pointIndex");
                Double pointLatitude = getDouble(rs, "pointLatitude");
                Double pointLongitude = getDouble(rs, "pointLongitude");
                Integer pointRadius = getInt(rs, "pointRadius");

                // Update the lastUpdate
                if (lastUpdate == null || lastUpdate.before(updated)) {
                    lastUpdate = updated;
                }

                if (skipId != null && skipId.intValue() == id.intValue()) {
                    continue;
                }

                if (legacyMessage != null && !legacyMessage.getLegacyId().equals(id)) {
                    legacyMessageService.saveLegacyMessage(legacyMessage);
                    legacyMessage = null;
                }

                // Handle first record of a new message
                if (legacyMessage == null) {

                    // Initialize the legacy message to update.
                    // If null is returned, the message should be skipped.
                    legacyMessage = legacyMessageService.initLegacyMessage(id, messageId, version);
                    if (legacyMessage == null) {
                        // Skip the import
                        skipId = id;
                        continue;
                    }

                    result.add(legacyMessage);
                    Message message = legacyMessage.getMessage();

                    // Update legacy message
                    legacyMessage.setLegacyId(id);
                    legacyMessage.setLegacyMessageId(messageId);
                    legacyMessage.setNavtexNo(navtexNo);
                    legacyMessage.setVersion(version);
                    legacyMessage.setUpdated(updated);

                    // Create the message series identifier
                    SeriesIdentifier identifier = new SeriesIdentifier();
                    identifier.setMainType(SeriesIdType.MSI);
                    message.setSeriesIdentifier(identifier);
                    if (StringUtils.isNotBlank(navtexNo) && navtexNo.split("-").length == 3) {
                        // Extract the series identifier from the navtext number
                        String[] parts = navtexNo.split("-");
                        identifier.setAuthority(parts[0]);
                        identifier.setNumber(Integer.valueOf(parts[1]));
                        identifier.setYear(2000 + Integer.valueOf(parts[2]));

                    } else {
                        // Some legacy MSI do not have a navtex number.
                        // Give them a number > 1000, since these are unused
                        Calendar cal = Calendar.getInstance();
                        cal.setTime(validFrom);
                        int year = cal.get(Calendar.YEAR);

                        if (!statusDraft) {
                            Sequence sequence = new DefaultSequence(
                                    "LEGACY_MESSAGE_SERIES_ID_MSI_" + app.getOrganization() + "_" + year, 1000);
                            identifier.setNumber((int) sequences.getNextValue(sequence));
                        }
                        identifier.setAuthority(app.getOrganization());
                        identifier.setYear(year);
                    }

                    // Message data
                    message.setCreated(created);
                    message.setUpdated(updated);
                    if ("Navtex".equals(messageType) || "Navwarning".equals(messageType)) {
                        message.setType(Type.SUBAREA_WARNING);
                    } else {
                        message.setType(Type.COASTAL_WARNING);
                    }

                    Date now = new Date();
                    Status status = Status.PUBLISHED;
                    if (deleted != null && statusDraft) {
                        status = Status.DELETED;
                    } else if (deleted != null && validTo != null && deleted.after(validTo)) {
                        status = Status.EXPIRED;
                    } else if (deleted != null) {
                        status = Status.CANCELLED;
                    } else if (statusDraft) {
                        status = Status.DRAFT;
                    } else if (validTo != null && now.after(validTo)) {
                        status = Status.EXPIRED;
                    }
                    message.setStatus(status);

                    message.setValidFrom(validFrom);
                    message.setValidTo((validTo != null) ? validTo : deleted);
                    try {
                        message.setPriority(Priority.valueOf(priority));
                    } catch (Exception ex) {
                        message.setPriority(Priority.NONE);
                    }

                    // Message Desc
                    if (StringUtils.isNotBlank(title) || StringUtils.isNotBlank(descriptionEn)
                            || StringUtils.isNotBlank(area3En)) {
                        MessageDesc descEn = message.checkCreateDesc("en");
                        descEn.setTitle(StringUtils.defaultString(title, descriptionEn));
                        descEn.setDescription(TextUtils.txt2html(descriptionEn));
                        descEn.setVicinity(area3En);
                    }
                    if (StringUtils.isNotBlank(title) || StringUtils.isNotBlank(descriptionDa)
                            || StringUtils.isNotBlank(area3Da)) {
                        MessageDesc descDa = message.checkCreateDesc("da");
                        descDa.setTitle(StringUtils.defaultString(title, descriptionDa));
                        descDa.setDescription(TextUtils.txt2html(descriptionDa));
                        descDa.setVicinity(area3Da);
                    }

                    // Areas
                    Area area = createAreaTemplate(area1En, area1Da, null);
                    // Annoyingly, legacy data has Danmark as a sub-area of Danmark
                    if (!StringUtils.equals(area1En, area2En) || !StringUtils.equals(area1Da, area2Da)) {
                        area = createAreaTemplate(area2En, area2Da, area);
                    }
                    message.setArea(area);

                    // Categories
                    message.getCategories().clear();
                    // Because of changes to the category structure, categories are no longer imported
                    /**
                    Category category = createCategoryTemplate(category1En, category1Da, null);
                    category = createCategoryTemplate(category2En, category2Da, category);
                    if (category != null) {
                    message.getCategories().add(category);
                    }
                     **/

                    // Locations
                    message.getLocations().clear();
                    if (pointLatitude != null) {
                        Location.LocationType type;
                        switch (locationType) {
                        case "Point":
                            type = Location.LocationType.POINT;
                            break;
                        case "Polygon":
                            type = Location.LocationType.POLYGON;
                            break;
                        case "Points":
                            type = Location.LocationType.POLYLINE;
                            break;
                        case "Polyline":
                            type = Location.LocationType.POLYLINE;
                            break;
                        default:
                            type = Location.LocationType.POLYLINE;
                        }
                        Location loc1 = new Location(type);
                        if (pointRadius != null) {
                            loc1.setRadius(pointRadius);
                        }
                        message.getLocations().add(loc1);
                    }
                }

                if (pointLatitude != null) {
                    Location loc1 = legacyMessage.getMessage().getLocations().get(0);
                    // If the type of the location is POINT, there must only be one point per location
                    if (loc1.getType() == Location.LocationType.POINT && loc1.getPoints().size() > 0) {
                        loc1 = new Location(Location.LocationType.POINT);
                        legacyMessage.getMessage().getLocations().add(loc1);
                    }
                    loc1.addPoint(new Point(loc1, pointLatitude, pointLongitude, pointIndex));
                }
            }

            if (legacyMessage != null) {
                legacyMessageService.saveLegacyMessage(legacyMessage);
            }

            if (result.size() > 0) {
                log.info(String.format("Import of %d legacy messages completed in %d ms", result.size(),
                        System.currentTimeMillis() - t0));
            }

            rs.close();
            stmt.close();
            conn.close();
        } catch (Exception ex) {
            log.error("Failed fetching legacy MSI messages from database ", ex);
        } finally {
            try {
                stmt.close();
            } catch (Exception ex) {
            }
            try {
                conn.close();
            } catch (Exception ex) {
            }
        }

        return lastUpdate;
    }

    /**
     * Creates an Area template based on the given Danish and English name
     * and optionally a parent Area
     * @param nameEn English name
     * @param nameDa Danish name
     * @param parent parent area
     * @return the Area template, or null if the names are empty
     */
    public static Area createAreaTemplate(String nameEn, String nameDa, Area parent) {
        Area area = null;
        if (StringUtils.isNotBlank(nameEn) || StringUtils.isNotBlank(nameDa)) {
            area = new Area();
            if (StringUtils.isNotBlank(nameEn)) {
                area.createDesc("en").setName(nameEn);
            }
            if (StringUtils.isNotBlank(nameDa)) {
                area.createDesc("da").setName(nameDa);
            }
            area.setParent(parent);
        }
        return area;
    }

    /**
     * Creates an Category template based on the given Danish and English name
     * and optionally a parent Category
     * @param nameEn English name
     * @param nameDa Danish name
     * @param parent parent area
     * @return the Category template, or null if the names are empty
     */
    public static Category createCategoryTemplate(String nameEn, String nameDa, Category parent) {
        Category category = null;
        if (StringUtils.isNotBlank(nameEn) || StringUtils.isNotBlank(nameDa)) {
            category = new Category();
            if (StringUtils.isNotBlank(nameEn)) {
                category.createDesc("en").setName(nameEn);
            }
            if (StringUtils.isNotBlank(nameDa)) {
                category.createDesc("da").setName(nameDa);
            }
            category.setParent(parent);
        }
        return category;
    }

    private String getString(ResultSet rs, String key) throws SQLException {
        String val = rs.getString(key);
        return rs.wasNull() ? null : val;
    }

    private Integer getInt(ResultSet rs, String key) throws SQLException {
        Integer val = rs.getInt(key);
        return rs.wasNull() ? null : val;
    }

    private Double getDouble(ResultSet rs, String key) throws SQLException {
        Double val = rs.getDouble(key);
        return rs.wasNull() ? null : val;
    }

    private Date getDate(ResultSet rs, String key) throws SQLException {
        Timestamp val = rs.getTimestamp(key);
        return rs.wasNull() ? null : val;
    }

    private Boolean getBoolean(ResultSet rs, String key) throws SQLException {
        boolean val = rs.getBoolean(key);
        return rs.wasNull() ? null : val;
    }

    /**
     * Computes the first day of the current year
     * @return the first day of the current year
     */
    private static Date getJanFirstThisYear() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.MONTH, 0);
        return TimeUtils.resetTime(cal.getTime());
    }
}