Calling a function periodically in Scala while another expensive function is computing - multithreading

I have a function that takes a long time to compute
def longFunc = {Thread.sleep(30000); true}
while this function is computing, I'd need to ping a server so it keeps waiting for the value of my function. But for the sake of the argument let's say I need to run the following function every 5 seconds while my longFunc is running
def shortFunc = println("pinging server! I am alive!")
To do this I have the following snippet and it works but I wonder if there is a better pattern for this scenario
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import java.util.{Timer, TimerTask}
import scala.concurrent.ExecutionContext.Implicits.global
def shortFunc = println("pinging server! I am alive!")
def longFunc = {Thread.sleep(30000); true}
val funcFuture = Future{longFunc}
val timer = new Timer()
def pinger = new TimerTask {
def run(): Unit = shortFunc
}
timer.schedule(pinger, 0L, 5000L) // ping the server every two minutes to say you are still working
val done = Await.result(funcFuture, 1 minutes)
pinger.cancel

I'm not actually sure if this is more elegant pattern or just for fun:
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
def waiter[T](futureToWait:Future[_], waitFunc: => T, timer: Duration) = Future {
while (!futureToWait.isCompleted) {
Try(Await.ready(futureToWait, timer))
waitFunc
}
}
def longFunc = {Thread.sleep(30000); true}
def shortFunc = println("pinging server! I am alive!")
val funcFuture = Future{longFunc}
waiter(funcFuture,shortFunc,5 second)
val done = Await.result(funcFuture, 1 minutes)
The same but shorter:
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
def longFunc = {Thread.sleep(30000); true}
def shortFunc = println("pinging server! I am alive!")
val funcFuture = Future{longFunc}
def ff:Future[_] = Future{
shortFunc
Try(Await.ready(funcFuture, 5 second)).getOrElse(ff)
}
ff
val done = Await.result(funcFuture, 1 minutes)

Related

Trying to mock two classes with Groovy Spock Mock: GroovyCastException

I try to mock two classes in a Jenkins Pipeline var-script, but however I order the definition of the Mock I receive a GroovyCastException, while the mock for the second class is initiated
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Mock for type 'ClassA'' with class 'com.pipeline.ClassA$$EnhancerByCGLIB$$9b005c28' to class 'com.pipeline.DBReader'
This is how my Spock-Test looks like:
import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification
import com.pipeline.ClassA
import com.pipeline.DBReader
import groovy.sql.Sql
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
import hudson.model.Result
class execScriptSpec extends JenkinsPipelineSpecification {
def execPipelineScript = null
def mockStage = [timeout: [time: 'mocktime']]
def mockScript = [...]
def setup() {
execPipelineScript = loadPipelineScriptForTest("vars/execScript.groovy")
}
def "Test Pipeline-Script"() {
setup:
GroovyMock(ClassA, global: true)
new ClassA(*_) >> Mock(ClassA) {
loadScope(_) >> null
loadJsonFromURL(_) >> null
}
GroovyMock(DBReader, global: true)
new DBReader(*_) >> Mock(DBReader) {
loadDBDriver(_) >> null
}
when:
execPipelineScript(mockScript, mockStage)
then:
true
And that is the code under test:
import com.pipeline.DBReader
import com.pipeline.ClassA
def call(script, stage) {
def pipelineConfig = script.pipelineConfig
def stageStatus = pipelineConfig.general.stageStatus
def projectName = script.env.project
// DB connection parameters
def dbName = script.deliveryConfig.system.dbName
def dbSchema = script.deliveryConfig.system.dbSchema
def dbServer = script.deliveryConfig.system.dbServer
def dbUser = script.deliveryConfig.system.dbUser
// DB Driver to use
def dbDriver = script.deliveryConfig.system.dbDriver
def dbPassword = script.env.password
// Create new DB-Client
DBReader dbClient = new DBReader(dbName, dbSchema, dbServer, dbUser, dbPassword, dbDriver)
dbClient.loadDBDriver()
def contextParameter = script.params['Context']
ClassA mapGavToVersionRange = new ClassA(projectName, contextParameter, script)
classAInstance.loadDeliveryScope( dbClient )
def url = 'https://some.valid.url'
classAInstance.loadJsonFromURL(url)
...
So don't get me wrong: The actual mocking of one or the other class works (if I put only one of them in my test), but as soon as I put both of them, the second one will not work. And I currently have no idea, why this happens.
Would be great, if anybody has an idea :-)
Thanks a lot!

GitBlit add a hook

I have a GitBlit instance on a windows server, and i want to set a hook on post receive callback to start a gitlab ci pipeline on another server.
I already have set a GitlabCi trigger who works well, but my hook doesn't. Here is build-gitlab-ci.groovy file :
import com.gitblit.GitBlit
import com.gitblit.Keys
import com.gitblit.models.RepositoryModel
import com.gitblit.models.UserModel
import com.gitblit.utils.JGitUtils
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.transport.ReceiveCommand
import org.eclipse.jgit.transport.ReceiveCommand.Result
import org.slf4j.Logger
logger.info("Gitlab-CI hook triggered by ${user.username} for ${repository.name}")
// POST :
def sendPostRequest(urlString, paramString) {
def url = new URL(urlString)
def conn = url.openConnection()
conn.setDoOutput(true)
def writer = new OutputStreamWriter(conn.getOutputStream())
writer.write(paramString)
writer.flush()
String line
def reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))
while ((line = reader.readLine()) != null) {
println line
}
writer.close()
reader.close()
}
sendPostRequest("https://xxxxx/api/v4/projects/1/trigger/pipeline", "token=xxxxxxxx&ref=master")
The project configuration :
Moreover, i don't know where logger.info write the log, so i don't know if my script was executed well. Thanks for help
I found my problem, it was a SSL self-certificate problem. I added this code to ignore it :
import com.gitblit.GitBlit
import com.gitblit.Keys
import com.gitblit.models.RepositoryModel
import com.gitblit.models.UserModel
import com.gitblit.utils.JGitUtils
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.transport.ReceiveCommand
import org.eclipse.jgit.transport.ReceiveCommand.Result
import org.slf4j.Logger
logger.info("Gitlab-CI hook triggered by ${user.username} for ${repository.name}")
def nullTrustManager = [
checkClientTrusted: { chain, authType -> },
checkServerTrusted: { chain, authType -> },
getAcceptedIssuers: { null }
]
def nullHostnameVerifier = [
verify: { hostname, session -> hostname.startsWith('yuml.me')}
]
javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL")
sc.init(null, [nullTrustManager as javax.net.ssl.X509TrustManager] as javax.net.ssl.X509TrustManager[], null)
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(nullHostnameVerifier as javax.net.ssl.HostnameVerifier)
def url = new URL("https://xxxx/api/v4/projects/{idProject}/trigger/pipeline")
def conn = url.openConnection()
conn.setDoOutput(true)
def writer = new OutputStreamWriter(conn.getOutputStream())
writer.write("token={token}&ref={branch}")
writer.flush()
String line
def reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))
while ((line = reader.readLine()) != null) {
println line
}
writer.close()
reader.close()
And I identified the error checking the logs in E:\gitblit-1.7.1\logs\gitblit-stdout.{date}.log.
NB : stdout file date can be very old. Gitblit doesn't create a file per day. Mine had a name expired 4 months ago.

Scala Chat Application, separate threads for local IO and socket IO

I'm writing a chat application in Scala, the problem is with the clients, the client reads from StdIn (which blocks) before sending the data to the echo server, so if multiple clients are connected then they don't receive data from the server until reading from StdIn has completed. I'm thinking that local IO, i.e reading from StdIn and reading/writing to the socket should be on separate threads but I can't think of a way to do this, below is the Client singleton code:
import java.net._
import scala.io._
import java.io._
import java.security._
object Client {
var msgAcc = ""
def main(args: Array[String]): Unit = {
val conn = new ClientConnection(InetAddress.getByName(args(0)), args(1).toInt)
val server = conn.connect()
println("Enter a username")
val user = new User(StdIn.readLine())
println("Welcome to the chat " + user.username)
sys.addShutdownHook(this.shutdown(conn, server))
while (true) {
val txMsg = StdIn.readLine()//should be on a separate thread?
if (txMsg != null) {
conn.sendMsg(server, user, txMsg)
val rxMsg = conn.getMsg(server)
val parser = new JsonParser(rxMsg)
val formattedMsg = parser.formatMsg(parser.toJson())
println(formattedMsg)
msgAcc = msgAcc + formattedMsg + "\n"
}
}
}
def shutdown(conn: ClientConnection, server: Socket): Unit = {
conn.close(server)
val fileWriter = new BufferedWriter(new FileWriter(new File("history.txt"), true))
fileWriter.write(msgAcc)
fileWriter.close()
println("Leaving chat, thanks for using")
}
}
below is the ClientConnection class used in conjunction with the Client singleton:
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.SocketFactory
import java.net.Socket
import java.net.InetAddress
import java.net.InetSocketAddress
import java.security._
import java.io._
import scala.io._
import java.util.GregorianCalendar
import java.util.Calendar
import java.util.Date
import com.sun.net.ssl.internal.ssl.Provider
import scala.util.parsing.json._
class ClientConnection(host: InetAddress, port: Int) {
def connect(): Socket = {
Security.addProvider(new Provider())
val sslFactory = SSLSocketFactory.getDefault()
val sslSocket = sslFactory.createSocket(host, port).asInstanceOf[SSLSocket]
sslSocket
}
def getMsg(server: Socket): String = new BufferedSource(server.getInputStream()).getLines().next()
def sendMsg(server: Socket, user: User, msg: String): Unit = {
val out = new PrintStream(server.getOutputStream())
out.println(this.toMinifiedJson(user.username, msg))
out.flush()
}
private def toMinifiedJson(user: String, msg: String): String = {
s"""{"time":"${this.getTime()}","username":"$user","msg":"$msg"}"""
}
private def getTime(): String = {
val cal = Calendar.getInstance()
cal.setTime(new Date())
"(" + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND) + ")"
}
def close(server: Socket): Unit = server.close()
}
You can add concurrency by using Scala Akka Actors. As of this writing the current Scala version is 2.11.8. See Actor documentation here:
http://docs.scala-lang.org/overviews/core/actors.html
This chat example is old but demonstrates a technique to handle in the neighborhood of a million simultaneous clients using Actors:
http://doc.akka.io/docs/akka/1.3.1/scala/tutorial-chat-server.html
Finally you can also Google the Twitter Finagle project which uses Scala and provides servers with concurrency. A lot of work to learn it I think...

Groovy Creates current datetime + x minutes

I try to get current date time + 2 minutes with groovy:
import groovy.time.*
import org.codehaus.groovy.runtime.TimeCategory
import groovy.time.TimeCategory
currentDate1 = new Date()
use( TimeCategory ) {
after2Mins = date + 2.minutes
}
log.info after2Mins
and I got this error:
groovy.lang.MissingPropertyException: No such property: date for
class: Script8 error at line: 7
Should be:
import groovy.time.*
import org.codehaus.groovy.runtime.TimeCategory
import groovy.time.TimeCategory
def currentDate1 = new Date()
use( TimeCategory ) {
after2Mins = currentDate1 + 2.minutes
}
println after2Mins
You're missing def before currentDate1 and used date instead of currentDate1 in TimeCategory closure.

Groovy exception

Please help. I can't understand whats wrong with my script.
import org.apache.log4j.Category
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.builder.JqlQueryBuilder
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
import java.util.Date
import java.util.Calendar
import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.issue.Issue
import java.util.List
import com.atlassian.jira.issue.IssueInputParameters
import com.atlassian.jira.user.util.UserManager
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.crowd.embedded.api.CrowdService
import com.atlassian.crowd.embedded.api.User
def Category log = Category.getInstance("com.onresolve.jira.groovy.PostFunction")
log.setLevel(org.apache.log4j.Level.DEBUG)
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def ctx = new JiraServiceContextImpl(user)
def searchRequestService = ComponentManager.getInstance().getSearchRequestService()
def searchProvider = ComponentManager.getInstance().getSearchProvider()
def sr = searchRequestService.getFilter(ctx, 17540)
def searchResult = searchProvider.search(sr?.getQuery(), user, PagerFilter.getUnlimitedFilter())
def issueManager = ComponentManager.getInstance().getIssueManager()
def issues = searchResult.getIssues().collect {issueManager.getIssueObject(it.id)}
for ( issue in issues ){
issueInputParameters issueToCreate = ComponentAccessor.getIssueService().newIssueInputParameters();
issueToCreate.setSummary("This is a test.");
issueToCreate.setDescription("Testing issue creation");
issueToCreate.setAssigneeId(user.getName());
issueService.createValidationResult validationResult = ComponentAccessor.getIssueService().validateCreate(user, issueToCreate);
if(!validationResult.isValid()){
for(String registeredErrorMessage:validationResult.getErrorCollection().getErrors().values())
{
printx "Failed"
}
}
else {
issueService.issueResult createdIssue = ComponentAccessor.getIssueService().create(user, validationResult);
}
}
return issues
I get the next excetpion :
groovy.lang.MissingMethodException: No signature of method:
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.issueInputParameters()
is applicable for argument types:
(com.atlassian.jira.issue.IssueInputParametersImpl) values:
[com.atlassian.jira.issue.IssueInputParametersImpl#6cde0354] at
Script87.run(Script87.groovy:34)
Thank you.
Shouldn't the issueInputParameters in this line be capitialized - IssueInputParameters:
issueInputParameters issueToCreate = ComponentAccessor.getIssueService().newIssueInputParameters();

Resources