You might remember my Templating post describing a Java class that implements a Typo3-like technique for a templating machine. I made a Java package out of it and you can use it right away. Just download it here. The API documentation is available, too.
Category: Java
Java code snippets and solutions
I decided recently to publish my very simple utility classes for reading and writing CSV files. The main project page is now available. The package, published under the GNU Lesser General Public License, allows you to easily integrate CSV functionality into your application. The utilities can be configured to use different column delimiter and separator characters in case you need to adopt some other versions of CSV. The default configuration conforms to the Excel style of CSV.
Since this CSV package uses streams, you are able to read from any stream. And, of course, you can write to any stream. You could even synchronize in your application by applying the reader/writer synchronization described in one of my artices.
You can download the latest stable release at the main project page.
Here is a short example on reading a CSV file using the CSVReader class:
java.io.File f = new java.io.File("csv-test.csv"); csv.CSVReader in = new csv.CSVReader(new java.io.FileReader(f)); while (in.hasNext()) { String columns[] = in.next(); // Do something here } in.close(); |
Please note that the CSVReader class actually implements the Iterator interface.
Writing a CSV file is even easier. Just create an instance of the CSVWriter class and pass it your rows:
java.io.File f = new java.io.File("csv-test.csv"); CSVWriter out = new CSVWriter(new java.io.FileWriter(f)); out.printRow(new String[] { "0:0", "0:1", "0:2" }); out.printRow(new String[] { "1:0", "1:1", "1:2" }); out.close(); |
Documentation
The API documentation tells you all details and configuration parameters that you can use to control the behaviour of the reader and writer classes.
Synchronizing Reader and Writer Threads
Here are two functions that you should use when you want two threads, producer and consumer, to be synchronized. I used these functions mainly to ensure that the reader will stop until an object is ready to read. An advantage is that you can control how many objects are in memory at the same time.
protected List<Object> availableObjects = new ArrayList<Object>(); /** * Delivers the next object. * Used by the reader/consumer thread. */ public synchronized Object next() { Object rc; while (availableObjects.isEmpty()) { try { wait(); } catch (InterruptedException e) { } } rc = availableObjects.remove(0); notify(); return rc; } /** * Adds a new object to the list of available objects. * Used by the writer/producer thread. */ public synchronized void addObject(Object o) { while (availableObjects.size() >= 20) { try { wait(); } catch (InterruptedException e) {} } availableObjects.add(o); notify(); } |
The main idea was taken from Silberschatz’ book about Operating Systems. You have to make sure that you never call the next() method when the last object was read. So be careful when your number of objects produced is limited.
Tomcat automatically adds “no-cache” directives to each response. This is not a good idea when you are not using Apache as the serving host for static content. I found a very easy way to get rid of that HTTP header. Just add a new file named “context.xml” to the WEB-INF directory of your application:
1 2 3 4 | <context> <valve className="org.apache.catalina.authenticator.BasicAuthenticator" disableProxyCaching="false" /> </context> |
For finer control, please have a look at Symphonious’ article on that topic.
Handling Unix’ cron-like information in Java
I recently had the requirement to write scheduled tasks within Java. I decided to use cron-like syntax in order to define when a task should run. A special class was created handling the scheduling information. Here it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | package mypackage; import java.util.Calendar; import java.util.GregorianCalendar; /** * Provides cron-like scheduling information. * This class implements cron-like definition of scheduling information. * Various methods can be used to check whether a timestamp matches the * schedule or not. However, there is a slight difference between cron and * this class. Cron describes a match when either the day of month and month * or the day of week are met. This class requires both to be met for a match. * Also note that Calendar defines Sunday through Saturday with 1 through 7 respectively * @author RalphSchuster * */ public class CronSchedule { /** * Types being used. * This array defines the types and their indices. */ protected static int TYPES[] = new int[] { Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.DAY_OF_WEEK }; private AbstractTimeValue timeValues[][] = new AbstractTimeValue[TYPES.length][]; /** * Default constructor * Constructor with all terms set to "*". */ public CronSchedule() { this("*", "*", "*", "*", "*"); } /** * Constructor with cron-style string initialization. * The cron style is: $minute $hour $dayOfMonth $month $dayOfWeek * @param schedule */ public CronSchedule(String schedule) { set(schedule); } /** * Constructor with separate initialization values. * @param min - minute definition * @param hour - hour definition * @param dom - day of month definition * @param mon - month definition * @param dow - day of week definition */ public CronSchedule(String min, String hour, String dom, String mon, String dow) { set(Calendar.MINUTE, min); set(Calendar.HOUR_OF_DAY, hour); set(Calendar.DAY_OF_MONTH, dom); set(Calendar.MONTH, mon); set(Calendar.DAY_OF_WEEK, dow); } /** * Sets the cron schedule. * The cron style is: $minute $hour $dayOfMonth $month $dayOfWeek * The function will return any characters that follow the cron definition * @param schedule - cron-like schedule definition * @return characters following the cron definition. */ public String set(String schedule) { String parts[] = schedule.split(" ", TYPES.length+1); if (parts.length < TYPES.length) throw new IllegalArgumentException("Invalid cron format: "+schedule); for (int i=0; i<TYPES.length; i++) set(getType(i), parts[i]); return parts.length > TYPES.length ? parts[TYPES.length] : null; } /** * Sets the time values accordingly * @param type - Calendar constant to define what values will be set * @param values - comma-separated list of definitions for that type */ public void set(int type, String values) { // Split the values String parts[] = values.split(","); AbstractTimeValue result[] = new AbstractTimeValue[parts.length]; // Iterate over entries for (int i=0; i<parts .length; i++) { // Decide what time value is set and create it if (parts[i].indexOf("/") > 0) result[i] = new TimeSteps(parts[i]); else if (parts[i].indexOf("-") > 0) result[i] = new TimeRange(parts[i]); else if (parts[i].equals("*")) result[i] = new TimeAll(); else result[i] = new SingleTimeValue(parts[i]); } // Save the array set(type, result); } /** * Sets the values for a specific type * @param type - Calendar constant defining the time type * @param values - values to be set */ protected void set(int type, AbstractTimeValue values[]) { timeValues[getIndex(type)] = values; } /** * Returns the values for a specific time type * @param type - Calendar constant defining the type * @return time value definitions */ protected AbstractTimeValue[] getValues(int type) { return timeValues[getIndex(type)]; } /** * Returns the cron-like definition string for the given time value * @param type - Calendar constant defining time type * @return cron-like definition */ public String get(int type) { AbstractTimeValue values[] = getValues(type); String rc = ""; for (int i=0; i<values .length; i++) rc += ","+values[i].toString(); return rc.substring(1); } /** * Returns the cron-like definition of the schedule. */ public String toString() { String rc = ""; for (int i=0; i<TYPES.length; i++) { rc += " "+get(getType(i)); } return rc.trim(); } /** * Checks whether given timestamp matches with defined schedule. * This is default check method. All criteria must be met including seconds to be 0. * @param timeStamp - time in ms since Epoch time * @return true when schedule matches */ public boolean matches(long timeStamp) { return matches(getCalendar(timeStamp)); } /** * Checks whether given timestamp matches with defined schedule. * This is default check method. All criteria must be met including seconds to be 0. * @param cal - calendar date * @return true when schedule matches */ public boolean matches(Calendar cal) { return isMinute(cal) && (cal.get(Calendar.SECOND) == 0); } /** * Checks whether given timestamp matches with defined schedule. * This method can be used when seconds are not relevant for matching. * This is default check method. * @param timeStamp - time in ms since Epoch time * @return true when schedule matches */ public boolean isMinute(long timeStamp) { return isMinute(getCalendar(timeStamp)); } /** * Checks whether given calendar date matches with defined schedule. * This method can be used when seconds are not relevant for matching. * @param cal - calendar date * @return true when schedule matches */ public boolean isMinute(Calendar cal) { return matches(Calendar.MINUTE, cal) && isHour(cal); } /** * Checks whether given timestamp matches with defined hour schedule. * This method can be used when minute definition is not relevant for matching. * @param timestamp - time in ms since Epoch time * @return true when schedule matches */ public boolean isHour(long timeStamp) { return isHour(getCalendar(timeStamp)); } /** * Checks whether given calendar date matches with defined hour schedule. * This method can be used when minute definition is not relevant for matching. * @param cal - calendar date * @return true when schedule matches */ public boolean isHour(Calendar cal) { return matches(Calendar.HOUR_OF_DAY, cal) && isDay(cal); } /** * Checks whether given timestamp matches with defined day schedule. * This method can be used when minute and hour definitions are not relevant for matching. * @param timestamp - time in ms since Epoch time * @return true when schedule matches */ public boolean isDay(long timeStamp) { return isDay(getCalendar(timeStamp)); } /** * Checks whether given calendar date matches with defined day schedule. * This method can be used when minute and hour definitions are not relevant for matching. * @param cal - calendar date * @return true when schedule matches */ public boolean isDay(Calendar cal) { return matches(Calendar.DAY_OF_WEEK, cal) && matches(Calendar.DAY_OF_MONTH, cal) && matches(Calendar.MONTH, cal); } /** * Checks whether specific schedule definition matches against the given calendar date. * @param type - Calendar constant defining time type to check for * @param calendar - calendar representing the date to check * @return true when definition matches */ protected boolean matches(int type, Calendar calendar) { // get the definitions and the comparison value AbstractTimeValue defs[] = timeValues[getIndex(type)]; int value = calendar.get(type); // Any of the criteria must be met for (int i=0; i<defs.length; i++) { if (defs[i].matches(value)) return true; } return false; } /** * Creates the calendar for a timestamp. * @param timeStamp - timestamp * @return calendar */ protected Calendar getCalendar(long timeStamp) { Calendar rc = new GregorianCalendar(); rc.setTimeInMillis(timeStamp); return rc; } /** * Returns the type at the specified index * @param index - index * @return Calendar constant of type */ protected static int getType(int index) { return TYPES[index]; } /** * Returns the index for the specified Calendar type. * @param type - Calendar constant for type * @return internal index */ protected static int getIndex(int type) { for (int i=0; i<TYPES.length; i++) { if (TYPES[i] == type) return i; } throw new IllegalArgumentException("No such time type: "+type); } /** * Base class for timing values. * @author RalphSchuster */ public static abstract class AbstractTimeValue { /** * Returns true when given time value matches defined time. * @param timeValue - time value to evaluate * @return true when time matches */ public abstract boolean matches(int timeValue); } /** * Represents a single time value, e.g. 9 * @author RalphSchuster */ public static class SingleTimeValue extends AbstractTimeValue { private int value; public SingleTimeValue(int value) { setValue(value); } public SingleTimeValue(String value) { setValue(Integer.parseInt(value)); } /** * @return the value */ public int getValue() { return value; } /** * @param value the value to set */ public void setValue(int value) { this.value = value; } /** * Returns true when given time value matches defined value. * @param timeValue - time value to evaluate * @return true when time matches */ public boolean matches(int timeValue) { return timeValue == getValue(); } /** * Returns cron-like string of this definition. */ public String toString() { return ""+getValue(); } } /** * Represents a time range, e.g. 5-9 * @author RalphSchuster */ public static class TimeRange extends AbstractTimeValue { private int startValue; private int endValue; public TimeRange(int startValue, int endValue) { setStartValue(startValue); setEndValue(endValue); } public TimeRange(String range) { int dashPos = range.indexOf("-"); setStartValue(Integer.parseInt(range.substring(0, dashPos))); setEndValue(Integer.parseInt(range.substring(dashPos+1))); } /** * @return the endValue */ public int getEndValue() { return endValue; } /** * @param endValue the endValue to set */ public void setEndValue(int endValue) { this.endValue = endValue; } /** * @return the startValue */ public int getStartValue() { return startValue; } /** * @param startValue the startValue to set */ public void setStartValue(int startValue) { this.startValue = startValue; } /** * Returns true when given time value falls in range. * @param timeValue - time value to evaluate * @return true when time falls in range */ public boolean matches(int timeValue) { return (getStartValue() <= timeValue) && (timeValue <= getEndValue()); } /** * Returns cron-like string of this definition. */ public String toString() { return getStartValue()+"-"+getEndValue(); } } /** * Represents a time interval, e.g. 0-4/10 * @author RalphSchuster */ public static class TimeSteps extends AbstractTimeValue { private AbstractTimeValue range; private int steps; public TimeSteps(AbstractTimeValue range, int steps) { setRange(range); setSteps(steps); } public TimeSteps(String def) { int divPos = def.indexOf("/"); String r = def.substring(0, divPos); if (r.equals("*")) setRange(new TimeAll()); else if (r.indexOf("-") > 0) setRange(new TimeRange(r)); else throw new IllegalArgumentException("Invalid range: "+def); setSteps(Integer.parseInt(def.substring(divPos+1))); } /** * Returns true when given time value matches the interval. * @param timeValue - time value to evaluate * @return true when time matches the interval */ public boolean matches(int timeValue) { boolean rc = getRange().matches(timeValue); if (rc) rc = timeValue % getSteps() == 0; return rc; } /** * @return the range */ public AbstractTimeValue getRange() { return range; } /** * @param range the range to set */ public void setRange(AbstractTimeValue range) { this.range = range; } /** * @return the steps */ public int getSteps() { return steps; } /** * @param steps the steps to set */ public void setSteps(int steps) { this.steps = steps; } /** * Returns cron-like string of this definition. */ public String toString() { return getRange()+"/"+getSteps(); } } /** * Represents the ALL time, *. * @author RalphSchuster */ public static class TimeAll extends AbstractTimeValue { public TimeAll() { } /** * Returns always true. * @param timeValue - time value to evaluate * @return true */ public boolean matches(int timeValue) { return true; } /** * Returns cron-like string of this definition. */ public String toString() { return "*"; } } } |
Some explanations how you can use this class:
- Create the schedule by just passing in the cron-like definition to the constructor method.
- You can also create a schedule by using the default constructor and then setting the schedule with set(String). The advantage of this method is that it will return you any additional information that follows the schedule definition, e.g. as you can find it in /etc/crontab.
- Beware that Java defines Sunday as 1, Saturday as 7. The real crontab ranges from 0-6 (7 as sunday, too)
- matches(long) (line 149) is the default method to check whether a timestamp fulfills the requirements of the schedule definition. This method always checks whether the second of the timestamp is 0 (no millisecond check!).
- isMinute(long), isHour(long), isDay(long) check the given timestamp only if the respective time requirement is met. That mean isMinute() will ignore seconds, isHour() will ignore minutes and seconds, isDay() will ignore time of the day.
- Override getCalendar(long) (line 251) when you don’t wanna use the Gregorian Calendar or need a different timezone than your current default timezone.
- You can easily add more time value checks (e.g. week of the year) by simply enhancing the TYPES array (line 23)
Hashing Passwords
Here is some code that you can use to hash passwords or other secrets in Java. I usually prefer to have such methods in a separate utility class:
protected static MessageDigest getDigest() throws NoSuchAlgorithmException { if (digest == null) { digest = MessageDigest.getInstance(&qout;MD5&qout;); } return digest; } public static byte[] digestString(String s) { if (s == null) return null; try { MessageDigest digest = getDigest(); digest.update(s.getBytes()); return digest.digest(); } catch (Exception e) { log.error(&qout;Digesting problem:&qout;, e); } return null; } public static String encodePassword(String s) { byte b[] = digestString(s); if (b == null) return null; String rc = new String(Base64.encodeBase64(b)); if (rc.length() > 50) rc = rc.substring(0, 50); return rc; } |
Use the function encodePassword() to hash your string. Please note that the hash value is limited to a length of 50 characters.
Here is a short HOWTO for making some initial configuring when using Commons logging or log4j. Additionally this post will describe how to set the default locale in a servlet environment.
First you will need to create a new class derived from HttpServlet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package mypackage; import java.util.Locale; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.LogFactory; import org.apache.log4j.PropertyConfigurator; /** * Initializes log4j so it reads its configuration from WEB-INF directory and sets default locale. * @author Ralph Schuster * */ public class LogConfiguratorServlet extends HttpServlet { /** * Serial ID. */ private static final long serialVersionUID = -5756062055681369027L; private static final String DEFAULT_FILE = "WEB-INF/log4j.properties"; private static final String DEFAULT_LOCALE = "en_US"; // You can use "en", too. /** * Initializes log4j and sets default locale. */ public void init() { // Do the log4j configuration String prefix = getServletContext().getRealPath("/"); String file = getInitParameter("config-file"); // if the config-file is not set, then no point in trying String s = null; if (file != null) { s = prefix+file; } else { s = prefix+DEFAULT_FILE; } PropertyConfigurator.configure(s); LogFactory.getLog(getClass()).debug("log4j configuration file: "+s); // Do the locale configuration s = getInitParameter("locale"); if (s == null) s = DEFAULT_LOCALE; Locale available[] = Locale.getAvailableLocales(); for (int i=0; i<available.length; i++) { if (available[i].toString().equals(s)) { Locale.setDefault(available[i]); } } LogFactory.getLog(getClass()).debug("Default locale set to: "+Locale.getDefault()); } /** * Does nothing. */ public void doGet(HttpServletRequest req, HttpServletResponse res) { } } |
You then need to deploy the class in your servlet container and adjust the web.xml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <servlet> </servlet-name>log4j-init</servlet-name> <servlet-class>mypackage.LogConfiguratorServlet</servlet-class> <init-param> <param-name>config-file</param-name> <param-value>WEB-INF/log4j.properties</param-value> </init-param> <init-param> <param-name>locale</param-name> <param-value>de_DE</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> |
SimpleCaptcha for Servers
Currently there is a bug in SimpleCaptcha library that prevents it running in non-graphics environments. It will throw a java.awt.HeadlessException. Although the code does not rely on various graphics classes, they were left causing this problem.
I fixed the code and recompiled it. You can download the fixed JAR file here. Do not forget to set the system property java.awt.headless to true, e.g. by specifying Djava.awt.headless=true at command line.
Here is a class I wrote to apply TYPO3-like templating mechanism within Java. I am quite familiar with that kind of templates, so I decided to use it within one of my projects, too. The implementation requires Java 5.
You need to adopt the class’ package though 😉
Need to transform URL parameters and decode values such as “Hello%20World!”? Here is how:
Perl:
$s =~ s/%([\da-f][\da-f])/chr( hex($1) )/egi; |
Java:
s = java.net.URLEncoder.encode(s, "UTF-8"); |
PHP:
$s = urldecode($s); |