In my recent project, I have a need to monitor a directory for changes such as a new file created, modified or deleted. Surprisingly, JDK (up to JDK 6) does not have APIs to do that! JDK 7 does support it out of the box but I am stuck with the version 6
After looking around for an existing solution, I finally found that jNotify seems to fit the need. It supports all the three major platforms (Windows, Linux, and MacOS).The project has not released any update for nearly two years, so it's a little bit tricky to compile the native code. In this post, I will show you how! (at least on a Ubuntu Server 64bit)
In order to build anything serious on Ubuntu, you should install the package build-essentials
sudo apt-get install build-essential
Then download the source code of jNotify from its homepage. Extract it, open a terminal and issue the command: (assuming that you are at the directory jnotify-native-linux-0.93-src)
cd Release makeOn ArchLinux 64bit, the compilation will succeed and you will get a libjnotify.so in the same folder. But on my Ubuntu Server 10.04 LTS, I've got this:
/usr/include/asm-generic/fcntl.h:96: error: expected specifier-qualifier-list before ‘pid_t’After Googling around I found this thread. Basically, you have to change the file net_contentobjects_jnotify_linux_JNotify_linux.c by moving up the "unistd.h" up above the "sys/time.h". Now issue the command "make", the compilation will work as expected. Happy coding!
- Create a file called templates.gradle in the ~/.gradle/init.d/ folder (actually you can name it whatever you want as long as it has the extension .gradle)
- Edit that file and paste this little code snippet:
gradle.beforeProject { prj ->
prj.apply from: 'http://launchpad.net/gradle-templates/trunk/latest/+download/apply.groovy'
}
Now, you can create a project structure event without a build.gradle with the command:
gradle createJavaProjectHappy coding!
Spring Security with Digest authentication and encoded password
Posted on 7:48 AM under digest authentication hashed password spring security 1 comment
The title says it all. This post will show you how to configure Spring Security with Digest authentication and encoded password. But first, here is the Spring Security documentation about digest authentication and encoded password:
The configured UserDetailsService is needed because DigestAuthenticationFilter must have direct access to the clear text password of a user. Digest Authentication will NOT work if you are using encoded passwords in your DAO. The DAO collaborator, along with the UserCache, are typically shared directly with a DaoAuthenticationProvider. The authenticationEntryPoint property must be DigestAuthenticationEntryPoint, so that DigestAuthenticationFilter can obtain the correct realmName and key for digest calculations.It's not all true. Let's take a look at the source code of DigestAuthenticationFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
// omit code for simplicity
// Compute the expected response-digest (will be in hex form)
String serverDigestMd5;
// Don't catch IllegalArgumentException (already checked validity)
serverDigestMd5 = DigestAuthUtils.generateDigest(passwordAlreadyEncoded, username, realm, user.getPassword(),
request.getMethod(), uri, qop, nonce, nc, cnonce);
// omit code for simplicty
}
Digging a little more into the method generateDigest of the class DigestAuthUtils, we have:
static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String a1Md5 = null;
String a2 = httpMethod + ":" + uri;
String a2Md5 = md5Hex(a2);
if (passwordAlreadyEncoded) {
a1Md5 = password;
} else {
a1Md5 = DigestAuthUtils.encodePasswordInA1Format(username, realm, password);
}
// The rest of the code
A little more into encodePasswordInA1Format of DigestAuthUtils:
static String encodePasswordInA1Format(String username, String realm, String password) {
String a1 = username + ":" + realm + ":" + password;
String a1Md5 = md5Hex(a1);
return a1Md5;
}
From here, if we set the property passwordAlreadyEncoded of DigestAuthenticationFilter to true and create a suitable password encoder and salt source, we can make Spring Security digest authentication work with our tailored password encoder, salt source. Let's create them:
package com.mycompany.myproject;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
public class DigestAuthenticationAwareSaltSource implements SaltSource, InitializingBean {
private String digestRealm;
@Override
public void afterPropertiesSet() throws Exception {
Assert.hasText(digestRealm, "A Digest Realm must be set");
}
@Override
public Object getSalt(UserDetails user) {
return String.format("%s:%s:", user.getUsername(), digestRealm);
}
public void setDigestRealm(String digestRealm) {
this.digestRealm = digestRealm;
}
}
package com.mycompany.myproject;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.codec.Hex;
public class DigestAuthenticationAwarePasswordEncoder implements PasswordEncoder {
@Override
public String encodePassword(String rawPass, Object salt) throws DataAccessException {
String a1 = salt + rawPass;
return md5Hex(a1);
}
@Override
public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException {
String calculatedPass = md5Hex(salt + rawPass);
return calculatedPass.equals(encPass);
}
private String md5Hex(String data) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
return new String(Hex.encode(digest.digest(data.getBytes())));
}
}
And now you can do Digest Authentication with encoded password in Spring Security!
Setting up Jetty 7.4.x on CentOS 5.x as non-root daemon
Posted on 2:30 PM under centos daemon jetty non-root 0 comment
cd /opt wget http://download.eclipse.org/jetty/stable-7/dist/jetty-distribution-7.4.5.v20110725.tar.gz tar xzvf jetty-distribution-7.4.5.v20110725.tar.gz ln -s jetty-distribution-7.4.5.v20110725 jettyOK, we've just installed Jetty to /opt/jetty. Now we will setup Jetty to run on startup
cd /etc/init.d ln -s /opt/jetty/bin/jetty.sh jetty chkconfig --add jetty chkconfig --level 345 jetty onIt's a good practice to run Jetty as a non-root user, let's create a normal user called jetty
useradd -m jetty chown -R jetty:jetty /opt/jettyNow edit the startup script of Jetty located at /opt/jetty/bin/jetty.sh to include the following lines at the beginning (right after a bulk of commented lines describing various variables for Jetty)
JAVA_HOME=/opt/java JAVA=$JAVA_HOME/bin/java JAVA_OPTIONS=" -server -Xms256m -Xmx1024m -XX:+DisableExplicitGC " JETTY_HOME=/opt/jetty JETTY_USER=jetty JETTY_PORT=7070 JETTY_HOST=0.0.0.0 # If you don't set this to 0.0.0.0, jetty only listen on localhost JETTY_LOGS=/opt/jetty/logs/Now start up it:
service jetty startOpen your browser and navigate to http://
If you use Spring Security, remember to remove Spring Security filter in the web.xml file. Now in your context file (the context.xml file in your META-INF of your web archive or in ther conf/Catalina/localhost/ context file), adding the following:EntireApp /* GET POST client client BASIC
Finally, edit the tomcat-users.xml (usually located at
Now, every time the clients access the demo site, the browser will pop up a dialog asking for username and password. After successfully authenticated, the clients will use the demo site as normal.
This authentication mechanism is the Basic access authentication. As the title said, this is a quick and dirty way to secure a web application on Tomcat 6.
Hope this saves you some time.
Vietnamese resource bundle for PrettyTime
Posted on 9:48 AM under 2 comments
package com.ocpsoft.pretty.time.i18n;
import java.util.ListResourceBundle;
public class Resources_vi extends ListResourceBundle
{
private static final Object[][] OBJECTS = new Object[][] {
{ "CenturyPattern", "%n %u" },
{ "CenturyFuturePrefix", "" },
{ "CenturyFutureSuffix", " sau" },
{ "CenturyPastPrefix", "cách đây " },
{ "CenturyPastSuffix", "" },
{ "CenturyName", "thế kỷ" },
{ "CenturyPluralName", "thế kỷ" },
{ "DayPattern", "%n %u" },
{ "DayFuturePrefix", "" },
{ "DayFutureSuffix", " sau" },
{ "DayPastPrefix", "cách đây " },
{ "DayPastSuffix", "" },
{ "DayName", "ngày" },
{ "DayPluralName", "ngày" },
{ "DecadePattern", "%n %u" },
{ "DecadeFuturePrefix", "" },
{ "DecadeFutureSuffix", " sau" },
{ "DecadePastPrefix", "cách đây " },
{ "DecadePastSuffix", "" },
{ "DecadeName", "thập kỷ" },
{ "DecadePluralName", "thập kỷ" },
{ "HourPattern", "%n %u" },
{ "HourFuturePrefix", "" },
{ "HourFutureSuffix", " sau" },
{ "HourPastPrefix", "cách đây " },
{ "HourPastSuffix", "" },
{ "HourName", "giờ" },
{ "HourPluralName", "giờ" },
{ "JustNowPattern", "%u" },
{ "JustNowFuturePrefix", "" },
{ "JustNowFutureSuffix", " khắc sau" },
{ "JustNowPastPrefix", "cách đây " },
{ "JustNowPastSuffix", " khắc" },
{ "JustNowName", "" },
{ "JustNowPluralName", "" },
{ "MillenniumPattern", "%n %u" },
{ "MillenniumFuturePrefix", "" },
{ "MillenniumFutureSuffix", " sau" },
{ "MillenniumPastPrefix", "cách đây " },
{ "MillenniumPastSuffix", "" },
{ "MillenniumName", "thiên niên kỷ" },
{ "MillenniumPluralName", "thiên niên kỷ" },
{ "MillisecondPattern", "%n %u" },
{ "MillisecondFuturePrefix", "" },
{ "MillisecondFutureSuffix", " sau" },
{ "MillisecondPastPrefix", "cách đây " },
{ "MillisecondPastSuffix", "" },
{ "MillisecondName", "mili giây" },
{ "MillisecondPluralName", "mili giây" },
{ "MinutePattern", "%n %u" },
{ "MinuteFuturePrefix", "" },
{ "MinuteFutureSuffix", " sau" },
{ "MinutePastPrefix", "cách đây " },
{ "MinutePastSuffix", "" },
{ "MinuteName", "phút" },
{ "MinutePluralName", "phút" },
{ "MonthPattern", "%n %u" },
{ "MonthFuturePrefix", "" },
{ "MonthFutureSuffix", " sau" },
{ "MonthPastPrefix", "cách đây " },
{ "MonthPastSuffix", "" },
{ "MonthName", "tháng" },
{ "MonthPluralName", "tháng" },
{ "SecondPattern", "%n %u" },
{ "SecondFuturePrefix", "" },
{ "SecondFutureSuffix", " sau" },
{ "SecondPastPrefix", "cách đây " },
{ "SecondPastSuffix", "" },
{ "SecondName", "giây" },
{ "SecondPluralName", "giây" },
{ "WeekPattern", "%n %u" },
{ "WeekFuturePrefix", "" },
{ "WeekFutureSuffix", " sau" },
{ "WeekPastPrefix", "cách đây " },
{ "WeekPastSuffix", "" },
{ "WeekName", "tuần" },
{ "WeekPluralName", "tuần" },
{ "YearPattern", "%n %u" },
{ "YearFuturePrefix", "" },
{ "YearFutureSuffix", " sau" },
{ "YearPastPrefix", "cách đay " },
{ "YearPastSuffix", "" },
{ "YearName", "năm" },
{ "YearPluralName", "năm" },
{ "AbstractTimeUnitPattern", "" },
{ "AbstractTimeUnitFuturePrefix", "" },
{ "AbstractTimeUnitFutureSuffix", "" },
{ "AbstractTimeUnitPastPrefix", "" },
{ "AbstractTimeUnitPastSuffix", "" },
{ "AbstractTimeUnitName", "" },
{ "AbstractTimeUnitPluralName", "" } };
@Override
public Object[][] getContents()
{
return OBJECTS;
}
}
Hopefully they will integrate this snippet into the next release of PrettyTime.