Tuesday, December 18, 2007

Simple authentication over https with invalid certificate

Here's the story for opening an input stream on a url via https when the site has an invalid certificate (e.g. expired). You could place the certificate in your trusted store but this is a useful shortcut!

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SSL {

public static class SimpleAuthenticator extends Authenticator {
private final String username, password;

public SimpleAuthenticator(String username, String password) {
this.username = username;
this.password = password;
}

@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
}

public static void main(String[] args) throws IOException,
NoSuchAlgorithmException, KeyManagementException {
SSLContext sc = SSLContext.getInstance("SSL");
TrustManager trustManager = new X509TrustManager() {

@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// don't throw exception
}

@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// don't throw exception
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}

};
sc.init(null, new TrustManager[] { trustManager },
new java.security.SecureRandom());

HostnameVerifier hostNameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
java.net.URL url = new URL("https://hostname/stuff");
Authenticator.setDefault(new SimpleAuthenticator("username", "password"));
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setHostnameVerifier(hostNameVerifier);
con.setSSLSocketFactory(sc.getSocketFactory());

InputStream is = con.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
isr.close();
is.close();

}

}

Thursday, November 29, 2007

XML Schemas - Best Practices to Manage Change

I create xml schemas based on my experience processing them in an automated fashion. Here are some pointers that make things easy for an automated xml consumer and also allow more robust management of (inevitable) schema changes:

When you write code to process xml based on a schema, use objects generated from the schema. This gives you protection from schema change in as much as a change in the schema and a rebuild of your code (including the code generation of course) will then indicate needed changes in your code with compile errors. This is a tremendous help, wherever possible look for compile time indication of errors rather than runtime errors after deployment!

For Java JDK 6 use the xjc tool to generate classes from a schema. Script it up say in Ant as part of your project build.

Designing an xml schema is like designing a relational database, and involves elements of object oriented design. Know these things before embarking on schema design!

Reuse types, both simple and complex. That way when you write code with generated objects you will be able to resuse code working on particular types.

Give maximum size limits to your data types, then when you design database fields to capture this information, provide bigger database field sizes than you initially need to accomodate change (e.g. put 50 chars down as a limit for a surname in the schema but use 100 chars in the database field). Remember, disk space is cheap having the same data in a varchar(100) won't take up more space than in a varchar(50). Somewhat larger field size limits won't slow your database down either.

Use lower case hyphenated element names (<start-time>) or upper camel case (<StartTime>) in the xml. Hyphenated names are still translated into object properties like "startTime" by tools such as xjc.

If an xml element has an associated unit, include the unit in the element name (either the SI unit abbreviation, or spell it out, e.g length-feet, mass-pounds). i.e rather than <time>54</time> use <time-ms>54</time-ms> or <time-milliseconds>54</time-milliseconds>. That way when someone produces xml using generated objects they will always know how to scale the value for the required unit (without having to check documentation for the field).

Thursday, November 8, 2007

oracle sqlplus ant task

Having used oracle and java for yonks I thought I'd improve my toolset a bit with this ant task. It's pretty useful especially as it detects errors in the running of the script and can optionally fail.

This ant task provides all the options available for the sqlplus exe (and a few more).

Note that the end of your script needs an exit, otherwise the task just hangs around.

<target name="run.sqlplus">
<taskdef name="sqlplus" classname="moten.david.util.database.oracle.SqlPlusTask" classpathref="libraries" />
<sqlplus logon="scott/tiger@orcl" failonerror="false">
<sql>
update document set folder_id = 14
where document_id in (
select document_id
from message_details
where message_id in
(5204249,5204257,5204258,5204260,5204273,5204274,
5204280,5204287,5204288,5204290,5204291,5204297)
);

update message_details
set sent_received_dtg=sysdate
where message_id in
(5204249,5204257,5204258,5204260,5204273,5204274,
5204280,5204287,5204288,5204290,5204291,5204297);

commit;

exit;
</sql>
</sqlplus>
</target>


Download the jar and source from moten-util

Tuesday, November 6, 2007

oracle loadjava ant task

Created an ant task to perform loadjava for oracle 10g and later. Works nicely for me. See moten-util for the jar and source code.

<taskdef name="loadjava" classname="moten.david.util.database.oracle.LoadJavaTask" classpathref="libraries" />
<loadjava failonerror="true" resolve="false"
force="false" showchangesonly="false" showlog="true"
thin="true" user="scott/tiger@dbhost:1521:orcl">
<path>
<fileset dir="${lib}/axis1.4">
<include name="*.jar" />
<include name="log4j.properties" />
</fileset>
</path>
</loadjava>
Note that classpathref="libraries" assumes you have defined the class below in the classpathref "libraries". You can just use an embedded classpath element inside the taskdef to point to it (see the ant manual for details or use eclipse for autocomplete).

The java class:
package moten.david.util.database.oracle;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;

public class LoadJavaTask extends Task {
private boolean failOnError = true;
private boolean showLog = true;
private boolean showChangesOnly = true;
private boolean definer = false;
private String user;
private boolean thin = true;
private boolean resolve = true;
private boolean verbose = false;
private boolean force = false;
private boolean oci8 = false;
private boolean showHelp = false;
private List paths = new ArrayList();
private String encoding;
private boolean genmissing = false;
private String genmissingjar;
private String grants;
private boolean nousage = false;
private boolean help = false;
private boolean noverify = false;
private boolean order = false;
private String resolver;
private String schema;
private boolean synonym = false;
private String tableschema;

@Override
public void execute() throws BuildException {
log("executing");
super.execute();
ArrayList args = new ArrayList();
if (failOnError)
args.add("-failOnError");
if (showLog)
args.add("-showLog");
if (showChangesOnly)
args.add("-showChangesOnly");
if (showHelp)
args.add("-showHelp");
if (definer)
args.add("-definer");
if (encoding != null)
args.add("-encoding " + encoding);
if (force)
args.add("-force");
if (genmissing)
args.add("-genmissing");
if (genmissingjar != null) {
args.add("-genmissingjar");
args.add(genmissingjar);
}
if (grants != null) {
args.add("-grant");
args.add(grants);
}
if (help)
args.add("-help");
if (nousage)
args.add("-nousage");
if (noverify)
args.add("-noverify");
if (oci8)
args.add("-oci8");
if (order)
args.add("-order");
if (resolve)
args.add("-resolve");
if (resolver != null) {
args.add("-resolver");
args.add(resolver);
}
if (schema != null) {
args.add("-schema");
args.add(schema);
}
if (synonym)
args.add("-synonym");
if (thin)
args.add("-thin");
if (tableschema != null) {
args.add("-tableschema");
args.add(tableschema);
}
if (user != null) {
args.add("-user");
args.add(user);
}
if (verbose)
args.add("-verbose");
for (Iterator itPaths = paths.iterator(); itPaths.hasNext();) {
Path path = itPaths.next();
String[] includedFiles = path.list();
for (int i = 0; i < includedFiles.length; i++) {
args.add(includedFiles[i]);
}
}
log(args+"");
String[] arguments = args.toArray(new String[] {});
try {
Deployer.main(arguments);
} catch (IOException e) {
throw new BuildException(e);
}
}

public List getPaths() {
return paths;
}

public void setPaths(List paths) {
this.paths = paths;
}

public String getEncoding() {
return encoding;
}

public void setEncoding(String encoding) {
this.encoding = encoding;
}

public boolean isGenMissing() {
return genmissing;
}

public void setGenMissing(boolean genMissing) {
this.genmissing = genMissing;
}

public String getGenMissingJar() {
return genmissingjar;
}

public void setGenMissingJar(String genMissingJar) {
this.genmissingjar = genMissingJar;
}

public String getGrant() {
return grants;
}

public void setGrant(String grant) {
this.grants = grant;
}

public boolean isNousage() {
return nousage;
}

public void setNousage(boolean nousage) {
this.nousage = nousage;
}

public boolean isHelp() {
return help;
}

public void setHelp(boolean help) {
this.help = help;
}

public boolean isNoverify() {
return noverify;
}

public void setNoverify(boolean noverify) {
this.noverify = noverify;
}

public boolean isOrder() {
return order;
}

public void setOrder(boolean order) {
this.order = order;
}

public String getResolver() {
return resolver;
}

public void setResolver(String resolver) {
this.resolver = resolver;
}

public String getSchema() {
return schema;
}

public void setSchema(String schema) {
this.schema = schema;
}

public boolean isSynonym() {
return synonym;
}

public void setSynonym(boolean synonym) {
this.synonym = synonym;
}

public String getTableschema() {
return tableschema;
}

public void setTableschema(String tableschema) {
this.tableschema = tableschema;
}

public LoadJavaTask() {
// do nothing
}

public void addPath(Path path) {
paths.add(path);
}

public boolean isFailOnError() {
return failOnError;
}

public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}

public boolean isShowLog() {
return showLog;
}

public void setShowLog(boolean showLog) {
this.showLog = showLog;
}

public boolean isShowChangesOnly() {
return showChangesOnly;
}

public void setShowChangesOnly(boolean showChangesOnly) {
this.showChangesOnly = showChangesOnly;
}

public String getUser() {
return user;
}

public void setUser(String user) {
this.user = user;
}

public boolean isThin() {
return thin;
}

public void setThin(boolean thin) {
this.thin = thin;
}

public boolean isResolve() {
return resolve;
}

public void setResolve(boolean resolve) {
this.resolve = resolve;
}

public boolean isVerbose() {
return verbose;
}

public void setVerbose(boolean verbose) {
this.verbose = verbose;
}

public boolean isForce() {
return force;
}

public void setForce(boolean force) {
this.force = force;
}

public boolean isOci8() {
return oci8;
}

public void setOci8(boolean oci8) {
this.oci8 = oci8;
}

public boolean isShowHelp() {
return showHelp;
}

public void setShowHelp(boolean showHelp) {
this.showHelp = showHelp;
}

public boolean isDefiner() {
return definer;
}

public void setDefiner(boolean definer) {
this.definer = definer;
}

}

Here's the Deployer class:



package moten.david.util.database.oracle;

/*
* Created on 14/12/2005
*
* Author: DMoten
*/

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.security.Permission;

import oracle.aurora.server.tools.loadjava.LoadJavaMain;

public class Deployer {

/**
* Runs oracle.aurora.server.tools.loadjava.LoadJavaMain with args and
* appends output to file deployer.log. If the option -failOnError is
* included in the arguments then an Error will be thrown if the word
* 'errors' exists in the output from LoadJavaMain. If the option -showLog
* is included then -verbose info will be output (i.e. info for each class
* being loaded). The log is always shown if an error occurrs or even if a
* System.exit is called (the SecurityManager is overriden).
*
* Note that the LoadJavaMain method hangs if the login is incorrect. Watch
* out for that one!
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {

if (args != null && args.length > 0) {
System.out.println("Deploying " + args[args.length - 1]);
}
final PrintStream stdOut = System.out;
final PrintStream stdErr = System.err;
boolean errorOccurred = false;
MainParameters params = new MainParameters(args);
boolean failOnError = params.optionExists("-failOnError");
final boolean showLog = params.optionExists("-showLog");
final boolean showChangesOnly = params.optionExists("-showChangesOnly");
boolean showHelp = params.optionExists("-showHelp");
boolean logToFile = params.optionExists("-logToFile");
args = params.removeOption("-failOnError");
args = params.removeOption("-showLog");
args = params.removeOption("-showChangesOnly");
args = params.removeOption("-showHelp");
args = params.removeOption("-logToFile");
final ByteArrayOutputStream logBytes = new ByteArrayOutputStream() {
@Override
public synchronized void write(int b) {
super.write(b);
if (showLog)
stdOut.append((char) b);
}
};
final PrintStream myOut = new PrintStream(logBytes);
System.setOut(myOut);
System.setErr(myOut);

final SecurityManager sm = System.getSecurityManager();

System.setSecurityManager(new SecurityManager() {
@Override
public void checkAccept(String host, int port) {
// ok
}

@Override
public void checkAccess(Thread t) {
// ok
}

@Override
public void checkAccess(ThreadGroup g) {
// ok
}

@Override
public void checkAwtEventQueueAccess() {
// ok
}

@Override
public void checkConnect(String host, int port, Object context) { // ok
}

@Override
public void checkConnect(String host, int port) { // ok
}

@Override
public void checkCreateClassLoader() { // ok
}

@Override
public void checkDelete(String file) { // ok
}

@Override
public void checkExec(String cmd) { // ok
}

@Override
public void checkLink(String lib) { // ok
}

@Override
public void checkListen(int port) { // ok
}

@Override
public void checkMemberAccess(Class arg0, int arg1) { // ok
}

@Override
public void checkMulticast(InetAddress maddr) { // ok
}

@Override
public void checkPackageAccess(String pkg) { // ok
}

@Override
public void checkPackageDefinition(String pkg) { // ok
}

@Override
public void checkPermission(Permission perm, Object context) { // ok
}

@Override
public void checkPrintJobAccess() { // ok
}

@Override
public void checkPropertyAccess(String key) { // ok
}

@Override
public void checkRead(FileDescriptor fd) { // ok
}

@Override
public void checkRead(String file, Object context) { // ok
}

@Override
public void checkRead(String file) { // ok
}

@Override
public void checkSecurityAccess(String target) {// ok
}

@Override
public void checkSetFactory() {// ok
}

@Override
public void checkSystemClipboardAccess() {// ok
}

@Override
public boolean checkTopLevelWindow(Object window) {// ok
return sm.checkTopLevelWindow(window);
}

@Override
public void checkWrite(FileDescriptor fd) {// ok
}

@Override
public void checkWrite(String file) {// ok
}

@Override
public void checkPermission(Permission perm) {// ok
// sm.checkPermission(perm);
}

@Override
public void checkExit(int status) {
if (status != 0) {
myOut.close();
stdOut.println(logBytes.toString());
}
sm.checkExit(status);
}

@Override
public void checkPropertiesAccess() {
super.checkPropertiesAccess();
}
});

if (showHelp)
LoadJavaMain.main(new String[] { "-help" });

try {
StringBuffer s = new StringBuffer();
for (String element : args) {
s.append(" '" + element + "'");
}
myOut.println("running LoadJavaMain class with args: "
+ s.toString());
LoadJavaMain.main(args);
} catch (Error e) {
e.printStackTrace();
errorOccurred = true;
}
myOut.close();
errorOccurred = errorOccurred
|| logBytes.toString().indexOf("errors :") > 0;
FileOutputStream fos = null;
if (logToFile)
fos = new FileOutputStream(new File("deployJava.log"));
String log;
if (showChangesOnly) {
StringBuffer b = new StringBuffer(logBytes.toString());
while (b.toString().indexOf("identical:") >= 0) {
int startIndex = b.toString().indexOf("identical:");
int endIndex = b.toString().indexOf("\n", startIndex);
b.delete(startIndex, endIndex);
}
while (b.toString().indexOf("skipping :") >= 0) {
int startIndex = b.toString().indexOf("skipping :");
int endIndex = b.toString().indexOf("\n", startIndex);
b.delete(startIndex, endIndex);
}
log = b.toString();
} else
log = logBytes.toString();

if (logToFile) {
fos.write(log.getBytes());
if (errorOccurred) {
String msg = "\nErrors Occurred!";
fos.write(msg.getBytes());
stdOut.write(msg.getBytes());
}
fos.close();
}
System.setOut(stdOut);
System.setErr(stdErr);
if (failOnError) {
if (errorOccurred) {
if (!showLog)
System.out.println(log);
throw new DeployerError(
"deployment failed due to error appearing in ");
}
}
}
}

class DeployerError extends Error {

private static final long serialVersionUID = 1809358860733551336L;

public DeployerError(String msg) {
super(msg);
}
}


MainParameters class:



package au.gov.amsa.util.database.oracle;

/*
* Created on 13/01/2006
*
* Author: DMoten
*/

import java.util.ArrayList;

public class MainParameters {

String[] args;

public MainParameters(String[] args) {
this.args = args;
}

public boolean optionExists(String option) {
boolean found = false;
for (int i = 0; i < args.length; i++) {
found = found || args[i].equalsIgnoreCase(option);
}
return found;
}

public String[] removeOption(String option) {
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < args.length; i++) {
if (!args[i].equalsIgnoreCase(option)) {
list.add(args[i]);
}
}
String[] argsNew = new String[list.size()];
for (int i = 0;i<argsNew.length;i++) {
argsNew[i]=(String) list.get(i);
}
this.args = argsNew;
return args;
}

@Override
public String toString() {
StringBuffer s = new StringBuffer();
for (int i=0;i<args.length;i++) {
s.append(args[i]+"\n");
}
return s.toString();
}

public static void main(String[] args) {
args = new String[] {"a","b","c"};
MainParameters m = new MainParameters(args);
m.removeOption("b");
}
}


free hit counter