Skip to content

Commit 6375b85

Browse files
authored
Merge pull request #157 from querqy/play_3_scala_2_13
Update to Play 3.0, Scala 2.13 and JDK 17
2 parents 5257e1d + f36fa22 commit 6375b85

File tree

49 files changed

+174
-131
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+174
-131
lines changed

Dockerfile

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# syntax = docker/dockerfile:1.0-experimental
2-
FROM openjdk:11-buster as builder
2+
FROM eclipse-temurin:17-jdk-focal as builder
33

44
ARG NODE_VERSION=16
55

6+
RUN apt-get update \
7+
&& apt-get install -y curl gnupg
8+
69
RUN echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | tee -a /etc/apt/sources.list.d/sbt.list \
710
&& curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | apt-key add \
811
&& apt-get update \
@@ -22,7 +25,7 @@ WORKDIR /smui
2225

2326
RUN --mount=target=/root/.ivy2,type=cache sbt "set assembly / test := {}" clean assembly
2427

25-
FROM openjdk:11-jre-slim-buster
28+
FROM eclipse-temurin:17-jre-focal
2629

2730
RUN apt-get update \
2831
&& apt-get install -y --no-install-recommends openssh-client sshpass bash curl git \

app/controllers/ApiController.scala

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package controllers
22

33
import java.io.{OutputStream, PipedInputStream, PipedOutputStream}
4-
import akka.stream.scaladsl.{Source, StreamConverters}
5-
import akka.util.ByteString
4+
import org.apache.pekko.stream.scaladsl.{Source, StreamConverters}
5+
import org.apache.pekko.util.ByteString
66

77
import javax.inject.Inject
88
import play.api.Logging
@@ -14,7 +14,6 @@ import java.nio.file.Paths
1414
import java.time.format.DateTimeFormatter
1515
import java.time.LocalDateTime
1616
import scala.concurrent.{ExecutionContext, Future}
17-
import collection.JavaConverters._
1817
import models.FeatureToggleModel.FeatureToggleService
1918
import models._
2019
import models.config.SmuiVersion
@@ -24,10 +23,11 @@ import models.querqy.QuerqyRulesTxtGenerator
2423
import models.reports.RulesUsageReport
2524
import models.rules.{DeleteRule, FilterRule, RedirectRule, SynonymRule, UpDownRule}
2625
import models.spellings.{CanonicalSpellingId, CanonicalSpellingValidator, CanonicalSpellingWithAlternatives}
27-
import org.pac4j.core.profile.{ProfileManager, UserProfile}
28-
import org.pac4j.play.PlayWebContext
26+
import org.pac4j.core.profile.UserProfile
27+
import org.pac4j.play.context.PlayFrameworkParameters
2928
import org.pac4j.play.scala.{Security, SecurityComponents}
3029
import play.api.libs.Files
30+
import scala.jdk.CollectionConverters.ListHasAsScala
3131
import services.{RulesTxtDeploymentService, RulesTxtImportService, RulesUsageService}
3232

3333

@@ -48,7 +48,7 @@ class ApiController @Inject()(val controllerComponents: SecurityComponents,
4848

4949
case class ApiResult(result: String, message: String, returnId: Option[Id])
5050

51-
implicit val apiResultWrites = Json.writes[ApiResult]
51+
implicit val apiResultWrites: OWrites[ApiResult] = Json.writes[ApiResult]
5252

5353
def getFeatureToggles: Action[AnyContent] = Action {
5454
Ok(Json.toJson(featureToggleService.getJsFrontendToggleList))
@@ -411,7 +411,7 @@ class ApiController @Inject()(val controllerComponents: SecurityComponents,
411411
rules_txt.ref.copyTo(Paths.get(tmp_file_path), replace = true)
412412
// process rules.txt file
413413
val bufferedSource = scala.io.Source.fromFile(tmp_file_path)
414-
val filePayload = bufferedSource.getLines.mkString("\n")
414+
val filePayload = bufferedSource.getLines().mkString("\n")
415415
try {
416416
val importStatistics = rulesTxtImportService.importFromFilePayload(filePayload, SolrIndexId(solrIndexId))
417417
val apiResultMsg = "Import from rules.txt file successful with following statistics:\n" +
@@ -442,7 +442,7 @@ class ApiController @Inject()(val controllerComponents: SecurityComponents,
442442

443443
case class DeploymentInfo(msg: Option[String])
444444

445-
implicit val logDeploymentInfoWrites = Json.writes[DeploymentInfo]
445+
implicit val logDeploymentInfoWrites: OWrites[DeploymentInfo] = Json.writes[DeploymentInfo]
446446

447447
@deprecated("The old style of retrieving a deployment log summary as plain text will be removed", "SMUI version > 3.15.1")
448448
def getLatestDeploymentResultV1(solrIndexId: String, targetSystem: String): Action[AnyContent] = Action.async { request: Request[AnyContent] =>
@@ -490,7 +490,7 @@ class ApiController @Inject()(val controllerComponents: SecurityComponents,
490490

491491
case class DeploymentDetailedInfo(targetSystem: String, formattedDateTime: String, result: Int)
492492

493-
implicit val logDeploymentDetailedInfoWrites = Json.writes[DeploymentDetailedInfo]
493+
implicit val logDeploymentDetailedInfoWrites: OWrites[DeploymentDetailedInfo] = Json.writes[DeploymentDetailedInfo]
494494

495495
def getLatestDeploymentResult(solrIndexId: String): Action[AnyContent] = Action.async { request: Request[AnyContent] =>
496496
Future {
@@ -543,7 +543,7 @@ class ApiController @Inject()(val controllerComponents: SecurityComponents,
543543
val ERROR = Value("ERROR")
544544
}
545545

546-
implicit val smuiVersionInfoWrites = Json.writes[SmuiVersionInfo]
546+
implicit val smuiVersionInfoWrites: OWrites[SmuiVersionInfo] = Json.writes[SmuiVersionInfo]
547547

548548
// TODO consider outsourcing this "business logic" into the (config) model
549549
def getLatestVersionInfo() = Action.async {
@@ -686,8 +686,10 @@ class ApiController @Inject()(val controllerComponents: SecurityComponents,
686686
}
687687

688688
private def getProfiles(request: RequestHeader): List[UserProfile] = {
689-
val webContext = new PlayWebContext(request)
690-
val profileManager = new ProfileManager(webContext, controllerComponents.sessionStore)
689+
val parameters = new PlayFrameworkParameters(request)
690+
val webContext = controllerComponents.config.getWebContextFactory.newContext(parameters)
691+
val sessionStore = controllerComponents.config.getSessionStoreFactory.newSessionStore(parameters)
692+
val profileManager = controllerComponents.config.getProfileManagerFactory.apply(webContext, sessionStore)
691693
profileManager.getProfiles.asScala.toList
692694
}
693695

app/models/DatabaseExecutionContext.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package models
22

33
import javax.inject._
44

5-
import akka.actor.ActorSystem
5+
import org.apache.pekko.actor.ActorSystem
66
import play.api.libs.concurrent.CustomExecutionContext
77

88
/**

app/models/SearchManagementRepository.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class SearchManagementRepository @Inject()(dbapi: DBApi, toggleService: FeatureT
4646
def deleteSolrIndex(solrIndexId: String): Int = db.withTransaction { implicit connection =>
4747

4848
val solrIndexIdId = SolrIndexId(solrIndexId)
49-
val inputTags = InputTag.loadAll.filter(_.solrIndexId== Option(solrIndexIdId))
49+
val inputTags = InputTag.loadAll().filter(_.solrIndexId== Option(solrIndexIdId))
5050
if (inputTags.size > 0) {
5151
throw new Exception("Can't delete rules collection that has " + inputTags.size + "tags existing");
5252
}

app/models/config/TargetEnvironment.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ package object TargetEnvironment extends Logging {
3636
implicit val jsonFormatTargetEnvironmentGroup: OFormat[TargetEnvironmentGroup] = Json.format[TargetEnvironmentGroup]
3737
implicit val jsonFormatTargetEnvironmentInstance: OFormat[TargetEnvironmentInstance] = Json.format[TargetEnvironmentInstance]
3838

39-
implicit val jsonFormatTargetEnvironmentConfig = new Format[ Seq[TargetEnvironmentInstance] ] {
39+
implicit val jsonFormatTargetEnvironmentConfig: Format[Seq[TargetEnvironmentInstance]] = new Format[Seq[TargetEnvironmentInstance] ] {
4040

4141
def writes(targetEnvironmentConfig: Seq[TargetEnvironmentInstance]): JsValue =
4242
JsArray(

app/models/eventhistory/ActivityLog.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ object ActivityLog extends Logging {
9393
private def diffTermStatus(entity: String, beforeTerm: String, beforeStatus: Boolean, afterTerm: String, afterStatus: Boolean, smuiEventType: SmuiEventType.Value): Option[DiffSummary] = {
9494

9595
val termDiff = if (beforeTerm.trim.equals(afterTerm.trim)) None else Some(afterTerm.trim)
96-
val statDiff = if (beforeStatus.equals(afterStatus)) None else Some(afterStatus)
96+
val statDiff = if (beforeStatus == afterStatus) None else Some(afterStatus)
9797

9898
if (termDiff.isDefined || statDiff.isDefined) {
9999

app/models/input/TagInputAssociation.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ object TagInputAssociation {
6060
map { case tag ~ inputId =>
6161
inputId -> tag
6262
}
63-
}.groupBy(_._1).mapValues(_.map(_._2))
63+
}.groupBy(_._1).view.mapValues(_.map(_._2)).toMap
6464
}
6565

6666
def deleteBySearchInputId(id: SearchInputId)(implicit connection: Connection): Int = {

app/models/querqy/QuerqyRulesTxtGenerator.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,11 @@ class QuerqyRulesTxtGenerator @Inject()(searchManagementRepository: SearchManage
110110
}
111111
if (featureToggleService.isRuleTaggingActive) {
112112
val tagsByProperty = searchInput.tags.filter(i => i.exported && i.property.nonEmpty).groupBy(_.property.get)
113-
jsonProperties ++= tagsByProperty.mapValues(tags => Json.toJsFieldJsValueWrapper(tags.map(_.value))).toSeq
113+
jsonProperties ++= tagsByProperty.view.mapValues(tags => Json.toJsFieldJsValueWrapper(tags.map(_.value))).toSeq
114114
}
115115

116116
if (jsonProperties.nonEmpty) {
117-
retSearchInputRulesTxtPartial.append(renderJsonProperties(jsonProperties))
117+
retSearchInputRulesTxtPartial.append(renderJsonProperties(jsonProperties.toSeq))
118118
}
119119

120120
retSearchInputRulesTxtPartial.toString()

app/models/rules/Rule.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ trait RuleObject[T <: Rule] extends CommonRuleFields {
5555

5656
val sqlParser: RowParser[T]
5757

58-
def updateForSearchInput(searchInputId: SearchInputId, rules: Seq[T])(implicit connection: Connection) {
58+
def updateForSearchInput(searchInputId: SearchInputId, rules: Seq[T])
59+
(implicit connection: Connection): Unit = {
5960
// TODO consider to really determine an update/delete diff to ensure that last_update timestamps only updated for affected rules
6061

6162
SQL"delete from #$TABLE_NAME where #$SEARCH_INPUT_ID = $searchInputId".execute()

app/models/rules/SynonymRule.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object SynonymRule extends RuleObjectWithTerm[SynonymRule] {
4949
SQL"select * from #$TABLE_NAME where #$TYPE = #$TYPE_UNDIRECTED AND #$SEARCH_INPUT_ID in ($idGroup)".as((sqlParser ~ get[SearchInputId](SEARCH_INPUT_ID)).*).map { case rule ~ id =>
5050
id -> rule
5151
}
52-
}.groupBy(_._1).mapValues(_.map(_._2))
52+
}.groupBy(_._1).view.mapValues(_.map(_._2)).toMap
5353
}
5454

5555
override def createWithNewIdFrom(rule: SynonymRule): SynonymRule = {

app/models/spellings/AlternativeSpelling.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,6 @@ object AlternativeSpelling {
7979
SQL"select * from #$TABLE_NAME where #$CANONICAL_SPELLING_ID in ($idGroup)".as((sqlParser ~ get[CanonicalSpellingId](CANONICAL_SPELLING_ID)).*).map { case alternativeSpelling ~ id =>
8080
id -> alternativeSpelling
8181
}
82-
}.groupBy(_._1).mapValues(_.map(_._2))
82+
}.groupBy(_._1).view.mapValues(_.map(_._2)).toMap
8383
}
8484
}

app/models/spellings/CanonicalSpellingValidator.scala

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ object CanonicalSpellingValidator {
9595
val allSpellings = allCanonicalSpellings
9696
.flatMap(canonical => canonical.alternativeSpellings.map(alternative => alternative.term.toLowerCase.trim -> canonical.term.toLowerCase.trim))
9797
.groupBy(_._1)
98+
.view
9899
.mapValues(_.map(_._2))
99100
allSpellings.get(canonical).map { canonicalsHavingThatAlternative =>
100101
s"Canonical spelling $canonical is already an alternative spelling of ${canonicalsHavingThatAlternative.mkString(",")}"

app/modules/ConfiguredBasicAuthAuthenticator.scala

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
package modules
22

3-
import org.pac4j.core.context.WebContext
4-
import org.pac4j.core.context.session.SessionStore
5-
import org.pac4j.core.credentials.{Credentials, UsernamePasswordCredentials}
3+
import org.pac4j.core.context.CallContext
64
import org.pac4j.core.credentials.authenticator.Authenticator
5+
import org.pac4j.core.credentials.{Credentials, UsernamePasswordCredentials}
76
import org.pac4j.core.exception.CredentialsException
87
import org.pac4j.core.profile.CommonProfile
98
import org.pac4j.core.util.{CommonHelper, Pac4jConstants}
109

10+
import java.util.Optional
11+
1112

1213
case class ConfiguredBasicAuthAuthenticator(validUserId: String, validPassword: String) extends Authenticator {
1314

14-
override def validate(credentials: Credentials, context: WebContext, sessionStore: SessionStore): Unit = {
15+
override def validate(callContext: CallContext, credentials: Credentials): Optional[Credentials] = {
16+
import scala.jdk.javaapi.OptionConverters
17+
1518
if (credentials == null) throw new CredentialsException("No credential")
1619
val userCredentials = credentials.asInstanceOf[UsernamePasswordCredentials]
17-
val username = userCredentials.getUsername()
18-
val password = userCredentials.getPassword()
20+
val username = userCredentials.getUsername
21+
val password = userCredentials.getPassword
1922
if (CommonHelper.isBlank(username)) throw new CredentialsException("Username cannot be blank")
2023
if (CommonHelper.isBlank(password)) throw new CredentialsException("Password cannot be blank")
2124
if (CommonHelper.areNotEquals(username.toLowerCase, validUserId.toLowerCase)) throw new CredentialsException("Username : '" + username + "' does not match valid user")
@@ -24,6 +27,7 @@ case class ConfiguredBasicAuthAuthenticator(validUserId: String, validPassword:
2427
profile.setId(username)
2528
profile.addAttribute(Pac4jConstants.USERNAME, username)
2629
userCredentials.setUserProfile(profile)
30+
OptionConverters.toJava(Some(userCredentials))
2731
}
2832

2933
}

app/modules/SecurityModule.scala

+24-14
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package modules
22

33
import com.google.inject.{AbstractModule, Provides}
4-
import org.pac4j.core.client.{Client, Clients}
4+
import org.pac4j.core.client.Clients
55
import org.pac4j.core.client.direct.AnonymousClient
66
import org.pac4j.core.config.Config
7-
import org.pac4j.core.context.session.SessionStore
7+
import org.pac4j.core.context.FrameworkParameters
8+
import org.pac4j.core.context.session.{SessionStore, SessionStoreFactory}
89
import org.pac4j.core.profile.CommonProfile
10+
import org.pac4j.core.profile.factory.ProfileManagerFactory
911
import org.pac4j.http.client.direct.DirectBasicAuthClient
12+
import org.pac4j.play.context.PlayContextFactory
13+
import org.pac4j.play.http.PlayHttpActionAdapter
1014
import org.pac4j.play.scala.{DefaultSecurityComponents, Pac4jScalaTemplateHelper, SecurityComponents}
1115
import org.pac4j.play.store.{PlayCookieSessionStore, ShiroAesDataEncrypter}
12-
import org.pac4j.play.{CallbackController, LogoutController}
16+
import org.pac4j.play.{CallbackController, LogoutController, PlayWebContext}
1317
import org.pac4j.saml.client.SAML2Client
1418
import org.pac4j.saml.config.SAML2Configuration
1519
import play.api.{Configuration, Environment}
@@ -42,23 +46,31 @@ class SecurityModule(environment: Environment, configuration: Configuration) ext
4246
}
4347

4448
@Provides
45-
def provideConfig(): Config = {
49+
def provideConfig(sessionStore: SessionStore): Config = {
4650
val maybeConfiguredClientName = configuration.getOptional[String](ConfigKeyAuthClient).filter(_.nonEmpty)
47-
val config: Option[Config] = maybeConfiguredClientName.map {
48-
case "DirectBasicAuthClient" => createConfiguredDirectBasicAuthConfig(s"$ConfigKeyPrefixClientConfig.ConfiguredDirectBasicAuthClient")
49-
case "SAML2Client" => createSaml2Config(s"$ConfigKeyPrefixClientConfig.SAML2Client")
51+
val maybeClients = maybeConfiguredClientName.map {
52+
case "DirectBasicAuthClient" => createConfiguredDirectBasicAuthClient(s"$ConfigKeyPrefixClientConfig.ConfiguredDirectBasicAuthClient")
53+
case "SAML2Client" => createSaml2Client(s"$ConfigKeyPrefixClientConfig.SAML2Client")
5054
case other => throw new RuntimeException(s"Unsupported auth client config value: $other")
5155
}
52-
config.getOrElse(new Config())
56+
val config = new Config()
57+
for (clients <- maybeClients) {
58+
config.setClients(clients)
59+
}
60+
config.setSessionStoreFactory((_: FrameworkParameters) => sessionStore)
61+
config.setHttpActionAdapter(PlayHttpActionAdapter.INSTANCE)
62+
config.setWebContextFactory(PlayContextFactory.INSTANCE)
63+
config.setProfileManagerFactory(ProfileManagerFactory.DEFAULT)
64+
config
5365
}
5466

55-
private def createConfiguredDirectBasicAuthConfig(keyPrefix: String): Config = {
67+
private def createConfiguredDirectBasicAuthClient(keyPrefix: String): Clients = {
5668
val username = configuration.get[String](s"$keyPrefix.username")
5769
val password = configuration.get[String](s"$keyPrefix.password")
58-
new Config(new DirectBasicAuthClient(ConfiguredBasicAuthAuthenticator(username, password)))
70+
new Clients(new DirectBasicAuthClient(ConfiguredBasicAuthAuthenticator(username, password)))
5971
}
6072

61-
private def createSaml2Config(keyPrefix: String): Config = {
73+
private def createSaml2Client(keyPrefix: String): Clients = {
6274
val cfg = new SAML2Configuration(
6375
configuration.get[String](s"$keyPrefix.keystore"),
6476
configuration.get[String](s"$keyPrefix.keystorePassword"),
@@ -68,10 +80,8 @@ class SecurityModule(environment: Environment, configuration: Configuration) ext
6880
cfg.setServiceProviderEntityId(configuration.get[String](s"$keyPrefix.serviceProviderEntityId"))
6981
cfg.setServiceProviderMetadataPath(configuration.get[String](s"$keyPrefix.serviceProviderMetadataPath"))
7082
cfg.setMaximumAuthenticationLifetime(configuration.get[Long](s"$keyPrefix.maximumAuthenticationLifetime"))
71-
val allClients = Option(new SAML2Client(cfg)).toSeq :+ new AnonymousClient()
7283
// callback URL path as configured in `routes`
73-
val clients = new Clients(s"$baseUrl/callback", allClients:_*)
74-
new Config(clients)
84+
new Clients(s"$baseUrl/callback", new SAML2Client(cfg), new AnonymousClient)
7585
}
7686

7787
}

app/services/RulesTxtImportService.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class RulesTxtImportService @Inject() (querqyRulesTxtGenerator: QuerqyRulesTxtGe
220220
def tagsFingerprint(input: PreliminarySearchInput): String = {
221221
//Sorting is necessary because play.api.libs.json considers JsArray with same elements but different ordering as not equal. (behaviour tested up to play version 2.8.8)
222222
def sortArrays(json: JsValue): JsValue = json match {
223-
case JsObject(obj) => JsObject(obj.toMap.mapValues(sortArrays(_)).toList)
223+
case JsObject(obj) => JsObject(obj.toMap.view.mapValues(sortArrays).toList)
224224
case JsArray(arr) => JsArray(arr.map(sortArrays).sortBy(_.toString))
225225
case other => other
226226
}
@@ -257,7 +257,7 @@ class RulesTxtImportService @Inject() (querqyRulesTxtGenerator: QuerqyRulesTxtGe
257257
if(a_synonymRule.term == b_input.term) {
258258
println("^-- Found according synonym on " + b_input.term + " in = " + a_synonymRule)
259259
a_synonymRule.asInstanceOf[PreliminarySynonymRule].synonymType = SynonymRule.TYPE_UNDIRECTED
260-
break
260+
break()
261261
}
262262
}
263263
skip_i += j

app/services/RulesUsageService.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import org.apache.commons.csv.CSVFormat
55
import play.api.{Configuration, Logging}
66

77
import javax.inject.Inject
8-
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
8+
import scala.jdk.CollectionConverters.IterableHasAsScala
99

1010
case class RulesUsage(inputId: SearchInputId,
1111
keywords: String,

0 commit comments

Comments
 (0)