Cool usage of TimeCategory in Groovy

Groovy, the programming language based on JVM implements a feature called Categories. It is originally borrowed from Objective-C . Simple explanation for this feature can be the ability to implement new methods in existing classes without modifying their original code which in some way is injecting new methods through a Category class. For more information official documentation can be found here .

Rather interesting for me was playing with the TimeCategory class for writing a short and easy script for fixing some datetime columns in database. This class offers a convenient way of Date and Time manipulation.

General syntax for categories is the following:

use ( Class ) {
// Some code
}

Concrete usage of TimeCategory:

use ( TimeCategory ) {
// application on numbers:
println 1.minute.from.now
println 10.hours.ago
// application on dates
def someDate = new Date()
println someDate - 3.months
}

Seems weird? From when Integer has months, minutes, hours etc. methods ? Well it still doesn’t have any of that, however those methods are dynamically added with the TimeCategory use.

If you are interested how is this possible I suggest you to go through TimeCategory API and source code if possible. Also this forum post can be useful for deeper understanding of the groovy magic.

And last but not least, an example groovy script for your pleasure.


@GrabConfig(systemClassLoader=true)
@Grab(group='mysql', module='mysql-connector-java', version='5.1.27')

import groovy.time.TimeCategory
import java.sql.Timestamp

sql = groovy.sql.Sql.newInstance(
"jdbc:mysql://hostname:3306/DB_name?autoReconnect=true",
"user",
"password",
"com.mysql.jdbc.Driver")

def rows= [:]

// Select Data
sql.eachRow("select * from Table_Name"){
def impDates = new ImportedDates() // This is some custom Class found in the same package/directory if script
impDates.dateColumn = it.dateColumn

if(impDates.dateColumn!=null){
use(TimeCategory){
impDates.dateColumn = impDates.dateColumn - 1.day // Shift dateColumn for one day backwards in time
}
}

rows.put(it.UID,impDates) // Put private key and ImportedDate object in Map

}

// Update Data
rows.each {row->
ImportedDates id = row.value
// Check if value is different from null, if it is convert it to Timestamp(we use datetime column in db) and execute update query
dateColumn  = null
if(id.dateColumn) dateColumn = new Timestamp(id.dateColumn.getTime())

// Actual update query
sql.executeUpdate('update Table_Name set dateColumn = ? ' +
'where UID like ?',
[dateColumn, row.key.toString()])

}

Cheers.

Remove old backups with Bash Shell

Hello folks, I present you a bash script I wrote recently. It’s purpose  is deleting ‘old’ directories  marked by time period expressed in days. The script is simple and at its bases uses the command find to locate directories that we want to remove.

The general thinking on which this script is based are directories with backups categorized by some logical distinction and retention period for the second level of directories that are sorted by some other means like let’s say date. The script run by cron job searches the second level of directories and deletes the one older than some number of days n.

Example structure:

/mnt/backup/apps/2014-06-20/*

/mnt/backup/dbs/2014-06-1/*

/mnt/backup/logs/2014-06-1/*

 

If we want to exclude some of the directories in /mnt/backup , for example /mnt/backup/logs we can do that by putting the path into the variable EXCLUDED_DIRS. Note that only the directories from depth 1 can be excluded with this implementation (/mnt/backup/<> = OK , /mnt/backup/logs/<> != OK ).

EXCLUDED_DIRS=”/mnt/backup/logs /some/other/etc”

 

USAGE of the script:

<script_name> {arg1:/path/to/folder} {arg2:n days}  

or

./deleteOldDirs.sh         /mnt/backup           60

 

Note on find. For checking the timestamp of the directory I use ctime like option from find.  When this argument is used ,  find checks for the iNode pointing to the file/folder and collects information (like permissions, owner, etc) from which can be determinated when it was modified. In other hand if we want to check modification of the data in the backup itself we can use the argument mtime instead.

 

The script , beware of removing yourself.

May the force be with you !

#!/bin/bash

#
# GNRP LICENSE: Script licensed by GOD
#
# @author: Igor Ivanovski <igor at genrepsoft.net>
#
# July, 2014
#

#
# *** VARIABLES
#

PATH=$1 # First argument should be PATH of the backup folder
DAYS=$2 # Secound argument should be the max number of days we want to keep backups

GREP="/bin/grep"
MOUNT="/bin/mount"
FIND="/usr/bin/find"
RM="/bin/rm"

PATH_STATUS=0
MOUNT_STATUS=0

EXCLUDED_DIRS="/home/igor/Documents /home/igor/Programs"

#
# *** CHECKERS
#

if [ $# -eq 0 ]; then
    echo "No arguments provided"
    exit 1
fi

if [ -z $PATH ] || [ -z $DAYS ]; then
    echo "Not all arguments provided"
    exit 1
fi

#
# *** FUNCTIONS
#

function check_if_path_exists {

	[ ! -d $1 ] && echo "Path is invalild" || PATH_STATUS=1
}

function check_mount {
	 if [ -n "$($GREP $1 /etc/fstab)" ]; then
                echo "Mounting point exists..."
		if [ ! -n "$($MOUNT -l | $GREP $1)" ]; then
		    echo "...but not mounted."
		    echo "Mounting now."
                    $MOUNT $1
                    [ $? -eq 0 ] && { echo "Mounting was successfull!"; MOUNT_STATUS=1; } || echo "Can't mount";
		fi
         else
                echo "Mounting point doesn't exist"
         fi
}

# Excludes only if parent directory fits
function check_if_parent_dir_excluded { 
            for dir in $EXCLUDED_DIRS; do
		size=${#dir}
	        check=${1:0:size}
		#echo "$dir : ${#dir} == $check : ${#check}"
		if [ $dir ==  $check ]; then 
			echo "1"
		fi
	    done
}

function find_dirs {
	$FIND $PATH -mindepth 2 -maxdepth 2 -type d -ctime +$DAYS -print0
}

function print_dirs {
	while read -r n; do
                 result=$(check_if_parent_dir_excluded $n)
		 [ -z $result ] && printf '%q\n' "$n" || echo "Found EXCLUDE_DIR: $n" 
	done < <($FIND $PATH -mindepth 2 -maxdepth 2 -type d -ctime +$DAYS -print) # Use NL delimiter
}

function del_dirs {
        while read -r -d '' n; do
                 printf '%q\n' "$n"
                 result=$(check_if_parent_dir_excluded $n)
                 if [ -z $result ]; then
			 echo "Directory will be deleted now..."
			 $RM -rf $n
			 [ $? -eq 0 ] && echo "Gone!" || echo "Some error occured!"
		 else	
	 		echo "Found EXCLUDE_DIR: $n continue..." 
		 fi
        done < <($FIND $PATH -mindepth 2 -maxdepth 2 -type d -ctime +$DAYS -print0) # Use NUL delimiter
}

#
# *** MAIN
#

check_if_path_exists $PATH
[ $PATH_STATUS -eq 0 ] && check_mount $PATH
[ $MOUNT_STATUS -eq 1 ] && check_if_path_exists $PATH
[ $PATH_STATUS -eq 1 ] &&  del_dirs || { echo "Bye."; exit 0; }

#
# *** END
#

Deploying Java Applications: Apache Tomcat + IIS 7.5

I’m on vacation and I’m writing my first technical post. That’s unfortunate 🙂

So this will be for all you lads and gentleman (and ladies) struggling in the world of Java and its ecosystem. Have you ever deployed Java apps on IIS? Well If I am the man to be consulted I’ll never choose that solution. But of course here comes the project managers, the Active Directory stuff and so on, when you have such climate you must bend yourself and do your best.

I have the following scenario: couple of different Java apps deployed on Tomcats. Well that doesn’t sound so bad there are easy service wizard installs of Apache Tomcat on Windows, easier deployment of the same so I will not really bother with such a trivial things, I hope you can do that by yourself.

Where is the problem? Well there a plural applications and one open port for the outside world. That’s right, the standard HTTP 80 port.

Simple thing we should do is to port forward and url rewrite the addressing applications.

Before finding this solution I was aware of one way on connecting Java container with IIS, through ISAPI and JK. But that requires changing Windows registry, defining couple of configuration files, using isapi_redirect , in other words messy and hardly-working solution. There are couple of guides on the net, just google it if you want to try them. The next solution is clean, simple and productive.

The IIS I’m working on is version 7.5. That is nice because the fellow Microsoft guys introduce a new solution to us, ARR. The use of this module here is to be set up to work as proxy server and that is exactly what we need in this situation.

After installing this extension on the server machine (I’m working on Windows Server 2008) next thing is to enable proxy server. Follow this steps:

  1. Open the IIS Manager and in the IIS Group there should be Application Request Routing Cache module showing up. Select it.
  2. So here we are. In the Action menu choose – Server Proxy Settings .
  3. For the following scenario this set up will work : Enable proxy = true, checked keep-alive, time-out by will, reverse rewrite host headers in response checked. Apply the changes and proceed.

Half work done. Next thing that we need is the URL rewrite feature. Install it. (Here I don’t mention  anything about the features and roles that need to be installed to make IIS working. I think that is just not-needed information here. You have the wizards in Computer Management, use them).

Well I hope you got URL rewrite working. That’s where we will define the rewrites.

Lets imagine. Two  Tomcat installation with just one application per server.

Installation folders: D:\Tomcat1 , D:\Tomcat2 . We need only the HTTP connectors, so in $tomcat_home\conf\server.xml you can comment out the AJP connectors.

Don’t forget while installing the Tomcats, to configure them on different ports. Example :

  <Connector port=”8080″ protocol=”HTTP/1.1″ proxyName=”www.example.com” proxyPort=”80″
               connectionTimeout=”20000″
               redirectPort=”8443″ />

  <Connector port=”8081″ protocol=”HTTP/1.1″ proxyName=”www.example.com” proxyPort=”80″
               connectionTimeout=”20000″
               redirectPort=”8443″ />

You ask yourself why I have put proxyName and proxyPort here. Well they are not required but since we want this application to work for outside world as I mentioned , you don’t want Java calls like getServerName() to return localhost  and local port. We want ajax and applets to work. This way we ensure the right host name and port are returned.

Installation ports: Tomcat1 on port 8080, Tomcat2 on port 8081. Installed apps: on Tomcat1 app1, on Tomcat2 app2.

So we can open the deployed applications on following urls : http://localhost:8080/app1 and http://localhost:8081/app2 .

It’s time to finalize the installations. We want the apps to be accessible on the following urls http://www.example.com/app1 and http://www.example.com/app2 .

This is where URL rewrite comes in play and that’s where we do the things explicitly. You can define URL rewrite rules in the main configuration but I prefer creating new site bound to port 80 and defining it there. Add new site with name exampe.com and for physical path put D:\ or whatever you want for your local situation, this is just example. That’s the place where web.config file will be created and everything that we do in the IIS manager will be mapped in configuration file, which is an xml configuration file the IIS use.

  1. In the new site, IIS group, open URL rewrite.
  2. Add new rule from the actions menu. Choose blank rule.
  3. Configurations : Name – Tomcat1 rule,  requested url -> matches the pattern , simple pattern: (app1.+) , ignore cases true,  action type – rewrite , rewrite url: http://localhost:8080/{R:0}
  4. Apply , make another rule for the other tomcat and  just change the name, pattern and the port.
  5. Restart IIS and you got it.

Just to get things straight. URL rewrite allow us to filter by some rule patterns, conditions and replace server variables. We just defined simple rewrite rule. That’s why we use {R:0} . It simple variable for the first rule pattern. There can be more of course , the format is {R:N} , where N is from 0 to 9.

This was very simple. I hope this will give you some clues how can you manipulate this modules to do more complex stuff. And you are not limited to use only Java servers of course. Any socket listening on any port can work.

I wish you happy days, for the end a reward from me

It is first, so let it be groggy

Welcome to you , welcome to me, what this blog will be?

Well it won’t be just groggy I hope. Primarily it will be written after work is done, when I’m tired, little restless and maybe a little more creative.

I will bother with technician stuff, sysadmin daily ad-hoc solutions, programming , things that interest me, maybe I will write something about you. Personal opinions will be included also.

So cheers.