BAG2LoadActionBean.java

/*
 * Copyright (C) 2021 B3Partners B.V.
 *
 * SPDX-License-Identifier: MIT
 *
 */

package nl.b3p.brmo.service.stripes;

import java.sql.Connection;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import javax.sql.DataSource;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;
import nl.b3p.brmo.bag2.loader.BAG2Database;
import nl.b3p.brmo.bag2.loader.BAG2ProgressReporter;
import nl.b3p.brmo.bag2.loader.cli.BAG2DatabaseOptions;
import nl.b3p.brmo.bag2.loader.cli.BAG2LoadOptions;
import nl.b3p.brmo.bag2.loader.cli.BAG2LoaderMain;
import nl.b3p.brmo.bag2.schema.BAG2SchemaMapper;
import nl.b3p.brmo.loader.util.BrmoException;
import nl.b3p.brmo.service.util.ConfigUtil;
import nl.b3p.brmo.sql.dialect.OracleDialect;
import nl.b3p.brmo.sql.dialect.PostGISDialect;
import nl.b3p.jdbc.util.converter.PGConnectionUnwrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stripesstuff.plugin.waitpage.WaitPage;

public class BAG2LoadActionBean implements ActionBean {
  private static final Log LOG = LogFactory.getLog(BAG2LoadActionBean.class);

  private ActionBeanContext context;

  private static final String JSP = "/WEB-INF/jsp/bestand/bag2.jsp";
  private static final String JSP_LOAD = "/WEB-INF/jsp/bestand/bag2load.jsp";

  private String files =
      "https://service.pdok.nl/kadaster/adressen/atom/v1_0/downloads/lvbag-extract-nl.zip";

  private DataSource rsgbbag;
  private Throwable namingException;
  private boolean connectionOk = false;
  private String connectionString;
  private String databaseName;
  private String databaseUserName;
  private String databaseDialect;
  private Exception connectionException;

  private Date standLoadTechnischeDatum;
  private Date currentTechnischeDatum;
  private Date standLoadTime;

  private boolean loading;
  private Date loadStart;
  private boolean loadResult;
  private Exception loadException;
  private String loadingLog;

  // <editor-fold desc="getters en setters">
  @Override
  public ActionBeanContext getContext() {
    return context;
  }

  @Override
  public void setContext(ActionBeanContext context) {
    this.context = context;
  }

  public DataSource getRsgbbag() {
    return rsgbbag;
  }

  public void setRsgbbag(DataSource rsgbbag) {
    this.rsgbbag = rsgbbag;
  }

  public Throwable getNamingException() {
    return namingException;
  }

  public void setNamingException(Throwable namingException) {
    this.namingException = namingException;
  }

  public boolean isConnectionOk() {
    return connectionOk;
  }

  public void setConnectionOk(boolean connectionOk) {
    this.connectionOk = connectionOk;
  }

  public String getConnectionString() {
    return connectionString;
  }

  public void setConnectionString(String connectionString) {
    this.connectionString = connectionString;
  }

  public String getDatabaseName() {
    return databaseName;
  }

  public void setDatabaseName(String databaseName) {
    this.databaseName = databaseName;
  }

  public String getDatabaseUserName() {
    return databaseUserName;
  }

  public void setDatabaseUserName(String databaseUserName) {
    this.databaseUserName = databaseUserName;
  }

  public String getDatabaseDialect() {
    return databaseDialect;
  }

  public void setDatabaseDialect(String databaseDialect) {
    this.databaseDialect = databaseDialect;
  }

  public Exception getConnectionException() {
    return connectionException;
  }

  public void setConnectionException(Exception connectionException) {
    this.connectionException = connectionException;
  }

  public Date getStandLoadTechnischeDatum() {
    return standLoadTechnischeDatum;
  }

  public void setStandLoadTechnischeDatum(Date standLoadTechnischeDatum) {
    this.standLoadTechnischeDatum = standLoadTechnischeDatum;
  }

  public Date getCurrentTechnischeDatum() {
    return currentTechnischeDatum;
  }

  public void setCurrentTechnischeDatum(Date currentTechnischeDatum) {
    this.currentTechnischeDatum = currentTechnischeDatum;
  }

  public Date getStandLoadTime() {
    return standLoadTime;
  }

  public void setStandLoadTime(Date standLoadTime) {
    this.standLoadTime = standLoadTime;
  }

  public String getFiles() {
    return files;
  }

  public void setFiles(String files) {
    this.files = files;
  }

  public boolean isLoading() {
    return loading;
  }

  public void setLoading(boolean loading) {
    this.loading = loading;
  }

  public boolean isLoadResult() {
    return loadResult;
  }

  public void setLoadResult(boolean loadResult) {
    this.loadResult = loadResult;
  }

  public String getLoadingLog() {
    return loadingLog;
  }

  public void setLoadingLog(String loadingLog) {
    this.loadingLog = loadingLog;
  }

  public Date getLoadStart() {
    return loadStart;
  }

  public void setLoadStart(Date loadStart) {
    this.loadStart = loadStart;
  }

  public Date getUpdateTime() {
    return new Date();
  }

  public void setUpdateTime(Date updateTime) {}

  public Exception getLoadException() {
    return loadException;
  }

  public void setLoadException(Exception loadException) {
    this.loadException = loadException;
  }

  // </editor-fold>

  @Before(on = "form")
  public void checkDatabase() {
    try {
      rsgbbag = ConfigUtil.getDataSourceRsgbBag(false);
    } catch (Exception e) {
      if (e instanceof BrmoException) {
        namingException = e.getCause();
      } else {
        namingException = e;
      }
    }

    if (rsgbbag != null) {
      try (Connection c = rsgbbag.getConnection()) {
        connectionOk = true;
        connectionString = c.getMetaData().getURL();
        databaseName = c.getMetaData().getDatabaseProductVersion();
        if (!databaseName.contains(c.getMetaData().getDatabaseProductName())) {
          databaseName = c.getMetaData().getDatabaseProductName() + " " + databaseName;
        }
        databaseUserName = c.getMetaData().getUserName();
        BAG2Database bag2Database = new BAG2Database(new BAG2DatabaseOptions(), c);

        if (bag2Database.getDialect() instanceof PostGISDialect) {
          databaseDialect = "postgis";
        } else if (bag2Database.getDialect() instanceof OracleDialect) {
          databaseDialect = "oracle";
        } else {
          throw new IllegalArgumentException("Unknown database dialect");
        }

        try {
          String s =
              bag2Database.getMetadata(BAG2SchemaMapper.Metadata.STAND_LOAD_TECHNISCHE_DATUM);
          if (s != null) {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            currentTechnischeDatum =
                df.parse(
                    bag2Database.getMetadata(BAG2SchemaMapper.Metadata.CURRENT_TECHNISCHE_DATUM));
            standLoadTechnischeDatum = df.parse(s);
            standLoadTime =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .parse(bag2Database.getMetadata(BAG2SchemaMapper.Metadata.STAND_LOAD_TIME));
          }
        } catch (SQLException e) {
          // Metadata table does not exist
        }

      } catch (Exception e) {
        connectionOk = false;
        connectionException = e;
      }
    }
  }

  @DefaultHandler
  public Resolution form() {
    return new ForwardResolution(JSP);
  }

  @WaitPage(path = JSP_LOAD, delay = 1000, refresh = 5000)
  public Resolution load() throws Exception {
    loading = true;
    loadStart = new Date();
    try (Connection rsgbBagConnection = ConfigUtil.getDataSourceRsgbBag(true).getConnection()) {
      BAG2DatabaseOptions databaseOptions = new BAG2DatabaseOptions();
      // Niet nodig (rsgbbagConnection wordt gebruikt), maar voor de duidelijkheid
      databaseOptions.setConnectionString(rsgbBagConnection.getMetaData().getURL());
      Connection connection = rsgbBagConnection;
      if (databaseOptions.getConnectionString().startsWith("jdbc:postgresql:")) {
        // Voor gebruik van pgCopy is unwrappen van de connectie nodig.
        // Ook al doet PostGISCopyInsertBatch zelf ook een unwrap, de PGConnectionUnwrapper
        // kan ook Tomcat JNDI
        // connection pool unwrapping aan welke niet met een normale Connection.unwrap()
        // werkt.
        connection = (Connection) PGConnectionUnwrapper.unwrap(rsgbBagConnection);
        databaseOptions.setUsePgCopy(true);
      }
      BAG2Database bag2Database =
          new BAG2Database(databaseOptions, connection) {
            /** connectie niet sluiten; dat doen we later als we helemaal klaar zijn */
            @Override
            public void close() {
              LOG.debug("Had de BAG database connectie kunnen sluiten... maar niet gedaan.");
            }
          };
      BAG2LoaderMain.configureLogging(false);
      BAG2LoaderMain main = new BAG2LoaderMain();
      BAG2LoadOptions loadOptions = new BAG2LoadOptions();
      main.loadFiles(
          bag2Database,
          databaseOptions,
          loadOptions,
          new BAG2ProgressReporter(),
          Arrays.stream(files.split("\n")).map(String::trim).toArray(String[]::new),
          null);
      this.loadResult = true;
    } catch (Exception e) {
      this.loadResult = false;
      this.loadException = e;
      LOG.error("Error loading BAG 2.0", e);
    } finally {
      this.loading = false;
    }
    return new ForwardResolution(JSP_LOAD);
  }
}