diff --git a/pom.xml b/pom.xml index 6cdac6dead..ded9ad197e 100644 --- a/pom.xml +++ b/pom.xml @@ -227,6 +227,10 @@ org.springframework.boot spring-boot-starter-jdbc + + org.springframework.session + spring-session-jdbc + @@ -445,9 +449,9 @@ 1.16 - com.typesafe.akka - akka-slf4j_2.11 - 2.5.31 + org.scala-lang + scala-library + 2.11.12 @@ -516,6 +520,12 @@ 9.22.3 + + software.amazon.awssdk + cloudwatch + 2.20.162 + + org.springframework.boot @@ -609,16 +619,15 @@ - com.typesafe.akka - akka-testkit_2.11 - 2.5.31 + org.testcontainers + postgresql + 1.20.3 test org.testcontainers - postgresql - 1.19.0 - test + localstack + 1.20.3 diff --git a/src/main/java/fi/vm/sade/valinta/kooste/App.java b/src/main/java/fi/vm/sade/valinta/kooste/App.java index 587bcd3713..12d8fd83fd 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/App.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/App.java @@ -1,12 +1,13 @@ package fi.vm.sade.valinta.kooste; -import java.io.*; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, MongoAutoConfiguration.class}) +@EnableScheduling public class App { public static final String CONTEXT_PATH = "/valintalaskentakoostepalvelu"; diff --git a/src/main/java/fi/vm/sade/valinta/kooste/configuration/AwsConfiguration.java b/src/main/java/fi/vm/sade/valinta/kooste/configuration/AwsConfiguration.java new file mode 100644 index 0000000000..f5b28813d9 --- /dev/null +++ b/src/main/java/fi/vm/sade/valinta/kooste/configuration/AwsConfiguration.java @@ -0,0 +1,38 @@ +package fi.vm.sade.valinta.kooste.configuration; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; +import software.amazon.awssdk.services.cloudwatch.CloudWatchClientBuilder; + +@Configuration +public class AwsConfiguration { + + @Bean + public CloudWatchClient cloudWatchClient(Environment environment) throws URISyntaxException { + boolean isLocal = + Arrays.stream(environment.getActiveProfiles()) + .anyMatch( + env -> + (env.equalsIgnoreCase("test") + || env.equalsIgnoreCase("mockservices") + || env.equalsIgnoreCase("dev"))); + + CloudWatchClientBuilder builder = + CloudWatchClient.builder().credentialsProvider(DefaultCredentialsProvider.create()); + + if (isLocal) { + builder + .region(Region.US_EAST_1) + .endpointOverride(new URI("http://localhost:" + System.getProperty("localstackPort"))); + } + + return builder.build(); + } +} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/configuration/SecurityConfiguration.java b/src/main/java/fi/vm/sade/valinta/kooste/configuration/SecurityConfiguration.java index 03a4d00481..47b3ea548a 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/configuration/SecurityConfiguration.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/configuration/SecurityConfiguration.java @@ -21,12 +21,16 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession; +import org.springframework.session.web.http.CookieSerializer; +import org.springframework.session.web.http.DefaultCookieSerializer; @Profile({"default", "dev"}) @Configuration @Order(2) @EnableMethodSecurity(securedEnabled = true) @EnableWebSecurity +@EnableJdbcHttpSession public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private Environment environment; @@ -152,4 +156,14 @@ protected void configure(HttpSecurity http) throws Exception { protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(casAuthenticationProvider()); } + + @Bean + public CookieSerializer cookieSerializer() { + DefaultCookieSerializer serializer = new DefaultCookieSerializer(); + serializer.setUseSecureCookie(true); + serializer.setCookieName("JSESSIONID"); + serializer.setCookiePath("/valintalaskentakoostepalvelu"); + serializer.setUseBase64Encoding(false); + return serializer; + } } diff --git a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/HttpClients.java b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/HttpClients.java index 27befd193a..a243bd1fad 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/HttpClients.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/HttpClients.java @@ -274,6 +274,7 @@ public RestCasClient getSuoritusrekisteriCasClient( return new RestCasClient( new CasConfig.CasConfigBuilder( username, password, ticketsUrl, service, CSRF_VALUE, CALLER_ID, "") + .setNumberOfRetries(0) .setJsessionName("JSESSIONID") .build()); } diff --git a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/seuranta/LaskentaSeurantaAsyncResource.java b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/seuranta/LaskentaSeurantaAsyncResource.java deleted file mode 100644 index d159c31451..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/seuranta/LaskentaSeurantaAsyncResource.java +++ /dev/null @@ -1,40 +0,0 @@ -package fi.vm.sade.valinta.kooste.external.resource.seuranta; - -import fi.vm.sade.valinta.kooste.valintalaskenta.resource.LaskentaParams; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeDto; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeTila; -import fi.vm.sade.valinta.seuranta.dto.IlmoitusDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTila; -import fi.vm.sade.valinta.seuranta.dto.TunnisteDto; -import io.reactivex.Observable; -import java.util.List; -import java.util.Optional; -import org.springframework.http.ResponseEntity; - -public interface LaskentaSeurantaAsyncResource { - - Observable> otaSeuraavaLaskentaTyonAlle(); - - Observable laskenta(String uuid); - - Observable resetoiTilat(String uuid); - - Observable luoLaskenta( - LaskentaParams laskentaParams, List hakukohdeOids); - - Observable merkkaaHakukohteenTila( - String uuid, - String hakukohdeOid, - HakukohdeTila tila, - Optional ilmoitusDtoOptional); - - Observable merkkaaLaskennanTila( - String uuid, - LaskentaTila tila, - HakukohdeTila hakukohdetila, - Optional ilmoitusDtoOptional); - - Observable merkkaaLaskennanTila( - String uuid, LaskentaTila tila, Optional ilmoitusDtoOptional); -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/seuranta/impl/LaskentaSeurantaAsyncResourceImpl.java b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/seuranta/impl/LaskentaSeurantaAsyncResourceImpl.java deleted file mode 100644 index 75d88288d6..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/seuranta/impl/LaskentaSeurantaAsyncResourceImpl.java +++ /dev/null @@ -1,195 +0,0 @@ -package fi.vm.sade.valinta.kooste.external.resource.seuranta.impl; - -import com.google.gson.reflect.TypeToken; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.viestintapalvelu.RestCasClient; -import fi.vm.sade.valinta.kooste.url.UrlConfiguration; -import fi.vm.sade.valinta.kooste.valintalaskenta.resource.LaskentaParams; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeDto; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeTila; -import fi.vm.sade.valinta.seuranta.dto.IlmoitusDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTila; -import fi.vm.sade.valinta.seuranta.dto.TunnisteDto; -import io.reactivex.Observable; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; - -@Service -public class LaskentaSeurantaAsyncResourceImpl implements LaskentaSeurantaAsyncResource { - private final Logger LOG = LoggerFactory.getLogger(getClass()); - - private final RestCasClient restCasClient; - - private final UrlConfiguration urlConfiguration; - - @Autowired - public LaskentaSeurantaAsyncResourceImpl( - @Qualifier("ValintalaskentaCasClient") RestCasClient restCasClient) { - this.restCasClient = restCasClient; - this.urlConfiguration = UrlConfiguration.getInstance(); - } - - @Override - public Observable> otaSeuraavaLaskentaTyonAlle() { - Function> extractor = - response -> StringUtils.isBlank(response) ? Optional.empty() : Optional.of(response); - return Observable.fromFuture( - this.restCasClient - .get( - this.urlConfiguration.url( - "seuranta-service.seuranta.laskenta.otaseuraavalaskentatyonalle"), - Map.of("Accept", "text/plain"), - 10 * 60 * 1000) - .thenApply(response -> response.getResponseBody()) - .thenApply(extractor)); - } - - public Observable laskenta(String uuid) { - return Observable.fromFuture( - this.restCasClient.get( - this.urlConfiguration.url("seuranta-service.seuranta.kuormantasaus.laskenta", uuid), - new TypeToken() {}, - Collections.emptyMap(), - 10 * 60 * 1000)); - } - - public Observable resetoiTilat(String uuid) { - return Observable.fromFuture( - this.restCasClient.put( - this.urlConfiguration.url( - "seuranta-service.seuranta.kuormantasaus.laskenta.resetoi", uuid), - new TypeToken() {}, - uuid, - Collections.emptyMap(), - 10 * 60 * 1000)); - } - - public Observable luoLaskenta( - LaskentaParams laskentaParams, List hakukohdeOids) { - - String url = - this.urlConfiguration.url( - "seuranta-service.seuranta.kuormantasaus.laskenta.tyyppi", - laskentaParams.getHakuOid(), - laskentaParams.getLaskentatyyppi()); - url += "?userOID=" + URLEncoder.encode(laskentaParams.getUserOID(), StandardCharsets.UTF_8); - if (laskentaParams.getNimi() != null) { - url += "&nimi=" + URLEncoder.encode(laskentaParams.getNimi(), StandardCharsets.UTF_8); - } - url += "&haunnimi=" + URLEncoder.encode(laskentaParams.getHaunNimi(), StandardCharsets.UTF_8); - url += "&erillishaku=" + laskentaParams.isErillishaku(); - if (laskentaParams.getValinnanvaihe() != null) { - url += "&valinnanvaihe=" + laskentaParams.getValinnanvaihe(); - } - if (laskentaParams.getIsValintakoelaskenta() != null) { - url += "&valintakoelaskenta=" + laskentaParams.getIsValintakoelaskenta(); - } - - return Observable.fromFuture( - this.restCasClient.post( - url, new TypeToken<>() {}, hakukohdeOids, Collections.emptyMap(), 10 * 60 * 1000)); - } - - public Observable merkkaaLaskennanTila( - String uuid, LaskentaTila tila, Optional ilmoitusDtoOptional) { - String url = - this.urlConfiguration.url( - "seuranta-service.seuranta.kuormantasaus.laskenta.tila", uuid, tila); - try { - if (ilmoitusDtoOptional.isPresent()) { - return Observable.fromFuture( - this.restCasClient - .post(url, ilmoitusDtoOptional.get(), Collections.emptyMap(), 10 * 60 * 1000) - .thenApply(r -> ResponseEntity.ok().build())); - } else { - return Observable.fromFuture( - this.restCasClient - .put(url, tila, Collections.emptyMap(), 10 * 60 * 1000) - .thenApply(r -> ResponseEntity.ok().build())); - } - } catch (Exception e) { - LOG.error("Seurantapalvelun kutsu paatyi virheeseen!" + url, e); - return Observable.error(e); - } - } - - public Observable merkkaaLaskennanTila( - String uuid, - LaskentaTila tila, - HakukohdeTila hakukohdetila, - Optional ilmoitusDtoOptional) { - String url = - this.urlConfiguration.url( - "seuranta-service.seuranta.kuormantasaus.laskenta.tila.hakukohde", - uuid, - tila, - hakukohdetila); - try { - if (ilmoitusDtoOptional.isPresent()) { - return Observable.fromFuture( - this.restCasClient - .post(url, ilmoitusDtoOptional.get(), Collections.emptyMap(), 10 * 60 * 1000) - .thenApply(r -> ResponseEntity.ok().build())); - } else { - return Observable.fromFuture( - this.restCasClient - .put(url, new TypeToken<>() {}, tila, Collections.emptyMap(), 10 * 60 * 1000) - .thenApply(r -> ResponseEntity.ok().build())); - } - } catch (Exception e) { - LOG.error("Seurantapalvelun kutsu " + url + " laskennalle " + uuid + " paatyi virheeseen", e); - return Observable.error(e); - } - } - - @Override - public Observable merkkaaHakukohteenTila( - String uuid, - String hakukohdeOid, - HakukohdeTila tila, - Optional ilmoitusDtoOptional) { - String url = - this.urlConfiguration.url( - "seuranta-service.seuranta.kuormantasaus.laskenta.hakukohde.tila", - uuid, - hakukohdeOid, - tila); - try { - if (ilmoitusDtoOptional.isPresent()) { - return Observable.fromFuture( - this.restCasClient - .post(url, ilmoitusDtoOptional.get(), Collections.emptyMap(), 10 * 60 * 1000) - .thenApply(r -> ResponseEntity.ok().build())); - } else { - return Observable.fromFuture( - this.restCasClient - .put(url, new TypeToken<>() {}, tila, Collections.emptyMap(), 10 * 60 * 1000) - .thenApply(r -> ResponseEntity.ok().build())); - } - } catch (Exception e) { - LOG.error( - "Seurantapalvelun kutsu " - + url - + " laskennalle " - + uuid - + " ja hakukohteelle " - + hakukohdeOid - + " paatyi virheeseen", - e); - return Observable.error(e); - } - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/ValintaperusteetAsyncResource.java b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/ValintaperusteetAsyncResource.java index c8ced1d4b1..9402a59311 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/ValintaperusteetAsyncResource.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/ValintaperusteetAsyncResource.java @@ -3,7 +3,6 @@ import fi.vm.sade.service.valintaperusteet.dto.HakukohdeImportDTO; import fi.vm.sade.service.valintaperusteet.dto.HakukohdeJaValintakoeDTO; import fi.vm.sade.service.valintaperusteet.dto.HakukohdeJaValintaperusteDTO; -import fi.vm.sade.service.valintaperusteet.dto.HakukohdeViiteDTO; import fi.vm.sade.service.valintaperusteet.dto.ValinnanVaiheJonoillaDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintakoeDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteDTO; @@ -25,9 +24,6 @@ Observable>> haeValintatapajonotSijoittelul CompletableFuture> haeHakijaryhmat(String hakukohdeOid); - // @GET /valintaperusteet-service/resources/hakukohde/haku/{} - Observable> haunHakukohteet(String hakuOid); - CompletableFuture> haeValintaperusteet( String hakukohdeOid, Integer valinnanVaiheJarjestysluku); diff --git a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/impl/ValintaperusteetAsyncResourceImpl.java b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/impl/ValintaperusteetAsyncResourceImpl.java index 894c93fc3a..d44b590b3c 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/impl/ValintaperusteetAsyncResourceImpl.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/valintaperusteet/impl/ValintaperusteetAsyncResourceImpl.java @@ -5,7 +5,6 @@ import fi.vm.sade.service.valintaperusteet.dto.HakukohdeImportDTO; import fi.vm.sade.service.valintaperusteet.dto.HakukohdeJaValintakoeDTO; import fi.vm.sade.service.valintaperusteet.dto.HakukohdeJaValintaperusteDTO; -import fi.vm.sade.service.valintaperusteet.dto.HakukohdeViiteDTO; import fi.vm.sade.service.valintaperusteet.dto.ValinnanVaiheJonoillaDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintakoeDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteDTO; @@ -82,16 +81,6 @@ public CompletableFuture> haeValintaperusteet( url, new TypeToken>() {}, Collections.emptyMap(), 60 * 60 * 1000); } - public Observable> haunHakukohteet(String hakuOid) { - return Observable.fromFuture( - this.httpClient.get( - this.urlConfiguration.url( - "valintaperusteet-service.valintalaskentakoostepalvelu.hakukohde.haku", hakuOid), - new TypeToken>() {}, - Collections.emptyMap(), - 10 * 60 * 1000)); - } - @Override public Observable tuoHakukohde(HakukohdeImportDTO hakukohde) { return Observable.fromFuture( diff --git a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/viestintapalvelu/RestCasClient.java b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/viestintapalvelu/RestCasClient.java index 51168efb30..1fcd102214 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/external/resource/viestintapalvelu/RestCasClient.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/external/resource/viestintapalvelu/RestCasClient.java @@ -141,8 +141,11 @@ private CompletableFuture executeAndThrowOnError(Request request) { throw new RestCasClientException( response, String.format( - "Error calling url %s with method %s, response code %s", - request.getUrl(), request.getMethod(), response.getStatusCode())); + "Error calling url %s with method %s, response code %s, message: %s", + request.getUrl(), + request.getMethod(), + response.getStatusCode(), + response.getResponseBody())); } }); } diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/ConcurrencyLimiter.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/ConcurrencyLimiter.java new file mode 100644 index 0000000000..cb5a0cade7 --- /dev/null +++ b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/ConcurrencyLimiter.java @@ -0,0 +1,187 @@ +package fi.vm.sade.valinta.kooste.valintalaskenta.actor; + +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Luokka jonka avulla voi hallita lähtötietojen haun rinnakkaisuutta taustajärjestelmistä. Perustuu + * semaforiin, ts. siihen että rinnakkaisille kutsuille on tarjolla tietty määrä lupia, joita kutsut + * tarvitsevat. Lupia voi tarvita joko yhden per kutsu, tai sitten suuremman määrän riippuen + * haettavan data määrästä (esim. suoritusrekisteri). Tämä luokka pitää myös kirjaa odotukseen ja + * kutsujen suorittamiseen käytetystä ajasta jotta tämä voidaan raportoida CloudWatchiin. + */ +public class ConcurrencyLimiter { + + private static final Logger LOG = LoggerFactory.getLogger(ConcurrencyLimiter.class); + + public static final Duration ONGOING = Duration.ofMillis(Long.MIN_VALUE + 1); + public static final Duration TIMEOUT = Duration.ofMillis(Long.MIN_VALUE + 2); + public static final Duration ERROR = Duration.ofMillis(Long.MIN_VALUE + 3); + + private int maxPermits; + private final String nimi; + private final Semaphore semaphore; + private final ExecutorService executor; + private final AtomicInteger waiting; + private final AtomicInteger active; + + /** + * Luo uuden limitterin + * + * @param permits kuinka monta rinnakkaista "lupaa" taustajärjestelmäkutsuihin on tarjolla (yksi + * kutsu saattaa tarvita useamman luvan) + * @param nimi limitterin nimi + * @param executor limitterissä käytettävä {@link Executor} + */ + public ConcurrencyLimiter(int permits, String nimi, ExecutorService executor) { + this.nimi = nimi; + this.maxPermits = permits; + this.semaphore = new Semaphore(permits, true); + this.executor = executor; + this.waiting = new AtomicInteger(0); + this.active = new AtomicInteger(0); + } + + /** + * Asettaa rinnakkaisten "lupien" määrän + * + * @param newPermits uusi lupien määrä + */ + public void setMaxPermits(int newPermits) { + int dPermits = newPermits - this.maxPermits; + this.maxPermits = newPermits; + if (dPermits == 0) { + return; + } + if (dPermits > 0) { + LOG.info("Lisätään vaiheen " + this.nimi + " limitteriin " + dPermits + " permittiä."); + this.semaphore.release(dPermits); + } else { + LOG.info("Vähennetään vaiheen " + this.nimi + " limitteristä " + -dPermits + " permittiä."); + this.executor.submit(() -> this.semaphore.acquireUninterruptibly(-dPermits)); + } + } + + /** + * Palauttaa kuinka monta pyyntöä odottaa lupaa + * + * @return + */ + public int getWaiting() { + return this.waiting.get(); + } + + /** + * Palauttaa kuinka montaa pyyntöä suoritetaan + * + * @return + */ + public int getActive() { + return this.active.get(); + } + + /** + * Palauttaa limitterin nimen + * + * @return + */ + public String getNimi() { + return this.nimi; + } + + public static String asDurationString(Duration duration) { + if (duration == ONGOING) { + return "ongoing"; + } else if (duration == TIMEOUT) { + return "timeout"; + } else if (duration == ERROR) { + return "error"; + } + return duration.toMillis() + ""; + } + + /** + * Suoritetaan ratelimitoitu pyyntö + * + * @param permits kuinka monta "lupaa" pyyntöön tarvitaan + * @param waitDurations mappi johon tallennetaan odotuksen kesto + * @param invokeDurations mappi johon tallennetaan suorituksen kesto + * @param supplier {@link Supplier} joka palauttaa suoritettavan pyynnön + * @return {@link CompletableFuture} joka palauttaa pyynnön paluuarvon + */ + public CompletableFuture withConcurrencyLimit( + int permits, + Map waitDurations, + Map invokeDurations, + Supplier> supplier) { + + Instant waitStart = Instant.now(); + CompletableFuture future = new CompletableFuture<>(); + int requiredPermits = Math.min(this.maxPermits, permits); + + this.executor.submit( + () -> { + // haetaan lupa suorittaa pyyntö ja tallennetaan odottamiseen mennyt aika + this.waiting.incrementAndGet(); + try { + this.semaphore.acquireUninterruptibly(requiredPermits); + } finally { + this.waiting.decrementAndGet(); + } + + if (nimi.equals("suoritukset")) { + LOG.info( + "Haettiin " + + permits + + " permittiä, jäi " + + this.semaphore.availablePermits() + + " permittiä, ajossa " + + this.getActive() + + ", odottaa " + + this.getWaiting()); + } + + Instant invokeStart = Instant.now(); + waitDurations.put(this.nimi, Duration.between(waitStart, invokeStart)); + + // suoritetaan pyyntö + this.active.incrementAndGet(); + try { + invokeDurations.put(this.nimi, ONGOING); + T result = supplier.get().get(); + invokeDurations.put(this.nimi, Duration.between(invokeStart, Instant.now())); + future.complete(result); + } catch (ExecutionException e) { + Throwable underlyingCause = getUnderlyingCause(e); + if (underlyingCause instanceof TimeoutException) { + invokeDurations.put(this.nimi, TIMEOUT); + future.completeExceptionally(underlyingCause); + } else { + invokeDurations.put(this.nimi, ERROR); + future.completeExceptionally(e); + } + } catch (Exception e) { + invokeDurations.put(this.nimi, ERROR); + future.completeExceptionally(e); + } finally { + semaphore.release(requiredPermits); + this.active.decrementAndGet(); + } + }); + + return future; + } + + private static Throwable getUnderlyingCause(Throwable t) { + if (t.getCause() != null) { + return getUnderlyingCause(t.getCause()); + } + return t; + } +} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/HakukohdeLaskuri.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/HakukohdeLaskuri.java deleted file mode 100644 index 278fca310d..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/HakukohdeLaskuri.java +++ /dev/null @@ -1,42 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import com.google.common.hash.BloomFilter; -import com.google.common.hash.Funnels; -import java.nio.charset.Charset; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class HakukohdeLaskuri extends Laskuri { - private static final Logger LOG = LoggerFactory.getLogger(HakukohdeLaskuri.class); - private final BloomFilter duplicateChecker; - - public HakukohdeLaskuri(int hakukohteita) { - super(hakukohteita); - this.duplicateChecker = - BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), hakukohteita); - } - - public boolean done(String hakukohdeOid) { - try { - if (!duplicateChecker.put(hakukohdeOid)) { - LOG.error( - "Hakukohde {} saattoi olla jo merkittyna valmiiksi! Havainto oli virheellinen noin 3% todennakoisyydella!", - hakukohdeOid); - } - } catch (Exception e) { - LOG.error("Bloomfilterin kutsu epaonnistui!", e); - } - int l = tiputaLaskuria(); - if (l < 0) { - int yhteensa = getYhteensa(); - LOG.error( - "Hakukohteita merkitty valmiiksi odotettua enemman! {}/{} eli ylimaaraisia merkintoja on {}", - (-l) + yhteensa, - yhteensa, - -l); - return false; - } - boolean ready = l == 0; - return ready; - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActor.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActor.java deleted file mode 100644 index fffd94014b..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActor.java +++ /dev/null @@ -1,13 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import akka.actor.TypedActor; - -public interface LaskentaActor extends TypedActor.PostStop { - String getHakuOid(); - - boolean isValmis(); - - void start(); - - void lopeta(); -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorFactory.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorFactory.java deleted file mode 100644 index 00e71b9ef9..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorFactory.java +++ /dev/null @@ -1,725 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import static fi.vm.sade.valinta.sharedutils.http.ObservableUtil.wrapAsRunOnlyOnceObservable; -import static io.reactivex.Observable.just; -import static java.util.Collections.emptyList; -import static org.apache.commons.lang3.tuple.Pair.of; - -import fi.vm.sade.auditlog.Changes; -import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetDTO; -import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetHakijaryhmaDTO; -import fi.vm.sade.service.valintaperusteet.dto.ValintatapajonoJarjestyskriteereillaDTO; -import fi.vm.sade.valinta.kooste.KoosteAudit; -import fi.vm.sade.valinta.kooste.external.resource.ataru.AtaruAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.hakuapp.ApplicationAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.koski.KoskiOppija; -import fi.vm.sade.valinta.kooste.external.resource.oppijanumerorekisteri.OppijanumerorekisteriAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.oppijanumerorekisteri.dto.HenkiloViiteDto; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.SuoritusrekisteriAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.dto.Oppija; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.Haku; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.TarjontaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintalaskenta.ValintalaskentaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintaperusteet.ValintaperusteetAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintapiste.ValintapisteAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintapiste.dto.PisteetWithLastModified; -import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; -import fi.vm.sade.valinta.kooste.util.HakemusWrapper; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaResurssinhakuWrapper.PyynnonTunniste; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.kooste.valintalaskenta.util.HakemuksetConverterUtil; -import fi.vm.sade.valinta.sharedutils.AuditLog; -import fi.vm.sade.valinta.sharedutils.ValintaResource; -import fi.vm.sade.valinta.sharedutils.ValintaperusteetOperation; -import fi.vm.sade.valintalaskenta.domain.dto.LaskeDTO; -import fi.vm.sade.valintalaskenta.domain.dto.SuoritustiedotDTO; -import io.reactivex.Observable; -import java.math.BigDecimal; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.apache.commons.lang3.tuple.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.jmx.export.annotation.ManagedOperation; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.stereotype.Service; - -@Service -@ManagedResource( - objectName = "OPH:name=LaskentaActorFactory", - description = "LaskentaActorFactory mbean") -public class LaskentaActorFactory { - private static final Logger LOG = LoggerFactory.getLogger(LaskentaActorFactory.class); - - private final ValintapisteAsyncResource valintapisteAsyncResource; - private final ValintalaskentaAsyncResource valintalaskentaAsyncResource; - private final ApplicationAsyncResource applicationAsyncResource; - private final AtaruAsyncResource ataruAsyncResource; - private final ValintaperusteetAsyncResource valintaperusteetAsyncResource; - private final LaskentaSeurantaAsyncResource laskentaSeurantaAsyncResource; - private final SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource; - private final OppijanumerorekisteriAsyncResource oppijanumerorekisteriAsyncResource; - private final TarjontaAsyncResource tarjontaAsyncResource; - private final KoskiService koskiService; - private final HakemuksetConverterUtil hakemuksetConverterUtil; - private volatile int splittaus; - - @Autowired - public LaskentaActorFactory( - @Value("${valintalaskentakoostepalvelu.laskennan.splittaus:1}") int splittaus, - ValintalaskentaAsyncResource valintalaskentaAsyncResource, - ApplicationAsyncResource applicationAsyncResource, - AtaruAsyncResource ataruAsyncResource, - ValintaperusteetAsyncResource valintaperusteetAsyncResource, - LaskentaSeurantaAsyncResource laskentaSeurantaAsyncResource, - SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource, - TarjontaAsyncResource tarjontaAsyncResource, - ValintapisteAsyncResource valintapisteAsyncResource, - KoskiService koskiService, - HakemuksetConverterUtil hakemuksetConverterUtil, - OppijanumerorekisteriAsyncResource oppijanumerorekisteriAsyncResource) { - this.splittaus = splittaus; - this.valintalaskentaAsyncResource = valintalaskentaAsyncResource; - this.applicationAsyncResource = applicationAsyncResource; - this.ataruAsyncResource = ataruAsyncResource; - this.valintaperusteetAsyncResource = valintaperusteetAsyncResource; - this.laskentaSeurantaAsyncResource = laskentaSeurantaAsyncResource; - this.suoritusrekisteriAsyncResource = suoritusrekisteriAsyncResource; - this.tarjontaAsyncResource = tarjontaAsyncResource; - this.valintapisteAsyncResource = valintapisteAsyncResource; - this.koskiService = koskiService; - this.hakemuksetConverterUtil = hakemuksetConverterUtil; - this.oppijanumerorekisteriAsyncResource = oppijanumerorekisteriAsyncResource; - } - - private Pair> headAndTail(Collection c) { - String head = c.iterator().next(); - Collection tail = c.stream().skip(1L).collect(Collectors.toList()); - return Pair.of(head, tail); - } - - private Observable, List>> fetchRecursively( - Function> haeLaskeDTOHakukohteelle, - Observable, List>> tulosObservable) { - return tulosObservable.switchMap( - haetutResurssit -> { - Collection oids = haetutResurssit.getKey(); - if (oids.isEmpty()) { - return tulosObservable; - } else { - Pair> headWithTail = headAndTail(oids); - Observable, List>> aiemmatJaUusiResurssi = - haeLaskeDTOHakukohteelle - .apply(headWithTail.getKey()) - .map( - laskeDTO -> - Pair.of( - headWithTail.getRight(), - Stream.concat( - Stream.of(laskeDTO), haetutResurssit.getRight().stream()) - .collect(Collectors.toList()))); - return aiemmatJaUusiResurssi.switchMap( - haetut -> fetchRecursively(haeLaskeDTOHakukohteelle, Observable.just(haetut))); - } - }); - } - - private LaskentaActor createValintaryhmaActor( - AuditSession auditSession, - LaskentaSupervisor laskentaSupervisor, - Haku haku, - LaskentaActorParams a, - Date nyt) { - LaskentaActorParams fakeOnlyOneHakukohdeParams = - new LaskentaActorParams( - a.getLaskentaStartParams(), - Collections.singletonList(new HakukohdeJaOrganisaatio()), - a.getParametritDTO()); - fakeOnlyOneHakukohdeParams.setValintaryhmalaskenta(true); - final SuoritustiedotDTO suoritustiedot = new SuoritustiedotDTO(); - return laskentaHakukohteittainActor( - laskentaSupervisor, - fakeOnlyOneHakukohdeParams, - hakukohdeJaOrganisaatio -> { - String uuid = a.getUuid(); - Collection hakukohdeOids = - a.getHakukohdeOids().stream() - .map(HakukohdeJaOrganisaatio::getHakukohdeOid) - .collect(Collectors.toList()); - String hakukohteidenNimi = - String.format("Valintaryhmälaskenta %s hakukohteella", hakukohdeOids.size()); - LOG.info("(Uuid={}) {}", uuid, hakukohteidenNimi); - Observable, List>> recursiveSequentialFetch = - just(of(hakukohdeOids, emptyList())); - - Function> fetchLaskeDTO = - h -> - Observable.fromFuture( - fetchResourcesForOneLaskenta( - auditSession, uuid, haku, h, a, true, true, suoritustiedot, nyt)); - - Observable laskenta = - fetchRecursively(fetchLaskeDTO, recursiveSequentialFetch) - .switchMap( - hksAndDtos -> { - List allLaskeDTOs = hksAndDtos.getRight(); - if (!hksAndDtos.getKey().isEmpty()) { // sanity check - throw new RuntimeException("Kaikkia hakukohteita ei ollut vielä haettu!"); - } else if (allLaskeDTOs.size() != hakukohdeOids.size()) { - throw new RuntimeException( - "Hakukohteita oli " - + hakukohdeOids.size() - + " mutta haettuja laskeDTOita oli " - + allLaskeDTOs.size() - + "!"); - } - return valintalaskentaAsyncResource.laskeJaSijoittele( - uuid, allLaskeDTOs, suoritustiedot); - }); - return laskenta; - }); - } - - @ManagedOperation - public void setLaskentaSplitCount(int splitCount) { - this.splittaus = splitCount; - LOG.info("Laskenta split count asetettu arvoon {}", splitCount); - } - - private LaskentaActor createValintakoelaskentaActor( - AuditSession auditSession, - LaskentaSupervisor laskentaSupervisor, - Haku haku, - LaskentaActorParams actorParams, - Date nyt) { - final String uuid = actorParams.getUuid(); - return laskentaHakukohteittainActor( - laskentaSupervisor, - actorParams, - hakukohdeJaOrganisaatio -> { - String hakukohdeOid = hakukohdeJaOrganisaatio.getHakukohdeOid(); - SuoritustiedotDTO suoritustiedot = new SuoritustiedotDTO(); - Observable laskenta = - Observable.fromFuture( - fetchResourcesForOneLaskenta( - auditSession, - uuid, - haku, - hakukohdeOid, - actorParams, - false, - false, - suoritustiedot, - nyt)) - .switchMap( - timedSwitchMap( - (took, exception) -> { - if (exception.isPresent()) { - LOG.error( - "(Uuid={}) (Kesto {}s) Laskenta hakukohteelle {} on päättynyt virheeseen: {}", - uuid, - millisToString(took), - hakukohdeOid, - exception.get()); - } else { - LOG.info( - "(Uuid={}) (Kesto {}s) Laskenta hakukohteelle {} on päättynyt onnistuneesti.", - uuid, - millisToString(took), - hakukohdeOid); - } - }, - laskeDTO -> - valintalaskentaAsyncResource.valintakokeet( - laskeDTO, suoritustiedot))); - return laskenta; - }); - } - - private io.reactivex.functions.Function> timedSwitchMap( - BiConsumer> log, - Function> f) { // Function> switchMap) { - return (A a) -> { - long start = System.currentTimeMillis(); - Observable t = wrapAsRunOnlyOnceObservable(f.apply(a)); - t.subscribe( - (n) -> log.accept(System.currentTimeMillis() - start, Optional.empty()), - (n) -> log.accept(System.currentTimeMillis() - start, Optional.ofNullable(n))); - return t; - }; - } - - private static String millisToString(long millis) { - return new BigDecimal(millis) - .divide(new BigDecimal(1000), 2, BigDecimal.ROUND_HALF_UP) - .toPlainString(); - } - - private LaskentaActor createValintalaskentaActor( - AuditSession auditSession, - LaskentaSupervisor laskentaSupervisor, - Haku haku, - LaskentaActorParams actorParams) { - final String uuid = actorParams.getUuid(); - final Date nyt = new Date(); - LOG.info( - String.format( - "Jos laskennassa %s on jonoja, joita ei lasketa %s jälkeen, ei haeta niille tietoja Koskesta.", - actorParams.getUuid(), nyt)); - return laskentaHakukohteittainActor( - laskentaSupervisor, - actorParams, - hakukohdeJaOrganisaatio -> { - String hakukohdeOid = hakukohdeJaOrganisaatio.getHakukohdeOid(); - LOG.info("(Uuid={}) Haetaan laskennan resursseja hakukohteelle {}", uuid, hakukohdeOid); - - SuoritustiedotDTO suoritustiedot = new SuoritustiedotDTO(); - Observable laskenta = - Observable.fromFuture( - fetchResourcesForOneLaskenta( - auditSession, - uuid, - haku, - hakukohdeOid, - actorParams, - false, - true, - suoritustiedot, - nyt)) - .switchMap( - timedSwitchMap( - (took, exception) -> { - if (exception.isPresent()) { - LOG.error( - "(Uuid={}) (Kesto {}s) Laskenta hakukohteelle {} on päättynyt virheeseen: {}", - uuid, - millisToString(took), - hakukohdeOid, - exception.get()); - } else { - LOG.info( - "(Uuid={}) (Kesto {}s) Laskenta hakukohteelle {} on päättynyt onnistuneesti.", - uuid, - millisToString(took), - hakukohdeOid); - } - }, - laskeDTO -> - valintalaskentaAsyncResource.laske(laskeDTO, suoritustiedot))); - return laskenta; - }); - } - - private LaskentaActor createValintalaskentaJaValintakoelaskentaActor( - AuditSession auditSession, - LaskentaSupervisor laskentaSupervisor, - Haku haku, - LaskentaActorParams actorParams, - Date nyt) { - final String uuid = actorParams.getUuid(); - return laskentaHakukohteittainActor( - laskentaSupervisor, - actorParams, - hakukohdeJaOrganisaatio -> { - String hakukohdeOid = hakukohdeJaOrganisaatio.getHakukohdeOid(); - LOG.info( - "(Uuid={}) Haetaan laskennan + valintakoelaskennan resursseja hakukohteelle {}", - uuid, - hakukohdeOid); - SuoritustiedotDTO suoritustiedot = new SuoritustiedotDTO(); - Observable laskenta = - Observable.fromFuture( - fetchResourcesForOneLaskenta( - auditSession, - uuid, - haku, - hakukohdeOid, - actorParams, - false, - true, - suoritustiedot, - nyt)) - .switchMap( - timedSwitchMap( - (took, exception) -> { - if (exception.isPresent()) { - LOG.error( - "(Uuid={}) (Kesto {}s) Laskenta hakukohteelle {} on päättynyt virheeseen: {}", - uuid, - millisToString(took), - hakukohdeOid, - exception.get()); - } else { - LOG.info( - "(Uuid={}) (Kesto {}s) Laskenta hakukohteelle {} on päättynyt onnistuneesti.", - uuid, - millisToString(took), - hakukohdeOid); - } - }, - laskeDTO -> - valintalaskentaAsyncResource.laskeKaikki(laskeDTO, suoritustiedot))); - return laskenta; - }); - } - - public LaskentaActor createLaskentaActor( - AuditSession auditSession, - LaskentaSupervisor laskentaSupervisor, - Haku haku, - LaskentaActorParams actorParams) { - final Date nyt = new Date(); - LOG.info( - String.format( - "Jos laskennassa %s on jonoja, joita ei lasketa %s jälkeen, ei haeta niille tietoja Koskesta.", - actorParams.getUuid(), nyt)); - - if (LaskentaTyyppi.VALINTARYHMALASKENTA.equals(actorParams.getLaskentaTyyppi())) { - LOG.info("Muodostetaan VALINTARYHMALASKENTA"); - auditLogLaskentaStart(auditSession, actorParams, haku.oid, "VALINTARYHMALASKENTA"); - return createValintaryhmaActor(auditSession, laskentaSupervisor, haku, actorParams, nyt); - } - if (LaskentaTyyppi.VALINTAKOELASKENTA.equals(actorParams.getLaskentaTyyppi())) { - LOG.info("Muodostetaan VALINTAKOELASKENTA"); - auditLogLaskentaStart(auditSession, actorParams, haku.oid, "VALINTAKOELASKENTA"); - return createValintakoelaskentaActor( - auditSession, laskentaSupervisor, haku, actorParams, nyt); - } - if (LaskentaTyyppi.VALINTALASKENTA.equals(actorParams.getLaskentaTyyppi())) { - LOG.info("Muodostetaan VALINTALASKENTA"); - auditLogLaskentaStart(auditSession, actorParams, haku.oid, "VALINTALASKENTA"); - return createValintalaskentaActor(auditSession, laskentaSupervisor, haku, actorParams); - } - LOG.info( - "Muodostetaan KAIKKI VAIHEET LASKENTA koska valinnanvaihe oli {} ja valintakoelaskenta ehto {}", - actorParams.getValinnanvaihe(), - actorParams.isValintakoelaskenta()); - auditLogLaskentaStart(auditSession, actorParams, haku.oid, "KAIKKI VAIHEET LASKENTA"); - return createValintalaskentaJaValintakoelaskentaActor( - auditSession, laskentaSupervisor, haku, actorParams, nyt); - } - - private void auditLogLaskentaStart( - AuditSession auditSession, LaskentaActorParams actorParams, String hakuOid, String tyyppi) { - Map additionalAuditInfo = new HashMap<>(); - additionalAuditInfo.put("tyyppi", tyyppi); - additionalAuditInfo.put("uuid", actorParams.getLaskentaStartParams().getUuid()); - additionalAuditInfo.put( - "hakukohteet", - actorParams.getLaskentaStartParams().getHakukohdeDtos().stream() - .map(HakukohdeJaOrganisaatio::getHakukohdeOid) - .collect(Collectors.toList()) - .toString()); - AuditLog.log( - KoosteAudit.AUDIT, - auditSession.asAuditUser(), - ValintaperusteetOperation.LASKENTATOTEUTUS_LUONTI, - ValintaResource.LASKENTATOTEUTUS, - hakuOid, - Changes.EMPTY, - additionalAuditInfo); - } - - private LaskentaActor laskentaHakukohteittainActor( - LaskentaSupervisor laskentaSupervisor, - LaskentaActorParams actorParams, - io.reactivex.functions.Function> r) { - return new LaskentaActorForSingleHakukohde( - actorParams, r, laskentaSupervisor, laskentaSeurantaAsyncResource, splittaus); - } - - private CompletableFuture getLaskeDtoFuture( - String uuid, - Haku haku, - String hakukohdeOid, - LaskentaActorParams actorParams, - boolean withHakijaRyhmat, - CompletableFuture> valintaperusteetF, - CompletableFuture> oppijatF, - CompletableFuture>> hakukohdeRyhmasForHakukohdesF, - CompletableFuture valintapisteetForHakukohdesF, - CompletableFuture> hakijaryhmatF, - CompletableFuture> hakemuksetF, - CompletableFuture> koskiOppijaByOppijaOidF) { - return CompletableFuture.allOf( - valintapisteetForHakukohdesF, - hakijaryhmatF, - valintaperusteetF, - hakemuksetF, - oppijatF, - hakukohdeRyhmasForHakukohdesF, - koskiOppijaByOppijaOidF) - .thenApplyAsync( - x -> { - List valintaperusteet = valintaperusteetF.join(); - verifyValintalaskentaKaytossaOrThrowError(uuid, hakukohdeOid, valintaperusteet); - verifyJonokriteeritOrThrowError(uuid, hakukohdeOid, valintaperusteet); - LOG.info( - "(Uuid: {}) Kaikki resurssit hakukohteelle {} saatu. Kootaan ja palautetaan LaskeDTO.", - uuid, - hakukohdeOid); - - Map> ryhmatHakukohteittain = - hakukohdeRyhmasForHakukohdesF.join(); - PisteetWithLastModified pisteetWithLastModified = valintapisteetForHakukohdesF.join(); - List hakemukset = hakemuksetF.join(); - List oppijat = oppijatF.join(); - Map koskiOppijatOppijanumeroittain = - koskiOppijaByOppijaOidF.join(); - koskiOppijatOppijanumeroittain.forEach( - (k, v) -> { - LOG.debug(String.format("Koskesta löytyi oppijalle %s datat: %s", k, v)); - }); - - if (!withHakijaRyhmat) { - return new LaskeDTO( - uuid, - haku.isKorkeakouluhaku(), - actorParams.isErillishaku(), - hakukohdeOid, - hakemuksetConverterUtil.muodostaHakemuksetDTOfromHakemukset( - haku, - hakukohdeOid, - ryhmatHakukohteittain, - hakemukset, - pisteetWithLastModified.valintapisteet, - oppijat, - actorParams.getParametritDTO(), - true, - true), - valintaperusteet); - - } else { - return new LaskeDTO( - uuid, - haku.isKorkeakouluhaku(), - actorParams.isErillishaku(), - hakukohdeOid, - hakemuksetConverterUtil.muodostaHakemuksetDTOfromHakemukset( - haku, - hakukohdeOid, - ryhmatHakukohteittain, - hakemukset, - pisteetWithLastModified.valintapisteet, - oppijat, - actorParams.getParametritDTO(), - true, - true), - valintaperusteet, - hakijaryhmatF.join()); - } - }); - } - - private void verifyValintalaskentaKaytossaOrThrowError( - String uuid, String hakukohdeOid, List valintaperusteetList) { - boolean jokinValintatapajonoKayttaaValintalaskentaa = - valintaperusteetList.stream() - .map(ValintaperusteetDTO::getValinnanVaihe) - .flatMap(v -> v.getValintatapajono().stream()) - .anyMatch(ValintatapajonoJarjestyskriteereillaDTO::getKaytetaanValintalaskentaa); - - if (!jokinValintatapajonoKayttaaValintalaskentaa) { - String errorMessage = - String.format( - "(Uuid: %s) Hakukohteen %s valittujen valinnanvaiheiden valintatapajonoissa ei käytetä valintalaskentaa, joten valintalaskentaa ei voida jatkaa ja se keskeytetään", - uuid, hakukohdeOid); - LOG.error(errorMessage); - throw new RuntimeException(errorMessage); - } - } - - private void verifyJonokriteeritOrThrowError( - String uuid, String hakukohdeOid, List valintaperusteetList) { - Predicate - valintatapajonoHasPuuttuvaJonokriteeri = - new Predicate<>() { - @Override - public boolean test(ValintatapajonoJarjestyskriteereillaDTO valintatapajono) { - boolean kaytetaanValintalaskentaa = valintatapajono.getKaytetaanValintalaskentaa(); - boolean hasJarjestyskriteerit = !valintatapajono.getJarjestyskriteerit().isEmpty(); - - return (kaytetaanValintalaskentaa && !hasJarjestyskriteerit) - || (!kaytetaanValintalaskentaa && hasJarjestyskriteerit); - } - }; - Optional - valintatapajonoPuutteellisellaJonokriteerilla = - valintaperusteetList.stream() - .map(ValintaperusteetDTO::getValinnanVaihe) - .flatMap(v -> v.getValintatapajono().stream()) - .filter(valintatapajonoHasPuuttuvaJonokriteeri) - .findFirst(); - - if (valintatapajonoPuutteellisellaJonokriteerilla.isPresent()) { - ValintatapajonoJarjestyskriteereillaDTO valintatapajono = - valintatapajonoPuutteellisellaJonokriteerilla.get(); - String errorMessage = - String.format( - "(Uuid: %s) Hakukohteen %s valintatapajonolla %s on joko valintalaskenta ilman jonokriteereitä tai jonokriteereitä ilman valintalaskentaa, joten valintalaskentaa ei voida jatkaa ja se keskeytetään", - uuid, hakukohdeOid, valintatapajono.getOid()); - LOG.error(errorMessage); - throw new RuntimeException(errorMessage); - } - } - - private CompletableFuture fetchResourcesForOneLaskenta( - final AuditSession auditSession, - final String uuid, - Haku haku, - final String hakukohdeOid, - LaskentaActorParams actorParams, - boolean retryHakemuksetAndOppijat, - boolean withHakijaRyhmat, - SuoritustiedotDTO suoritustiedotDTO, - Date nyt) { - final String hakuOid = haku.oid; - - PyynnonTunniste tunniste = - new PyynnonTunniste( - "Please put individual resource source identifier here!", uuid, hakukohdeOid); - - CompletableFuture> hakemukset; - if (haku.isHakemuspalvelu()) { - boolean haetaanHarkinnanvaraisuudet = haku.isAmmatillinenJaLukio() && haku.isKoutaHaku(); - hakemukset = - createResurssiFuture( - tunniste, - "applicationAsyncResource.getApplications", - () -> - ataruAsyncResource.getApplicationsByHakukohde( - hakukohdeOid, haetaanHarkinnanvaraisuudet), - retryHakemuksetAndOppijat); - } else { - hakemukset = - createResurssiFuture( - tunniste, - "applicationAsyncResource.getApplicationsByOid", - () -> applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohdeOid), - retryHakemuksetAndOppijat); - } - CompletableFuture> henkiloViitteet = - hakemukset.thenComposeAsync( - hws -> { - List viitteet = - hws.stream() - .map( - hw -> - new HenkiloViiteDto(hw.getApplicationPersonOid(), hw.getPersonOid())) - .collect(Collectors.toList()); - return CompletableFuture.completedFuture(viitteet); - }); - - CompletableFuture> oppijasForOidsFromHakemukses = - henkiloViitteet.thenComposeAsync( - hws -> { - LOG.info("Got henkiloViittees: {}", hws); - Map masterToOriginal = - hws.stream() - .collect( - Collectors.toMap( - HenkiloViiteDto::getMasterOid, HenkiloViiteDto::getHenkiloOid)); - List oppijaOids = new ArrayList<>(masterToOriginal.keySet()); - LOG.info( - "Got personOids from hakemukses and getting Oppijas for these: {} for hakukohde {}", - oppijaOids, - hakukohdeOid); - return createResurssiFuture( - tunniste, - "suoritusrekisteriAsyncResource.getSuorituksetByOppijas", - () -> - suoritusrekisteriAsyncResource.getSuorituksetByOppijas( - oppijaOids, hakuOid, true), - retryHakemuksetAndOppijat) - .thenApply( - oppijat -> { - oppijat.forEach( - oppija -> - oppija.setOppijanumero( - masterToOriginal.get(oppija.getOppijanumero()))); - return oppijat; - }); - }); - - CompletableFuture> valintaperusteet = - createResurssiFuture( - tunniste, - "valintaperusteetAsyncResource.haeValintaperusteet", - () -> - valintaperusteetAsyncResource.haeValintaperusteet( - hakukohdeOid, actorParams.getValinnanvaihe())); - CompletableFuture>> hakukohdeRyhmasForHakukohdes = - createResurssiFuture( - tunniste, - "tarjontaAsyncResource.hakukohdeRyhmasForHakukohdes", - () -> tarjontaAsyncResource.hakukohdeRyhmasForHakukohdes(hakuOid)); - CompletableFuture valintapisteetHakemuksille = - hakemukset.thenComposeAsync( - hakemusWrappers -> { - List hakemusOids = - hakemusWrappers.stream().map(HakemusWrapper::getOid).collect(Collectors.toList()); - return createResurssiFuture( - tunniste, - "valintapisteAsyncResource.getValintapisteetWithHakemusOidsAsFuture", - () -> - valintapisteAsyncResource.getValintapisteetWithHakemusOidsAsFuture( - hakemusOids, auditSession), - retryHakemuksetAndOppijat); - }); - CompletableFuture> hakijaryhmat = - withHakijaRyhmat - ? createResurssiFuture( - tunniste, - "valintaperusteetAsyncResource.haeHakijaryhmat", - () -> valintaperusteetAsyncResource.haeHakijaryhmat(hakukohdeOid)) - : CompletableFuture.completedFuture(emptyList()); - CompletableFuture> koskiOppijaByOppijaOid = - createResurssiFuture( - tunniste, - "koskiService.haeKoskiOppijat", - () -> - koskiService.haeKoskiOppijat( - hakukohdeOid, valintaperusteet, hakemukset, suoritustiedotDTO, nyt)); - - LOG.info( - "(Uuid: {}) Odotetaan kaikkien resurssihakujen valmistumista hakukohteelle {}, jotta voidaan palauttaa ne yhtenä pakettina.", - uuid, - hakukohdeOid); - return getLaskeDtoFuture( - uuid, - haku, - hakukohdeOid, - actorParams, - withHakijaRyhmat, - valintaperusteet, - oppijasForOidsFromHakemukses, - hakukohdeRyhmasForHakukohdes, - valintapisteetHakemuksille, - hakijaryhmat, - hakemukset, - koskiOppijaByOppijaOid); - } - - private CompletableFuture createResurssiFuture( - PyynnonTunniste tunniste, - String resurssi, - Supplier> sourceFuture, - boolean retry) { - return LaskentaResurssinhakuWrapper.luoLaskentaResurssinHakuFuture( - sourceFuture, tunniste.withNimi(resurssi), retry); - } - - private CompletableFuture createResurssiFuture( - PyynnonTunniste tunniste, String resurssi, Supplier> sourceFuture) { - return createResurssiFuture(tunniste, resurssi, sourceFuture, false); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorForSingleHakukohde.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorForSingleHakukohde.java deleted file mode 100644 index c302acd21f..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorForSingleHakukohde.java +++ /dev/null @@ -1,308 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorForSingleHakukohde.State.COMPLETE; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorForSingleHakukohde.State.FIRST_ATTEMPTS; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorForSingleHakukohde.State.RERUNS; -import static fi.vm.sade.valinta.seuranta.dto.IlmoitusDto.ilmoitus; -import static fi.vm.sade.valinta.seuranta.dto.IlmoitusDto.virheilmoitus; - -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeTila; -import fi.vm.sade.valinta.seuranta.dto.IlmoitusDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTila; -import io.reactivex.Observable; -import io.reactivex.functions.Function; -import io.reactivex.schedulers.Schedulers; -import java.util.Arrays; -import java.util.Optional; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.IntStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; - -class LaskentaActorForSingleHakukohde implements LaskentaActor { - private static final Logger LOG = LoggerFactory.getLogger(LaskentaActorForSingleHakukohde.class); - - private final AtomicReference state = new AtomicReference<>(FIRST_ATTEMPTS); - private final AtomicInteger successTotal = new AtomicInteger(0); - private final AtomicInteger retryTotal = new AtomicInteger(0); - private final AtomicInteger failedTotal = new AtomicInteger(0); - private final LaskentaActorParams actorParams; - private final Function> - hakukohteenLaskenta; - private final LaskentaSupervisor laskentaSupervisor; - private final LaskentaSeurantaAsyncResource laskentaSeurantaAsyncResource; - private final int splittaus; - private final ConcurrentLinkedQueue hakukohdeQueue; - private final ConcurrentLinkedQueue retryQueue = - new ConcurrentLinkedQueue<>(); - private final boolean isValintaryhmalaskenta; - private Optional valintaryhmalaskennanTulos; - - public LaskentaActorForSingleHakukohde( - LaskentaActorParams actorParams, - Function> hakukohteenLaskenta, - LaskentaSupervisor laskentaSupervisor, - LaskentaSeurantaAsyncResource laskentaSeurantaAsyncResource, - int splittaus) { - this.actorParams = actorParams; - this.hakukohteenLaskenta = hakukohteenLaskenta; - this.laskentaSupervisor = laskentaSupervisor; - this.laskentaSeurantaAsyncResource = laskentaSeurantaAsyncResource; - this.splittaus = splittaus; - hakukohdeQueue = new ConcurrentLinkedQueue<>(actorParams.getHakukohdeOids()); - this.isValintaryhmalaskenta = actorParams.isValintaryhmalaskenta(); - this.valintaryhmalaskennanTulos = Optional.empty(); - } - - public void start() { - LOG.info( - "(Uuid={}) Laskenta-actor käynnistetty haulle {}, hakukohteita yhteensä {}, splittaus {} ", - uuid(), - getHakuOid(), - totalKohteet(), - splittaus); - final boolean onkoTarveSplitata = actorParams.getHakukohdeOids().size() > 20; - IntStream.range(0, onkoTarveSplitata ? splittaus : 1) - .forEach( - i -> { - LOG.info("Käynnistetään laskenta " + i); - laskeSeuraavaHakukohde(); - }); - } - - private void laskeSeuraavaHakukohde() { - final Optional hkJaOrg; - final boolean fromRetryQueue; - if (FIRST_ATTEMPTS.equals(state.get())) { - hkJaOrg = Optional.ofNullable(hakukohdeQueue.poll()); - fromRetryQueue = false; - } else if (RERUNS.equals(state.get())) { - hkJaOrg = Optional.ofNullable(retryQueue.poll()); - fromRetryQueue = true; - } else { - throw new IllegalStateException( - getClass().getSimpleName() + " on ajamassa laskentaa, vaikka tila on " + state.get()); - } - - if (hkJaOrg.isPresent()) { - try { - HakukohdeJaOrganisaatio hakukohdeJaOrganisaatio = hkJaOrg.get(); - String hakukohdeOid = hakukohdeJaOrganisaatio.getHakukohdeOid(); - Observable laskentaTimer = - Observable.timer(3L, TimeUnit.HOURS) - .switchMap( - t -> - Observable.error( - new TimeoutException( - "Laskentaa odotettiin 90 minuuttia ja ohitettiin"))); - Observable.amb( - Arrays.asList(hakukohteenLaskenta.apply(hakukohdeJaOrganisaatio), laskentaTimer)) - .subscribeOn(Schedulers.newThread()) - .subscribe( - s -> handleSuccessfulLaskentaResult(fromRetryQueue, hakukohdeOid), - e -> handleFailedLaskentaResult(fromRetryQueue, hakukohdeJaOrganisaatio, e)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } else { - handleEmptyWorkQueueResult(); - } - } - - private void handleSuccessfulLaskentaResult(boolean fromRetryQueue, String hakukohdeOid) { - if (fromRetryQueue) { - LOG.info( - "(Uuid={}) Hakukohteen ({}) laskenta onnistui uudelleenyrityksellä. Valmiita kohteita laskennassa yhteensä {}/{}", - uuid(), - hakukohdeOid, - successTotal.incrementAndGet(), - totalKohteet()); - } else { - LOG.info( - "(Uuid={}) Hakukohteen ({}) laskenta onnistui. Valmiita kohteita laskennassa yhteensä {}/{}", - uuid(), - hakukohdeOid, - successTotal.incrementAndGet(), - totalKohteet()); - } - if (!isValintaryhmalaskenta) { - HakukohdeTila tila = HakukohdeTila.VALMIS; - laskentaSeurantaAsyncResource - .merkkaaHakukohteenTila(uuid(), hakukohdeOid, tila, Optional.empty()) - .subscribeOn(Schedulers.newThread()) - .subscribe( - ok -> - LOG.info( - "(Uuid={}) Hakukohteen ({}) laskenta on valmis, hakukohteen tila saatiin merkattua seurantaan.", - uuid(), - hakukohdeOid), - t -> - LOG.error( - String.format( - "(UUID = %s) Hakukohteen (%s) tilan (%s) merkkaaminen epaonnistui!", - uuid(), hakukohdeOid, tila), - t)); - } else { - LOG.info( - "Ei merkitä valintaryhmälaskennan hakukohteiden tilaa seurantaan. (Onnistunut laskenta)"); - } - laskeSeuraavaHakukohde(); - } - - private void handleFailedLaskentaResult( - boolean fromRetryQueue, HakukohdeJaOrganisaatio hakukohdeJaOrganisaatio, Throwable failure) { - String hakukohdeOid = hakukohdeJaOrganisaatio.getHakukohdeOid(); - if (!fromRetryQueue) { - LOG.warn( - "(Uuid={}) Lisätään hakukohde ({}) epäonnistuneiden jonoon uudelleenyritystä varten. Uudelleenyritettäviä kohteita laskennassa yhteensä {}/{}", - uuid(), - hakukohdeOid, - retryTotal.incrementAndGet(), - totalKohteet(), - failure); - retryQueue.add(hakukohdeJaOrganisaatio); - } else { - LOG.error( - "(Uuid={}) Hakukohteen ({}) laskenta epäonnistui myös uudelleenyrityksellä. Lopullisesti epäonnistuneita kohteita laskennassa yhteensä {}/{}", - uuid(), - hakukohdeOid, - failedTotal.incrementAndGet(), - totalKohteet(), - failure); - if (!isValintaryhmalaskenta) { - try { - HakukohdeTila tila = HakukohdeTila.KESKEYTETTY; - laskentaSeurantaAsyncResource - .merkkaaHakukohteenTila( - uuid(), - hakukohdeOid, - tila, - Optional.of( - virheilmoitus( - failure.getMessage(), Arrays.toString(failure.getStackTrace())))) - .subscribeOn(Schedulers.newThread()) - .subscribe( - ok -> - LOG.error( - "(Uuid={}) Laskenta epäonnistui hakukohteelle {}, tila merkattu onnistuneesti seurantaan ", - uuid(), - hakukohdeOid), - t -> - LOG.error( - String.format( - "(UUID = %s) Hakukohteen (%s) tilan (%s) merkkaaminen epaonnistui!", - uuid(), hakukohdeOid, tila), - t)); - } catch (Throwable e1) { - LOG.error( - "(Uuid={}) Hakukohteen ({}) laskenta epäonnistui mutta ei saatu merkattua ", - uuid(), - hakukohdeOid, - e1); - } - } else { - LOG.error( - "(Uuid={}) Valintaryhmälaskenta on lopullisesti epäonnistunut: {}.", - uuid(), - failure.getMessage()); - this.valintaryhmalaskennanTulos = - Optional.of( - virheilmoitus( - "Valintaryhmälaskenta epäonnistui: " + failure.getMessage(), - Arrays.toString(failure.getStackTrace()))); - } - } - laskeSeuraavaHakukohde(); - } - - private void handleEmptyWorkQueueResult() { - if (state.compareAndSet(FIRST_ATTEMPTS, RERUNS)) { - if (retryQueue.peek() != null) { - LOG.info( - "Laskenta (uuid={}) olisi päättynyt, mutta sisältää keskeytettyjä hakukohteita. Yritetään epäonnistuneita kohteita ({} kpl) uudelleen.", - uuid(), - retryTotal.get()); - final boolean splitRetry = retryQueue.size() > 20; - IntStream.range(0, splitRetry ? splittaus : 1).forEach(i -> laskeSeuraavaHakukohde()); - return; - } else { - LOG.info( - "Laskennassa (uuid={}) ei ole epäonnistuneita hakukohteita uudelleenyritettäviksi.", - uuid()); - } - } - if (totalKohteet() == (successTotal.get() + failedTotal.get())) { - if (COMPLETE.equals(state.getAndSet(COMPLETE))) { - LOG.error( - "state == " + state + " but it is being set to that again! Looks like a bug!", - new Exception()); - } - lopeta(); - } - } - - public void lopeta() { - final Observable tilanmerkkausObservable; - if (!COMPLETE.equals(state.get())) { - LOG.warn("#### (Uuid={}) Laskenta lopetettu", uuid()); - tilanmerkkausObservable = - laskentaSeurantaAsyncResource.merkkaaLaskennanTila( - uuid(), LaskentaTila.PERUUTETTU, Optional.of(ilmoitus("Laskenta on peruutettu"))); - } else if (valintaryhmalaskennanTulos.isPresent()) { - LOG.error("#### (Uuid={}) Valintaryhmälaskenta on epäonnistunut.", uuid()); - tilanmerkkausObservable = - laskentaSeurantaAsyncResource.merkkaaLaskennanTila( - uuid(), LaskentaTila.PERUUTETTU, valintaryhmalaskennanTulos); - } else { - LOG.info( - "#### (Uuid={}) Laskenta valmis koska ei enää hakukohteita käsiteltävänä. " - + "Onnistuneita {}, Uudelleenyrityksiä {}, Lopullisesti epäonnistuneita {}", - uuid(), - successTotal.get(), - retryTotal.get(), - failedTotal.get()); - tilanmerkkausObservable = - laskentaSeurantaAsyncResource.merkkaaLaskennanTila( - uuid(), LaskentaTila.VALMIS, Optional.empty()); - } - tilanmerkkausObservable - .subscribeOn(Schedulers.newThread()) - .subscribe( - response -> laskentaSupervisor.ready(uuid()), - e -> LOG.error("Ongelma laskennan merkkaamisessa loppuneeksi", e)); - } - - public void postStop() { - LOG.info("PostStop ajettu"); - lopeta(); - } - - private String uuid() { - return actorParams.getUuid(); - } - - private int totalKohteet() { - return actorParams.getHakukohdeOids().size(); - } - - public String getHakuOid() { - return actorParams.getHakuOid(); - } - - public boolean isValmis() { - return false; - } - - public enum State { - FIRST_ATTEMPTS, - RERUNS, - COMPLETE - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorParams.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorParams.java deleted file mode 100644 index c6b5e3bdeb..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorParams.java +++ /dev/null @@ -1,99 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.dto.ParametritDTO; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaStartParams; -import java.util.Collection; -import java.util.stream.Collectors; - -public class LaskentaActorParams { - private static final Integer HAE_KAIKKI_VALINNANVAIHEET = -1; - - private final LaskentaStartParams laskentaStartParams; - private final ParametritDTO parametritDTO; - private final Collection hakukohdeOids; - private boolean isValintaryhmalaskenta; - - public LaskentaActorParams(LaskentaStartParams laskentaStartParams, ParametritDTO parametritDTO) { - this( - laskentaStartParams, - laskentaStartParams.getHakukohdeDtos().stream() - .map(hk -> new HakukohdeJaOrganisaatio(hk.getHakukohdeOid(), hk.getOrganisaatioOid())) - .collect(Collectors.toList()), - parametritDTO); - } - - public LaskentaActorParams( - LaskentaStartParams laskentaStartParams, - Collection hakukohdeOids, - ParametritDTO parametritDTO) { - this.laskentaStartParams = laskentaStartParams; - this.parametritDTO = parametritDTO; - this.hakukohdeOids = hakukohdeOids; - this.isValintaryhmalaskenta = false; - } - - public String getUuid() { - return laskentaStartParams.getUuid(); - } - - public String getHakuOid() { - return laskentaStartParams.getHakuOid(); - } - - public LaskentaStartParams getLaskentaStartParams() { - return laskentaStartParams; - } - - public ParametritDTO getParametritDTO() { - return parametritDTO; - } - - public boolean isErillishaku() { - return laskentaStartParams.isErillishaku(); - } - - public Boolean isValintakoelaskenta() { - return laskentaStartParams.getValintakoelaskenta(); - } - - public boolean isOsittainen() { - return laskentaStartParams.isOsittainenLaskenta(); - } - - /** Tilapainen workaround resurssin valinnanvaiheen normalisointiin. */ - public Integer getValinnanvaihe() { - return HAE_KAIKKI_VALINNANVAIHEET.equals(laskentaStartParams.getValinnanvaihe()) - ? null - : laskentaStartParams.getValinnanvaihe(); - } - - public Collection getHakukohdeOids() { - return hakukohdeOids; - } - - public void setValintaryhmalaskenta(boolean value) { - this.isValintaryhmalaskenta = value; - } - - public boolean isValintaryhmalaskenta() { - return this.isValintaryhmalaskenta; - } - - /** Tilapainen workaround resurssin valinnanvaiheen normalisointiin. */ - public LaskentaTyyppi getLaskentaTyyppi() { - if (fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi.VALINTARYHMA.equals( - laskentaStartParams.getTyyppi())) { - return LaskentaTyyppi.VALINTARYHMALASKENTA; - } - if (Boolean.TRUE.equals(laskentaStartParams.getValintakoelaskenta())) { - return LaskentaTyyppi.VALINTAKOELASKENTA; - } else { - if (laskentaStartParams.getValinnanvaihe() == null) { - return LaskentaTyyppi.KAIKKI; - } else { - return LaskentaTyyppi.VALINTALASKENTA; - } - } - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorSystem.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorSystem.java deleted file mode 100644 index d62b7be660..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorSystem.java +++ /dev/null @@ -1,221 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.MaxWorkerCount; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.NoWorkAvailable; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.ResetWorkerCount; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.StartAllWorkers; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.WorkAvailable; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.WorkerAvailable; -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.props; - -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.TypedActor; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.typesafe.config.ConfigFactory; -import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.dto.ParametritDTO; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.Haku; -import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Laskenta; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaStartParams; -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRoute; -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRouteValvomo; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.jmx.export.annotation.ManagedOperation; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.stereotype.Service; -import scala.concurrent.duration.FiniteDuration; - -@Service -@ManagedResource( - objectName = "OPH:name=LaskentaActorSystem", - description = "LaskentaActorSystem mbean") -public class LaskentaActorSystem - implements ValintalaskentaKerrallaRouteValvomo, - ValintalaskentaKerrallaRoute, - LaskentaSupervisor, - ApplicationListener { - private static final Logger LOG = LoggerFactory.getLogger(LaskentaActorSystem.class); - - private final LaskentaActorFactory laskentaActorFactory; - - private final LaskentaSeurantaAsyncResource seurantaAsyncResource; - private final ActorSystem actorSystem; - private final ActorRef laskennanKaynnistajaActor; - private final Map runningLaskentas = Maps.newConcurrentMap(); - private final LaskentaStarter laskentaStarter; - - @Autowired - public LaskentaActorSystem( - LaskentaSeurantaAsyncResource seurantaAsyncResource, - LaskentaStarter laskentaStarter, - LaskentaActorFactory laskentaActorFactory, - @Value("${valintalaskentakoostepalvelu.maxWorkerCount:8}") int maxWorkers) { - this.laskentaActorFactory = laskentaActorFactory; - this.laskentaStarter = laskentaStarter; - this.seurantaAsyncResource = seurantaAsyncResource; - this.actorSystem = - ActorSystem.create("ValintalaskentaActorSystem", ConfigFactory.defaultOverrides()); - laskennanKaynnistajaActor = actorSystem.actorOf(props(this, maxWorkers)); - } - - @ManagedOperation - public void resetActorCounter() { - laskennanKaynnistajaActor.tell(new ResetWorkerCount(), ActorRef.noSender()); - } - - @ManagedOperation - public void setMaxWorkerCount(int maxWorkerCount) { - laskennanKaynnistajaActor.tell(new MaxWorkerCount(maxWorkerCount), ActorRef.noSender()); - } - - @ManagedOperation - public void startAllWorkers() { - laskennanKaynnistajaActor.tell(new StartAllWorkers(), ActorRef.noSender()); - } - - @Override - public void workAvailable() { - laskennanKaynnistajaActor.tell(new WorkAvailable(), ActorRef.noSender()); - } - - @Override - public void onApplicationEvent(ContextRefreshedEvent event) { - laskennanKaynnistajaActor.tell(new StartAllWorkers(), ActorRef.noSender()); - } - - @Override - public void suoritaValintalaskentaKerralla( - final Haku haku, - final ParametritDTO parametritDTO, - final LaskentaStartParams laskentaStartParams) { - AuditSession auditSession = laskentaStartParams.getAuditSession(); - LaskentaActor laskentaActor = - laskentaActorFactory.createLaskentaActor( - auditSession, this, haku, new LaskentaActorParams(laskentaStartParams, parametritDTO)); - startLaskentaActor(laskentaStartParams, laskentaActor); - } - - @Override - public List runningLaskentas() { - return Lists.newArrayList(runningLaskentas.values()); - } - - @Override - public Optional fetchLaskenta(String uuid) { - return Optional.ofNullable(runningLaskentas.get(uuid)); - } - - @Override - public void ready(String uuid) { - LOG.trace("Ilmoitettu actor valmiiksi laskennalle (" + uuid + ")"); - LaskentaActorWrapper actorWrapper = runningLaskentas.remove(uuid); - if (actorWrapper == null) { - throw new IllegalStateException( - "Ei löytynyt valmiiksi merkattavaa actoria laskennalle " + uuid); - } - stopActor(uuid, actorWrapper.laskentaActor()); - } - - public void fetchAndStartLaskenta() { - seurantaAsyncResource - .otaSeuraavaLaskentaTyonAlle() - .subscribe( - this::startLaskentaIfWorkAvailable, - (Throwable t) -> { - LOG.warn("Uutta laskentaa ei saatu tyon alle seurannasta. Yritetään uudelleen.", t); - _fetchAndStartLaskentaRetry(); // FIXME kill me OK-152 - }); - } - - private void _fetchAndStartLaskentaRetry() { - seurantaAsyncResource - .otaSeuraavaLaskentaTyonAlle() - .subscribe( - this::startLaskentaIfWorkAvailable, - (Throwable t) -> { - String message = "Uutta laskentaa ei saatu tyon alle seurannasta."; - LOG.error(message, t); - actorSystem - .scheduler() - .scheduleOnce( - FiniteDuration.create(5, TimeUnit.SECONDS), - laskennanKaynnistajaActor, - new WorkerAvailable(), - actorSystem.dispatcher(), - ActorRef.noSender()); - throw new RuntimeException(message, t); - }); - } - - private void startLaskentaIfWorkAvailable(Optional uuid) { - if (!uuid.isPresent()) { - LOG.trace("Ei laskettavaa"); - laskennanKaynnistajaActor.tell(new NoWorkAvailable(), ActorRef.noSender()); - } else { - LOG.info("Luodaan ja aloitetaan Laskenta uuid:lle {}", uuid.get()); - laskentaStarter.fetchLaskentaParams( - laskennanKaynnistajaActor, - uuid.get(), - (haku, params) -> - startLaskentaActor( - params.getLaskentaStartParams(), - laskentaActorFactory.createLaskentaActor( - params.getLaskentaStartParams().getAuditSession(), this, haku, params))); - } - } - - protected void startLaskentaActor(LaskentaStartParams params, LaskentaActor laskentaActor) { - String uuid = params.getUuid(); - String hakuOid = params.getHakuOid(); - - runningLaskentas.merge( - uuid, - new LaskentaActorWrapper(params, laskentaActor), - (LaskentaActorWrapper oldValue, LaskentaActorWrapper value) -> { - LOG.warn( - "\r\n###\r\n### Laskenta uuid:lle {} haulle {} oli jo kaynnissa! Lopetataan vanha laskenta!\r\n###", - uuid, - hakuOid); - stopActor(uuid, oldValue.laskentaActor()); - return value; - }); - - try { - laskentaActor.start(); - } catch (Exception e) { - runningLaskentas.remove(uuid); - LOG.error( - "\r\n###\r\n### Laskenta uuid:lle {} haulle {} ei kaynnistynyt!\r\n###", - uuid, - hakuOid, - e); - } - } - - private void stopActor(String uuid, LaskentaActor actor) { - LOG.trace("Pysaytetaan actor laskennalle (" + uuid + ")"); - laskennanKaynnistajaActor.tell(new WorkerAvailable(), ActorRef.noSender()); - if (actor != null) { - try { - TypedActor.get(actorSystem).poisonPill(actor); - LOG.trace("PoisonPill lahetetty onnistuneesti Actorille " + uuid); - } catch (Exception e) { - LOG.error("PoisonPill lahetys epaonnistui Actorille " + uuid, e); - } - } else { - LOG.warn("Yritettiin sammuttaa laskenta " + uuid + ", mutta laskenta ei ollut enaa ajossa!"); - } - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorWrapper.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorWrapper.java deleted file mode 100644 index 87b289d0d6..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaActorWrapper.java +++ /dev/null @@ -1,43 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Laskenta; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaStartParams; - -public class LaskentaActorWrapper implements Laskenta { - - private final String uuid; - private final String hakuOid; - private final boolean osittainen; - private final LaskentaActor laskentaActor; - - public LaskentaActorWrapper(LaskentaStartParams params, LaskentaActor laskentaActor) { - this.uuid = params.getUuid(); - this.hakuOid = params.getHakuOid(); - this.osittainen = params.isOsittainenLaskenta(); - this.laskentaActor = laskentaActor; - } - - public LaskentaActor laskentaActor() { - return laskentaActor; - } - - public String getHakuOid() { - return hakuOid; - } - - public String getUuid() { - return uuid; - } - - public boolean isOsittainenLaskenta() { - return osittainen; - } - - public boolean isValmis() { - return false; - } - - public void lopeta() { - laskentaActor.lopeta(); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaResurssiProvider.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaResurssiProvider.java new file mode 100644 index 0000000000..885ff8d090 --- /dev/null +++ b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaResurssiProvider.java @@ -0,0 +1,678 @@ +package fi.vm.sade.valinta.kooste.valintalaskenta.actor; + +import static java.util.Collections.emptyList; + +import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetDTO; +import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetHakijaryhmaDTO; +import fi.vm.sade.service.valintaperusteet.dto.ValintatapajonoJarjestyskriteereillaDTO; +import fi.vm.sade.valinta.kooste.external.resource.ataru.AtaruAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.koski.KoskiOppija; +import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.OhjausparametritAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.dto.ParametritDTO; +import fi.vm.sade.valinta.kooste.external.resource.oppijanumerorekisteri.OppijanumerorekisteriAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.oppijanumerorekisteri.dto.HenkiloViiteDto; +import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.SuoritusrekisteriAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.dto.Oppija; +import fi.vm.sade.valinta.kooste.external.resource.tarjonta.Haku; +import fi.vm.sade.valinta.kooste.external.resource.tarjonta.TarjontaAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.valintaperusteet.ValintaperusteetAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.valintapiste.ValintapisteAsyncResource; +import fi.vm.sade.valinta.kooste.external.resource.valintapiste.dto.PisteetWithLastModified; +import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; +import fi.vm.sade.valinta.kooste.util.HakemusWrapper; +import fi.vm.sade.valinta.kooste.valintalaskenta.dao.ParametritDao; +import fi.vm.sade.valinta.kooste.valintalaskenta.util.HakemuksetConverterUtil; +import fi.vm.sade.valintalaskenta.domain.dto.LaskeDTO; +import fi.vm.sade.valintalaskenta.domain.dto.SuoritustiedotDTO; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; +import software.amazon.awssdk.services.cloudwatch.model.Dimension; +import software.amazon.awssdk.services.cloudwatch.model.MetricDatum; +import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest; +import software.amazon.awssdk.services.cloudwatch.model.StandardUnit; + +@Service +public class LaskentaResurssiProvider { + + private static final Logger LOG = LoggerFactory.getLogger(LaskentaResurssiProvider.class); + + private final ValintapisteAsyncResource valintapisteAsyncResource; + private final AtaruAsyncResource ataruAsyncResource; + private final ValintaperusteetAsyncResource valintaperusteetAsyncResource; + private final SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource; + private final OppijanumerorekisteriAsyncResource oppijanumerorekisteriAsyncResource; + private final TarjontaAsyncResource tarjontaAsyncResource; + private final KoskiService koskiService; + private final HakemuksetConverterUtil hakemuksetConverterUtil; + private final OhjausparametritAsyncResource ohjausparametritAsyncResource; + private final ParametritDao parametritDao; + private final ExecutorService executor = Executors.newWorkStealingPool(256); + + private final int NO_LIMIT_PERMITS = Integer.MAX_VALUE; + private final ConcurrencyLimiter parametritLimiter = + new ConcurrencyLimiter(NO_LIMIT_PERMITS, "parametrit", this.executor); + private final ConcurrencyLimiter hakuLimiter = + new ConcurrencyLimiter(NO_LIMIT_PERMITS, "haku", this.executor); + private final ConcurrencyLimiter hakukohderyhmatLimiter = + new ConcurrencyLimiter(NO_LIMIT_PERMITS, "hakukohderyhmat", this.executor); + private final ConcurrencyLimiter valintapisteetLimiter = + new ConcurrencyLimiter(NO_LIMIT_PERMITS, "valintapisteet", this.executor); + private final ConcurrencyLimiter hakijaryhmatLimiter = + new ConcurrencyLimiter(NO_LIMIT_PERMITS, "hakijaryhmat", this.executor); + private final ConcurrencyLimiter koskioppijatLimiter = + new ConcurrencyLimiter(16, "koskioppijat", this.executor); + private final ConcurrencyLimiter ataruhakemuksetLimiter = + new ConcurrencyLimiter(16, "ataruhakemukset", this.executor); + private final ConcurrencyLimiter valintaperusteetLimiter = + new ConcurrencyLimiter(16, "valintaperusteet", this.executor); + private final ConcurrencyLimiter suorituksetLimiter = + new ConcurrencyLimiter(1000, "suoritukset", this.executor); + + private final Map limiters = + Map.of( + "parametritLimiter", parametritLimiter, + "hakuLimiter", hakuLimiter, + "ataruhakemuksetLimiter", ataruhakemuksetLimiter, + "hakukohderyhmatLimiter", hakukohderyhmatLimiter, + "valintapisteetLimiter", valintapisteetLimiter, + "hakijaryhmatLimiter", hakijaryhmatLimiter, + "koskioppijatLimiter", koskioppijatLimiter, + "valintaperusteetLimiter", valintaperusteetLimiter, + "suorituksetLimiter", suorituksetLimiter); + + private final CloudWatchClient cloudWatchClient; + + private final String environmentName; + + @Autowired + public LaskentaResurssiProvider( + AtaruAsyncResource ataruAsyncResource, + ValintaperusteetAsyncResource valintaperusteetAsyncResource, + SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource, + TarjontaAsyncResource tarjontaAsyncResource, + ValintapisteAsyncResource valintapisteAsyncResource, + KoskiService koskiService, + HakemuksetConverterUtil hakemuksetConverterUtil, + OppijanumerorekisteriAsyncResource oppijanumerorekisteriAsyncResource, + OhjausparametritAsyncResource ohjausparametritAsyncResource, + CloudWatchClient cloudWatchClient, + ParametritDao parametritDao, + @Value("${environment.name}") String environmentName) { + this.ataruAsyncResource = ataruAsyncResource; + this.valintaperusteetAsyncResource = valintaperusteetAsyncResource; + this.suoritusrekisteriAsyncResource = suoritusrekisteriAsyncResource; + this.tarjontaAsyncResource = tarjontaAsyncResource; + this.valintapisteAsyncResource = valintapisteAsyncResource; + this.koskiService = koskiService; + this.hakemuksetConverterUtil = hakemuksetConverterUtil; + this.oppijanumerorekisteriAsyncResource = oppijanumerorekisteriAsyncResource; + this.ohjausparametritAsyncResource = ohjausparametritAsyncResource; + this.cloudWatchClient = cloudWatchClient; + this.environmentName = environmentName; + this.parametritDao = parametritDao; + this.lueParametrit(); + } + + @Scheduled(initialDelay = 15, fixedDelay = 15, timeUnit = TimeUnit.SECONDS) + public void lueParametrit() { + this.executor.submit( + () -> { + this.parametritDao + .lueParametrit() + .forEach( + (k, v) -> { + if (this.limiters.containsKey(k)) { + this.limiters.get(k).setMaxPermits(Integer.parseInt(v)); + } + }); + }); + } + + private void tallennaLokitJaMetriikat( + String hakukohdeOid, + Map waitDurations, + Map invokeDurations) { + Collection datums = new ArrayList<>(); + datums.addAll( + waitDurations.entrySet().stream() + .map( + e -> + MetricDatum.builder() + .metricName("odotus") + .value((double) e.getValue().toMillis()) + .storageResolution(60) + .dimensions( + List.of(Dimension.builder().name("vaihe").value(e.getKey()).build())) + .timestamp(Instant.now()) + .unit(StandardUnit.MILLISECONDS) + .build()) + .collect(Collectors.toList())); + + datums.addAll( + invokeDurations.entrySet().stream() + .map( + e -> { + if (e.getValue() == ConcurrencyLimiter.ERROR) { + return MetricDatum.builder() + .metricName("errors") + .value(1.0) + .storageResolution(60) + .dimensions( + List.of(Dimension.builder().name("vaihe").value(e.getKey()).build())) + .timestamp(Instant.now()) + .unit(StandardUnit.COUNT) + .build(); + } else if (e.getValue() == ConcurrencyLimiter.TIMEOUT) { + return MetricDatum.builder() + .metricName("timeouts") + .value(1.0) + .storageResolution(60) + .dimensions( + List.of(Dimension.builder().name("vaihe").value(e.getKey()).build())) + .timestamp(Instant.now()) + .unit(StandardUnit.COUNT) + .build(); + } else { + return MetricDatum.builder() + .metricName("kesto") + .value((double) e.getValue().toMillis()) + .storageResolution(60) + .dimensions( + List.of(Dimension.builder().name("vaihe").value(e.getKey()).build())) + .timestamp(Instant.now()) + .unit(StandardUnit.MILLISECONDS) + .build(); + } + }) + .collect(Collectors.toList())); + + CompletableFuture.supplyAsync( + () -> + this.cloudWatchClient.putMetricData( + PutMetricDataRequest.builder() + .namespace(this.environmentName + "-valintalaskenta") + .metricData(datums) + .build()), + this.executor); + + LOG.info( + "Odotukset: Hakukohde: " + + hakukohdeOid + + ": " + + waitDurations.entrySet().stream() + .map(e -> e.getKey() + ":" + ConcurrencyLimiter.asDurationString(e.getValue())) + .collect(Collectors.joining(", "))); + + LOG.info( + "Kestot: Hakukohde: " + + hakukohdeOid + + ": " + + invokeDurations.entrySet().stream() + .map(e -> e.getKey() + ":" + ConcurrencyLimiter.asDurationString(e.getValue())) + .collect(Collectors.joining(", "))); + } + + @Scheduled(initialDelay = 15, fixedDelay = 15, timeUnit = TimeUnit.SECONDS) + public void tallennaMaarat() { + Collection datums = new ArrayList<>(); + + datums.addAll( + this.limiters.values().stream() + .filter(limiter -> limiter.getActive() > 0) + .map( + limiter -> + MetricDatum.builder() + .metricName("active") + .value((double) limiter.getActive()) + .storageResolution(1) + .dimensions( + List.of( + Dimension.builder().name("vaihe").value(limiter.getNimi()).build())) + .timestamp(Instant.now()) + .unit(StandardUnit.COUNT) + .build()) + .toList()); + + datums.addAll( + this.limiters.values().stream() + .filter(limiter -> limiter.getWaiting() > 0) + .map( + limiter -> + MetricDatum.builder() + .metricName("waiting") + .value((double) limiter.getWaiting()) + .storageResolution(1) + .dimensions( + List.of( + Dimension.builder().name("vaihe").value(limiter.getNimi()).build())) + .timestamp(Instant.now()) + .unit(StandardUnit.COUNT) + .build()) + .toList()); + + if (!datums.isEmpty()) { + CompletableFuture.supplyAsync( + () -> + this.cloudWatchClient.putMetricData( + PutMetricDataRequest.builder() + .namespace(this.environmentName + "-valintalaskenta") + .metricData(datums) + .build()), + this.executor); + } + } + + private CompletableFuture getLaskeDtoFuture( + String uuid, + CompletableFuture haku, + String hakukohdeOid, + boolean isErillishaku, + CompletableFuture parametritDTO, + boolean withHakijaRyhmat, + CompletableFuture> valintaperusteetF, + CompletableFuture> oppijatF, + CompletableFuture>> hakukohdeRyhmasForHakukohdesF, + CompletableFuture valintapisteetForHakukohdesF, + CompletableFuture> hakijaryhmatF, + CompletableFuture> hakemuksetF, + CompletableFuture> koskiOppijaByOppijaOidF) { + return CompletableFuture.allOf( + haku, + parametritDTO, + valintapisteetForHakukohdesF, + hakijaryhmatF, + valintaperusteetF, + hakemuksetF, + oppijatF, + hakukohdeRyhmasForHakukohdesF, + koskiOppijaByOppijaOidF) + .thenApplyAsync( + x -> { + List valintaperusteet = valintaperusteetF.join(); + verifyJonokriteeritOrThrowError(uuid, hakukohdeOid, valintaperusteet); + LOG.info( + "(Uuid: {}) Kaikki resurssit hakukohteelle {} saatu. Kootaan ja palautetaan LaskeDTO.", + uuid, + hakukohdeOid); + + Map> ryhmatHakukohteittain = + hakukohdeRyhmasForHakukohdesF.join(); + PisteetWithLastModified pisteetWithLastModified = valintapisteetForHakukohdesF.join(); + List hakemukset = hakemuksetF.join(); + List oppijat = oppijatF.join(); + Map koskiOppijatOppijanumeroittain = + koskiOppijaByOppijaOidF.join(); + koskiOppijatOppijanumeroittain.forEach( + (k, v) -> { + LOG.debug(String.format("Koskesta löytyi oppijalle %s datat: %s", k, v)); + }); + + if (!withHakijaRyhmat) { + return new LaskeDTO( + uuid, + haku.join().isKorkeakouluhaku(), + isErillishaku, + hakukohdeOid, + hakemuksetConverterUtil.muodostaHakemuksetDTOfromHakemukset( + haku.join(), + hakukohdeOid, + ryhmatHakukohteittain, + hakemukset, + pisteetWithLastModified.valintapisteet, + oppijat, + parametritDTO.join(), + true, + true), + valintaperusteet); + + } else { + return new LaskeDTO( + uuid, + haku.join().isKorkeakouluhaku(), + isErillishaku, + hakukohdeOid, + hakemuksetConverterUtil.muodostaHakemuksetDTOfromHakemukset( + haku.join(), + hakukohdeOid, + ryhmatHakukohteittain, + hakemukset, + pisteetWithLastModified.valintapisteet, + oppijat, + parametritDTO.join(), + true, + true), + valintaperusteet, + hakijaryhmatF.join()); + } + }, + this.executor); + } + + private boolean isValintalaskentaKaytossa(List valintaperusteetList) { + boolean jokinValintatapajonoKayttaaValintalaskentaa = + valintaperusteetList.stream() + .map(ValintaperusteetDTO::getValinnanVaihe) + .flatMap(v -> v.getValintatapajono().stream()) + .anyMatch(ValintatapajonoJarjestyskriteereillaDTO::getKaytetaanValintalaskentaa); + + return jokinValintatapajonoKayttaaValintalaskentaa; + } + + private void verifyJonokriteeritOrThrowError( + String uuid, String hakukohdeOid, List valintaperusteetList) { + Predicate + valintatapajonoHasPuuttuvaJonokriteeri = + new Predicate<>() { + @Override + public boolean test(ValintatapajonoJarjestyskriteereillaDTO valintatapajono) { + boolean kaytetaanValintalaskentaa = valintatapajono.getKaytetaanValintalaskentaa(); + boolean hasJarjestyskriteerit = !valintatapajono.getJarjestyskriteerit().isEmpty(); + + return (kaytetaanValintalaskentaa && !hasJarjestyskriteerit) + || (!kaytetaanValintalaskentaa && hasJarjestyskriteerit); + } + }; + Optional + valintatapajonoPuutteellisellaJonokriteerilla = + valintaperusteetList.stream() + .map(ValintaperusteetDTO::getValinnanVaihe) + .flatMap(v -> v.getValintatapajono().stream()) + .filter(valintatapajonoHasPuuttuvaJonokriteeri) + .findFirst(); + + if (valintatapajonoPuutteellisellaJonokriteerilla.isPresent()) { + ValintatapajonoJarjestyskriteereillaDTO valintatapajono = + valintatapajonoPuutteellisellaJonokriteerilla.get(); + String errorMessage = + String.format( + "(Uuid: %s) Hakukohteen %s valintatapajonolla %s on joko valintalaskenta ilman jonokriteereitä tai jonokriteereitä ilman valintalaskentaa, joten valintalaskentaa ei voida jatkaa ja se keskeytetään", + uuid, hakukohdeOid, valintatapajono.getOid()); + LOG.error(errorMessage); + throw new RuntimeException(errorMessage); + } + } + + public CompletableFuture fetchResourcesForOneLaskenta( + final String uuid, + final String hakuOid, + final String hakukohdeOid, + final Integer valinnanVaihe, + AuditSession auditSession, + boolean isErillishaku, + boolean retryHakemuksetAndOppijat, + boolean withHakijaRyhmat, + Date nyt) { + + Instant start = Instant.now(); + Map waitDurations = new ConcurrentHashMap<>(); + Map invokeDurations = new ConcurrentHashMap<>(); + + final CompletableFuture parametritDTOFuture = + this.parametritLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> ohjausparametritAsyncResource.haeHaunOhjausparametrit(hakuOid)); + final CompletableFuture hakuFuture = + this.hakuLimiter.withConcurrencyLimit( + 1, waitDurations, invokeDurations, () -> tarjontaAsyncResource.haeHaku(hakuOid)); + + SuoritustiedotDTO suoritustiedotDTO = new SuoritustiedotDTO(); + + LaskentaResurssinhakuWrapper.PyynnonTunniste tunniste = + new LaskentaResurssinhakuWrapper.PyynnonTunniste( + "Please put individual resource source identifier here!", uuid, hakukohdeOid); + + CompletableFuture> valintaperusteet = + this.valintaperusteetLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> + createResurssiFuture( + tunniste, + "valintaperusteetAsyncResource.haeValintaperusteet", + () -> + valintaperusteetAsyncResource.haeValintaperusteet( + hakukohdeOid, valinnanVaihe)) + .thenApplyAsync( + vp -> { + if (!isValintalaskentaKaytossa(vp)) { + throw new RuntimeException( + "Valintalaskenta ei ole käytössä hakukohteelle " + hakukohdeOid); + } + return vp; + }, + this.executor)); + + CompletableFuture> hakemukset = + hakuFuture.thenComposeAsync( + haku -> { + if (haku.isHakemuspalvelu()) { + boolean haetaanHarkinnanvaraisuudet = + haku.isAmmatillinenJaLukio() && haku.isKoutaHaku(); + return this.ataruhakemuksetLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> + createResurssiFuture( + tunniste, + "applicationAsyncResource.getApplications", + () -> + ataruAsyncResource.getApplicationsByHakukohde( + hakukohdeOid, haetaanHarkinnanvaraisuudet), + retryHakemuksetAndOppijat)); + } else { + throw new RuntimeException( + "HakuApp lähtötietoja ei tueta enää: hakukohde " + hakukohdeOid); + } + }, + this.executor); + + CompletableFuture> henkiloViitteet = + hakemukset.thenComposeAsync( + hws -> { + List viitteet = + hws.stream() + .map( + hw -> + new HenkiloViiteDto(hw.getApplicationPersonOid(), hw.getPersonOid())) + .collect(Collectors.toList()); + return CompletableFuture.completedFuture(viitteet); + }, + this.executor); + + CompletableFuture> oppijasForOidsFromHakemukses = + henkiloViitteet.thenComposeAsync( + hws -> + this.suorituksetLimiter.withConcurrencyLimit( + hws.size() + 50, + waitDurations, + invokeDurations, + () -> { + LOG.info( + "Haetaan suoritukset hakukohteen " + + hakukohdeOid + + " " + + hws.size() + + " oppijalle"); + + LOG.info("Got henkiloViittees: {}", hws); + Map masterToOriginal = + hws.stream() + .collect( + Collectors.toMap( + HenkiloViiteDto::getMasterOid, + HenkiloViiteDto::getHenkiloOid)); + List oppijaOids = new ArrayList<>(masterToOriginal.keySet()); + LOG.info( + "Got personOids from hakemukses and getting Oppijas for these: {} for hakukohde {}", + oppijaOids, + hakukohdeOid); + return createResurssiFuture( + tunniste, + "suoritusrekisteriAsyncResource.getSuorituksetByOppijas", + () -> + suoritusrekisteriAsyncResource.getSuorituksetByOppijas( + oppijaOids, hakuOid, true), + retryHakemuksetAndOppijat) + .thenApply( + oppijat -> { + oppijat.forEach( + oppija -> + oppija.setOppijanumero( + masterToOriginal.get(oppija.getOppijanumero()))); + return oppijat; + }); + }), + this.executor); + + CompletableFuture>> hakukohdeRyhmasForHakukohdes = + this.hakukohderyhmatLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> + createResurssiFuture( + tunniste, + "tarjontaAsyncResource.hakukohdeRyhmasForHakukohdes", + () -> tarjontaAsyncResource.hakukohdeRyhmasForHakukohdes(hakuOid))); + + CompletableFuture valintapisteetHakemuksille = + hakemukset.thenComposeAsync( + hakemusWrappers -> { + List hakemusOids = + hakemusWrappers.stream().map(HakemusWrapper::getOid).collect(Collectors.toList()); + return this.valintapisteetLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> + createResurssiFuture( + tunniste, + "valintapisteAsyncResource.getValintapisteetWithHakemusOidsAsFuture", + () -> + valintapisteAsyncResource.getValintapisteetWithHakemusOidsAsFuture( + hakemusOids, auditSession), + retryHakemuksetAndOppijat)); + }, + this.executor); + + CompletableFuture> hakijaryhmat = + withHakijaRyhmat + ? this.hakijaryhmatLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> + createResurssiFuture( + tunniste, + "valintaperusteetAsyncResource.haeHakijaryhmat", + () -> valintaperusteetAsyncResource.haeHakijaryhmat(hakukohdeOid))) + : CompletableFuture.completedFuture(emptyList()); + CompletableFuture> koskiOppijaByOppijaOid = + CompletableFuture.allOf(valintaperusteet, hakemukset) + .thenComposeAsync( + unused -> + this.koskioppijatLimiter.withConcurrencyLimit( + 1, + waitDurations, + invokeDurations, + () -> + createResurssiFuture( + tunniste, + "koskiService.haeKoskiOppijat", + () -> + koskiService.haeKoskiOppijat( + hakukohdeOid, + valintaperusteet, + hakemukset, + suoritustiedotDTO, + nyt))), + this.executor); + + LOG.info( + "(Uuid: {}) Odotetaan kaikkien resurssihakujen valmistumista hakukohteelle {}, jotta voidaan palauttaa ne yhtenä pakettina.", + uuid, + hakukohdeOid); + CompletableFuture result = new CompletableFuture<>(); + getLaskeDtoFuture( + uuid, + hakuFuture, + hakukohdeOid, + isErillishaku, + parametritDTOFuture, + withHakijaRyhmat, + valintaperusteet, + oppijasForOidsFromHakemukses, + hakukohdeRyhmasForHakukohdes, + valintapisteetHakemuksille, + hakijaryhmat, + hakemukset, + koskiOppijaByOppijaOid) + .thenAcceptAsync( + laskeDTO -> { + laskeDTO.populoiSuoritustiedotHakemuksille(suoritustiedotDTO); + invokeDurations.put("Total", Duration.between(start, Instant.now())); + LOG.info( + "Haettiin lähtötiedot hakukohteelle " + + hakukohdeOid + + ", start: " + + start + + ", end: " + + Instant.now()); + this.tallennaLokitJaMetriikat(hakukohdeOid, waitDurations, invokeDurations); + result.complete(laskeDTO); + }, + this.executor) + .orTimeout(9 * 60 * 1000l, TimeUnit.MILLISECONDS) + .exceptionallyAsync( + ex -> { + Throwable underlyingCause = getUnderlyingCause(ex); + invokeDurations.put( + "Total" + (underlyingCause instanceof TimeoutException ? " (timeout)" : ""), + Duration.between(start, Instant.now())); + this.tallennaLokitJaMetriikat(hakukohdeOid, waitDurations, invokeDurations); + result.completeExceptionally(underlyingCause); + return null; + }, + this.executor); + return result; + } + + private static Throwable getUnderlyingCause(Throwable t) { + if (t.getCause() != null) { + return getUnderlyingCause(t.getCause()); + } + return t; + } + + private CompletableFuture createResurssiFuture( + LaskentaResurssinhakuWrapper.PyynnonTunniste tunniste, + String resurssi, + Supplier> sourceFuture, + boolean retry) { + return LaskentaResurssinhakuWrapper.luoLaskentaResurssinHakuFuture( + sourceFuture, tunniste.withNimi(resurssi), retry); + } + + private CompletableFuture createResurssiFuture( + LaskentaResurssinhakuWrapper.PyynnonTunniste tunniste, + String resurssi, + Supplier> sourceFuture) { + return createResurssiFuture(tunniste, resurssi, sourceFuture, false); + } +} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaStarter.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaStarter.java deleted file mode 100644 index 5256729c54..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaStarter.java +++ /dev/null @@ -1,237 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import akka.actor.ActorRef; -import fi.vm.sade.service.valintaperusteet.dto.HakukohdeViiteDTO; -import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.OhjausparametritAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.dto.ParametritDTO; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.Haku; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.TarjontaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintaperusteet.ValintaperusteetAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; -import fi.vm.sade.valinta.kooste.function.SynkronoituLaskuri; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaStartParams; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Maski; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeTila; -import fi.vm.sade.valinta.seuranta.dto.IlmoitusDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTila; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import io.reactivex.Observable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class LaskentaStarter { - private static final Logger LOG = LoggerFactory.getLogger(LaskentaStarter.class); - - private final OhjausparametritAsyncResource ohjausparametritAsyncResource; - private final ValintaperusteetAsyncResource valintaperusteetAsyncResource; - private final LaskentaSeurantaAsyncResource seurantaAsyncResource; - private final TarjontaAsyncResource tarjontaAsyncResource; - - @Autowired - public LaskentaStarter( - OhjausparametritAsyncResource ohjausparametritAsyncResource, - ValintaperusteetAsyncResource valintaperusteetAsyncResource, - LaskentaSeurantaAsyncResource seurantaAsyncResource, - TarjontaAsyncResource tarjontaAsyncResource) { - this.ohjausparametritAsyncResource = ohjausparametritAsyncResource; - this.valintaperusteetAsyncResource = valintaperusteetAsyncResource; - this.seurantaAsyncResource = seurantaAsyncResource; - this.tarjontaAsyncResource = tarjontaAsyncResource; - } - - public void fetchLaskentaParams( - ActorRef laskennanKaynnistajaActor, - final String uuid, - final BiConsumer startActor) { - seurantaAsyncResource - .laskenta(uuid) - .subscribe( - (LaskentaDto laskenta) -> { - String hakuOid = laskenta.getHakuOid(); - if (StringUtils.isBlank(hakuOid)) { - LOG.error("Yritettiin hakea hakukohteita ilman hakuOidia!"); - throw new RuntimeException("Yritettiin hakea hakukohteita ilman hakuOidia!"); - } - valintaperusteetAsyncResource - .haunHakukohteet(hakuOid) - .subscribe( - (List hakukohdeViitteet) -> { - Collection hakukohdeOids = - maskHakukohteet(hakuOid, hakukohdeViitteet, laskenta); - if (!hakukohdeOids.isEmpty()) { - fetchHakuInformation( - laskennanKaynnistajaActor, - hakuOid, - hakukohdeOids, - laskenta, - startActor); - } else { - cancelLaskenta( - laskennanKaynnistajaActor, - "Haulla " - + laskenta.getUuid() - + " ei saatu hakukohteita! Onko valinnat synkronoitu tarjonnan kanssa?", - null, - uuid); - } - }, - (Throwable t) -> - cancelLaskenta( - laskennanKaynnistajaActor, - "Haun kohteiden haku epäonnistui haulle: " + uuid, - Optional.empty(), - uuid)); - }, - (Throwable t) -> - cancelLaskenta( - laskennanKaynnistajaActor, - "Laskennan haku epäonnistui ", - Optional.of(t), - uuid)); - } - - private static Collection maskHakukohteet( - String hakuOid, List hakukohdeViitteet, LaskentaDto laskenta) { - LOG.info("Tarkastellaan hakukohdeviitteita haulle {}", hakuOid); - - final List haunHakukohdeOidit = - hakukohdeViitteet != null - ? publishedNonNulltoHakukohdeJaOrganisaatio(hakukohdeViitteet) - : new ArrayList<>(); - final Maski maski = createMaskiFromLaskenta(laskenta); - - return maski.maskaa(haunHakukohdeOidit); - } - - private void fetchHakuInformation( - ActorRef laskennankaynnistajaActor, - String hakuOid, - Collection haunHakukohdeOidit, - LaskentaDto laskenta, - BiConsumer startActor) { - AtomicReference hakuRef = new AtomicReference<>(); - AtomicReference parametritRef = new AtomicReference<>(); - SynkronoituLaskuri counter = - SynkronoituLaskuri.builder() - .setLaskurinAlkuarvo(2) - .setSynkronoituToiminto(() -> startActor.accept(hakuRef.get(), parametritRef.get())) - .build(); - Observable.fromFuture(tarjontaAsyncResource.haeHaku(hakuOid)) - .subscribe( - haku -> { - hakuRef.set(haku); - counter.vahennaLaskuriaJaJosValmisNiinSuoritaToiminto(); - }, - (Throwable t) -> - cancelLaskenta( - laskennankaynnistajaActor, - "Tarjontatietojen haku epäonnistui: ", - Optional.of(t), - laskenta.getUuid())); - - Observable.fromFuture(ohjausparametritAsyncResource.haeHaunOhjausparametrit(hakuOid)) - .subscribe( - parametrit -> { - parametritRef.set( - laskentaActorParams(hakuOid, laskenta, haunHakukohdeOidit, parametrit)); - counter.vahennaLaskuriaJaJosValmisNiinSuoritaToiminto(); - }, - (Throwable t) -> - cancelLaskenta( - laskennankaynnistajaActor, - "Ohjausparametrien luku epäonnistui: ", - Optional.of(t), - laskenta.getUuid())); - } - - private static AuditSession koosteAuditSession(LaskentaDto laskenta) { - final String userAgent = "-"; - final String inetAddress = "127.0.0.1"; - AuditSession auditSession = - new AuditSession(laskenta.getUserOID(), Collections.emptyList(), userAgent, inetAddress); - auditSession.setSessionId(laskenta.getUuid()); - auditSession.setPersonOid(laskenta.getUserOID()); - return auditSession; - } - - private static LaskentaActorParams laskentaActorParams( - String hakuOid, - LaskentaDto laskenta, - Collection haunHakukohdeOidit, - ParametritDTO parametrit) { - return new LaskentaActorParams( - new LaskentaStartParams( - koosteAuditSession(laskenta), - laskenta.getUuid(), - hakuOid, - laskenta.isErillishaku(), - true, - LaskentaTyyppi.VALINTARYHMA.equals(laskenta.getTyyppi()), - laskenta.getValinnanvaihe(), - laskenta.getValintakoelaskenta(), - haunHakukohdeOidit, - laskenta.getTyyppi()), - parametrit); - } - - private static List publishedNonNulltoHakukohdeJaOrganisaatio( - final List hakukohdeViitteet) { - return hakukohdeViitteet.stream() - .filter(Objects::nonNull) - .filter(h -> h.getOid() != null) - .filter(h -> h.getTila().equals("JULKAISTU")) - .map(h -> new HakukohdeJaOrganisaatio(h.getOid(), h.getTarjoajaOid())) - .collect(Collectors.toList()); - } - - private void cancelLaskenta( - ActorRef laskennanKaynnistajaActor, String msg, Optional t, String uuid) { - if (t.isPresent()) LOG.error(msg, t); - else LOG.error(msg); - LaskentaTila tila = LaskentaTila.VALMIS; - HakukohdeTila hakukohdetila = HakukohdeTila.KESKEYTETTY; - Optional ilmoitusDtoOptional = - t.map( - poikkeus -> IlmoitusDto.virheilmoitus(msg, Arrays.toString(poikkeus.getStackTrace()))); - seurantaAsyncResource - .merkkaaLaskennanTila(uuid, tila, hakukohdetila, ilmoitusDtoOptional) - .subscribe( - ok -> {}, - fail -> - LOG.error( - String.format( - "(UUID = %s) Laskennan tilan (laskenta=%s, hakukohde=%s) merkkaaminen epaonnistui!", - uuid, tila, hakukohdetila), - fail)); - laskennanKaynnistajaActor.tell(new LaskentaStarterActor.WorkerAvailable(), ActorRef.noSender()); - } - - private static Maski createMaskiFromLaskenta(final LaskentaDto laskenta) { - final List hakukohdeOids = - laskenta.getHakukohteet().stream() - .filter(h -> !HakukohdeTila.VALMIS.equals(h.getTila())) - .map(h -> new HakukohdeJaOrganisaatio(h.getHakukohdeOid(), h.getOrganisaatioOid())) - .map(HakukohdeJaOrganisaatio::getHakukohdeOid) - .collect(Collectors.toList()); - - return Maski.whitelist(hakukohdeOids); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaSupervisor.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaSupervisor.java deleted file mode 100644 index b7e9074470..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaSupervisor.java +++ /dev/null @@ -1,9 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRouteValvomo; - -public interface LaskentaSupervisor extends ValintalaskentaKerrallaRouteValvomo { - void ready(String uuid); - - void fetchAndStartLaskenta(); -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaTyyppi.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaTyyppi.java deleted file mode 100644 index d4ba9d5a34..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/LaskentaTyyppi.java +++ /dev/null @@ -1,8 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -public enum LaskentaTyyppi { - VALINTAKOELASKENTA, - KAIKKI, - VALINTALASKENTA, - VALINTARYHMALASKENTA -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/Laskuri.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/Laskuri.java deleted file mode 100644 index b0b20951ba..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/Laskuri.java +++ /dev/null @@ -1,29 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor; - -import java.util.concurrent.atomic.AtomicInteger; - -public class Laskuri { - private final AtomicInteger laskuri; - private final int yhteensa; - - public Laskuri(int tehtavia) { - this.yhteensa = tehtavia; - this.laskuri = new AtomicInteger(tehtavia); - } - - protected int tiputaLaskuria() { - return laskuri.decrementAndGet(); - } - - public boolean isDone() { - return laskuri.get() <= 0; - } - - public boolean isOverDone() { - return laskuri.get() < 0; - } - - public int getYhteensa() { - return yhteensa; - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/ValintaryhmaLaskentaActorImpl.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/ValintaryhmaLaskentaActorImpl.java deleted file mode 100644 index 8b13789179..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/ValintaryhmaLaskentaActorImpl.java +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/dto/HakukohdeJaOrganisaatio.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/dto/HakukohdeJaOrganisaatio.java deleted file mode 100644 index 7fb539f554..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/dto/HakukohdeJaOrganisaatio.java +++ /dev/null @@ -1,31 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto; - -import org.apache.commons.lang3.builder.ToStringBuilder; - -public class HakukohdeJaOrganisaatio { - private String hakukohdeOid; - private String organisaatioOid; - - public HakukohdeJaOrganisaatio() { - this.hakukohdeOid = ""; - this.organisaatioOid = ""; - } - - public HakukohdeJaOrganisaatio(String hakukohdeOid, String organisaatioOid) { - this.hakukohdeOid = hakukohdeOid; - this.organisaatioOid = organisaatioOid; - } - - public String getHakukohdeOid() { - return hakukohdeOid; - } - - public String getOrganisaatioOid() { - return organisaatioOid; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/dto/UuidHakukohdeJaOrganisaatio.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/dto/UuidHakukohdeJaOrganisaatio.java deleted file mode 100644 index 67ea62703e..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/dto/UuidHakukohdeJaOrganisaatio.java +++ /dev/null @@ -1,26 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto; - -import org.apache.commons.lang3.builder.ToStringBuilder; - -public class UuidHakukohdeJaOrganisaatio { - private final String uuid; - private final HakukohdeJaOrganisaatio hakukohdeJaOrganisaatio; - - public UuidHakukohdeJaOrganisaatio(String uuid, HakukohdeJaOrganisaatio hakukohdeJaOrganisaatio) { - this.uuid = uuid; - this.hakukohdeJaOrganisaatio = hakukohdeJaOrganisaatio; - } - - public HakukohdeJaOrganisaatio getHakukohdeJaOrganisaatio() { - return hakukohdeJaOrganisaatio; - } - - public String getUuid() { - return uuid; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/laskenta/LaskentaStarterActor.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/laskenta/LaskentaStarterActor.java deleted file mode 100644 index 92d90a1422..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/laskenta/LaskentaStarterActor.java +++ /dev/null @@ -1,97 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta; - -import akka.actor.Props; -import akka.actor.UntypedActor; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaSupervisor; -import java.util.concurrent.atomic.AtomicInteger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class LaskentaStarterActor extends UntypedActor { - private final Logger LOG = LoggerFactory.getLogger(LaskentaStarterActor.class); - - private final LaskentaSupervisor laskentaSupervisor; - private int maxWorkers; - private AtomicInteger workerCount = new AtomicInteger(0); - - private LaskentaStarterActor(final LaskentaSupervisor laskentaSupervisor, final int maxWorkers) { - this.laskentaSupervisor = laskentaSupervisor; - this.maxWorkers = maxWorkers; - LOG.info("Creating LaskentaStarterActor with maxWorkerCount {}", maxWorkers); - } - - public static Props props(final LaskentaSupervisor laskentaSupervisor, final int maxWorkers) { - return Props.create( - LaskentaStarterActor.class, () -> new LaskentaStarterActor(laskentaSupervisor, maxWorkers)); - } - - @Override - public void onReceive(Object message) { - if (WorkAvailable.class.isInstance(message)) { - startLaskentaIfWorkersAvailable(); - } else if (StartAllWorkers.class.isInstance(message)) { - LOG.info("Starting all workers. Current workerCount: " + workerCount.get()); - while (workerCount.get() < maxWorkers) { - startLaskentaIfWorkersAvailable(); - } - } else if (WorkerAvailable.class.isInstance(message)) { - decrementWorkerCount(); - startLaskentaIfWorkersAvailable(); - } else if (NoWorkAvailable.class.isInstance(message)) { - decrementWorkerCount(); - } else if (ResetWorkerCount.class.isInstance(message)) { - workerCount.set(0); - LOG.info("Worker count reset: " + workerCount.get()); - } else if (MaxWorkerCount.class.isInstance(message)) { - MaxWorkerCount count = (MaxWorkerCount) message; - LOG.info("Set maxworker count to " + count.maxWorkerCount); - maxWorkers = count.maxWorkerCount; - } else { - LOG.error("Unknown message: " + message); - } - } - - private void startLaskentaIfWorkersAvailable() { - int wasNumberOfWorkers = - workerCount.getAndUpdate( - current -> { - if (current < maxWorkers) { - return ++current; - } else { - return current; - } - }); - LOG.info("Process; maxWorkers: {}, workerCount: {}", maxWorkers, wasNumberOfWorkers); - if (wasNumberOfWorkers < maxWorkers) { // if it was less than maxWorkers then it was incremented - LOG.info("Reserving a new worker, workerCount: {}", (wasNumberOfWorkers + 1)); - laskentaSupervisor.fetchAndStartLaskenta(); - } - } - - public int getWorkerCount() { - return workerCount.get(); - } - - private void decrementWorkerCount() { - int workerCount = this.workerCount.updateAndGet(i -> i > 0 ? i - 1 : i); - LOG.info("Releasing worker, workerCount: {}", workerCount); - } - - public static class WorkAvailable {} - - public static class NoWorkAvailable {} - - public static class WorkerAvailable {} - - public static class ResetWorkerCount {} - - public static class StartAllWorkers {} - - public static class MaxWorkerCount { - public final int maxWorkerCount; - - public MaxWorkerCount(int count) { - this.maxWorkerCount = count; - } - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dao/ParametritDao.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dao/ParametritDao.java new file mode 100644 index 0000000000..a1575697d3 --- /dev/null +++ b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dao/ParametritDao.java @@ -0,0 +1,13 @@ +package fi.vm.sade.valinta.kooste.valintalaskenta.dao; + +import java.util.Map; + +public interface ParametritDao { + + /** + * Lukee parametrit tietokannasta + * + * @return parametrien arvot + */ + Map lueParametrit(); +} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dao/impl/ParametritDaoImpl.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dao/impl/ParametritDaoImpl.java new file mode 100644 index 0000000000..f99162b9b6 --- /dev/null +++ b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dao/impl/ParametritDaoImpl.java @@ -0,0 +1,28 @@ +package fi.vm.sade.valinta.kooste.valintalaskenta.dao.impl; + +import fi.vm.sade.valinta.kooste.valintalaskenta.dao.ParametritDao; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +@Component +public class ParametritDaoImpl implements ParametritDao { + + private final JdbcTemplate jdbcTemplate; + + public ParametritDaoImpl(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public Map lueParametrit() { + return this.jdbcTemplate + .query( + "SELECT nimi, arvo FROM parametrit", + (rs, rowNum) -> Pair.of(rs.getString("nimi"), rs.getString("arvo"))) + .stream() + .collect(Collectors.toMap(p -> p.getLeft(), p -> p.getRight())); + } +} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/Laskenta.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/Laskenta.java deleted file mode 100644 index f181e9cd07..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/Laskenta.java +++ /dev/null @@ -1,5 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -public interface Laskenta extends LaskentaInfo, LaskentaLopeta { - boolean isValmis(); -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaInfo.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaInfo.java deleted file mode 100644 index e1b978a359..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -public interface LaskentaInfo { - String getUuid(); - - String getHakuOid(); - - boolean isOsittainenLaskenta(); // eli maskilla aloitettu osajoukko koko laskennasta -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaJaValintaperusteetJaHakemukset.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaJaValintaperusteetJaHakemukset.java deleted file mode 100644 index b421b5dac4..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaJaValintaperusteetJaHakemukset.java +++ /dev/null @@ -1,59 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetDTO; -import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetHakijaryhmaDTO; -import fi.vm.sade.valinta.kooste.external.resource.hakuapp.dto.ApplicationAdditionalDataDTO; -import fi.vm.sade.valinta.kooste.external.resource.hakuapp.dto.Hakemus; -import java.util.List; - -public class LaskentaJaValintaperusteetJaHakemukset { - private final LaskentaStartParams laskenta; - private final List valintaperusteet; - private final List hakemukset; - private final List lisatiedot; - private final List hakijaryhmat; - private final String hakukohdeOid; - - public LaskentaJaValintaperusteetJaHakemukset( - LaskentaStartParams laskenta, - String hakukohdeOid, - List valintaperusteet, - List hakemukset, - List lisatiedot, - List hakijaryhmat) { - this.laskenta = laskenta; - this.hakukohdeOid = hakukohdeOid; - this.valintaperusteet = valintaperusteet; - this.hakemukset = hakemukset; - this.lisatiedot = lisatiedot; - this.hakijaryhmat = hakijaryhmat; - } - - public List getHakijaryhmat() { - return hakijaryhmat; - } - - public boolean isValmisLaskettavaksi() { - return valintaperusteet != null && hakemukset != null; - } - - public List getLisatiedot() { - return lisatiedot; - } - - public List getHakemukset() { - return hakemukset; - } - - public String getHakukohdeOid() { - return hakukohdeOid; - } - - public LaskentaStartParams getLaskenta() { - return laskenta; - } - - public List getValintaperusteet() { - return valintaperusteet; - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaLopeta.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaLopeta.java deleted file mode 100644 index 559157b1cf..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaLopeta.java +++ /dev/null @@ -1,5 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -public interface LaskentaLopeta { - void lopeta(); -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaStartParams.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaStartParams.java deleted file mode 100644 index e665cce69d..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaStartParams.java +++ /dev/null @@ -1,112 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import java.util.Collection; - -public class LaskentaStartParams implements LaskentaInfo { - private static final String NIMI_FORMAT = "Laskenta hakuOid(%s) uuid(%s) hakukohteita(%d)"; - private final String uuid; - private final String hakuOid; - private final boolean osittainenLaskenta; - private final Integer valinnanvaihe; - private final boolean erillishaku; - private final Boolean valintakoelaskenta; - private final boolean valintaryhmalaskenta; - private final Collection hakukohdeDtos; - private final LaskentaTyyppi tyyppi; - private final AuditSession auditSession; - - public LaskentaStartParams( - AuditSession auditSession, - String uuid, - String hakuOid, - boolean erillishaku, - Integer valinnanvaihe, - Boolean valintakoelaskenta, - Collection hakukohdeDtos, - LaskentaTyyppi tyyppi) { - this.auditSession = auditSession; - this.uuid = uuid; - this.hakuOid = hakuOid; - this.osittainenLaskenta = false; - this.valinnanvaihe = valinnanvaihe; - this.valintakoelaskenta = valintakoelaskenta; - this.hakukohdeDtos = hakukohdeDtos; - this.valintaryhmalaskenta = false; - this.tyyppi = tyyppi; - this.erillishaku = erillishaku; - } - - public LaskentaStartParams( - AuditSession auditSession, - String uuid, - String hakuOid, - boolean erillishaku, - boolean osittainenLaskenta, - boolean valintaryhmalaskenta, - Integer valinnanvaihe, - Boolean valintakoelaskenta, - Collection hakukohdeDtos, - LaskentaTyyppi tyyppi) { - this.auditSession = auditSession; - this.uuid = uuid; - this.hakuOid = hakuOid; - this.osittainenLaskenta = osittainenLaskenta; - this.valintaryhmalaskenta = valintaryhmalaskenta; - this.valinnanvaihe = valinnanvaihe; - this.valintakoelaskenta = valintakoelaskenta; - this.hakukohdeDtos = hakukohdeDtos; - this.tyyppi = tyyppi; - this.erillishaku = erillishaku; - } - - public AuditSession getAuditSession() { - return auditSession; - } - - public LaskentaTyyppi getTyyppi() { - return tyyppi; - } - - public Collection getHakukohdeDtos() { - return hakukohdeDtos; - } - - public Integer getValinnanvaihe() { - return valinnanvaihe; - } - - public Boolean getValintakoelaskenta() { - return valintakoelaskenta; - } - - public boolean isOsittainenLaskenta() { - return osittainenLaskenta; - } - - public boolean isValintaryhmaLaskenta() { - return valintaryhmalaskenta; - } - - public boolean isErillishaku() { - return erillishaku; - } - - public boolean isValintaryhmalaskenta() { - return valintaryhmalaskenta; - } - - public String getHakuOid() { - return hakuOid; - } - - public String getUuid() { - return uuid; - } - - public String toString() { - return String.format(NIMI_FORMAT, hakuOid, uuid, hakukohdeDtos.size()); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaWrapper.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaWrapper.java deleted file mode 100644 index 06bebc7f7b..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/LaskentaWrapper.java +++ /dev/null @@ -1,164 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetDTO; -import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetHakijaryhmaDTO; -import fi.vm.sade.valinta.kooste.external.resource.hakuapp.dto.ApplicationAdditionalDataDTO; -import fi.vm.sade.valinta.kooste.external.resource.hakuapp.dto.Hakemus; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LaskentaWrapper { - private static final Logger LOG = LoggerFactory.getLogger(LaskentaWrapper.class); - private final boolean onkoToitaOikeaMaara; - private final boolean onkoLaskemattakinTehtyEliHakukohteelleEiOllutHakemuksiaTaiValintaperusteita; - private final boolean onkoOhitettavaEliValintaperusteetTaiHakemuksetTaiLisatiedotPuuttui; - private final List valintaperusteet; - private final List hakemukset; - private final List lisatiedot; - private final List hakijaryhmat; - private final String hakukohdeOid; - private final LaskentaStartParams laskenta; - - public LaskentaWrapper(List tyot) { - if (tyot == null || tyot.size() != 4) { - this.onkoToitaOikeaMaara = false; - this.valintaperusteet = null; - this.hakemukset = null; - this.lisatiedot = null; - this.onkoLaskemattakinTehtyEliHakukohteelleEiOllutHakemuksiaTaiValintaperusteita = false; - this.onkoOhitettavaEliValintaperusteetTaiHakemuksetTaiLisatiedotPuuttui = false; - this.hakukohdeOid = null; - this.laskenta = null; - this.hakijaryhmat = null; - } else { - this.onkoToitaOikeaMaara = true; - this.valintaperusteet = extractValintaperusteet(tyot); - this.hakemukset = extractHakemukset(tyot); - this.lisatiedot = extractLisatiedot(tyot); - this.hakukohdeOid = extractHakukohdeOid(tyot); - this.laskenta = extractLaskenta(tyot); - this.hakijaryhmat = extractHakijaryhmat(tyot); - this.onkoLaskemattakinTehtyEliHakukohteelleEiOllutHakemuksiaTaiValintaperusteita = - isOnkoJokuDataJoukkoTyhja(); - this.onkoOhitettavaEliValintaperusteetTaiHakemuksetTaiLisatiedotPuuttui = - isOnkoJokuDataJoukkoNullReferenssi(); - } - } - - public List getHakemukset() { - return hakemukset; - } - - public List getHakijaryhmat() { - return hakijaryhmat; - } - - public LaskentaStartParams getLaskenta() { - return laskenta; - } - - public String getHakukohdeOid() { - return hakukohdeOid; - } - - public boolean isOnkoLaskemattakinTehtyEliHakukohteelleEiOllutHakemuksiaTaiValintaperusteita() { - return onkoLaskemattakinTehtyEliHakukohteelleEiOllutHakemuksiaTaiValintaperusteita; - } - - public boolean isOnkoOhitettavaEliValintaperusteetTaiHakemuksetTaiLisatiedotPuuttui() { - return onkoOhitettavaEliValintaperusteetTaiHakemuksetTaiLisatiedotPuuttui; - } - - public List getLisatiedot() { - return lisatiedot; - } - - public boolean isOnkoToitaOikeaMaara() { - return onkoToitaOikeaMaara; - } - - public List getValintaperusteet() { - return valintaperusteet; - } - - private boolean isOnkoJokuDataJoukkoTyhja() { - return (valintaperusteet != null && valintaperusteet.isEmpty()) - || (hakemukset != null && hakemukset.isEmpty()); - } - - private boolean isOnkoJokuDataJoukkoNullReferenssi() { - return valintaperusteet == null - || hakemukset == null - || lisatiedot == null - || hakijaryhmat == null; - } - - private String extractHakukohdeOid(List tyot) { - return tyot.iterator().next().getHakukohdeOid(); - } - - private LaskentaStartParams extractLaskenta(List tyot) { - return tyot.iterator().next().getLaskenta(); - } - - private List extractValintaperusteet( - List tyot) { - for (LaskentaJaValintaperusteetJaHakemukset v : tyot) { - if (v.getValintaperusteet() != null) { - return v.getValintaperusteet(); - } - } - return null; - } - - private List extractHakemukset(List tyot) { - for (LaskentaJaValintaperusteetJaHakemukset v : tyot) { - if (v.getHakemukset() != null) { - return v.getHakemukset(); - } - } - return null; - } - - private List extractHakijaryhmat( - List tyot) { - for (LaskentaJaValintaperusteetJaHakemukset v : tyot) { - if (v.getHakijaryhmat() != null) { - return v.getHakijaryhmat(); - } - } - return null; - } - - private List extractLisatiedot( - List tyot) { - for (LaskentaJaValintaperusteetJaHakemukset v : tyot) { - if (v.getLisatiedot() != null) { - return v.getLisatiedot(); - } - } - return null; - } - - private List getHakemuksetLisatiedoilla() { - final Map appData = - lisatiedot.parallelStream() - .collect(Collectors.toMap(ApplicationAdditionalDataDTO::getOid, i -> i)); - return hakemukset.parallelStream() - .map( - h -> { - Map addData = appData.get(h.getOid()).getAdditionalData(); - if (addData == null) { - LOG.warn("Lisatietoja ei saatu hakemukselle {}", h.getOid()); - addData = Collections.emptyMap(); - } - h.setAdditionalInfo(addData); - return h; - }) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/Maski.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/Maski.java deleted file mode 100644 index 39c0482448..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/dto/Maski.java +++ /dev/null @@ -1,118 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.dto; - -import com.google.common.collect.Sets; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Maski osittaiseen laskentaan haulle */ -public class Maski { - private static final Logger LOG = LoggerFactory.getLogger(Maski.class); - private final Collection hakukohteet; - private final boolean whiteList; - - public Maski() { - this.whiteList = false; - this.hakukohteet = null; - } - - private Maski(boolean whitelist, Collection hakukohteet) { - this.whiteList = whitelist; - this.hakukohteet = hakukohteet; - } - - public boolean isMask() { - return hakukohteet != null && !hakukohteet.isEmpty(); - } - - public static Maski whitelist(Collection hakukohteet) { - return new Maski(true, hakukohteet); - } - - public static Maski blacklist(Collection hakukohteet) { - return new Maski(false, hakukohteet); - } - - public boolean isBlacklist() { - return !whiteList && isMask(); - } - - public boolean isWhitelist() { - return whiteList && isMask(); - } - - public Collection maskaa( - Collection originalHjaO) { - Set lopulliset = Collections.emptySet(); - Set original = - originalHjaO.stream() - .map(HakukohdeJaOrganisaatio::getHakukohdeOid) - .collect(Collectors.toSet()); - Set hakukohdeOidsMask = Sets.newHashSet(hakukohteet); - if (isMask()) { - if (isWhitelist()) { - lopulliset = - applyWhitelist( - hakukohdeOidsMask, - original, - (s -> - LOG.error( - "Haku ei taysin vastaa syotetyn whitelistin hakukohteita! Puuttuvat hakukohteet \r\n{}", - Arrays.toString(s.toArray())))); - } else if (isBlacklist()) { - lopulliset = - applyBlacklist( - hakukohdeOidsMask, - original, - (s -> - LOG.error( - "Haku ei taysin vastaa syotetyn blacklistin hakukohteita! Ylimaaraiset hakukohteet \r\n{}", - Arrays.toString(s.toArray())))); - } - } - final Set lopullisetFilter = lopulliset; - return originalHjaO.stream() - .filter(hk -> lopullisetFilter.contains(hk.getHakukohdeOid())) - .collect(Collectors.toList()); - } - - public String toString() { - String maskTypeAsString = isWhitelist() ? "Whitelist" : "Blacklist"; - String hakukohteetAsString = - hakukohteet != null ? Arrays.toString(hakukohteet.toArray()) : StringUtils.EMPTY; - return String.format(maskTypeAsString + " Maski {}", hakukohteetAsString); - } - - private Set applyWhitelist(Set w, Set t, Consumer> ylimaaraiset) { - if (t.containsAll(w)) { - return w; - } else { - Set s = Sets.newHashSet(w); - s.removeAll(t); - ylimaaraiset.accept(s); - Set copy = Sets.newHashSet(w); - copy.removeAll(s); - return copy; - } - } - - private Set applyBlacklist(Set b, Set t, Consumer> ylimaaraiset) { - Set copy = Sets.newHashSet(t); - copy.removeAll(b); - if (t.containsAll(b)) { - return copy; - } else { - Set s = Sets.newHashSet(b); - s.removeAll(t); - ylimaaraiset.accept(s); - return copy; - } - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/excel/LaskentaDtoAsExcel.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/excel/LaskentaDtoAsExcel.java deleted file mode 100644 index 3b13653b44..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/excel/LaskentaDtoAsExcel.java +++ /dev/null @@ -1,60 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.excel; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import fi.vm.sade.valinta.kooste.util.ExcelExportUtil; -import fi.vm.sade.valinta.seuranta.dto.*; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -public class LaskentaDtoAsExcel { - - public static byte[] laskentaDtoAsExcel(LaskentaDto laskenta) { - Map sheetAndGrid = Maps.newHashMap(); - { - List grid = Lists.newArrayList(); - if (laskenta.getIlmoitus() != null) { - IlmoitusDto ilmoitus = laskenta.getIlmoitus(); - if (IlmoitusTyyppi.VIRHE.equals(ilmoitus.getTyyppi())) { - grid.add(new Object[] {"Virheilmoitus:", ilmoitus.getOtsikko()}); - } else { - grid.add(new Object[] {"Ilmoitus:", ilmoitus.getOtsikko()}); - } - } - grid.add(new Object[] {""}); - grid.add(new Object[] {"Suorittamattomat hakukohteet"}); - if (laskenta.getHakukohteet() != null) { - for (HakukohdeDto hakukohde : - laskenta.getHakukohteet().stream() - .filter(h -> !HakukohdeTila.VALMIS.equals(h.getTila())) - .collect(Collectors.toList())) { - List rivi = Lists.newArrayList(); - rivi.add(hakukohde.getHakukohdeOid()); - if (hakukohde.getIlmoitukset() != null) { - rivi.addAll( - hakukohde.getIlmoitukset().stream() - .map(i -> i.getOtsikko()) - .collect(Collectors.toList())); - } - grid.add(rivi.toArray()); - } - } - sheetAndGrid.put("Kesken", grid.toArray(new Object[][] {})); - } - { - List grid = Lists.newArrayList(); - grid.add(new Object[] {"Valmistuneet hakukohteet"}); - if (laskenta.getHakukohteet() != null) { - for (HakukohdeDto hakukohde : - laskenta.getHakukohteet().stream() - .filter(h -> HakukohdeTila.VALMIS.equals(h.getTila())) - .collect(Collectors.toList())) { - grid.add(new Object[] {hakukohde.getHakukohdeOid()}); - } - } - sheetAndGrid.put("Valmiit", grid.toArray(new Object[][] {})); - } - return ExcelExportUtil.exportGridSheetsAsXlsBytes(sheetAndGrid); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/LaskentaParams.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/LaskentaParams.java deleted file mode 100644 index 51b16d3a58..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/LaskentaParams.java +++ /dev/null @@ -1,83 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.resource; - -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Maski; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import java.util.Optional; -import org.apache.commons.lang.StringUtils; - -public class LaskentaParams { - private final String userOID; - private final String haunNimi; - private final String nimi; - private final LaskentaTyyppi laskentatyyppi; - private final Boolean isValintakoelaskenta; - private final Integer valinnanvaihe; - private final String hakuOid; - private final Optional maski; - private final boolean isErillishaku; - - public LaskentaParams( - String userOID, - String haunNimi, - String nimi, - LaskentaTyyppi laskentatyyppi, - Boolean isValintakoelaskenta, - Integer valinnanvaihe, - String hakuOid, - Optional maski, - boolean isErillishaku) { - this.userOID = userOID; - this.haunNimi = haunNimi; - this.nimi = nimi; - this.laskentatyyppi = laskentatyyppi; - this.isValintakoelaskenta = isValintakoelaskenta; - this.valinnanvaihe = valinnanvaihe; - - if (StringUtils.isBlank(hakuOid)) { - throw new RuntimeException("HakuOid on pakollinen"); - } - this.hakuOid = hakuOid; - - if (maski == null) { - throw new RuntimeException("maski on pakollinen"); - } - this.maski = maski; - this.isErillishaku = isErillishaku; - } - - public String getHaunNimi() { - return haunNimi; - } - - public String getNimi() { - return nimi; - } - - public String getUserOID() { - return userOID; - } - - public LaskentaTyyppi getLaskentatyyppi() { - return laskentatyyppi; - } - - public Boolean getIsValintakoelaskenta() { - return isValintakoelaskenta; - } - - public Integer getValinnanvaihe() { - return valinnanvaihe; - } - - public String getHakuOid() { - return hakuOid; - } - - public Optional getMaski() { - return maski; - } - - public boolean isErillishaku() { - return isErillishaku; - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaResource.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaResource.java index 4ef0cae6f9..d82bd87747 100644 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaResource.java +++ b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaResource.java @@ -1,36 +1,20 @@ package fi.vm.sade.valinta.kooste.valintalaskenta.resource; -import static fi.vm.sade.valinta.seuranta.dto.IlmoitusDto.ilmoitus; import static java.util.Arrays.asList; import fi.vm.sade.valinta.kooste.AuthorizationUtil; -import fi.vm.sade.valinta.kooste.dto.Vastaus; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.pistesyotto.service.HakukohdeOIDAuthorityCheck; +import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; import fi.vm.sade.valinta.kooste.security.AuthorityCheckService; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Laskenta; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Maski; -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRouteValvomo; -import fi.vm.sade.valinta.seuranta.dto.LaskentaDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTila; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import io.reactivex.Observable; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; +import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaResurssiProvider; +import fi.vm.sade.valintalaskenta.domain.dto.LaskeDTO; import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; @@ -50,375 +34,96 @@ public class ValintalaskentaKerrallaResource { "ROLE_APP_VALINTOJENTOTEUTTAMINENKK_CRUD", "ROLE_APP_VALINTOJENTOTEUTTAMINENKK_READ_UPDATE"); - @Autowired private ValintalaskentaKerrallaRouteValvomo valintalaskentaValvomo; - @Autowired private ValintalaskentaKerrallaService valintalaskentaKerrallaService; - @Autowired private ValintalaskentaStatusExcelHandler valintalaskentaStatusExcelHandler; - @Autowired private LaskentaSeurantaAsyncResource seurantaAsyncResource; @Autowired private AuthorityCheckService authorityCheckService; + @Autowired private LaskentaResurssiProvider laskentaResurssiProvider; + + private static AuditSession koosteAuditSession() { + final String userOID = AuthorizationUtil.getCurrentUser(); + final String userAgent = "-"; + final String inetAddress = "127.0.0.1"; + AuditSession auditSession = + new AuditSession(userOID, Collections.emptyList(), userAgent, inetAddress); + auditSession.setSessionId(""); + auditSession.setPersonOid(userOID); + return auditSession; + } - @PostMapping(value = "/haku/{hakuOid}/tyyppi/HAKU", produces = MediaType.APPLICATION_JSON_VALUE) - public DeferredResult> valintalaskentaKokoHaulle( - @PathVariable("hakuOid") String hakuOid, - @RequestParam(value = "erillishaku", required = false) Boolean erillishaku, - @RequestParam(value = "valinnanvaihe", required = false) Integer valinnanvaihe, - @RequestParam(value = "valintakoelaskenta", required = false) Boolean valintakoelaskenta, - @RequestParam(value = "haunnimi", required = false) String haunnimi, - @RequestParam(value = "nimi", required = false) String nimi) { - authorityCheckService.checkAuthorizationForHaku(hakuOid, valintalaskentaAllowedRoles); - DeferredResult> result = new DeferredResult<>(1 * 60 * 1000l); - - try { - result.onTimeout( - () -> { - LOG.error( - "Laskennan kaynnistys timeuottasi kutsulle /haku/{}/tyyppi/HAKU?valinnanvaihe={}&valintakoelaskenta={}\r\n{}", - hakuOid, - valinnanvaihe, - valintakoelaskenta); - result.setErrorResult( - ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT) - .body("Ajo laskennalle aikakatkaistu!")); - }); - - final String userOID = AuthorizationUtil.getCurrentUser(); - valintalaskentaKerrallaService.kaynnistaLaskentaHaulle( - new LaskentaParams( - userOID, - haunnimi, - nimi, - LaskentaTyyppi.HAKU, - valintakoelaskenta, - valinnanvaihe, - hakuOid, - Optional.empty(), - Boolean.TRUE.equals(erillishaku)), - result); - } catch (Throwable e) { - LOG.error("Laskennan kaynnistamisessa tapahtui odottamaton virhe!", e); - result.setErrorResult( - ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body("Odottamaton virhe laskennan kaynnistamisessa! " + e.getMessage())); + private static Throwable getUnderlyingCause(Throwable t) { + if (t.getCause() != null) { + return getUnderlyingCause(t.getCause()); } - - return result; + return t; } - @PostMapping( - value = "/haku/{hakuOid}/tyyppi/{tyyppi}/whitelist/{whitelist:.+}", - consumes = MediaType.APPLICATION_JSON_VALUE, + @GetMapping( + value = "/haku/{hakuOid}/hakukohde/{hakukohdeOid}/lahtotiedot", produces = MediaType.APPLICATION_JSON_VALUE) - public DeferredResult> valintalaskentaHaulle( + public DeferredResult> valintalaskennanLahtotiedot( @PathVariable("hakuOid") String hakuOid, - @RequestParam(value = "erillishaku", required = false) Boolean erillishaku, - @RequestParam(value = "valinnanvaihe", required = false) Integer valinnanvaihe, - @RequestParam(value = "valintakoelaskenta", required = false) Boolean valintakoelaskenta, - @RequestParam(value = "haunnimi", required = false) String haunnimi, - @RequestParam(value = "nimi", required = false) String nimi, - @RequestParam(value = "valintaryhma", required = false) String valintaryhmaOid, - @PathVariable("tyyppi") LaskentaTyyppi laskentatyyppi, - @PathVariable("whitelist") boolean whitelist, - @RequestBody List stringMaski) { - DeferredResult> result = new DeferredResult<>(1 * 60 * 1000l); + @PathVariable("hakukohdeOid") String hakukohdeOid, + @RequestParam(value = "uuid", defaultValue = "123") String uuid, + @RequestParam(value = "erillishaku", required = false, defaultValue = "false") + Boolean erillishaku, + @RequestParam(value = "valinnanvaihe", required = false, defaultValue = "-1") + Integer valinnanvaihe, + @RequestParam(value = "valintakoelaskenta", required = false, defaultValue = "false") + Boolean valintakoelaskenta, + @RequestParam(value = "retryHakemuksetAndOppijat", required = false, defaultValue = "false") + Boolean retryHakemuksetAndOppijat, + @RequestParam(value = "withHakijaRyhmat", required = false, defaultValue = "false") + Boolean withHakijaRyhmat) { + authorityCheckService.checkAuthorizationForHaku(hakuOid, valintalaskentaAllowedRoles); + DeferredResult> result = new DeferredResult<>(10 * 60 * 1000l); + + Date nyt = new Date(); try { result.onTimeout( () -> { - final String hakukohdeOids = hakukohdeOidsFromMaskiToString(stringMaski); LOG.error( - "Laskennan kaynnistys timeouttasi kutsulle /haku/{}/tyyppi/{}/whitelist/{}?valinnanvaihe={}&valintakoelaskenta={}\r\n{}", + "Lähtötietojen haku timeouttasi kutsulle /haku/{}/hakukohde/{}/lahtotiedot", hakuOid, - laskentatyyppi, - whitelist, - valinnanvaihe, - valintakoelaskenta, - hakukohdeOids); + hakukohdeOid); result.setErrorResult( ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT) - .body("Uudelleen ajo laskennalle aikakatkaistu!")); + .body("Lähtötietojen haku hakukohteelle aikakatkaistu!")); }); - Maski maski = whitelist ? Maski.whitelist(stringMaski) : Maski.blacklist(stringMaski); - final String userOID = AuthorizationUtil.getCurrentUser(); - - Observable authorityCheckObservable; - if (LaskentaTyyppi.VALINTARYHMA.equals(laskentatyyppi)) { - authorityCheckService.checkAuthorizationForValintaryhma( - valintaryhmaOid, valintalaskentaAllowedRoles); - authorityCheckObservable = Observable.empty(); - } else { - authorityCheckObservable = - Observable.fromFuture( - authorityCheckService.getAuthorityCheckForRoles(valintalaskentaAllowedRoles)); - } - - valintalaskentaKerrallaService.kaynnistaLaskentaHaulle( - new LaskentaParams( - userOID, - haunnimi, - nimi, - laskentatyyppi, - valintakoelaskenta, - valinnanvaihe, + this.laskentaResurssiProvider + .fetchResourcesForOneLaskenta( + uuid, hakuOid, - Optional.of(maski), - Boolean.TRUE.equals(erillishaku)), - result, - authorityCheckObservable); - - } catch (AccessDeniedException e) { - result.setErrorResult(ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage())); - } catch (Throwable e) { - LOG.error("Laskennan kaynnistamisessa tapahtui odottamaton virhe!", e); - result.setErrorResult( - ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body("Odottamaton virhe laskennan kaynnistamisessa! " + e.getMessage())); - throw e; - } - - return result; - } - - @PostMapping(value = "/uudelleenyrita/{uuid:.+}", produces = MediaType.APPLICATION_JSON_VALUE) - public DeferredResult> uudelleenajoLaskennalle( - @PathVariable("uuid") String uuid) { - DeferredResult> result = new DeferredResult<>(1 * 60 * 1000l); - result.onTimeout( - () -> { - LOG.error("Uudelleen ajo laskennalle({}) timeouttasi!", uuid); - result.setErrorResult( - ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT) - .body("Uudelleen ajo laskennalle timeouttasi!")); - }); - - checkAuthorizationForLaskentaFromSeuranta(uuid) - .subscribe( - allowed -> { - kaynnistaLaskentaUudelleen(uuid, result); - }, - error -> { - LOG.error( - "Valintalaskennan uudelleenajo epäonnistui, koska käyttöoikeudet eivät riittäneet!"); - result.setErrorResult( - ResponseEntity.status(HttpStatus.FORBIDDEN).body(error.getMessage())); - }); - - return result; - } - - private void kaynnistaLaskentaUudelleen( - String uuid, DeferredResult> result) { - try { - valintalaskentaKerrallaService.kaynnistaLaskentaUudelleen(uuid, result); + hakukohdeOid, + valinnanvaihe == -1 ? null : valinnanvaihe, + koosteAuditSession(), + erillishaku, + retryHakemuksetAndOppijat, + withHakijaRyhmat, + nyt) + .thenApply(laskeDTO -> result.setResult(new ResponseEntity<>(laskeDTO, HttpStatus.OK))) + .exceptionally( + e -> { + Throwable cause = getUnderlyingCause(e); + LOG.error( + "Hakukohteen " + + hakukohdeOid + + " tietojen hakemisessa tapahtui odottamaton virhe!", + cause); + result.setErrorResult( + ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body( + "Odottamaton virhe laskennan tietojen hakemisessa! " + + cause.getMessage())); + return null; + }); } catch (Throwable e) { - LOG.error("Laskennan kaynnistamisessa tapahtui odottamaton virhe", e); + LOG.error( + "Hakukohteen " + hakukohdeOid + " tietojen hakemisessa tapahtui odottamaton virhe!", e); result.setErrorResult( ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body("Odottamaton virhe laskennan kaynnistamisessa! " + e.getMessage())); + .body("Odottamaton virhe laskennan tietojen hakemisessa! " + e.getMessage())); } - } - - @GetMapping(value = "/status", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Valintalaskennan tila", - responses = { - @ApiResponse( - responseCode = "OK", - content = @Content(schema = @Schema(implementation = Laskenta.class))) - }) - public List status() { - return valintalaskentaValvomo.runningLaskentas(); - } - - @GetMapping(value = "/status/{uuid:.+}", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Valintalaskennan tila", - responses = { - @ApiResponse( - responseCode = "OK", - content = @Content(schema = @Schema(implementation = Laskenta.class))) - }) - public ResponseEntity status(@PathVariable("uuid") String uuid) { - checkAuthorizationForLaskentaFromSeuranta(uuid); - Optional> result = - valintalaskentaValvomo - .fetchLaskenta(uuid) - .map(l -> ResponseEntity.status(HttpStatus.OK).body(l)); - - if (result.isPresent()) { - return result.get(); - } - return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Valintalaskenta ei ole muistissa!"); - } - - @GetMapping(value = "/status/{uuid}/xls", produces = "application/vnd.ms-excel") - @Operation( - summary = "Valintalaskennan tila", - responses = { - @ApiResponse( - responseCode = "OK", - content = @Content(schema = @Schema(implementation = byte[].class))) - }) - public DeferredResult> statusXls(@PathVariable("uuid") final String uuid) { - - DeferredResult> result = new DeferredResult<>(15 * 60 * 1000l); - result.onTimeout( - () -> { - result.setErrorResult(valintalaskentaStatusExcelHandler.createTimeoutErrorXls(uuid)); - }); - - checkAuthorizationForLaskentaFromSeuranta(uuid) - .subscribe( - allowed -> { - valintalaskentaStatusExcelHandler.getStatusXls(uuid, result); - }, - error -> { - LOG.error( - "Valintalaskennan tilan haku epäonnistui, koska käyttöoikeudet eivät riittäneet!"); - result.setErrorResult( - ResponseEntity.status(HttpStatus.FORBIDDEN).body(error.getMessage())); - }); return result; } - - @GetMapping(value = "/status/{uuid}/yhteenveto", produces = "application/json") - @Operation( - summary = "Valintalaskennan tilan yhteenveto", - responses = { - @ApiResponse( - responseCode = "OK", - content = @Content(schema = @Schema(implementation = LaskentaDto.class))) - }) - public DeferredResult> statusYhteenveto( - @PathVariable("uuid") final String uuid) { - - DeferredResult> result = new DeferredResult<>(60 * 1000L); - result.onTimeout( - () -> { - LOG.error("Valintalaskennan tilan {} hakeminen timeouttasi!", uuid); - result.setErrorResult( - ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT) - .body("Valintalaskennan tilan hakeminen timeouttasi!")); - }); - - checkAuthorizationForLaskentaFromSeuranta(uuid) - .subscribe( - allowed -> { - seurantaAsyncResource - .laskenta(uuid) - .subscribe( - laskenta -> result.setResult(ResponseEntity.of(Optional.of(laskenta))), - poikkeus -> { - LOG.error( - "Tietojen haussa seurantapalvelusta(/laskenta/" - + uuid - + ") tapahtui virhe", - poikkeus); - result.setErrorResult(poikkeus); - }); - }, - error -> { - LOG.error( - "Valintalaskennan tilan haku epäonnistui, koska käyttöoikeudet eivät riittäneet!"); - result.setErrorResult( - ResponseEntity.status(HttpStatus.FORBIDDEN).body(error.getMessage())); - }); - - return result; - } - - @DeleteMapping(value = "/haku/{uuid:.+}") - public ResponseEntity lopetaLaskenta( - @PathVariable("uuid") String uuid, - @RequestParam(value = "lopetaVainJonossaOlevaLaskenta", required = false) - Boolean lopetaVainJonossaOlevaLaskenta) { - if (uuid == null) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Uuid on pakollinen"); - } - // Jos käyttöoikeustarkastelu epäonnistuu, tulee poikkeus, tämän suoritus - // keskeytyy ja poikkeus muuttuu http-virhekoodiksi. - checkAuthorizationForLaskentaFromSeuranta(uuid).blockingFirst(); - peruutaLaskenta(uuid, lopetaVainJonossaOlevaLaskenta); - // Palauta OK odottamatta vastausta peruutuspyyntöön - return ResponseEntity.status(HttpStatus.OK).build(); - } - - private void peruutaLaskenta(String uuid, Boolean lopetaVainJonossaOlevaLaskenta) { - if (Boolean.TRUE.equals(lopetaVainJonossaOlevaLaskenta)) { - boolean onkoLaskentaVielaJonossa = valintalaskentaValvomo.fetchLaskenta(uuid) == null; - if (!onkoLaskentaVielaJonossa) { - // Laskentaa suoritetaan jo joten ei pysayteta - return; - } - } - stop(uuid); - seurantaAsyncResource - .merkkaaLaskennanTila( - uuid, LaskentaTila.PERUUTETTU, Optional.of(ilmoitus("Peruutettu käyttäjän toimesta"))) - .subscribe(ok -> stop(uuid), nok -> stop(uuid)); - return; - } - - private void stop(String uuid) { - valintalaskentaValvomo.fetchLaskenta(uuid).ifPresent(Laskenta::lopeta); - } - - private String hakukohdeOidsFromMaskiToString(List maski) { - if (maski != null && !maski.isEmpty()) { - try { - Object[] hakukohdeOidArray = maski.toArray(); - StringBuilder sb = new StringBuilder(); - sb.append( - Arrays.toString( - Arrays.copyOfRange(hakukohdeOidArray, 0, Math.min(hakukohdeOidArray.length, 10)))); - if (hakukohdeOidArray.length > 10) { - sb.append(" ensimmaiset 10 hakukohdetta maskissa jossa on yhteensa hakukohteita ") - .append(hakukohdeOidArray.length); - } else { - sb.append(" maskin hakukohteet"); - } - return sb.toString(); - } catch (Exception e) { - LOG.error("hakukohdeOidsFromMaskiToString", e); - return e.getMessage(); - } - } - return null; - } - - private Boolean checkAuthorizationForLaskentaInContext( - AuthorityCheckService.Context context, LaskentaDto laskentaDto) { - if (LaskentaTyyppi.HAKU.equals(laskentaDto.getTyyppi())) { - authorityCheckService.withContext( - context, - () -> { - authorityCheckService.checkAuthorizationForHaku( - laskentaDto.getHakuOid(), valintalaskentaAllowedRoles); - }); - } else { - final List hakukohdeOids = - laskentaDto.getHakukohteet().stream() - .map(hk -> hk.getHakukohdeOid()) - .collect(Collectors.toList()); - authorityCheckService.withContext( - context, - () -> { - authorityCheckService.checkAuthorizationForHakukohteet( - hakukohdeOids, valintalaskentaAllowedRoles); - }); - } - return Boolean.TRUE; - } - - private Observable checkAuthorizationForLaskentaFromSeuranta(String uuid) { - // Tallenna tätä pyyntöä suorittavan säikeen konteksti, jotta samaan käyttäjätietoon - // voidaan viitata tarkastelun suorittavasta säikeestä. - AuthorityCheckService.Context context = authorityCheckService.getContext(); - return getLaskentaDtoFromSeuranta(uuid) - .map(laskentaDto -> checkAuthorizationForLaskentaInContext(context, laskentaDto)); - } - - private Observable getLaskentaDtoFromSeuranta(String uuid) { - return seurantaAsyncResource.laskenta(uuid); - } } diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaService.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaService.java deleted file mode 100644 index 1fb198a7ea..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaKerrallaService.java +++ /dev/null @@ -1,282 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.resource; - -import fi.vm.sade.service.valintaperusteet.dto.HakukohdeViiteDTO; -import fi.vm.sade.valinta.kooste.dto.Vastaus; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.valintaperusteet.ValintaperusteetAsyncResource; -import fi.vm.sade.valinta.kooste.pistesyotto.service.HakukohdeOIDAuthorityCheck; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Laskenta; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaInfo; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Maski; -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRoute; -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRouteValvomo; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaDto; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import fi.vm.sade.valinta.seuranta.dto.TunnisteDto; -import io.reactivex.Observable; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import org.apache.commons.lang3.tuple.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.stereotype.Service; -import org.springframework.web.context.request.async.DeferredResult; - -@Service -public class ValintalaskentaKerrallaService { - private static final Logger LOG = LoggerFactory.getLogger(ValintalaskentaKerrallaService.class); - - @Autowired private ValintalaskentaKerrallaRouteValvomo valintalaskentaValvomo; - @Autowired private ValintaperusteetAsyncResource valintaperusteetAsyncResource; - @Autowired private ValintalaskentaKerrallaRoute valintalaskentaRoute; - @Autowired private LaskentaSeurantaAsyncResource seurantaAsyncResource; - - public ValintalaskentaKerrallaService() {} - - public void kaynnistaLaskentaHaulle( - LaskentaParams laskentaParams, DeferredResult> result) { - kaynnistaLaskentaHaulle(laskentaParams, result, Observable.empty()); - } - - public void kaynnistaLaskentaHaulle( - LaskentaParams laskentaParams, - DeferredResult> result, - Observable authCheck) { - String hakuOid = laskentaParams.getHakuOid(); - Optional uuidForExistingNonMaskedLaskenta = - uuidForExistingNonMaskedLaskenta(laskentaParams.getMaski(), hakuOid); - - if (uuidForExistingNonMaskedLaskenta.isPresent()) { - String uuid = uuidForExistingNonMaskedLaskenta.get(); - LOG.warn( - "Laskenta on jo kaynnissa haulle {} joten palautetaan seurantatunnus({}) ajossa olevaan hakuun", - uuid, - uuid); - result.setResult(redirectResponse(new TunnisteDto(uuid, false))); - } else { - LOG.info("Aloitetaan laskenta haulle {}", hakuOid); - valintaperusteetAsyncResource - .haunHakukohteet(hakuOid) - .subscribe( - (List hakukohdeViitteet) -> { - Collection haunHakukohteetOids = - kasitteleHakukohdeViitteet( - hakukohdeViitteet, hakuOid, laskentaParams.getMaski(), result); - - if (!LaskentaTyyppi.VALINTARYHMA.equals(laskentaParams.getLaskentatyyppi())) { - authCheck - .blockingNext() - .forEach( - authorityCheck -> - haunHakukohteetOids.forEach( - hk -> { - if (!authorityCheck.test(hk.getHakukohdeOid())) { - LOG.error( - String.format( - "Ei oikeutta aloittaa laskentaa hakukohteelle %s haussa %s", - hk.getHakukohdeOid(), hakuOid)); - throw new AccessDeniedException( - "Ei oikeutta aloittaa laskentaa"); - } - })); - } - - createLaskenta( - haunHakukohteetOids, - (TunnisteDto uuid) -> notifyWorkAvailable(uuid, result), - laskentaParams, - result); - }, - (Throwable poikkeus) -> { - LOG.error("kaynnistaLaskentaHaulle throws", poikkeus); - result.setErrorResult(errorResponse(poikkeus.getMessage())); - }); - } - } - - public void kaynnistaLaskentaUudelleen( - final String uuid, final DeferredResult> result) { - valintalaskentaValvomo - .fetchLaskenta(uuid) - .filter(ValintalaskentaKerrallaService::ajossaolevaLaskenta) - .ifPresentOrElse( - laskenta -> { - palautaAjossaolevaLaskenta(uuid, result); - }, - () -> { - resetoiTilat(uuid, result); - }); - } - - private static final boolean ajossaolevaLaskenta(Laskenta laskenta) { - return !laskenta.isValmis(); - } - - private void palautaAjossaolevaLaskenta( - String uuid, DeferredResult> result) { - LOG.warn("Laskenta {} on viela ajossa, joten palautetaan linkki siihen.", uuid); - result.setResult(redirectResponse(new TunnisteDto(uuid, false))); - } - - private void resetoiTilat(String uuid, DeferredResult> result) { - seurantaAsyncResource - .resetoiTilat(uuid) - .flatMap( - (LaskentaDto laskenta) -> - Observable.just(laskenta) - .zipWith( - valintaperusteetAsyncResource.haunHakukohteet(laskenta.getHakuOid()), - Pair::of)) - .subscribe( - (Pair> laskentaJaHakukohdeViitteet) -> { - LaskentaDto laskenta = laskentaJaHakukohdeViitteet.getLeft(); - notifyWorkAvailable( - new TunnisteDto(laskenta.getUuid(), laskenta.getLuotiinkoUusiLaskenta()), result); - }, - (Throwable t) -> { - LOG.error("Laskennan uudelleenajo epäonnistui. Uuid: " + uuid, t); - result.setErrorResult(errorResponse("Uudelleen ajo laskennalle heitti poikkeuksen!")); - }); - } - - private Optional haeAjossaOlevaLaskentaHaulle(final String hakuOid) { - return valintalaskentaValvomo.runningLaskentas().stream() - .filter(l -> hakuOid.equals(l.getHakuOid()) && !l.isOsittainenLaskenta()) - .findFirst(); - } - - private static Collection kasitteleHakukohdeViitteet( - final List hakukohdeViitteet, - final String hakuOid, - final Optional maski, - final DeferredResult> result) { - LOG.info("Tarkastellaan hakukohdeviitteita haulle {}", hakuOid); - - if (hakukohdeViitteet == null || hakukohdeViitteet.isEmpty()) { - LOG.error("Valintaperusteet palautti tyhjat hakukohdeviitteet haulle {}!", hakuOid); - throw new NullPointerException("Valintaperusteet palautti tyhjat hakukohdeviitteet!"); - } - final List haunHakukohdeOids = - hakukohdeViitteet.stream() - .filter(Objects::nonNull) - .filter(hakukohdeOid -> hakukohdeOid.getOid() != null) - .filter(hakukohdeOid -> hakukohdeOid.getTila().equals("JULKAISTU")) - .map(u -> new HakukohdeJaOrganisaatio(u.getOid(), u.getTarjoajaOid())) - .collect(Collectors.toList()); - - Collection oids = - maski.map(m -> m.maskaa(haunHakukohdeOids)).orElse(haunHakukohdeOids); - if (oids.isEmpty()) { - String msg = - "Haulla " - + hakuOid - + " ei saatu hakukohteita! Onko valinnat synkronoitu tarjonnan kanssa?"; - LOG.error(msg); - result.setErrorResult(errorResponse(msg)); - throw new RuntimeException(msg); - } else { - return oids; - } - } - - private void notifyWorkAvailable( - final TunnisteDto uuid, final DeferredResult> result) { - // ohitetaan ajossa olevan laskennan kaynnistaminen - if (uuid.getLuotiinkoUusiLaskenta()) { - valintalaskentaRoute.workAvailable(); - } - result.setResult(redirectResponse(uuid)); - } - - private void createLaskenta( - Collection hakukohdeData, - Consumer laskennanAloitus, - LaskentaParams laskentaParams, - DeferredResult> result) { - final List hakukohdeDtos = toHakukohdeDto(hakukohdeData); - validateHakukohdeDtos(hakukohdeData, hakukohdeDtos, result); - seurantaAsyncResource - .luoLaskenta(laskentaParams, hakukohdeDtos) - .subscribe( - laskennanAloitus::accept, - (Throwable t) -> { - LOG.info( - "Seurannasta uuden laskennan haku paatyi virheeseen. Yritetään uudelleen.", t); - createLaskentaRetry( - hakukohdeDtos, laskennanAloitus, laskentaParams, result); // FIXME kill me OK-152! - }); - } - - private void createLaskentaRetry( - List hakukohdeDtos, - Consumer laskennanAloitus, - LaskentaParams laskentaParams, - DeferredResult> result) { - seurantaAsyncResource - .luoLaskenta(laskentaParams, hakukohdeDtos) - .subscribe( - laskennanAloitus::accept, - (Throwable t) -> { - LOG.error("Seurannasta uuden laskennan haku paatyi virheeseen", t); - result.setErrorResult(errorResponse(t.getMessage())); - }); - } - - private static void validateHakukohdeDtos( - Collection hakukohdeData, - List hakukohdeDtos, - DeferredResult> result) { - if (hakukohdeDtos.isEmpty()) { - String msg = "Laskentaa ei voida aloittaa hakukohteille joilta puuttuu organisaatio!"; - LOG.error(msg); - result.setErrorResult(errorResponse(msg)); - throw new RuntimeException(msg); - } - if (hakukohdeDtos.size() < hakukohdeData.size()) { - LOG.warn( - "Hakukohteita puuttuvien organisaatio-oidien vuoksi filtteroinnin jalkeen {}/{}!", - hakukohdeDtos.size(), - hakukohdeData.size()); - } else { - LOG.info( - "Hakukohteita filtteroinnin jalkeen {}/{}!", hakukohdeDtos.size(), hakukohdeData.size()); - } - } - - private static ResponseEntity redirectResponse(final TunnisteDto target) { - return ResponseEntity.status(HttpStatus.OK) - .body(Vastaus.laskennanSeuraus(target.getUuid(), target.getLuotiinkoUusiLaskenta())); - } - - private static ResponseEntity errorResponse(final String errorMessage) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage); - } - - private static List toHakukohdeDto( - Collection hakukohdeData) { - return hakukohdeData.stream() - .filter(Objects::nonNull) - .filter(hk -> hk.getHakukohdeOid() != null) - .filter(hk -> hk.getOrganisaatioOid() != null) - .map(hk -> new HakukohdeDto(hk.getHakukohdeOid(), hk.getOrganisaatioOid())) - .collect(Collectors.toList()); - } - - private Optional uuidForExistingNonMaskedLaskenta(Optional maski, String hakuOid) { - final Optional ajossaOlevaLaskentaHaulle = - !maski.isPresent() || !maski.get().isMask() - ? haeAjossaOlevaLaskentaHaulle(hakuOid) - : Optional.empty(); - return ajossaOlevaLaskentaHaulle.map(LaskentaInfo::getUuid); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaStatusExcelHandler.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaStatusExcelHandler.java deleted file mode 100644 index e3215b3b26..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/resource/ValintalaskentaStatusExcelHandler.java +++ /dev/null @@ -1,98 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.resource; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; -import fi.vm.sade.valinta.kooste.util.ExcelExportUtil; -import fi.vm.sade.valinta.kooste.valintalaskenta.excel.LaskentaDtoAsExcel; -import java.util.List; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.context.request.async.DeferredResult; - -@Service -public class ValintalaskentaStatusExcelHandler { - private static final Logger LOG = - LoggerFactory.getLogger(ValintalaskentaStatusExcelHandler.class); - - @Autowired private LaskentaSeurantaAsyncResource seurantaAsyncResource; - - public ResponseEntity createTimeoutErrorXls(final String uuid) { - final List grid = Lists.newArrayList(); - grid.add( - new Object[] { - "Kysely seuranapalveluun (kohteelle /laksenta/" - + uuid - + ") aikakatkaistiin. Palvelu saattaa olla ylikuormittunut!" - }); - - final byte[] bytes = getExcelSheetAndGridBytes("Aikakatkaistu", grid); - LOG.error("Aikakatkaisu Excelin luonnille (kohde /laskenta/{})", uuid); - return excelResponse("yhteenveto_aikakatkaistu.xls", bytes); - } - - public void getStatusXls(final String uuid, DeferredResult> result) { - seurantaAsyncResource - .laskenta(uuid) - .subscribe( - laskenta -> { - try { - byte[] bytes = LaskentaDtoAsExcel.laskentaDtoAsExcel(laskenta); - result.setResult(excelResponse("yhteenveto.xls", bytes)); - } catch (Throwable e) { - LOG.error( - "Excelin muodostuksessa(kohteelle /laskenta/" + uuid + ") tapahtui virhe", e); - result.setResult( - luoVirheExcelVastaus( - "yhteenveto_virhe.xls", "Virhe Excelin muodostuksessa!", e)); - throw e; - } - }, - poikkeus -> { - LOG.error( - "Excelin tietojen haussa seurantapalvelusta(/laskenta/" - + uuid - + ") tapahtui virhe", - poikkeus); - result.setResult( - luoVirheExcelVastaus( - "yhteenveto_seurantavirhe.xls", - "Virhe seurantapavelun kutsumisessa!", - poikkeus)); - }); - } - - private ResponseEntity luoVirheExcelVastaus( - final String tiedostonNimi, final String virheViesti, final Throwable poikkeus) { - final List grid = Lists.newArrayList(); - grid.add(new Object[] {virheViesti}); - grid.add(new Object[] {poikkeus.getMessage()}); - - for (StackTraceElement se : poikkeus.getStackTrace()) { - grid.add(new Object[] {se}); - } - - final byte[] bytes = getExcelSheetAndGridBytes("Virhe", grid); - return excelResponse(tiedostonNimi, bytes); - } - - private ResponseEntity excelResponse(final String tiedostonnimi, byte[] bytes) { - return ResponseEntity.status(HttpStatus.OK) - .header("Content-Length", bytes.length + "") - .header("Content-Type", "application/vnd.ms-excel") - .header("Content-Disposition", "attachment; filename=\"" + tiedostonnimi + "\"") - .body(bytes); - } - - private byte[] getExcelSheetAndGridBytes(final String sheetName, final List grid) { - final Map sheetAndGrid = Maps.newHashMap(); - sheetAndGrid.put(sheetName, grid.toArray(new Object[][] {})); - - return ExcelExportUtil.exportGridSheetsAsXlsBytes(sheetAndGrid); - } -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/route/ValintalaskentaKerrallaRoute.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/route/ValintalaskentaKerrallaRoute.java deleted file mode 100644 index 29bfcb5080..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/route/ValintalaskentaKerrallaRoute.java +++ /dev/null @@ -1,12 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.route; - -import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.dto.ParametritDTO; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.Haku; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaStartParams; - -public interface ValintalaskentaKerrallaRoute { - void suoritaValintalaskentaKerralla( - final Haku haku, final ParametritDTO parametritDTO, LaskentaStartParams laskentaStartParams); - - void workAvailable(); -} diff --git a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/route/ValintalaskentaKerrallaRouteValvomo.java b/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/route/ValintalaskentaKerrallaRouteValvomo.java deleted file mode 100644 index 88331e2464..0000000000 --- a/src/main/java/fi/vm/sade/valinta/kooste/valintalaskenta/route/ValintalaskentaKerrallaRouteValvomo.java +++ /dev/null @@ -1,11 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.route; - -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.Laskenta; -import java.util.List; -import java.util.Optional; - -public interface ValintalaskentaKerrallaRouteValvomo { - Optional fetchLaskenta(String uuid); - - List runningLaskentas(); -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4295cbf237..4b6159a52b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,4 @@ logging: level: - fi.vm.sade.javautils.nio.cas: warn # CasClient näyttää tunkevan INFO-entryyn koko responsen joten filtteröidään ne pois \ No newline at end of file + fi.vm.sade.javautils.nio.cas: warn # CasClient näyttää tunkevan INFO-entryyn koko responsen joten filtteröidään ne pois + com.github.dockerjava: info \ No newline at end of file diff --git a/src/main/resources/db/migration/V202410071200000__spring_session.sql b/src/main/resources/db/migration/V202410071200000__spring_session.sql new file mode 100644 index 0000000000..70c8d046e3 --- /dev/null +++ b/src/main/resources/db/migration/V202410071200000__spring_session.sql @@ -0,0 +1,22 @@ +CREATE TABLE if not exists SPRING_SESSION ( + PRIMARY_ID CHAR(36) NOT NULL, + SESSION_ID CHAR(36) NOT NULL, + CREATION_TIME BIGINT NOT NULL, + LAST_ACCESS_TIME BIGINT NOT NULL, + MAX_INACTIVE_INTERVAL INT NOT NULL, + EXPIRY_TIME BIGINT NOT NULL, + PRINCIPAL_NAME VARCHAR(100), + CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID) +); + +CREATE UNIQUE INDEX if not exists SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID); +CREATE INDEX if not exists SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME); +CREATE INDEX if not exists SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME); + +CREATE TABLE if not exists SPRING_SESSION_ATTRIBUTES ( + SESSION_PRIMARY_ID CHAR(36) NOT NULL, + ATTRIBUTE_NAME VARCHAR(200) NOT NULL, + ATTRIBUTE_BYTES BYTEA NOT NULL, + CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME), + CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V202410071300000__parametrit.sql b/src/main/resources/db/migration/V202410071300000__parametrit.sql new file mode 100644 index 0000000000..51559a81a9 --- /dev/null +++ b/src/main/resources/db/migration/V202410071300000__parametrit.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS parametrit ( + nimi TEXT PRIMARY KEY, + arvo TEXT NOT NULL +); + +INSERT INTO parametrit (nimi, arvo) VALUES ('parametritLimiter', '1000') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('hakuLimiter', '1000') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('hakukohderyhmatLimiter', '1000') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('valintapisteetLimiter', '1000') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('hakijaryhmatLimiter', '1000') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('koskioppijatLimiter', '16') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('ataruhakemuksetLimiter', '16') ON CONFLICT DO NOTHING; +INSERT INTO parametrit (nimi, arvo) VALUES ('valintaperusteetLimiter', '16') ON CONFLICT DO NOTHING; + +-- käytetään eri lailla, permittejä varataan per hakija +INSERT INTO parametrit (nimi, arvo) VALUES ('suorituksetLimiter', '1000') ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/src/main/resources/oph-configuration/common.properties.template b/src/main/resources/oph-configuration/common.properties.template index e22dfb42a4..3d43d76296 100644 --- a/src/main/resources/oph-configuration/common.properties.template +++ b/src/main/resources/oph-configuration/common.properties.template @@ -134,3 +134,5 @@ valintalaskentakoostepalvelu.postgresql.password={{host_postgresql_valintalasken valintalaskentakoostepalvelu.postgresql.maxactive={{host_postgresql_valintalaskentakoostepalvelu_max_active}} valintalaskentakoostepalvelu.postgresql.maxwait={{host_postgresql_valintalaskentakoostepalvelu_max_wait}} valintalaskentakoostepalvelu.postgresql.driver=org.postgresql.Driver + +environment.name={{ataru_environment_name}} diff --git a/src/test/java/fi/vm/sade/valinta/kooste/DevApp.java b/src/test/java/fi/vm/sade/valinta/kooste/DevApp.java index abcaaefe24..3f4eb9f936 100644 --- a/src/test/java/fi/vm/sade/valinta/kooste/DevApp.java +++ b/src/test/java/fi/vm/sade/valinta/kooste/DevApp.java @@ -1,10 +1,43 @@ package fi.vm.sade.valinta.kooste; +import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCH; + +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.util.TestSocketUtils; +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.utility.DockerImageName; + public class DevApp { - private static final String ENVIRONMENT = "hahtuva"; + private static final Logger LOG = LoggerFactory.getLogger(DevApp.class); + + private static final String ENVIRONMENT = "untuva"; + + private static final int localstackPort = TestSocketUtils.findAvailableTcpPort(); + + private static final LocalStackContainer localStackContainer = + new LocalStackContainer(new DockerImageName("localstack/localstack:2.2.0")) + .withServices(CLOUDWATCH) + .withLogConsumer(frame -> LOG.info(frame.getUtf8StringWithoutLineEnding())) + .withExposedPorts(4566) + .withCreateContainerCmdModifier( + m -> + m.withHostConfig( + new HostConfig() + .withPortBindings( + new PortBinding( + Ports.Binding.bindPort(localstackPort), new ExposedPort(4566))))); public static void main(String[] args) { + System.setProperty("localstackPort", localstackPort + ""); + System.setProperty("aws.accessKeyId", "localstack"); + System.setProperty("aws.secretAccessKey", "localstack"); + // ssl-konfiguraatio System.setProperty("server.ssl.key-store-type", "PKCS12"); System.setProperty("server.ssl.key-store", "classpath:keystore.p12"); @@ -53,6 +86,7 @@ public static void main(String[] args) { System.setProperty("valintalaskentakoostepalvelu.postgresql.driver", ""); TempDockerDB.start(); + localStackContainer.start(); App.start(); } } diff --git a/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaContext.java b/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaContext.java index fe1f6ea0c3..a827738241 100644 --- a/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaContext.java +++ b/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaContext.java @@ -3,17 +3,13 @@ import fi.vm.sade.valinta.kooste.external.resource.hakuapp.ApplicationAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.OhjausparametritAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.organisaatio.OrganisaatioAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.SuoritusrekisteriAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.tarjonta.TarjontaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.valintalaskenta.ValintalaskentaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.valintaperusteet.ValintaperusteetAsyncResource; import fi.vm.sade.valinta.kooste.mocks.MockAuthorityCheckService; import fi.vm.sade.valinta.kooste.security.AuthorityCheckService; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorSystem; import fi.vm.sade.valinta.kooste.valintalaskenta.resource.ValintalaskentaKerrallaResource; -import fi.vm.sade.valinta.kooste.valintalaskenta.resource.ValintalaskentaKerrallaService; -import fi.vm.sade.valinta.kooste.valintalaskenta.resource.ValintalaskentaStatusExcelHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -41,21 +37,11 @@ public OhjausparametritAsyncResource ohjausparametritAsyncResource() { return Mocks.ohjausparametritAsyncResource; } - @Bean - public LaskentaSeurantaAsyncResource laskentaSeurantaAsyncResource() { - return Mocks.laskentaSeurantaAsyncResource; - } - @Bean public ValintalaskentaAsyncResource valintalaskentaAsyncResource() { return Mocks.valintalaskentaAsyncResource; } - @Bean - public ValintalaskentaStatusExcelHandler valintalaskentaStatusExcelHandler() { - return Mocks.valintalaskentaStatusExcelHandler; - } - @Bean public SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource() { return Mocks.suoritusrekisteriAsyncResource; @@ -66,16 +52,6 @@ public ValintalaskentaKerrallaResource valintalaskentaKerrallaResource() { return new ValintalaskentaKerrallaResource(); } - @Bean - public ValintalaskentaKerrallaService valintalaskentaKerrallaService() { - return new ValintalaskentaKerrallaService(); - } - - @Bean - public LaskentaActorSystem valintalaskentaKerrallaRoute() { - return Mocks.laskentaActorSystem; - } - @Bean public OrganisaatioAsyncResource organisaatioAsyncResource() { return Mocks.organisaatioAsyncResource; diff --git a/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaFailTest.java b/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaFailTest.java deleted file mode 100644 index d86d2462c4..0000000000 --- a/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/LaskentaKerrallaFailTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package fi.vm.sade.valinta.kooste.laskentakerralla; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -import fi.vm.sade.valinta.kooste.dto.Vastaus; -import fi.vm.sade.valinta.kooste.mocks.MockOrganisaationAsyncResource; -import fi.vm.sade.valinta.kooste.valintalaskenta.resource.ValintalaskentaKerrallaResource; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import io.reactivex.Observable; -import java.util.ArrayList; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; -import org.springframework.test.context.support.DirtiesContextTestExecutionListener; -import org.springframework.web.context.request.async.DeferredResult; - -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = LaskentaKerrallaContext.class) -@TestExecutionListeners({ - DependencyInjectionTestExecutionListener.class, - DirtiesContextTestExecutionListener.class -}) -@ActiveProfiles("laskentakerralla") -public class LaskentaKerrallaFailTest { - private static final String LASKENTASEURANTA_ID = "laskentaseuranta.id"; - - @Autowired ValintalaskentaKerrallaResource valintalaskentaKerralla; - - @BeforeAll - public static void resetMocks() { - Mocks.resetMocks(); - } - - @BeforeEach - public void yksiLaskentaTyonAlleJokaEpaonnistuu() { - when(Mocks.valintaperusteetAsyncResource.haunHakukohteet(any())) - .thenReturn(Observable.error(new Throwable("FAIL"))); - AtomicInteger seurantaCount = new AtomicInteger(0); - doAnswer( - invocation -> { - if (seurantaCount.getAndIncrement() < 1) return Observable.just(LASKENTASEURANTA_ID); - else { - return Observable.just(Optional.empty()); - } - }) - .when(Mocks.laskentaSeurantaAsyncResource) - .otaSeuraavaLaskentaTyonAlle(); - } - - @Test - public void testValintaperusteetHaunHakukohteetFail() { - MockOrganisaationAsyncResource.setOrganisaationTyyppiHierarkia(null); - DeferredResult> result = null; - - try { - result = - valintalaskentaKerralla.valintalaskentaHaulle( - "haku.oid", - false, - 0, - false, - "haun nimi", - "nimi", - "valintaryhma.oid", - LaskentaTyyppi.HAKUKOHDE, - false, - new ArrayList()); - } catch (Throwable t) { - } - - verifyNoInteractions(Mocks.applicationAsyncResource); - verifyNoInteractions(Mocks.ohjausparametritAsyncResource); - verifyNoInteractions(Mocks.valintalaskentaAsyncResource); - verifyNoInteractions(Mocks.suoritusrekisteriAsyncResource); - - assertEquals( - HttpStatus.INTERNAL_SERVER_ERROR, - ((ResponseEntity) result.getResult()).getStatusCode()); - } -} diff --git a/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/Mocks.java b/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/Mocks.java index d7ccb263b2..bde2eb5eef 100644 --- a/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/Mocks.java +++ b/src/test/java/fi/vm/sade/valinta/kooste/laskentakerralla/Mocks.java @@ -2,7 +2,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; import fi.vm.sade.valinta.kooste.external.resource.ataru.AtaruAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.hakuapp.ApplicationAsyncResource; @@ -10,7 +9,6 @@ import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.OhjausparametritAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.oppijanumerorekisteri.OppijanumerorekisteriAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.organisaatio.OrganisaatioAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.SuoritusrekisteriAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.tarjonta.TarjontaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.valintalaskenta.ValintalaskentaAsyncResource; @@ -18,19 +16,12 @@ import fi.vm.sade.valinta.kooste.external.resource.valintapiste.ValintapisteAsyncResource; import fi.vm.sade.valinta.kooste.mocks.MockOppijanumerorekisteriAsyncResource; import fi.vm.sade.valinta.kooste.valintalaskenta.actor.KoskiService; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorFactory; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorSystem; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaStarter; -import fi.vm.sade.valinta.kooste.valintalaskenta.resource.ValintalaskentaStatusExcelHandler; -import fi.vm.sade.valinta.kooste.valintalaskenta.route.ValintalaskentaKerrallaRouteValvomo; import fi.vm.sade.valinta.kooste.valintalaskenta.util.HakemuksetConverterUtil; public class Mocks { static OrganisaatioAsyncResource organisaatioAsyncResource = mock(OrganisaatioAsyncResource.class); - static ValintalaskentaKerrallaRouteValvomo valintalaskentaKerrallaRouteValvomo = - mock(ValintalaskentaKerrallaRouteValvomo.class); static ApplicationAsyncResource applicationAsyncResource = mock(ApplicationAsyncResource.class); static AtaruAsyncResource ataruAsyncResource = mock(AtaruAsyncResource.class); static HarkinnanvaraisuusAsyncResource harkinnanvaraisuusAsyncResource = @@ -39,15 +30,11 @@ public class Mocks { mock(ValintaperusteetAsyncResource.class); static OhjausparametritAsyncResource ohjausparametritAsyncResource = mock(OhjausparametritAsyncResource.class); - static LaskentaSeurantaAsyncResource laskentaSeurantaAsyncResource = - mock(LaskentaSeurantaAsyncResource.class); static ValintalaskentaAsyncResource valintalaskentaAsyncResource = mock(ValintalaskentaAsyncResource.class); static SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource = mock(SuoritusrekisteriAsyncResource.class); static TarjontaAsyncResource tarjontaAsyncResource = mock(TarjontaAsyncResource.class); - static ValintalaskentaStatusExcelHandler valintalaskentaStatusExcelHandler = - mock(ValintalaskentaStatusExcelHandler.class); static ValintapisteAsyncResource valintapisteAsyncResource = mock(ValintapisteAsyncResource.class); static KoskiService koskiService = mock(KoskiService.class); @@ -55,43 +42,16 @@ public class Mocks { new HakemuksetConverterUtil("9999-12-31", "9999-12-31", harkinnanvaraisuusAsyncResource); static OppijanumerorekisteriAsyncResource oppijanumerorekisteriAsyncResource = new MockOppijanumerorekisteriAsyncResource(); - static LaskentaActorSystem laskentaActorSystem = - spy( - new LaskentaActorSystem( - laskentaSeurantaAsyncResource, - new LaskentaStarter( - ohjausparametritAsyncResource, - valintaperusteetAsyncResource, - laskentaSeurantaAsyncResource, - tarjontaAsyncResource), - new LaskentaActorFactory( - 5, - valintalaskentaAsyncResource, - applicationAsyncResource, - ataruAsyncResource, - valintaperusteetAsyncResource, - laskentaSeurantaAsyncResource, - suoritusrekisteriAsyncResource, - tarjontaAsyncResource, - valintapisteAsyncResource, - koskiService, - hakemuksetConverterUtil, - oppijanumerorekisteriAsyncResource), - 8)); public static void resetMocks() { reset(organisaatioAsyncResource); - reset(valintalaskentaKerrallaRouteValvomo); reset(applicationAsyncResource); reset(ataruAsyncResource); reset(valintaperusteetAsyncResource); reset(ohjausparametritAsyncResource); - reset(laskentaSeurantaAsyncResource); reset(valintalaskentaAsyncResource); reset(suoritusrekisteriAsyncResource); reset(tarjontaAsyncResource); reset(valintapisteAsyncResource); - reset(valintalaskentaStatusExcelHandler); - reset(laskentaActorSystem); } } diff --git a/src/test/java/fi/vm/sade/valinta/kooste/mocks/MockValintaperusteetAsyncResource.java b/src/test/java/fi/vm/sade/valinta/kooste/mocks/MockValintaperusteetAsyncResource.java index 1277e93986..f4c2c904bc 100644 --- a/src/test/java/fi/vm/sade/valinta/kooste/mocks/MockValintaperusteetAsyncResource.java +++ b/src/test/java/fi/vm/sade/valinta/kooste/mocks/MockValintaperusteetAsyncResource.java @@ -3,7 +3,6 @@ import fi.vm.sade.service.valintaperusteet.dto.HakukohdeImportDTO; import fi.vm.sade.service.valintaperusteet.dto.HakukohdeJaValintakoeDTO; import fi.vm.sade.service.valintaperusteet.dto.HakukohdeJaValintaperusteDTO; -import fi.vm.sade.service.valintaperusteet.dto.HakukohdeViiteDTO; import fi.vm.sade.service.valintaperusteet.dto.ValinnanVaiheJonoillaDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintakoeDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteDTO; @@ -106,11 +105,6 @@ public Observable> haeHakukohteetValinnanvaiheelle(String oid) { return Observable.just(hakukohteetValinnanvaiheelleResultReference.get()); } - @Override - public Observable> haunHakukohteet(String hakuOid) { - throw new UnsupportedOperationException(); - } - @Override public CompletableFuture> findAvaimet(String hakukohdeOid) { return CompletableFuture.completedFuture(valintaperusteetResultReference.get()); diff --git a/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/ValintalaskentaTest.java b/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/ValintalaskentaTest.java index 7df9730bb1..40494e629e 100644 --- a/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/ValintalaskentaTest.java +++ b/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/ValintalaskentaTest.java @@ -1,31 +1,19 @@ package fi.vm.sade.valinta.kooste.valintalaskenta; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetJarjestyskriteeriDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintaperusteetValinnanVaiheDTO; import fi.vm.sade.service.valintaperusteet.dto.ValintatapajonoJarjestyskriteereillaDTO; -import fi.vm.sade.valinta.kooste.external.resource.ataru.AtaruAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.ataru.dto.AtaruHakemus; -import fi.vm.sade.valinta.kooste.external.resource.hakuapp.ApplicationAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.hakuapp.dto.Hakemus; -import fi.vm.sade.valinta.kooste.external.resource.harkinnanvaraisuus.HarkinnanvaraisuusAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.ohjausparametrit.OhjausparametritAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.oppijanumerorekisteri.OppijanumerorekisteriAsyncResource; -import fi.vm.sade.valinta.kooste.external.resource.seuranta.LaskentaSeurantaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.SuoritusrekisteriAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.suoritusrekisteri.dto.Oppija; -import fi.vm.sade.valinta.kooste.external.resource.tarjonta.Haku; import fi.vm.sade.valinta.kooste.external.resource.tarjonta.TarjontaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.valintalaskenta.ValintalaskentaAsyncResource; import fi.vm.sade.valinta.kooste.external.resource.valintaperusteet.ValintaperusteetAsyncResource; @@ -33,131 +21,39 @@ import fi.vm.sade.valinta.kooste.external.resource.valintapiste.dto.PisteetWithLastModified; import fi.vm.sade.valinta.kooste.external.resource.valintapiste.dto.Valintapisteet; import fi.vm.sade.valinta.kooste.external.resource.valintatulosservice.dto.AuditSession; -import fi.vm.sade.valinta.kooste.mocks.MockAtaruAsyncResource; -import fi.vm.sade.valinta.kooste.mocks.MockOppijanumerorekisteriAsyncResource; -import fi.vm.sade.valinta.kooste.util.HakuappHakemusWrapper; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.KoskiService; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorFactory; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaActorSystem; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaStarter; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.dto.HakukohdeJaOrganisaatio; -import fi.vm.sade.valinta.kooste.valintalaskenta.dto.LaskentaStartParams; -import fi.vm.sade.valinta.kooste.valintalaskenta.util.HakemuksetConverterUtil; -import fi.vm.sade.valinta.seuranta.dto.HakukohdeTila; -import fi.vm.sade.valinta.seuranta.dto.IlmoitusDto; -import fi.vm.sade.valinta.seuranta.dto.IlmoitusTyyppi; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTila; -import fi.vm.sade.valinta.seuranta.dto.LaskentaTyyppi; -import fi.vm.sade.valintalaskenta.domain.dto.LaskeDTO; import fi.vm.sade.valintalaskenta.domain.dto.SuoritustiedotDTO; import io.reactivex.Observable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatcher; -import org.mockito.Mockito; -import org.springframework.http.ResponseEntity; public class ValintalaskentaTest { private static final Hakemus hakemus = new Hakemus(); private static final AtaruHakemus ataruHakemus = new AtaruHakemus(); - private static final Date NYT = new Date(); - private final ApplicationAsyncResource applicationAsyncResource = - mock(ApplicationAsyncResource.class); - private final AtaruAsyncResource ataruAsyncResource = mock(AtaruAsyncResource.class); private final SuoritusrekisteriAsyncResource suoritusrekisteriAsyncResource = mock(SuoritusrekisteriAsyncResource.class); private final ValintalaskentaAsyncResource valintalaskentaAsyncResource = mock(ValintalaskentaAsyncResource.class); private final ValintaperusteetAsyncResource valintaperusteetAsyncResource = mock(ValintaperusteetAsyncResource.class); - private final LaskentaSeurantaAsyncResource seurantaAsyncResource = - mock(LaskentaSeurantaAsyncResource.class); private final TarjontaAsyncResource tarjontaAsyncResource = mock(TarjontaAsyncResource.class); - private final HarkinnanvaraisuusAsyncResource harkinnanvaraisuusAsyncResource = - mock(HarkinnanvaraisuusAsyncResource.class); - private final OhjausparametritAsyncResource ohjausparametritAsyncResource = - mock(OhjausparametritAsyncResource.class); private final ValintapisteAsyncResource valintapisteAsyncResource = mock(ValintapisteAsyncResource.class); - private final KoskiService koskiService = mock(KoskiService.class); - private final HakemuksetConverterUtil hakemuksetConverterUtil = - new HakemuksetConverterUtil("9999-12-31", "9999-12-31", harkinnanvaraisuusAsyncResource); - static OppijanumerorekisteriAsyncResource oppijanumerorekisteriAsyncResource = - new MockOppijanumerorekisteriAsyncResource(); - private final LaskentaActorSystem laskentaActorSystem = - new LaskentaActorSystem( - seurantaAsyncResource, - new LaskentaStarter( - ohjausparametritAsyncResource, - valintaperusteetAsyncResource, - seurantaAsyncResource, - tarjontaAsyncResource), - new LaskentaActorFactory( - 5, - valintalaskentaAsyncResource, - applicationAsyncResource, - ataruAsyncResource, - valintaperusteetAsyncResource, - seurantaAsyncResource, - suoritusrekisteriAsyncResource, - tarjontaAsyncResource, - valintapisteAsyncResource, - koskiService, - hakemuksetConverterUtil, - oppijanumerorekisteriAsyncResource), - 8); private final String hakukohde1Oid = "h1"; private final String hakukohde2Oid = "h2"; private final String hakukohde3Oid = "h3"; private final String valintatapajono1Oid = "vj1"; - private final String valintatapajono2Oid = "vj2"; - private final String valintatapajono3Oid = "vj3"; private final String ataruHakukohdeOid = "1.2.246.562.20.90242725084"; private final String ataruHakukohdeOid2 = "1.2.246.562.20.38103650677"; - private final String uuid = "uuid"; private final String hakuOid = "hakuOid"; private final String ataruHakuOid = "1.2.246.562.29.805206009510"; - private final Haku hakuDTO = - new Haku(hakuOid, new HashMap<>(), new HashSet<>(), null, null, null, null, null, null); - private final Haku ataruHakuDTO = - new Haku( - ataruHakuOid, - new HashMap<>(), - new HashSet<>(), - "ataru-lomake-avain", - null, - null, - null, - null, - null); private final Oppija oppijaFromSure1 = new Oppija(); private final Oppija anonOppijaFromSure = new Oppija(); private final List hakemusOids = new ArrayList<>(); - private final List hakukohdeJaOrganisaatios = - Arrays.asList( - new HakukohdeJaOrganisaatio(hakukohde1Oid, "o1"), - new HakukohdeJaOrganisaatio(hakukohde2Oid, "o2"), - new HakukohdeJaOrganisaatio(hakukohde3Oid, "o3")); - private List ataruHakukohdeJaOrganisaatios = - Arrays.asList( - new HakukohdeJaOrganisaatio(ataruHakukohdeOid, "Organisaatio1"), - new HakukohdeJaOrganisaatio(ataruHakukohdeOid2, "Organisaatio2")); - private final AuditSession auditSession = - new AuditSession( - "1.2.3.4.5", - Collections.singletonList("APP_VALINTA_EVERYTHING_CRUD"), - "Firefox", - "127.0.0.1"); @BeforeEach public void setUpTestData() { @@ -265,319 +161,6 @@ public void setUpTestData() { .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); } - @Test - public void onnistuneestaValintalaskennastaPidetaanKirjaaSeurantapalveluun() - throws InterruptedException { - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde2Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde3Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde1Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde2Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde3Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.otaSeuraavaLaskentaTyonAlle()) - .thenReturn(Observable.just(Optional.empty())); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - null, - null, - hakukohdeJaOrganisaatios, - LaskentaTyyppi.HAKUKOHDE); - - laskentaActorSystem.suoritaValintalaskentaKerralla(hakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(seurantaAsyncResource).otaSeuraavaLaskentaTyonAlle(); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde1Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde2Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde3Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - @Test - public void valintaLaskentaHakeeKoskestaTiedot() throws InterruptedException { - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde2Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde3Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde1Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde2Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde3Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.otaSeuraavaLaskentaTyonAlle()) - .thenReturn(Observable.just(Optional.empty())); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - when(valintalaskentaAsyncResource.laskeKaikki( - any(LaskeDTO.class), any(SuoritustiedotDTO.class))) - .thenAnswer( - invocationOnMock -> { - LaskeDTO laskeDTO = invocationOnMock.getArgument(0); - assertThat(laskeDTO.getHakemus(), hasSize(1)); - return Observable.fromFuture(CompletableFuture.completedFuture("OK")); - }); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - null, - null, - hakukohdeJaOrganisaatios, - LaskentaTyyppi.HAKUKOHDE); - - laskentaActorSystem.suoritaValintalaskentaKerralla(hakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(seurantaAsyncResource).otaSeuraavaLaskentaTyonAlle(); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde1Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde2Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde3Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - verify(valintalaskentaAsyncResource, times(3)) - .laskeKaikki(any(LaskeDTO.class), any(SuoritustiedotDTO.class)); - verify(koskiService, times(3)) - .haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class)); - Mockito.verifyNoMoreInteractions(valintalaskentaAsyncResource); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - @Test - public void onnistuneestaValintalaskennastaAtaruHaullePidetaanKirjaaSeurantapalveluun() - throws InterruptedException { - when(ataruAsyncResource.getApplicationsByHakukohde(ataruHakukohdeOid, false)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - MockAtaruAsyncResource.getAtaruHakemusWrapper( - "1.2.246.562.11.00000000000000000063")))); - when(ataruAsyncResource.getApplicationsByHakukohde(ataruHakukohdeOid2, false)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - MockAtaruAsyncResource.getAtaruHakemusWrapper( - "1.2.246.562.11.00000000000000000063")))); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, ataruHakukohdeOid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, ataruHakukohdeOid2, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.otaSeuraavaLaskentaTyonAlle()) - .thenReturn(Observable.just(Optional.empty())); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - ataruHakuOid, - false, - null, - null, - ataruHakukohdeJaOrganisaatios, - LaskentaTyyppi.HAKUKOHDE); - - laskentaActorSystem.suoritaValintalaskentaKerralla(ataruHakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(seurantaAsyncResource).otaSeuraavaLaskentaTyonAlle(); - verify(ataruAsyncResource).getApplicationsByHakukohde(ataruHakukohdeOid, false); - verify(ataruAsyncResource).getApplicationsByHakukohde(ataruHakukohdeOid2, false); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, ataruHakukohdeOid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, ataruHakukohdeOid2, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - @Test - public void onnistuneestaValintaryhmalaskennastaPidetaanKirjaaSeurantapalveluun() - throws InterruptedException { - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde2Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde3Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - - Integer vaiheenNumero = 1; - - when(valintaperusteetAsyncResource.haeValintaperusteet(eq(hakukohde1Oid), eq(vaiheenNumero))) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - valintaperusteetWithValintatapajonoUsingValintalaskenta( - true, true, valintatapajono1Oid)))); - when(valintaperusteetAsyncResource.haeValintaperusteet(eq(hakukohde2Oid), eq(vaiheenNumero))) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - valintaperusteetWithValintatapajonoUsingValintalaskenta( - true, true, valintatapajono2Oid)))); - when(valintaperusteetAsyncResource.haeValintaperusteet(eq(hakukohde3Oid), eq(vaiheenNumero))) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - valintaperusteetWithValintatapajonoUsingValintalaskenta( - true, true, valintatapajono3Oid)))); - - when(valintaperusteetAsyncResource.haeHakijaryhmat(eq(hakukohde1Oid))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyList())); - when(valintaperusteetAsyncResource.haeHakijaryhmat(eq(hakukohde2Oid))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyList())); - when(valintaperusteetAsyncResource.haeHakijaryhmat(eq(hakukohde3Oid))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyList())); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - vaiheenNumero, - null, - hakukohdeJaOrganisaatios, - LaskentaTyyppi.VALINTARYHMA); - - when(valintalaskentaAsyncResource.laskeJaSijoittele( - eq(uuid), anyList(), any(SuoritustiedotDTO.class))) - .thenReturn(Observable.just("Valintaryhmälaskenta onnistui")); - when(seurantaAsyncResource.merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.noContent().build())); - when(seurantaAsyncResource.otaSeuraavaLaskentaTyonAlle()) - .thenReturn(Observable.just(Optional.empty())); - - laskentaActorSystem.suoritaValintalaskentaKerralla(hakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(valintaperusteetAsyncResource).haeValintaperusteet(eq(hakukohde1Oid), eq(vaiheenNumero)); - verify(valintaperusteetAsyncResource).haeValintaperusteet(eq(hakukohde2Oid), eq(vaiheenNumero)); - verify(valintaperusteetAsyncResource).haeValintaperusteet(eq(hakukohde3Oid), eq(vaiheenNumero)); - verify(valintaperusteetAsyncResource).haeHakijaryhmat(eq(hakukohde1Oid)); - verify(valintaperusteetAsyncResource).haeHakijaryhmat(eq(hakukohde2Oid)); - verify(valintaperusteetAsyncResource).haeHakijaryhmat(eq(hakukohde3Oid)); - Mockito.verifyNoMoreInteractions(valintaperusteetAsyncResource); - - verify(valintalaskentaAsyncResource) - .laskeJaSijoittele(eq(uuid), anyList(), any(SuoritustiedotDTO.class)); - Mockito.verifyNoMoreInteractions(valintalaskentaAsyncResource); - - verify(seurantaAsyncResource).otaSeuraavaLaskentaTyonAlle(); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - @Test - public void puuttuvaJonokriteeriAiheuttaaLaskennanEpaonnistumisen() throws InterruptedException { - int vaiheenNumero = 1; - - when(valintaperusteetAsyncResource.haeValintaperusteet(hakukohde1Oid, vaiheenNumero)) - .thenReturn( - CompletableFuture.completedFuture( - Arrays.asList( - valintaperusteetWithValintatapajonoUsingValintalaskenta( - true, true, valintatapajono1Oid), - valintaperusteetWithValintatapajonoUsingValintalaskenta( - true, false, valintatapajono2Oid), - valintaperusteetWithValintatapajonoUsingValintalaskenta( - false, true, valintatapajono3Oid)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - vaiheenNumero, - null, - Collections.singletonList(new HakukohdeJaOrganisaatio(hakukohde1Oid, "o1")), - LaskentaTyyppi.HAKUKOHDE); - laskentaActorSystem.suoritaValintalaskentaKerralla(hakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila( - eq(uuid), - eq(hakukohde1Oid), - eq(HakukohdeTila.KESKEYTETTY), - getIlmoitusDtoOptional( - "Hakukohteen h1 valintatapajonolla vj2 on joko valintalaskenta ilman jonokriteereitä")); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - private ValintaperusteetDTO valintaperusteetWithValintatapajonoUsingValintalaskenta( boolean kaytetaanValintalaskentaa, boolean hasJonokriteeri, String valintatapajonoOid) { ValintaperusteetDTO valintaperusteet = new ValintaperusteetDTO(); @@ -598,221 +181,4 @@ private ValintaperusteetDTO valintaperusteetWithValintatapajonoUsingValintalaske return valintaperusteet; } - - @Test - public void valintalaskentaEiKaytossaAiheuttaaLaskennanEpaonnistumisen() - throws InterruptedException { - int vaiheenNumero = 1; - - when(valintaperusteetAsyncResource.haeValintaperusteet(hakukohde1Oid, vaiheenNumero)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - valintaperusteetWithValintatapajonoUsingValintalaskenta( - false, false, valintatapajono1Oid)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - vaiheenNumero, - null, - Collections.singletonList(new HakukohdeJaOrganisaatio(hakukohde1Oid, "o1")), - LaskentaTyyppi.HAKUKOHDE); - laskentaActorSystem.suoritaValintalaskentaKerralla(hakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila( - eq(uuid), - eq(hakukohde1Oid), - eq(HakukohdeTila.KESKEYTETTY), - getIlmoitusDtoOptional( - String.format( - "Hakukohteen %s valittujen valinnanvaiheiden valintatapajonoissa ei käytetä valintalaskentaa", - hakukohde1Oid))); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - @Test - public void epaonnistuneetLaskennatKirjataanSeurantapalveluun() throws InterruptedException { - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenReturn( - CompletableFuture.failedFuture( - new RuntimeException( - getClass().getSimpleName() - + " : Ei saatu haettua hakemuksia kohteelle " - + hakukohde1Oid))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde2Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde3Oid)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList(new HakuappHakemusWrapper(hakemus)))); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde2Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.ok().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde3Oid, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.ok().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, hakukohde3Oid, HakukohdeTila.KESKEYTETTY, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.ok().build())); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - null, - null, - hakukohdeJaOrganisaatios, - LaskentaTyyppi.HAKUKOHDE); - - laskentaActorSystem.suoritaValintalaskentaKerralla(hakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(valintapisteAsyncResource, times(2)) - .getValintapisteetWithHakemusOidsAsFuture(eq(hakemusOids), any(AuditSession.class)); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde2Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, hakukohde3Oid, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila( - eq(uuid), - eq(hakukohde1Oid), - eq(HakukohdeTila.KESKEYTETTY), - getIlmoitusDtoOptional("Ei saatu haettua hakemuksia kohteelle " + hakukohde1Oid)); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - @Test - public void epaonnistuneetAtaruLaskennatKirjataanSeurantapalveluun() throws InterruptedException { - final String ataruHakemusOid = "1.2.246.562.11.00000000000000000063"; - final List ataruHakemusOids = new ArrayList<>(); - ataruHakemusOids.add(ataruHakemusOid); - when(ataruAsyncResource.getApplicationsByHakukohde(ataruHakukohdeOid, false)) - .thenReturn( - CompletableFuture.failedFuture( - new RuntimeException( - getClass().getSimpleName() - + " : Ei saatu haettua hakemuksia kohteelle " - + ataruHakukohdeOid))); - when(ataruAsyncResource.getApplicationsByHakukohde(ataruHakukohdeOid2, false)) - .thenReturn( - CompletableFuture.completedFuture( - Collections.singletonList( - MockAtaruAsyncResource.getAtaruHakemusWrapper(ataruHakemusOid)))); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, ataruHakukohdeOid2, HakukohdeTila.VALMIS, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.ok().build())); - when(seurantaAsyncResource.merkkaaHakukohteenTila( - uuid, ataruHakukohdeOid, HakukohdeTila.KESKEYTETTY, Optional.empty())) - .thenReturn(Observable.just(ResponseEntity.ok().build())); - when(koskiService.haeKoskiOppijat( - any(String.class), any(), any(), any(SuoritustiedotDTO.class), any(Date.class))) - .thenReturn(CompletableFuture.completedFuture(Collections.emptyMap())); - - LaskentaStartParams laskentaJaHaku = - new LaskentaStartParams( - auditSession, - uuid, - ataruHakuOid, - false, - null, - null, - ataruHakukohdeJaOrganisaatios, - LaskentaTyyppi.HAKUKOHDE); - - laskentaActorSystem.suoritaValintalaskentaKerralla(ataruHakuDTO, null, laskentaJaHaku); - Thread.sleep(500); - - verify(ataruAsyncResource, times(2)).getApplicationsByHakukohde(ataruHakukohdeOid, false); - verify(ataruAsyncResource).getApplicationsByHakukohde(ataruHakukohdeOid2, false); - verify(valintapisteAsyncResource, times(1)) - .getValintapisteetWithHakemusOidsAsFuture(eq(ataruHakemusOids), any(AuditSession.class)); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila(uuid, ataruHakukohdeOid2, HakukohdeTila.VALMIS, Optional.empty()); - verify(seurantaAsyncResource) - .merkkaaHakukohteenTila( - eq(uuid), - eq(ataruHakukohdeOid), - eq(HakukohdeTila.KESKEYTETTY), - getIlmoitusDtoOptional("Ei saatu haettua hakemuksia kohteelle " + ataruHakukohdeOid)); - verify(seurantaAsyncResource).merkkaaLaskennanTila(uuid, LaskentaTila.VALMIS, Optional.empty()); - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } - - private Optional getIlmoitusDtoOptional(String odotettuOtsikonSisalto) { - return argThat( - new ArgumentMatcher<>() { - @Override - public boolean matches(Optional argument) { - if (argument.isPresent()) { - IlmoitusDto ilmoitusDto = argument.get(); - return odotettuIlmoitustyyppi.equals(ilmoitusDto.getTyyppi()) - && ilmoitusDto.getOtsikko().contains(odotettuOtsikonSisalto); - } else { - return false; - } - } - - private final IlmoitusTyyppi odotettuIlmoitustyyppi = IlmoitusTyyppi.VIRHE; - - @Override - public String toString() { - return IlmoitusDto.class.getSimpleName() - + ", jossa " - + odotettuIlmoitustyyppi - + " ja otsikossa \"" - + odotettuOtsikonSisalto - + "\""; - } - }); - } - - @Test - public void poikkeukseenEpaonnistuneitaLaskentojaEiKirjataSeurantapalveluun() - throws InterruptedException { - when(applicationAsyncResource.getApplicationsByOid(hakuOid, hakukohde1Oid)) - .thenThrow( - new RuntimeException( - getClass().getSimpleName() - + " : Ei saatu taaskaan haettua hakemuksia kohteelle " - + hakukohde1Oid)); - - laskentaActorSystem.suoritaValintalaskentaKerralla( - hakuDTO, - null, - new LaskentaStartParams( - auditSession, - uuid, - hakuOid, - false, - null, - null, - hakukohdeJaOrganisaatios, - LaskentaTyyppi.HAKUKOHDE)); - Thread.sleep(500); - - Mockito.verifyNoMoreInteractions(seurantaAsyncResource); - } } diff --git a/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/laskenta/LaskentaStarterActorTest.java b/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/laskenta/LaskentaStarterActorTest.java deleted file mode 100644 index a061bbfb03..0000000000 --- a/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/actor/laskenta/LaskentaStarterActorTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta; - -import static fi.vm.sade.valinta.kooste.valintalaskenta.actor.laskenta.LaskentaStarterActor.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.*; - -import akka.actor.ActorRef; -import akka.actor.ActorSystem; -import akka.actor.Props; -import akka.testkit.TestActorRef; -import com.typesafe.config.ConfigFactory; -import fi.vm.sade.valinta.kooste.valintalaskenta.actor.LaskentaSupervisor; -import java.util.stream.IntStream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class LaskentaStarterActorTest { - - private static final int MAX_WORKER_COUNT = 10; - private final LaskentaSupervisor laskentaSupervisor = mock(LaskentaSupervisor.class); - private LaskentaStarterActor actor; - private TestActorRef ref; - - @BeforeEach - public void setUp() { - Props props = props(laskentaSupervisor, MAX_WORKER_COUNT); - ActorSystem actorSystem = - ActorSystem.create("ValintalaskentaActorSystem", ConfigFactory.defaultOverrides()); - this.ref = TestActorRef.create(actorSystem, props, "testA"); - this.actor = ref.underlyingActor(); - } - - @Test - public void createdActorHasNoWorkers() { - assertEquals(0, actor.getWorkerCount()); - } - - @Test - public void workerProvidedOnWorkAvailableMessage() throws Exception { - ref.tell(new WorkAvailable(), ActorRef.noSender()); - assertEquals(1, actor.getWorkerCount()); - verify(laskentaSupervisor, times(1)).fetchAndStartLaskenta(); - } - - @Test - public void maxWorkersCanBeInitialized() throws Exception { - signalWorkAvailableTimes(MAX_WORKER_COUNT); - assertEquals(MAX_WORKER_COUNT, actor.getWorkerCount()); - verify(laskentaSupervisor, times(MAX_WORKER_COUNT)).fetchAndStartLaskenta(); - } - - @Test - public void maxWorkersCanNotBeExceeded() throws Exception { - signalWorkAvailableTimes(MAX_WORKER_COUNT + 1); - assertEquals(MAX_WORKER_COUNT, actor.getWorkerCount()); - verify(laskentaSupervisor, times(MAX_WORKER_COUNT)).fetchAndStartLaskenta(); - } - - @Test - public void startLaskentaWhenWorkerAvailable() { - signalWorkAvailableTimes(MAX_WORKER_COUNT + 1); - ref.tell(new WorkerAvailable(), ActorRef.noSender()); - assertEquals(10, actor.getWorkerCount()); - verify(laskentaSupervisor, times(MAX_WORKER_COUNT + 1)).fetchAndStartLaskenta(); - } - - @Test - public void workersAreReleasedWhenNoWorkAvailable() { - ref.tell(new WorkAvailable(), ActorRef.noSender()); - ref.tell(new NoWorkAvailable(), ActorRef.noSender()); - assertEquals(0, actor.getWorkerCount()); - verify(laskentaSupervisor, times(1)).fetchAndStartLaskenta(); - } - - @Test - public void workerCountCanNotBeNegative() { - ref.tell(new NoWorkAvailable(), ActorRef.noSender()); - assertEquals(0, actor.getWorkerCount()); - verify(laskentaSupervisor, never()).fetchAndStartLaskenta(); - } - - private void signalWorkAvailableTimes(int count) { - IntStream.rangeClosed(1, count) - .forEach(i -> ref.tell(new WorkAvailable(), ActorRef.noSender())); - } -} diff --git a/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/excel/TestLaskentaDtoAsExcel.java b/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/excel/TestLaskentaDtoAsExcel.java deleted file mode 100644 index cf1a05e47a..0000000000 --- a/src/test/java/fi/vm/sade/valinta/kooste/valintalaskenta/excel/TestLaskentaDtoAsExcel.java +++ /dev/null @@ -1,41 +0,0 @@ -package fi.vm.sade.valinta.kooste.valintalaskenta.excel; - -import fi.vm.sade.valinta.seuranta.dto.*; -import java.io.IOException; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import org.junit.jupiter.api.Test; - -public class TestLaskentaDtoAsExcel { - - @Test - public void testLaskentaDtoAsExcel() throws IOException { - // uuid, hakuOid, luotu, tila, hakukohteet, valinnanvaihe, - // valintakoelaskenta - String uuid = null; - String hakuOid = null; - LaskentaTila tila = null; - List hakukohteet = null; - Integer valinnanvaihe = null; - Boolean valintakoelaskenta = null; - LaskentaDto l = - new LaskentaDto( - uuid, - "", - "", - "", - hakuOid, - new Date().getTime(), - tila, - LaskentaTyyppi.HAKUKOHDE, - new IlmoitusDto(IlmoitusTyyppi.VIRHE, "Joku virhe", Arrays.asList("A", "B", "C")), - hakukohteet, - false, - valinnanvaihe, - valintakoelaskenta, - null, - true); - LaskentaDtoAsExcel.laskentaDtoAsExcel(l); - } -} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index da37c23434..9fc7965625 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -4,3 +4,5 @@ valintalaskentakoostepalvelu.postgresql.password=password valintalaskentakoostepalvelu.postgresql.driver=org.testcontainers.jdbc.ContainerDatabaseDriver valintalaskentakoostepalvelu.postgresql.maxactive=10 valintalaskentakoostepalvelu.postgresql.maxwait=10000 + +environment.name=local