Support

Lorem ipsum dolor sit amet:

24h / 365days

We offer support for our customers

Mon - Fri 8:00am - 5:00pm (GMT +1)

Get in touch

Cybersteel Inc.
376-293 City Road, Suite 600
San Francisco, CA 94102

Have any questions?
+44 1234 567 890

Drop us a line
info@yourdomain.com

About us

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec.

Chapter 4. Monitoring, Multithreading, Scheduling and other Advanced Techniques

4. Monitoring, Multithreading, Scheduling and other Advanced Techniques

In this chapter we are covering several advanced scripting techniques such as how to monitor script executions (and possibly stop the execution of a script), how to work with multiple threads, how to use the registry to temporarily or persistently store values and how to schedule scripts. Let us start by looking at how ReportServer executes scripts via the terminal.

4.1. Execution of Scripts

Whenever you execute a script via the terminal command exec the script execution is wrapped into a new thread. This allows to monitor the execution and if necessary stop the script. You can display a list of the currently running scripts via the terminal command ps.

The following script doesn't do much, but it needs almost one minute for it:

import java.lang.Thread

Thread.sleep(60000)

"awake again"

If you run this script, the Terminal window will remain inactive during the time of execution. You can either open a second terminal window (CTRL+ALT+T), or wait for the script to return and then run it using the silent flag:

exec -s sleep.groovy

The silent flag tells ReportServer to ignore the output of the script and run it in the background.

Now, by using the command "ps" you can view executions which are presently active.

reportserver$ ps
ID		Date		User			Command			Thread Interrupted
1		03.05.13 16:23		3	exec -s sleep.groovy		false

By entering the kill command, you can cancel scripts. Here, first an interrupt will be sent to the script which in our case leads to sending the thread an interrupt. In our case we can interrupt the script as follows.

reportserver$ kill 1

When you call "ps" again you will see that the script was interrupted indeed. The following script, however, will not be terminated that easily. It catches any exception and thus also exceptions thrown on interrupt.

import java.lang.Thread

def slept = false;
while(! slept){
	try{
		Thread.sleep(60000)
      		slept = true;
  	} catch(all) {
  	}
}
"done"

If you run this script and try to terminate it using "kill ID", you will see that the script is still listed by the "ps" command. Note that the flag "interrupted" is now set.

reportserver$ ps
ID		Date			User		Command			Thread Interrupted
7	03.05.13 16:23		3		    exec -s sleep.groovy		true

To hard-terminate the execution you can enter kill -f ID. This will terminate the thread by Thread.stop(). Please keep in mind that this may have undesirable side effects. You will find a description of the Thread.stop() method and related issues under http://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html.

Note that scripts which are executed not via the terminal but, for example, during a script report execution or via the scheduler will not run in an extra thread. They will, thus, also not be listed by the ps command.

4.1.1. Starting scripts in the server thread

In some situations it might be helpful to avoid starting a script in an own thread, but to start it in the server thread. This means, however, that this script cannot be monitored and interrupted by "ps/kill". To start a script without an own thread use the -n flag.

4.2. Storing Values Between Scripts

When a script terminates its scope is cleared and any variables are lost. Sometimes it is helpful to store values which can then be retrieved at a later point, for example, by a different script. ReportServer provides script developers with two mechanisms to easily store and retrieve values at a later point. The script registry is kept in memory by ReportServer at all times and, thus, allows for very fast access. However, when ReportServer is restarted the registry is cleared. To store values persistently, ReportServer offers the so called PropertiesService. Values stored via this service will be pushed to the database.

The registry is available via the GLOBALS object. Consider the following script registryCnt.groovy.

def registry = GLOBALS.services['registry']
def cnt = registry.containsKey("myCnt") ? registry.get("myCnt") : 0

cnt++;
registry.put("myCnt", cnt)

"The count is at: " + cnt

Executing this script multiple times will yield the following output

reportserver$ exec registryCnt.groovy
The count is at: 1
reportserver$ exec registryCnt.groovy
The count is at: 2
reportserver$ exec registryCnt.groovy
The count is at: 3

The registry implements java.util.Map<String,Object>. You can find detailed documentation on the various methods at http://docs.oracle.com/javase/7/docs/api/java/util/Map.html.

To store values persistently you need to use the PropertiesService (net.datenwerke.gf.service.properties.PropertiesService: https://reportserver.net/api/latest/javadoc/net/datenwerke/gf/service/properties/PropertiesService.html) which is a regular ReportServer service. Let us implement our same counter script as persistentCnt.groovy.

import net.datenwerke.gf.service.properties.PropertiesService

def registry = GLOBALS.getInstance(PropertiesService)
def cnt = registry.containsKey('myCnt') ? registry.get('myCnt') as int : 0

cnt++
registry.setProperty('myCnt', cnt as String)

"The count is at: $cnt"

Note the as int and as String. The PropertiesService stores all its properties in form of character strings. If we execute the script, we get the following output.

reportserver$ exec persistentCnt.groovy
The count is at: 1
reportserver$ exec persistentCnt.groovy
The count is at: 1
reportserver$ exec persistentCnt.groovy
The count is at: 1

This is not quite as expected. If we have a look at the corresponding database table RS_PROPERTY you will see that the table is still empty. Alternatively, you can use the following terminal command

desc Property "hql:from Property"

which lists all entities of type Property. The reason is simple. We did not run the exec command in commit mode and, thus, the property change was not persisted. If we run the command again, this time with the -c flag, we get the following output.

reportserver$ exec -c persistentCnt.groovy
The count is at: 1
reportserver$ exec -c persistentCnt.groovy
The count is at: 2
reportserver$ exec -c persistentCnt.groovy
The count is at: 3

We can now also inspect the property using the desc command from before, which now yields:

d		key		version		value
1		myCnt	2			3

Note you can list, add and modify all entries in the properties mapping with the properties terminal command. You can find more information on the Admin Guide: https://reportserver.net/en/guides/admin/main/.

4.3. Multithreading in Scripts

Sometimes it can be convenient to have work done in parallel. The common way to do this in Groovy or Java is to conjure up multiple threads that do the actual work and run them in parallel. Usually you would want to have the threads controlled by a thread pool. For this ReportServer provides an easy mechanism. The following script is a simple example of how to create thread pools using ReportServer's DwAsyncService (net.datenwerke.async.DwAsyncService):

import net.datenwerke.async.DwAsyncService
import net.datenwerke.async.configurations.*

def aService = GLOBALS.getInstance(DwAsyncService)
def poolName = 'myPool'

// get pool
def pool = aService.initPool(poolName, new SingleThreadPoolConfig())

def runA = {
  (1..10).each{
	  tout.println "A says: $it"
      Thread.sleep(200)
  }
} as Runnable

def runB = {
  (1..10).each{
	  tout.println "B says: $it"
      Thread.sleep(200)
  }
} as Runnable

def futureA = pool.submit(runA)
def futureB = pool.submit(runB)

// wait for tasks
futureA.get()
futureB.get()

aService.shutdownPool(poolName)

The initPool method takes a name and a configuration and creates a new pool for the given configuration. If a pool with the same name already exists, it will first shutdown the old pool. If we run this script we get the following result:

reportserver$ exec threadExample.groovy
A says: 1
A says: 2
A says: 3
A says: 4
A says: 5
A says: 6
A says: 7
A says: 8
A says: 9
A says: 10
B says: 1
B says: 2
B says: 3
B says: 4
B says: 5
B says: 6
B says: 7
B says: 8
B says: 9
B says: 10

There are two things to note. First, the printlns are not immediately pushed to the client. Currently ReportServer waits until the script terminates before it sends the result (which includes printlns) to the client. Secondly, the script executed the two loops not in parallel, but consecutively. This is, because of our choice of thread pool. We used a SingleThreadPoolConfig, which constructs a thread pool consisting of a single thread. Besides the SingleThreadPoolConfig you can use a FixedThreadPoolConfig which generates a pool with a fixed size. Thus, if we exchange the SingleThreadPoolConfig by

new FixedThreadPoolConfig(2)

and rerun our program, we get the following (expected) output:

reportserver$ exec threadExample.groovy
A says: 1
B says: 1
B says: 2
A says: 2
A says: 3
B says: 3
B says: 4
A says: 4
B says: 5
A says: 5
B says: 6
A says: 6
A says: 7
B says: 7
B says: 8
A says: 8
B says: 9
A says: 9
B says: 10
A says: 10

4.4. Scheduling Scripts

The ReportServer scheduler is not only used to schedule reports, but it can also be used to schedule the execution of scripts. To schedule a script use the terminal command scheduleScript. It comes with two commands:

list Lists all scheduled scripts
execute Schedules a new script

To schedule a script you can use natural language expressions. Examples are

scheduleScript execute myScript.groovy " " today at 15:23
scheduleScript execute myScript.groovy " " every day at 15:23
scheduleScript execute myScript.groovy " " at 23.08.2012 15:23
scheduleScript execute myScript.groovy " " every workday at 15:23 starting on 15.03.2011 for 10 times
scheduleScript execute myScript.groovy " " every hour at 23 for 10 times
scheduleScript execute myScript.groovy " " today between 16:00 and 23:00 every 10 minutes
scheduleScript execute myScript.groovy " " every week on monday and wednesday at 23:12 starting on 27.09.2011 until 28.11.2012
scheduleScript execute myScript.groovy " " every month on day 2 at 12:12 starting on 27.09.2011 11:25 for 2 times

The " " (quotation marks) after the script name are the scripts arguments. If we schedule our previous persistentCnt.groovy script we get the following output

scheduleScript execute persistentCnt.groovy " " every hour at 23 for 10 times
Script persistentCnt.groovy scheduled. First execution: 13.12.2013 19:23:00

Via the scheduleScript list command you get an overview of all currently scheduled scripts:

reportserver$ scheduleScript list
id	scriptId	name						next firetime
1		573			persistentCnt.groovy	13.12.2013 19:23:00

To get an overview of the next fire times you can use the scheduler command.

reportserver$ scheduler listFireTimes 1
13.12.2013 19:23:00
13.12.2013 20:23:00
13.12.2013 21:23:00
13.12.2013 22:23:00
13.12.2013 23:23:00
14.12.2013 19:23:00
14.12.2013 20:23:00
14.12.2013 21:23:00
14.12.2013 22:23:00
14.12.2013 23:23:00

Here 1 denotes the schedule entry's id. The scheduler command can also be used for scheduled reports.

To remove an entry you can use the "scheduler remove" command, for example to remove the above entry you need to run

reportserver$ scheduler remove 1

InfoFabrik GmbH

Wir wollen, dass alle Unternehmen, Institutionen und Organisationen, die Daten auswerten, selbständig und zeitnah genau die Informationen erhalten, die sie für ein erfolgreiches Arbeiten benötigen.

InfoFabrik GmbH
Klingholzstr. 7
65189 Wiesbaden
Germany

+49 (0) 611 580 66 25

Kontaktieren Sie uns

Bitte rechnen Sie 6 plus 1.
Copyright 2007 - 2024 InfoFabrik GmbH. All Rights Reserved.

Auf unserer Website setzen wir Cookies und andere Technologien ein. Während einige davon essenziell sind, dienen andere dazu, die Website zu verbessern und den Erfolg unserer Kampagnen zu bewerten. Bei der Nutzung unserer Website werden Daten verarbeitet, um Anzeigen und Inhalte zu messen. Weitere Informationen dazu finden Sie in unserer Datenschutzerklärung. Sie haben jederzeit die Möglichkeit, Ihre Einstellungen anzupassen oder zu widerrufen.

Datenschutzerklärung Impressum
You are using an outdated browser. The website may not be displayed correctly. Close