Sunday, December 19, 2021

Reducing image size with GIMP in batch mode

 First we define a function which contains all the needed procedures. and save the script in


~/.gimp-2.8/scripts

with .scm extension.

In 2.10, the script directory is ~/.config/GIMP/2.10/scripts


(define (batch-reduce-img-size pattern reduceby)
  (let* ((filelist (cadr (file-glob pattern 1))))
    (while (not (null? filelist))
           (let* ((filename (car filelist))
                  (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
                  (imageWidth (car (gimp-image-width image)))
		   (imageHeight (car (gimp-image-height image)))
		   (newWidth (* imageWidth reduceby))
		   (newHeight (* imageHeight reduceby))
                  (drawable (car (gimp-image-get-active-layer image))))
				   (gimp-image-scale image newWidth newHeight)
                                  (gimp-file-save 1 image drawable filename filename)
                                  
(gimp-image-delete image))
           (set! filelist (cdr filelist)))))


run the script as 


gimp -i -b '(batch-reduce-img-size "*.JPG" 0.5)' -b '(gimp-quit 0)


IMPORTANT: The images will get over written




Sunday, May 17, 2020

JavaScript Arrow Function, Spread & Rest Operators And Destructuring Syntax

With ECMAScript6 (ES6) JavaScript got new syntax. Followings are some of the new syntax.

Arrow Functions

New syntax for defining a function
 Traditional Function                                            ES6 Arrow Function                                              
function myFunction() {    
...
}
const myFunction = () => {
...
}
function myFunction(name) {
...
}
const myFunction = (name) => {
...
}

also without parenthesis if only one argument

const myFunction = name => {
...
}
function add(n, m) {
    return n+m;
}
const add = (n, m) => n+m;
if a single line return then the return
keyword and the  curly brackets can be
omitted



Spread Operator

Used to split up array elements OR object properties

const newArray = [...oldArray, 1, 2];

const newObject = {...oldObject, newProerty:newValue}

newArray is a one dimensional array contains all the elements in oldArray and additionally 1, 2

newObject has all the properties of oldObject in addition to the it has newProperty 
(if the oldObject already has newProperty it will be overwritten)

Spread operator is useful when copying an array or an object.

Rest Operator

Used to merge a list of function arguments into an array

function sortArgs(...args) {
       return args.sort();
}

Destructuring

Easily extract array elements or object properties and store them in variables

Array Destructuring
[a, b] =  ["Hello", "World"]
console.log(a) // Hello
console.log(b) // World


Object Destructuring
{name} = {"name": "Aang", "age" : 112}
console.log(name) // Aang
console.log(age) // undefined 




Sunday, May 3, 2020

How to draw a quarter circle in HTML Canvas

A circle can be drawn in the Canvas element using the arc function.
For example
<html>
    <body>
        <canvas id="drawArea" width="1024" height="768"></canvas>
    </body>

    <script>
        var canvas = document.getElementById("drawArea");
        var canvasContext = canvas.getContext("2d");

        canvasContext.fillStyle = "black";
        canvasContext.beginPath();
        canvasContext.arc(100, 100, 50, 0, Math.PI*2, true);
        canvasContext.fill();
    </script>
</html>



Lets explore the arc function's parameters

arc(x-coordinate,  y-coordinate, radius,  start-angle, end-angle, [counter-clockwise])

x-coordinate : position x from the top left corner of the canvas area
y-coordinate : position y from the top left corner of the canvas area
radius : radius of the arc
start-angle : the angle(in Radian) where the arc should begin. The canvas angle is always measured in the clockwise starting from 0.
                          i.e in a clock, 3 represent 0 
                          6 --> 𝝅/2 (90 degree )
                          9 --> 𝝅 (180 degree)
                          12 --> 3𝝅/2 (270 degree)
end-angle: the angle where the arc should end
counter-clockwise : optional parameter. To draw the arc from the start-angle end- angle in the counter clockwise, default value is false (i. by  default it is clock wise)


drawing a circle is straight forward, start angle is 0 and end angle is 2𝝅 or (360 degree). but when drawing a half circle we have two option whether to draw the upper half or lower half. 

Drawing an upper half circle
canvasContext.arc(100, 100, 50, 0, Math.PI, true)
here the begin angle is set to zero and end to 180 degree( we can imagine from 3  to 9 in a clock) these two can be connected either via 6 or 12, since the counter-clockwise parameter is set to true an arc is drawn from 3 to 9 via 12 so it is a upper half circle.

Drawing a lower half circle 
Simply changing the counter-clockwise parameter to false, the arc will be drawn from 3 to 9 via 6 (clockwise)


Let's see how to draw a quarter circle
If we try to draw a quarter circle similar to the above  approach by giving a 90 degrees, i.e 
canvasContext.arc(100, 100, 50, 0, Math.PI/2, false)
This will draw a segment of circle rather than quarter sector. This is because the arc function will connect the points specified by the angle begin and angle end parameter. 



That is from the start angle (3'O clock) to end angle (6'O clock)an arc is drawn (end to start because of the counter clockwise is true)

So to draw a quarter circle, we have to draw two lines, those are connecting the start angle and end angle position towards the center of the circle 
First the position is moved to the center and a line is drawn to the starting angle position then the arc is drawn and from the ending position another line is drawn towards the center. 

canvasContext.fillStyle="black";
canvasContext.beginPath();
canvasContext.moveTo(100,100); canvasContext.lineTo(150,100); canvasContext.arc(100, 100, 50, 0, Math.PI/2, false); canvasContext.lineTo(100, 100); canvasContext.fill();


Drawing without filling will be like this

canvasContext.storeStyle="black";
canvasContext.beginPath();
canvasContext.moveTo(100,100);
canvasContext.lineTo(150,100);
canvasContext.arc(100, 100, 50, 0, Math.PI/2, false);
canvasContext.lineTo(100, 100);
canvasContext.stroke();



Wednesday, April 8, 2020

Invoking a C function from Java using JNI

This is our C function which we need to invoke from Java

hello.c
#include <stdio.h>

void sayHello() {
        printf("Hello world, from C\n");
}

Let's write the Java class which contains the native method to calls the C function

HelloWorld.java
public class HelloWorld {
    
    private native void callCHello();

    static {
        System.loadLibrary("chello");
    }

    public static void main(String[] args) {
        new HelloWorld().callCHello();
    }
    
}
Here we are loading the chello C library (we will be creating in the later part) which contains the implementation for the native method callCHello()

Compile the HelloWorld.java
javac HelloWorld.java

Create the native method header file
javah HelloWorld

This will generate HelloWorld.h file

Let's write the C code including the HelloWorld.h,  the function is named with the following convention.
Java_<Java class name>_<java native method name>

so here it will be:
Java_HelloWorld_callCHello

chello.c will call our original C function (sayHello()) which we needed to call from Java
chello.c
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"#include "hello.c"
JNIEXPORT void JNICALL Java_HelloWorld_callCHello(JNIEnv *env, jobject obj)  {
    sayHello();
    return;
}

Les's create the C library file to which we loaded in the Java code earlier with the following command

gcc chello.c -I $JAVA_HOME/include/ -I $JAVA_HOME/include/linux/ -shared -o libhello.so -fPIC

Please note that the so file name is libhello i.e with the "lib" prefix to the name we loaded in Java. Also I am including the linux related header files because I am working on linux. If it is windows it has to be changed as win32

let's run the HelloWorld java. We have to set the java.library.path to where the so file is located (in this case the current directory)
java HelloWorld

The output  will be "Hello World, from C"

Sunday, January 28, 2018

WSO2 ESB - Sample Class Mediator

In WSO2 ESB / EI when we want to write our own logic of mediation, class mediators are very helpful.

A class mediator can be written by extending AbstractMediator

Following is a sample class mediator which simply logs a sting message (Doesn't actually do any mediation)

This source can be used as a template to start a class mediator quickly

public class SampleClassMediator extends AbstractMediator {

    private static final Log log = LogFactory.getLog(SampleClassMediator.class);

    public boolean mediate(MessageContext messageContext) {
        log.info("This is a sample class mediator");
        return true;
    }
}

Full project can be found in  https://github.com/bsenduran/sample-class-mediator/tree/v1.0



Monday, January 8, 2018

Invoking a SOAP back-end with apache ab

Following is a sample request to invoke a soap backend using apache ab


ab -n 10 -c 5 -p req.xml -T "text/xml" -H "soapAction:getSimpleQuote" http://localhost:9000/services/SimpleStockQuoteService

Here
-n : number of requests
-c : number of concurrent requests
-p : post request payload file
-T : content type
-H: custom header

req.xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:getSimpleQuote xmlns:ser="http://services.samples">
         <ser:symbol>ABC</ser:symbol>
      </ser:getSimpleQuote>
   </soapenv:Body>
</soapenv:Envelope>

Friday, November 10, 2017

Setting up WSO2 EI Cluster

This post explains on setting up a WSO2 Enterprise Integrator (WSO2 EI)'s 2 node non worker manager cluster in 5 simple steps.

1. Setting up database
2. Configuring datasource, reqistry and user-mgt
3. Enabling clusting in axis2.xml
4. Configuring Node 2
5. Configure a common deployment directory

I will be using WSO2 EI 6.1.1 and MySQL 5.7 as the database.

1. Setting up database

Creating the database and tables

create database WSO2_EI_611_REG_DB;
use WSO2_EI_611_REG_DB;
source /path-to-<EI-HOME>/dbscripts/mysql5.7.sql;

create database WSO2_EI_611_UM_DB;
use WSO2_EI_611_UM_DB;
source /path-to-<EI-HOME>/dbscripts/mysql5.7.sql;

Adding the MySQL driver

Add the MySQL driver jar  to <EI-HOME>/lib directory

2. Configuring datasource, reqistry and user-mgt

master-datasources.xml

Update the <EI-HOME>/conf/datasources/master-datasources.xml with following data source configuration (You may need to modify the username / password / URL
    <datasource>
        <name>WSO2_SHARED_REG_DB</name>
        <description>The datasource is used for shared config and governance registry</description>
        <jndiConfig>
            <name>jdbc/WSO2SharedDB</name>
        </jndiConfig>
        <definition type="RDBMS">
            <configuration>
                <url>jdbc:mysql://localhost:3306/WSO2_EI_611_REG_DB</url>
                <username>user</username>
                <password>password</password>
                <driverClassName>com.mysql.jdbc.Driver</driverClassName>
                <maxActive>50</maxActive>
                <maxWait>60000</maxWait>
                <testOnBorrow>true</testOnBorrow>
                <validationQuery>SELECT 1</validationQuery>
                <validationInterval>30000</validationInterval>
            </configuration>
        </definition>
    </datasource>

    <datasource>
        <name>WSO2_UM_DB</name>
        <description>The datasource is used for user management</description>
        <jndiConfig>
            <name>jdbc/WSO2UmDB</name>
        </jndiConfig>
        <definition type="RDBMS">
            <configuration>
                <url>jdbc:mysql://localhost:3306/WSO2_EI_611_UM_DB</url>
                <username>user</username>
                <password>password</password>
                <driverClassName>com.mysql.jdbc.Driver</driverClassName>
                <maxActive>50</maxActive>
                <maxWait>60000</maxWait>
                <testOnBorrow>true</testOnBorrow>
                <validationQuery>SELECT 1</validationQuery>
                <validationInterval>30000</validationInterval>
            </configuration>
        </definition>
    </datasource>

registry.xml

Update the <EI-HOME>/conf/registry.xml with following
(Please keep the existing dbConfig as it is and add the following as new)
<dbConfig name="sharedregistry">
    <dataSource>jdbc/WSO2SharedDB</dataSource>
</dbConfig>
<remoteInstance url="https://localhost:9443/registry">
    <id>instanceid</id>
    <dbConfig>sharedregistry</dbConfig>
    <readOnly>false</readOnly>
    <enableCache>true</enableCache>
    <registryRoot>/</registryRoot>
</remoteInstance>
<mount path="/_system/config" overwrite="true">
    <instanceId>instanceid</instanceId>
    <targetPath>/_system/esbnodes</targetPath>
</mount>
<mount path="/_system/governance" overwrite="true">
    <instanceId>instanceid</instanceId>
    <targetPath>/_system/governance</targetPath>
</mount>

user-mgt.xml

Update the <EI-HOME>/conf/user-mgt.xml with following. Remove the existing dataSource property and add the following property to UserManger/Realm/Configuration
<Property name="dataSource">jdbc/WSO2UmDB</Property>

3. Enabling clusting in axis2.xml

By editing the following in <EI-HOME>/conf/axis2/axis2.xml enable the nonWokerManger cluster
<clustering class="org.wso2.carbon.core.clustering.hazelcast.HazelcastClusteringAgent" enable="true">
<parameter name="clusteringPattern">nonWorkerManager</parameter>
<parameter name="localMemberHost">ei.wso2.com</parameter>
ei.wso2.com has to be added to /etc/hosts. Giving the local IP address (127.0.0.1) as the localMemberHost is not recommended.
Updating the member list
<members>
    <member>
        <hostName>ei.wso2.com</hostName>
        <port>4200</port>
    </member>
</members> 
In this setup both the nodes are in the same machine, that's why the member's hostname and the localMemberHost are same. If you are setting up the cluster in a different machine please add the correct host name.

4. Configuring Node 2

Take a copy of the configured EI611 pack (can be name the copy as node2), And configure the following

Configure member list

Update the local member port (4200 is what we configured in the previous node(Node 1)'s, known member list)
<parameter name="localMemberPort">4200</parameter>
Update the member list as follows
(4100 is previous node(Node 1)'s, local port number)
<members>
    <member>
        <hostName>ei.wso2.com</hostName>
        <port>4100</port>
    </member>
</members>

Configure the port offset

If the cluster is in a same machine, then the port offset must be configured inorder to avoid the port binding conflicts
Edit the <EI-HOME>/conf/carbon.xml as follows (eg: 2 as offset) <Offset>2</Offset>

5. Configure a common deployment directory

Since we do not recommend the deployment synchronizer. Here we are using a file share to share the artifacts between the 2 nodes. For the other available options kindly have a look at https://docs.wso2.com/display/EI611/Clustering+the+ESB+Profile, Deploying artifacts across the nodes First remove the existing deployment directory from node2 and create a soft link to the node1's deployment directory

node2:repository$> rm -r deployment/
node2:repository$> ln -s <path-to-node1>/reposository/deployment deployment