• Enter Slide 1 Title Here

    This is slide 1 description. Go to Edit HTML of your blogger blog. Find these sentences. You can replace these sentences with your own words.

  • Enter Slide 2 Title Here

    This is slide 2 description. Go to Edit HTML of your blogger blog. Find these sentences. You can replace these sentences with your own words.

  • Enter Slide 3 Title Here

    This is slide 3 description. Go to Edit HTML of your blogger blog. Find these sentences. You can replace these sentences with your own words.

Wednesday, September 28, 2011

Facebook notification email settings can be customized

Facebook notification emails are received for most of the follow up events like comments on photos, tags in photos, wall comments and many more. For a regular Facebook user, it might be difficult to manage the amount of emails received; even some are a bit of less useful. Have you already started to hate these notifications? One such annoying instance is receiving notifications like "Xman commented on your friend Y-friend's image, video or status". This is useless (at least for us) if Xman is not a known person.


By identifying this fact Facebook has come up with a new feature to manage the notifications. According to Facebook team
"We're trying out a new feature to reduce the amount of email you receive from Facebook. Starting today, we are turning off most individual email notifications and instead, we'll send you a summary only if there are popular stories you may have missed."


All notification events including Photos, Videos, Links even Wall comments can be customized individually. This is not going to be too handy initially, but will be really useful later when this feature is familiar to users. To start with, there is a default-notification-list that Facebook team has created for us. This can be enabled with a check box selection at the top of notifications page.

With this Facebook users can enable/disable notifications according to individual interests while making sure the inbox contains only the important emails.

Related Article

Tuesday, September 27, 2011

Encrypted vs Hashed Passwords - Which is better?

Topics like password strength, protection, encryption are almost everywhere these days. Password maintenance related subjects like password managers, recovery tools and crackers are also gaining attention. A user name and a password is a must in almost all software applications like email applications, web sites, mobile and desktop applications; mainly to provide user specific information or functionality.
Security of the password is so much important not because the data stored behind a user account is so much valuable to the owner, but might be to some other bad guy who is looking for personal information. To overcome the pain of memorizing multiple passwords, users might use one single much stronger password across multiple applications which is a bad practice considering the security aspect.

As anyone would guess, most of the application specific databases are having a table named user or users including two columns named user name and password; and interestingly the password in plain text! If your application database is storing passwords in plain text format, there is no hope for security in your application. People would argue that the application is well protected, HTTPS or TLS is in action; so the users are safe. What if someone get access to your database? That is the end of the security of all your users; and if those users were reusing their most secret and strongest password across multiple web sites, can you imagine what will be the situation? If your application stores password in plain text, it must be time to think at least about encrypted passwords.

Is encryption good?

However the intention of this article is not to discuss about plain text passwords, but about encrypted passwords stored in databases. Plain text passwords can be encrypted using symmetric encryption algorithms like DES, AES or with any other algorithms and be stored inside the database. At the authentication (confirming the identity with user name and password), application will decrypt the encrypted password stored in database and compare with user provided password for equality. In this type of an password handling approach, even if someone get access to database tables the passwords will not be simply reusable. However there is a bad news in this approach as well. If somehow someone obtain the cryptographic algorithm along with the key used by your application, he/she will be able to view all the user passwords stored in your database by decryption. "This is the best option I got", a software developer may scream, but is there a better way?

Yes there is, may be you have missed the point here. Did you notice that there is no requirement to decrypt and compare? If there is one-way-only conversion approach where the password can be converted into some converted-word, but the reverse operation (generation of password from converted-word) is impossible. Now even if someone gets access to the database, there is no way that the passwords be reproduced or extracted using the converted-words. In this approach, there will be hardly anyway that some could know your users' top secret passwords; and this will protect the users using the same password across multiple applications. What algorithms can be used for this approach?

Cryptographic hash function

Cryptographic hash functions can be used to achieve one-way-only conversion requirement. As there is no support to go back from converted text to original text, there is no risk involved in the safety of the valuable and secret password. There are many well known and publicly available algorithms for this task, and most popular ones are MD5 and SHA-1. There are freely available tools implementing these algorithms; so incorporating hashed approach into applications is not a pain. Even though these algorithms provide a far better security, both MD5 and SHA-1 are proven to be weak and vulnerable. It is recommended to go with SHA-2 considering the preciousness of the password. However at the moment, there is an open competition to created a replacement algorithm for SHA-2 which is called SHA-3 and this will be available in 2012.

In summary; when an application level security is discussed/designed make sure that passwords are never kept in plain text, but at least in encrypted form; but try to reach the hash function based password handling as much as possible.

Related: Data Encryption Decryption using AES Algorithm, Key and Salt with Java Cryptography Extension

Wednesday, October 27, 2010

[Tomcat] How to change default JSESSIONID cookie/parameter identifier

Changing default JSESSIONID name of cookie and/or parameter is the objective. Deployed J2EE web applications use browser cookie or parameter based session management technique. By default session cookie name is defined as “JSESSIONID” and session id parameter as “jsessionid” in Apache Tomcat servers. These names can be renamed by specifying required values for correct system properties.

Requirements

This system properties based feature is only available in releases newer than Tomcat 5.5.28 and Tomcat 6.0.20.

System properties

Related system properties are;
  • org.apache.catalina.SESSION_COOKIE_NAME (for cookie name)
  • org.apache.catalina.SESSION_PARAMETER_NAME (for parameter name)

Passing system properties

System property can be passed using standard methodology; use “-D” parameter of Java command similar to following.
java -D<key>=<value>

Modify catalina.sh to pass system properties

Following extract is from a modified bin/catalina.sh to pass these system properties; similarly bin/catalina.bat can be modified for Windows based installations.

#!/bin/sh
# // .....
# ------------------------------------------
# Start/Stop Script for the CATALINA Server
# // .....
# ------------------------------------------
JAVA_OPTS="$JAVA_OPTS
-Dorg.apache.catalina.SESSION_COOKIE_NAME=MYJSESSIONID
-Dorg.apache.catalina.SESSION_PARAMETER_NAME=myjsessionid"
# // .....
Note:
The decision on whether cookie based or parameter based session management is used depend on client browser settings.

Related Articles

Wednesday, October 20, 2010

Thunderbird 3.1.5 Release with Critical security fixes

Mozilla Thunderbird 3.1.5 version released today. This free and open source email client is one of the better standalone ones available. Since development is done by a community of developers with the help of security experts; issue identification and resolution process is so efficient. Thunderbird 3.1.5 can be downloaded from here for any platform (Windows, Mac OS and Linux) in around fifty languages.

This release has fixes for five critical security vulnerabilities as shown below.
  • Unsafe library loading Vulnerabilities
  • Dangling pointer vulnerability in LookupGetterOrSetter
  • Use-after-free error in nsBarProp
  • Buffer overflow and memory corruption using document.write
  • Miscellaneous memory safety hazards (rv:1.9.2.11/ 1.9.1.14)

An upgrade to latest 3.1.5 version is advised to avoid any security issues. All credit goes to those great people who devote their time and effort on getting another release out.

[Linux] Find filenames list by content search and file format

Generally computer users can memorize the locations of different files that they read or store. As the number of files increases, they can use descriptive filenames making it easy to guess the content by looking at the file name. However when the number of files grows higher & higher in number, it becomes a pain to remember locations or filenames. Also when a computer is used by some other person than the owner, it becomes even harder to locate some files.

Search by partial name

find -name "<filename pattern>"

Above command is useful only if you can remember the filenames at least some parts of the file name. If you are a software developer, you know how many times you would want to search files based on the content.

Search by content

find <path> -name "<file name pattern>" -exec grep -l "<text to search>" {} \;

Above command can be used to find the files based on content search. For example to find a file with a value say "db.user" in a properties file inside "/opt/work/project" folder (including sub folders) following command can be used.

find /opt/work/project -name "*.properties" -exec grep -l "db.user" {} \;

IgnoreCase search can be done by adding "-i" attribute to grep command; check following command.

find /opt/work/project -name "*.properties" -exec grep -il "db.user" {} \;

When would content search be used?

  • You remember some parts of the content but not the file name
  • Find which property file contains an specific property used in your program
  • Locate the Style sheet (css file) containing a specific style class or property
  • Find Java class files that references some methods like "indexOf"
  • and so on...

Tuesday, October 19, 2010

[Java] The enum constant reference cannot be qualified in a case label of switch statement

The enum constant reference cannot be qualified in a case label is an error message you may encounter while using Java enum values against a switch statement. I assume you are aware Java allows enums to be used in switch statements other than convertible int values.

In Short

When a Java switch statement uses an enum parameter; qualified names of the enum values should not be used in case labels, but only the unqualified names; then switch statement will consider all the labels are referring to the enum type that is used as the parameter.

Why only unqualified values?

If qualified references were allowed for case labels; there would be no means to restrict the enum type used in the labels to be same as the parameter type of switch statement.

In Detail

An enum named “Status” which represent two statuses of an entity will be used in sample code.

package org.kamal.learnenum.user;

public enum Status {
REGISTERED,
TERMINATED
}

Following sample code uses “Status” enum to create a a greeting message (based on a complex logic). Look at the way the case label is written in switch statement; consider the differences in lines marked as 1 & 2.

package org.kamal.learnenum.test;

import org.kamal.learnenum.user.Status;

public class TestStatus {
public static String getMessage(Status status) {
String message;
switch (status) {
// case Status.REGISTERED: // line 1
case REGISTERED: // line 2
message = "Welcome";
break;
default:
message = "Good bye";
break;
}
return message;
}

public static void main(String[] args) {
String name = "John";
System.out.println(getMessage(Status.REGISTERED) + " " + name);
System.out.println(getMessage(Status.TERMINATED) + " " + name);
}
}

Status & TestStatus classes are in two different packaged; so to refer to a Status enum value inside TestStatus class we must use qualified name like follows.

Status status = Status.REGISTERED;

However when it comes to case labels of switch statements; enum should not be referred using qualified name. What? Are you sure? Yes; that is the truth. Only unqualified enum value must be used for case labels. The compiler will simply look at the type of the enum parameter to the switch() statement and refer to that enum class to locate the enum values.

Wednesday, October 7, 2009

Data Encryption Decryption using AES Algorithm, Key and Salt with Java Cryptography Extension

In this tutorial we will implement a full data encryption decryption cycle with Java (only data, not file encryption); encrypt some data using a secret key, salt and iterations and decrypt using the same parameters. We are using the Java Cryptography Extension (JCE) for data encryption/decryption operations. This extension is available in Java 1.4.2 and above; you will have to manually download it for older versions (here). Java supports a number of of encryption algorithms, however we will demonstrate only AES algorithm (the Advanced Encryption Standard) usage.

Why should we encrypt data?

Encryption and Decryption are highly important security steps; now there are file and data encryption software as well. Data encryption is the mechanism of converting a message (plain text) into some other text (called ciphertext) so that readers can not understand the original message; however some authorized party can understand that ciphertext using the method called Decryption which converts the ciphertext into original message. As many software applications store sensitive personal data in databases, encryption has become a must. Ideally nobody (including the software developers) should not be able to view these user specific real data.

Simple Data Encryption/Decryption Example with AES

For encryption we must use a secret key along with an algorithm. In the following example we use an algorithm called AES 128 and the bytes of the word "ThisIsASecretKey" as the secret key (the best secret key we found in this world). AES algorithm can use a key of 128 bits (16 bytes * 8); so we selected that key.

package org.kamal.crypto;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;

public class SimpleProtector {

private static final String ALGORITHM = "AES";
private static final byte[] keyValue =
new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' };

public static String encrypt(String valueToEnc) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encValue = c.doFinal(valueToEnc.getBytes());
String encryptedValue = new BASE64Encoder().encode(encValue);
return encryptedValue;
}

public static String decrypt(String encryptedValue) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}

private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, ALGORITHM);
// SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
// key = keyFactory.generateSecret(new DESKeySpec(keyValue));
return key;
}
}

We use "generateKey()" method to generate a secret key for AES algorithm with a given key. You can change the used algorithm by changing this Key generation; the commented out code shows the use of DES algorithm (Data Encryption Standard). Following is a simple class to test the above implementation.

package org.kamal.crypto;

public class TestSimpleProtector {

public static void main(String[] args) throws Exception {

String password = "mypassword";
String passwordEnc = SimpleProtector.encrypt(password);
String passwordDec = SimpleProtector.decrypt(passwordEnc);

System.out.println("Plain Text : " + password);
System.out.println("Encrypted : " + passwordEnc);
System.out.println("Decrypted : " + passwordDec);
}
}
Following is the output we got from above test; so you clearly see that the original text is reproduced after the decryption operation.

Plain Text : mypassword
Encrypted : sBhCap4urE50a/dGuhNgrw==
Decrypted : mypassword
The encrypted value is not simply related to the plain text, so it provides some security to the password.

The Risk with Simple Protector

When you use the above mentioned method to encrypt all passwords in your database, it makes an attacker's task easier. Since the same key is used, if an attacker find a way to get the plain text, the he can use that same method to get all the other plain-texts in the database in minutes.

Use Salt and iterations to improve

To make the attackers job harder we can use two methods; called adding Salt and using Iterations.
A Salt is another plain-text appended to the given plain text, before generating the encrypted value. We use a unique Salt per each plain-text so that the value used to encrypt is getting much stronger making it harder for an attacker to guess with a brute force or dictionary attack. Following class shows the improved version of SimpleProtector class.

package org.kamal.crypto;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;

public class Protector {

private static final String ALGORITHM = "AES";
private static final int ITERATIONS = 2;
private static final byte[] keyValue =
new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};

public static String encrypt(String value, String salt) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);

String valueToEnc = null;
String eValue = value;
for (int i = 0; i < ITERATIONS; i++) {
valueToEnc = salt + eValue;
byte[] encValue = c.doFinal(valueToEnc.getBytes());
eValue = new BASE64Encoder().encode(encValue);
}
return eValue;
}

public static String decrypt(String value, String salt) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);

String dValue = null;
String valueToDecrypt = value;
for (int i = 0; i < ITERATIONS; i++) {
byte[] decordedValue = new BASE64Decoder().decodeBuffer(valueToDecrypt);
byte[] decValue = c.doFinal(decordedValue);
dValue = new String(decValue).substring(salt.length());
valueToDecrypt = dValue;
}
return dValue;
}

private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, ALGORITHM);
// SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
// key = keyFactory.generateSecret(new DESKeySpec(keyValue));
return key;
}
}

You need to store the clear Salt with the stored encrypted value since the salt is required at the decrypt operation. We just used a sentence as the 'salt', but you should use a random letters/numbers for that to avoid any dictionary attacks.

package org.kamal.crypto;

public class TestProtector {

public static void main(String[] args) throws Exception {
String password = "mypassword";
String salt = "this is a simple clear salt";
String passwordEnc = Protector.encrypt(password, salt);
String passwordDec = Protector.decrypt(passwordEnc, salt);

System.out.println("Salt Text : " + salt);
System.out.println("Plain Text : " + password);
System.out.println("Encrypted : " + passwordEnc);
System.out.println("Decrypted : " + passwordDec);
}
}
The output from the above class looks as follows.

Salt Text : this is a simple clear salt
Plain Text : mypassword
Encrypted : GX8jEr04o6PC+IE+f6DXq+zuRiZ2nsSL+UYxYRh9vK28xt/GrLqnbrP3hkXSW5S3jNgzXkrLSmXm
fwRw+eBMWQJO+8tlSEE9D2Y9qEzowX18s4W81U/D9JL3JX7uJtwp
Decrypted : mypassword
When using Salt, you can store each (clear text) Salt in the database per each encrypted text. It would be better if we pass the secrete key while initializing or instantiating the (Protector) encryption class rather than hard coding it in the class itself (for simplicity we used a hard coded key for this tutorial).

Related Articles

Tuesday, July 28, 2009

[Ant] Build .WAR files in Eclipse for Web Applications

Eclipse JEE versions support Java Web Application projects, but other Eclipse versions do not. Java developers need to build WAR (web archive) files for deployments (yes, Exploded deployments are also possible). However Eclipse does not provide a direct way to create war files; developers write ant build files for this. So we thought of sharing a generic ant build file for Web Applications.

Our general Web Application's folder structure is shown in the image. In most cases, this structure will exactly match t your project structure; however the folder named "WebRoot" may be different to yours. (If your folder structure is different, let us know in comments section).

Ant Build file (build.xml)

Following is the general ant build file (build.xml).

<project name="MyWebApplication" basedir="." default="archive">

<property name="WEB-INF" value="${basedir}/WebRoot/WEB-INF" />
<property name="OUT" value="${basedir}/out" />
<property name="WAR_FILE_NAME" value="mywebapplication.war" />
<property name="TEMP" value="${basedir}/temp" />

<target name="help">
<echo>
--------------------------------------------------
compile - Compile
archive - Generate WAR file
--------------------------------------------------
</echo>
</target>

<target name="init">
<delete dir="${WEB-INF}/classes" />
<mkdir dir="${WEB-INF}/classes" />
</target>

<target name="compile" depends="init">
<javac srcdir="${basedir}/src"
destdir="${WEB-INF}/classes"
classpathref="libs">
</javac>
</target>

<target name="archive" depends="compile">
<delete dir="${OUT}" />
<mkdir dir="${OUT}" />
<delete dir="${TEMP}" />
<mkdir dir="${TEMP}" />
<copy todir="${TEMP}" >
<fileset dir="${basedir}/WebRoot">
</fileset>
</copy>
<move file="${TEMP}/log4j.properties"
todir="${TEMP}/WEB-INF/classes" />
<war destfile="${OUT}/${WAR_FILE_NAME}"
basedir="${TEMP}"
compress="true"
webxml="${TEMP}/WEB-INF/web.xml" />
<delete dir="${TEMP}" />
</target>

<path id="libs">
<fileset includes="*.jar" dir="${WEB-INF}/lib" />
</path>

</project>

You can go through the above xml file and see the process; we have created an attribute for WAR file's name.

<property name="WAR_FILE_NAME" value="mywebapplication.war" />

You should change the value "mywebproject.war" to match your project name. Save the above build.xml file inside Web applications project folder as shown in the folder structure image.

Ant build file has separate tasks for compiling the project and to build the war file. In Eclipse you just have to right click on this build file and select "Ant Build" to execute it. The war file will be generated and stored inside <web-project>/out folder.

Related Articles

How to open a .war (web archive) or .jar (java archive) files
Open and read any file in a .war file of a web application with Java

Thursday, July 9, 2009

[Tomcat] validateJarFile(servlet-api.jar) - jar not loaded. Offending class: javax/servlet/Servlet.class

validateJarFile jar not loadedorg.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(<APP_PATH>\WEB-INF\lib\servlet-api.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
.

We are using Apache Tomcat to deploy web applications, and getting the above message when Apache Tomcat is started. All web applications inside Tomcat are working fine, however no developer would want to see message (at least we do not want to see).

The above message is only a warning message due to the existence of multiples of the same javax.servler.Servlet.class for Tomcat runtime to pick; and this extra instance has come from a jar file named <APP_PATH>\WEB-INF\lib\servlet-api.jar. Tomcat has its own servlet-api.jar file; look at the following folders to locate the file (depending on the Tomcat version; location may be different).
  • <TOMCAT_HOME>\common\lib
  • <TOMCAT_HOME>\lib
Your web applications have the same servlet-api.jar file inside following folder.
  • <TOMCAT_HOME>\webapps\<PROJECT>\WEB-INF\lib
As the Tomcat runtime has the required javax/servlet/Servlet.class file loaded from its own lib\servlet-api.jar file, you do not need to place it inside each and every web application. After removing the jar file from your web application, you will not receive the message again.

Related Articles

Wednesday, July 8, 2009

Attempted to lock an already-locked dir: Subversion issue

"Attempted to lock an already-locked dir" - svn: Working copy locked; this message is frequently faced by users of SubVersion (SVN) source repository. The message is self explanatory; simply some source files are locked and no commit or update operations allowed on those files/folders. In some occasions, Software developers in a team receive this message in a SVN operation even though none of them have locked the files or folders; causing confusion. So how would you try and resolve?

We use Subversion to create and store our projects in repositories; Subclipe (an Eclipse plugin) as the client tool to connect to repository.

We tried following solutions

1. Commit Changes - change and commit changes into repository
2. File Delete - delete the files and commit the delete into repository
3. Override & update - override the local changes and update from repository

None of the above options resolved the issue, but got the same type of error messages. Followings are the error messages shown inside Eclipse while trying the above mentioned options.

commit -m C:/workspace/MyProject/config.properties
Attempted to lock an already-locked dir
svn: Working copy 'C:\workspace\MyProject' locked

update -r 13 C:/workspace/MyProject/src
Attempted to lock an already-locked dir
svn: Working copy 'C:\workspace\MyProject' locked

delete --force C:/workspace/MyProject/config.properties
Attempted to lock an already-locked dir
svn: Working copy 'C:\workspace\MyProject' locked

Use Cleanup command

Subversion:Team-CleanupSubversion supports a command named "Cleanup"; it is used to release the locks on a project.

If you have faced with the above issue while no one has locked the project files, you can run this command and resolve the issue.
You do this in Eclipse by right clicking the project; then Team->Cleanup as shown in the image.

Wednesday, November 5, 2008

Tomcat + Apache HTTP: Servers Connecting Guide

Apache Tomcat is a Servlet/JSP container and used to deploy dynamic Java contents like JSPs and Servlets. Tomcat has some web server capabilities, however it is not a full blown web server to serve high traffic web sites. In the meantime Apache HTTP server is a full blown web server. So in general Tomcat is configured along with Apache HTTP server to maintain a healthy web site with dynamic content. This article will cover the steps of configuring HTTP server and Tomcat connection, however this will not discuss installation details of Apache or Tomcat.

System Requirements

  • Apache HTTP server installed
    • download from here
    • we used version 2.2.10 for this article
  • Apache Tomcat installed
    • download from here
    • we used version 5.5 here
Short Names
Followings are the short names used to denote folders used in this article.
  • %APACHE_HTTP_HOME%
    • denotes Apache HTTP Server installation directory. In our testing machine, it is "D:\ASF\Apache"
  • %CATALINA_HOME%
    • Tomcat installation directory; for example: "D:\ASF\Tomcat"

Connection Configurations

The connection between HTTP server and Tomcat will be done using Apache Tomcat Connector module named mod_jk which is configured inside Apache HTTP server. For that first download the correct version of connector from here.

For version 2.2.10 of Apache HTTP; correct connector version is 2.2.4 (available here). Make sure to download the correct version, as incorrect version may result in errors.

Mod_jk configuration

Apache HTTP ServerNow the downloaded mod_jk module must be configured in Apache HTTP server. First rename the above downloaded file say mod_jk-1.2.26-httpd-2.2.4.so to "mod_jk.so". In Apache server, module files are stored under %APACHE_HTTP_HOME%\modules folder. Now copy the mod_jk.so file into this folder.

Then open the HTTP server configuration file named httpd.conf. It's located in %APACHE_HTTP_HOME%\conf folder. This file has a number of lines used to load Modules starting with "LoadModule ". Add the following line below these lines.

LoadModule jk_module modules/mod_jk.so

Now the downloaded mod_jk module is added into Apache server.

Tomcat Server Information

We need a new properties file to store details about the Tomcat server. Create a new file named workers.properties inside %APACHE_HTTP_HOME%\conf folder with the following content.

worker.list=localTomcat
worker.localTomcat.host=localhost
worker.localTomcat.port=8009
worker.localTomcat.type=ajp13

Pay attention to the above port 8009; this is the port defined inside %CATALINA_HOME%\conf\server.xml file as follows.

<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Tomcat ServerIf the port in AJP 1.3 connector section of Tomcat is different than 8009, change the above workers.properties file to match that.

Now we need to configure the workers.properties file in %APACHE_HTTP_HOME%\conf\httpd.conf file. For that add the following lines after the line that we added into httpd.conf in a previous step.

JkWorkersFile conf/workers.properties
JkShmFile logs/mod_jk.shm
JkLogFile logs/mod_jk.log

Now everything is setup. However still we have not yet defined the important part; which user requests to be forwarded to Tomcat server from Apache server.

URL Patterns

Following is the format of those configurations.

JkMount  <URL_PATTERN>  <WORKER>
  • <URL_PATTERN> - the url pattern that you need to forward to Tomcat
  • <WORKER> - the worker name defined in workers.properties file.
We can set up multiple url patterns as follows.

JkMount  /myProject/* localTomcat
JkMount /*.jsp localTomcat
JkMount /*.do localTomcat

First one will send all requests for a web application named "myProject" to Tomcat while the second will send all requests ending with ".jsp". The third line configures for all requests ending with ".do" (many use .do extension with Struts actions).

Add required configuration lines as above into httpd.conf (just after the lines we added in above steps).

Setup Completed

Now try the URLs of your application as follows (using Apache port number, rather than Tomcat port number). We assumed Apache HTTP server is running on port 80. Following as some sample urls for the above configurations.

http://localhost/myProject/
http://localhost/testApp/main/admin.jsp
http://localhost/strutsApp/login.do

Now Apache will direct all these request to Tomcat (running on a different port) and serve the client smoothly.

Related Article: How to change Tomcat port

Related Article