A blog about software development, primarily in Java and about web applications.

Sunday, December 13, 2009

Looking for Temporary Java Web Developer

Java Web Developer for temporary position at Stanford



The School of Medicine, Stanford University (http://med.stanford.edu/irt/) is looking for a strong, fast-learning Java Web Developer for a highly visible, complex web application.

Applicants must have the following skills:

- Java 6 or Java 5

- Object Oriented Design

- X/HTML coding

- CSS styling

- JSP, JSTL, and JSTL/EL

- Strong understanding of Java Servlets

- JDBC and SQL

- Hibernate 3.2

- Oracle 11g

- JUnit, Unit Testing

- QA Skills

Applications should have

- A good eye for web page design and layout

- A good eye for consistency in UI design and Java coding

- A strong focus on usability and user advocacy

- A strong work ethic and the ability to work well with a team

- Great communication skills

Applicants should be comfortable with the following tools:

- Eclipse IDE

- Oracle SQL Developer

- Ant Build Tool

- Apache Tomcat Servlet Container

- Apache Web Servers

- Developing on a Windows XP machine


Assignment will start at the beginning of January and last through April 30, 2010. After developing the new features for this applications, the applicant will be part of the QA team testing the application and resolving issues.


This is a non-benefited, temporary position.

Hours: Mon – Fri / approximately 9 to 6 (with an hour lunch break).

Location: Menlo Park, near CalTrain

Pay: $50 - $70/hour based on experience


You would be working in a fast paced environment with an expert team of web developers and report directly to the Director of Engineering.


Please forward your resume and have references, including at least one supervisor, available to:

Don Mitchell
donald.mitchell@stanford.edu
Director of Systems Engineering & Architecture
Stanford University School of Medicine



Tuesday, September 22, 2009

Vim Tip - replace comma-separated list of values in XML with individually tagged items

A twitter posting at http://stackoverflow.com/questions/1457537/help-with-grep-in-bbedit/ (formerly http://spreadsheets.google.com/viewform?formkey=dFR1ajFXOEVvd0JsRWNTWktVQzNfNUE6MA..) asked how to convert an XML tag that contained a comma-separated list of values into individually tagged items, each on their own line. They asked how to do this in BBEdit, which I don't use. This is how to do it in Vim:

:%s/<dc:subject>\(\_[^<]\+\)<\/dc:subject>/\=substitute(submatch(0), ",[ \t]\*", "<\/dc:subject>\r<dc:subject>", "g")/g

The above Vim search-replace command will handle tags that span multiple lines and multiple tagged lists on the same line. I use [ \t]\* in the substitute() call because the atom \s (any whitespace) did not work. I had to explicitly list a space and a tab as shown.

\_[^<]\+ matches the entire contents between the original opening and closing tag.

The \=substitute(submatch(0)...) replace string executes a new search and replace command on the matched text. It's this that replaces the comma, optionally followed by a space, with a new closing tag and a newline (\r).

Thursday, August 20, 2009

Tomcat Mime-type Settings

I recently had a user complain that a 3rd party web application that we support was returning garbage when people try to download Microsoft Office 2007 document types in IE7 (it works fine in Firefox). This particular 3rd party application runs in an Apache Tomcat servlet container. I just updated the server's web.xml file to have the following mime-type mappings:


<mime-mapping>
<extension>docm</extension>
<mime-type>application/vnd.ms-word.document.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>docx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.wordprocessingml.document</mime-type>
</mime-mapping>
<mime-mapping>
<extension>dotm</extension>
<mime-type>application/vnd.ms-word.template.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>dotx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.wordprocessingml.template</mime-type>
</mime-mapping>
<mime-mapping>
<extension>potm</extension>
<mime-type>application/vnd.ms-powerpoint.template.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>potx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.presentationml.template</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ppam</extension>
<mime-type>application/vnd.ms-powerpoint.addin.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ppsm</extension>
<mime-type>application/vnd.ms-powerpoint.slideshow.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ppsx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.presentationml.slideshow</mime-type>
</mime-mapping>
<mime-mapping>
<extension>pptm</extension>
<mime-type>application/vnd.ms-powerpoint.presentation.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>pptx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.presentationml.presentation</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xlam</extension>
<mime-type>application/vnd.ms-excel.addin.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xlsb</extension>
<mime-type>application/vnd.ms-excel.sheet.binary.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xlsm</extension>
<mime-type>application/vnd.ms-excel.sheet.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xlsx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xltm</extension>
<mime-type>application/vnd.ms-excel.template.macroEnabled.12</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xltx</extension>
<mime-type>application/vnd.openxmlformats-officedocument.spreadsheetml.template</mime-type>
</mime-mapping>

Wednesday, June 17, 2009

Resending Email (.eml) filesh

I recently had to resend a bunch of old email files and I wanted to preserve the original sent date on them.

I couldn't find an add-on for Thunderbird to do this. I did see an add-on that allows you set a future date for an email, but not a past date. I also couldn't find any other simple program or Cygwin command to use. So I just did it with Java and it ended up being rather simple.

The key thing here was preserving the sent date. I tried setting the clock on my machine back in time, but that doesn't really work because I'd have to reset it to a different date and time for each email and that's just too much manual work. My internet connection also doesn't like it when my machine's time is way off from reality.

So my solution was to first, in Thunderbird (my email client), save all of the emails I wanted to resend as .eml files in a folder. Then I wrote this simple program using Java Mail's API to read in the .eml file and to send the email again preserving all of the fields, except the TO address, which I changed based on my needs.

Here's the program:

import java.io.*;
import java.util.*;

import javax.mail.*;
import javax.mail.internet.*;

public class Eml {

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

String host = "smtp.foo.edu";
String from = "phu@foo.edu";
String to = "bar1@foo.edu, bar2@foo.edu";

Properties props = System.getProperties();
props.setProperty("mail.smtp.host", host);
props.put("mail.transport.protocol", "smtp");

Session mailSession = Session.getDefaultInstance(props, null);
mailSession.setDebug(true);

File emlFile = new File(args[0]);
InputStream source = new FileInputStream(emlFile);

MimeMessage message = new MimeMessage(mailSession, source);

System.out.println("--------------");
System.out.println("To: " + Arrays.asList(message.getAllRecipients()));
System.out.println("From: " + message.getFrom()[0]);
System.out.println("Subject: " + message.getSubject());
System.out.println("Sent Date: " + message.getSentDate());
System.out.println("Body: " + message.getContent());

message.setRecipients(Message.RecipientType.TO, to);
Transport.send(message);

System.out.println("Message sent....");
System.out.println("--------------");
}
}

It takes one argument, the name of the file. I used a simple Cygwin for command to loop through the *.eml files and run this program for each one. I could have added that logic to this Java program, but it was just as quick and easy to do it this way.

There's no error handling here and I used wildcards in the import lines which I usually avoid doing, but this works. I ran with it Java 5.

Friday, April 17, 2009

PHP and Tomcat 5

I've been recently looking at integrating PHP with Tomcat. So far I've been unsuccessful at finding a solution that works on all of our development platforms, in particular, finding a php-cgi implementation for Mac OS X has been hard.

On Windows XP, PHP/Java Bridge has worked well. Here's a couple of useful links:

http://php-java-bridge.sourceforge.net/pjb/index.php

http://php-java-bridge.sourceforge.net/pjb/FAQ.html

Wednesday, April 1, 2009

JSP Best Practices

I'm thinking of giving a talk on JSP best practices. We recently had a couple developers give an excellent talk on CSS and best practices and many of the concepts have corollaries in the underlying JSP rendering of a page. So I thought I start to gather some thoughts in this blog posting:

  1. Your JSP page should be valid XML

  2. Avoid Scriptlets and JSP directives <% ... %>

  3. Use ${fn:escapeXml(...)} frequently, even if you know the data is safe

  4. Use <c:url> tag properly, including it's nested <c:param> tag so that URLs are properly encoded.

  5. Do not put your URL parameters within the <c:url> tag, put them in nested <c:param> tags.

  6. Use the proper and most recent standard JSTL taglibs. Globally replace all old JSTL taglib declarations so you don't have a mix of them floating around.

  7. Don't store your own copy of the standard JSTL tld files, they will inevitably end up out of sync with the JAR file you are using.

  8. Validate the rendered HTML content

  9. Open your JSP page in an XML editor to validate it

  10. Differences between <% include %>, <js:include>, and <c:import>

  11. Page scope for <c:forEach> tag variables and how to pass them to included or imported pages

  12. Parameterizing include/imported JSP fragments

  13. Naming standards for JSP page and fragments

  14. Using a main page template

  15. Using value objects so JSP fragments can be reused

  16. Precompile your JSPs

Tuesday, March 31, 2009

Oracle Text Indexing - escaping user input

Here's a class passed to me for escaping keywords and dealing with how to pass user input to Oracle's text indexing functions. In particular the CONTAINS operator.


// from oracle web site.
package foo;

import java.util.Vector;

/**
* Escapes search phrases so they can be safely passed to Oracle's SQL
* CONTAINS operator.
*


* WARNING: it appears that this class is not thread safe and that
* it maintains state between calls and thus a new object should be created
* between each use rather than reusing the same object.
*/
public class QueryTranslator {

// XXX make thread safe and change so that reqWords and notWords get cleared
// out. If that's done this we don't have to create a new instance of this
// object each time we want to translate something.

private Vector reqWords = new Vector();

private Vector notWords = new Vector();

public String translate(String input) {
if (input.indexOf('{') > -1 || input.indexOf('}') > -1) {
throw new IllegalArgumentException("'}' and '{' should not appear in input");
}
processString(input);
if (this.reqWords.size() == 0) {
throw new IllegalArgumentException("no 'required' words in query: " + input);
}
String translatedQuery = getQuery();
if (translatedQuery.indexOf("()") > -1
|| translatedQuery.indexOf("{}") > -1
|| translatedQuery.indexOf("\\}") > -1) {
throw new IllegalArgumentException("can't construct a valid oracle text query from: " + input);
}
return translatedQuery;
}

private void addWord(final String word, final boolean isRequired) {

if (isRequired) {
this.reqWords.add(word);
} else {
this.notWords.add(word);
}
}

public void processString(final String input) {
int p = 0;
int startWord;
String theWord;

this.reqWords = new Vector();
this.notWords = new Vector();

while (true) { // Loop over all words

startWord = p;
while (p < input.length() && input.charAt(p) != ' ') {
// Check for quoted phrase
if (input.charAt(p) == '"') { // Quote - skip to next or end
p++; // skip the actual quote
while (p < input.length() && input.charAt(p) != '"') {
p++;
}
if (p < input.length()) {
p++; // Skip the final quote if found
}
} else {
p++;
}
}

// Got a word. Check for required/not wanted flags (+-)

theWord = input.substring(startWord, p);

// CY bug 11825, don't process zero length string
if (theWord.length() > 0) {
// CY changed this to required from optional to make it AND
// logic
boolean isRequired = true;

if (theWord.charAt(0) == '+' && theWord.length() > 1) {
isRequired = true;
theWord = theWord.substring(1);
}

else if (theWord.charAt(0) == '-' && theWord.length() > 1) {
isRequired = false;
theWord = theWord.substring(1);
}

// Replace * wild cards with %

theWord = theWord.replace('*', '%');

if (!"%".equals(theWord)) {
addWord(theWord, isRequired);
}

}
p++;
if (p >= input.length()) {
break;
}
}
}

// Get word gets a single word from the "words" vector,
// surrounds it in braces (to avoid reserved words)
// and attaches a WITHIN clause if appropriate.

private String getWord(final Vector words, final int pos) {
// here I added stuff for handling the wildcard, which doesn't work if
// in {}
String word = words.elementAt(pos);
if (word.indexOf('%') > -1) {
word = word.replaceAll("[\\W&&[^%]]", "");
if ("%".equals(word)) {
return "";
}
return word;
}
if (word.lastIndexOf('\\') == word.length() - 1) {
word = word.substring(0, word.length() - 1);
}
return "${".concat( word) + '}';
}

// getQuery returns a formatted, ready-to-run ConText query.
// In order to satisfy the altavista syntax, we have to generate
// the following query:

// ( req1 & req2 & ... reqN)
// | ( (req1 & req2 & .. reqN)*10*10
// & (req1, req2 , ... reqN , opt1 , opt2 , ... optN) )
// NOT (not1 | not2 | ... notN)

public String getQuery() {
StringBuffer sb = new StringBuffer();
// String tempString = "";

String boolOp = ""; // AND, OR, NOT operator
int reqCount; // Count of required words
int notCount; // Count of not wanted words
int i; // Loop control

boolOp = "";
reqCount = this.reqWords.size();
notCount = this.notWords.size();

if (this.reqWords.size() > 0) {
// Required words - first time

sb.append("((");
for (i = 0; i < reqCount; i++) {
sb.append(boolOp).append(getWord(this.reqWords, i));
boolOp = " & ";
}
}

if (reqCount > 0) {
sb.append(")) ");
}

if (notCount > 0) {
boolOp = " NOT ";
} else {
boolOp = "";
}

for (i = 0; i < notCount; i++) {
sb.append(boolOp).append(getWord(this.notWords, i));
boolOp = " NOT ";
}
return sb.toString();
}

public static void main(String args[]) {

if (args.length != 1) {
System.out.println("java " + QueryTranslator.class.getName()
+ " search_phrase");
System.exit(1);
}

System.out.println("Orginal Phrase: " + args[0]);
System.out.println("Translated Phrase: "
+ new QueryTranslator().translate(args[0]));
}
}


Friday, March 27, 2009

Aggregation of Social Content

This is an interesting article on aggregation of others' content.

http://www.web-strategist.com/blog/2009/03/24/breakdown-twitter-federated-media-and-microsofts-sponsored-aggregation/

RequestDispatcher

I've been running into some strange behavior in legacy code dealing with how RequestDispatcher objects are gotten when the incoming URL has invalid characters in the query string portion of the URL. This page didn't help much, but it was interesting reading.

http://www.roseindia.net/javacertification/wcd-guide/machanism.shtml

My problem seems to be caused by crawlers my parsing our pages and not properly unescaping XHTML entities such as & that they encounter in links or image URLs.