app/helpers/UserInfoCache.scala (118 lines of code) (raw):

package helpers import java.io.{File, FilenameFilter} import akka.actor.{ActorSystem, CoordinatedShutdown} import com.om.mxs.client.japi.UserInfo import javax.inject.{Inject, Singleton} import org.slf4j.LoggerFactory import play.api.Configuration import play.api.inject.ApplicationLifecycle import scala.concurrent.Await import scala.util.{Failure, Success} import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global @Singleton() class UserInfoCache @Inject() (config:Configuration,system:ActorSystem){ private val logger = LoggerFactory.getLogger(getClass) private val content:Map[String,UserInfoBuilder] = loadInFiles() final val byVaultId:Map[String, Seq[UserInfoBuilder]] = mapForVaultId private val vaultFileFilter = new FilenameFilter { override def accept(dir: File, name: String): Boolean ={ logger.warn(s"checking $name: ${name.endsWith(".vault")}") name.endsWith(".vault") } } /** * shuts down the app in the case of a fatal error. Does not return. * @param exitCode exit code to return */ private def terminate(exitCode:Int) = { Await.ready(system.terminate().andThen({ case _=>System.exit(exitCode) }), 2 hours) } /** * loads .vault information/login files from the specified directory and returns a map of * (ip address, vaultid)=>(userinfo). Userinfo entries with multiple addresses get multiple entries in the * returned map, one for each address each pointing to the same `UserInfo` instance. * @return Map of (String,UserInfo) */ protected def loadInFiles():Map[String,UserInfoBuilder] = { val vaultSettingsDir = config.get[String]("vaults.settings-path") logger.info(s"Loading configuration files from $vaultSettingsDir") val dir = new File(vaultSettingsDir) val files = dir.listFiles(vaultFileFilter) if(files==null){ logger.error(s"Could not find directory $vaultSettingsDir to load vault settings from") terminate(2) } val maybeBuilders = files.map(f=>{ if(f.isFile) { logger.info(s"Loading login info from ${f.getAbsolutePath}") Some(UserInfoBuilder.builderFromFile(f)) } else { None } }).collect({ case Some(entry)=>entry}) val failures = maybeBuilders.collect({case Failure(err)=>err}) if(failures.nonEmpty){ logger.error(s"${failures.length} out of ${maybeBuilders.length} vault files failed to load: ") failures.foreach(err=>logger.error("Vault file failed: ", err)) } val builders = maybeBuilders.collect({case Success(info)=>info}) if(builders.isEmpty){ logger.error(s"Could not load any vault information files from $vaultSettingsDir, exiting app") terminate(2) } //userInfos.foreach(info=>logger.debug(s"${info.toString}: ${info.getVault} on ${info.getAddresses.mkString(",")}")) val names = builders.map(_.vaultName.getOrElse("(no description)")) logger.info(s"Loaded ${builders.length} vault information files: $names") builders.map(b=>{ b.getUserInfo.map(userInfo=>{ userInfo.getAddresses.map(addr=>(s"$addr-${userInfo.getVault}", b)) }) }) .collect({case Success(elem)=>elem}) .flatten .toMap } /** * look up vault login information for the given appliance and vault ID * @param address IP address for appliance * @param vaultId vault ID as a string * @return either a UserInfo object in an Option or None if no such obect could be found */ def infoForAddress(address:String, vaultId:String) = { logger.debug(content.toString) logger.debug(s"looking up ${(address, vaultId)}") content.get(s"$address-$vaultId") } def allKnownVaults() = { content.values } /** * makes a shortcut map for lookups by vault ID */ protected def mapForVaultId = { def addToMap(entry:(String, UserInfoBuilder), remaining:Seq[(String, UserInfoBuilder)], existingEntries:Map[String, Seq[UserInfoBuilder]]): Map[String, Seq[UserInfoBuilder]] = { val updated = entry._2.vault match { case Some(newVaultId)=> existingEntries.get (newVaultId) match { case Some (existingValues) => val newValues = existingValues :+ entry._2 existingEntries + (newVaultId -> newValues) case None => val newValues = Seq (entry._2) existingEntries + (newVaultId-> newValues) } case None=> logger.warn(s"Vault informaiton ${entry._2} does not have a vault id??") existingEntries } if(remaining.isEmpty) { updated } else { addToMap(remaining.head, remaining.tail, updated) } } val contentSeq = content.toSeq contentSeq.headOption match { case Some(firstEntry)=> addToMap (firstEntry, contentSeq.tail, Map () ) case None=> logger.warn("No vaults were available to map by ID") Map[String, Seq[UserInfoBuilder]]() } } /** * look up the UserInfo for a given vault ID * @param vaultId vault ID to look up * @return */ def infoForVaultId(vaultId:String) = { byVaultId .get(vaultId) .flatMap(_.headOption) .map(_.getUserInfo) match { case Some(Success(userInfo))=>Some(userInfo) case Some(Failure(err))=> logger.error(s"Could not instatiate UserInfor for vault $vaultId: ${err.getMessage}", err) None case None=>None } } }