Monday, March 29, 2010

This blog has moved


This blog is now located at http://gman.eichberger.de/.
You will be automatically redirected in 30 seconds, or you may click here.

For feed subscribers, please update your feed subscriptions to
http://gman.eichberger.de/feeds/posts/default.

Thursday, February 25, 2010

NIO proxy for manipulating traffic

I wrote some little proxy for manipulating the traffic between a web service client and server - though it can be used for other traffic as well. I used those two fantastic sites to brush up on NIO: Example Depot and IBM developerWorks. Here's the code I came up with:


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOTunnel {
int port = 8080;
String hostName = "localhost";
int hostPort = 8089;
private ServerSocketChannel ssServerChannel;
private Selector selector;

protected SocketChannel createClientSocket() throws IOException {
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false); // Send a connection request to the
// server; this method is
// non-blocking
sChannel.connect(new InetSocketAddress(hostName, hostPort));

return sChannel;
}

protected void createServerChannel() throws IOException {
ssServerChannel = ServerSocketChannel.open();
ssServerChannel.configureBlocking(false);
ssServerChannel.socket().bind(new InetSocketAddress(port));

}

protected void setUpAndWait() throws IOException {
selector = Selector.open();
// Register both channels with selector
createServerChannel();
ssServerChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) { // Wait for an event
selector.select(); // Get list of selection keys with pending events
Iterator it = selector.selectedKeys().iterator(); // Process
// each
// key
while (it.hasNext()) { // Get the selection key
SelectionKey key = (SelectionKey) it.next(); // Remove it from
// the list to
// indicate that it
// is being
// processed
it.remove(); // Check if it's a connection request
SocketChannel socket;
if (key.isAcceptable()) {
System.out.println("Acceptable Key");
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
socket = (SocketChannel) ssc.accept();
socket.configureBlocking(false);
ConnectionHandler h = new ConnectionHandler(socket, createClientSocket());
h.run();
}

}
}
}

public static void main(String[] args) throws IOException {
NIOTunnel tunnel = new NIOTunnel();
tunnel.setUpAndWait();
}
}

public class ConnectionHandler extends Thread {
private SocketChannel serverChannel;
private SocketChannel clientChannel;
private Selector selector;
private ModifyTraffic modifyTraffic;

public ConnectionHandler(SocketChannel serverChannel, SocketChannel clientChannel) {
this.serverChannel = serverChannel;
this.clientChannel = clientChannel;
this.modifyTraffic = new ModifyTraffic();
}

public void run() {
try {
selector = Selector.open();
while (!clientChannel.finishConnect()) {
//wait
}
SelectionKey client = clientChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
SelectionKey server = serverChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);

client.attach(new WriteStorage(false));
server.attach(new WriteStorage(true));

while (client.isValid() && server.isValid() )
{
selector.select();
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();

while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
it.remove();

if (key.isValid() && key.isReadable()) {
String ret = readMessage(key);
WriteStorage ws = (WriteStorage) key.attachment();
if (ws.isServer()) {
((WriteStorage)client.attachment()).append(modifyTraffic.server(ret));
} else {
((WriteStorage)server.attachment()).append(modifyTraffic.client(ret));
}
continue;
}

if (key.isValid() && key.isWritable()) {
writeMessage(key, (WriteStorage)key.attachment());
}

}
}

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
} finally {
try {
selector.close();
clientChannel.close();
serverChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

public void writeMessage(SelectionKey key, WriteStorage writeStorage) throws IOException
{
SocketChannel socket = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.wrap(writeStorage.getString().getBytes());
int nBytes = socket.write(buffer);
writeStorage.clear();
}




public String readMessage(SelectionKey key) throws IOException
{
int nBytes = 0;
SocketChannel socket = (SocketChannel)key.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
nBytes = socket.read(buf);
if (nBytes == -1) {
return null;
}
buf.flip();
Charset charset = Charset.forName("us-ascii");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buf);
String result = charBuffer.toString();
System.out.println(result);
return result;

}
}

public class WriteStorage {
private StringBuffer toWrite;
private boolean server;

public WriteStorage(boolean server) {
this.setServer(server);
clear();
}

public boolean isServer() {
return server;
}
public void setServer(boolean server) {
this.server = server;
}

public void clear() {
toWrite = new StringBuffer();
}

public void append(String s) {
toWrite.append(s);
}

public String getString() {
return toWrite.toString();
}
}

Wednesday, January 20, 2010

WBem, Wiseman, Windows, Java and Negotiate authentication

I was playing around with WBem and tried to access the Windows Remote stuff on my Vista laptop. To be special Mocrosoft is calling their WBem implementation Windows Remote (WinRM). Because it's complicate here is some good blog entry on how to make WinRM work for you.

Back to Wiseman: Denis Rachal has posted some code to start/stop a service using Wiseman and WinRM to the Wiseman's user's mailing list on 6/18/2009. The problem with Wiseman is that you need to set up your WinRM to use unencrypted communication and basic authentication. Because the encryption is relatively new and "invented" by MS called "HTTP-SPNEGO-session-encrypted" the RFC (Google cache -- as you might expect from MS:-) is kind-a cryptic...

But we can do something about the authentication and use Negotiate by using the SpnegoHttpUrlConnection implementation from the SPNEGO project. Make sure to read through all their pre-flight documentation to set up kerberos and so on (keyword: krb5.conf, login.conf) The account you use needs to be at least a local administrator and it is helpful to prefix the domain for the user name (MY_DOMAIN\MY-USERNAME) Once this works you can rework Denis' example and have it support the Negotiate protocol as I did:


/**
* Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Authors: Denis Rachal (denis.rachal@hp.com)
*/
package com.hp.wsman.client.transport;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedActionException;
import java.security.SecureRandom;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.login.LoginException;
import javax.xml.bind.JAXBException;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;

import org.ietf.jgss.GSSException;

import net.sourceforge.spnego.SpnegoHttpURLConnection;

import sun.misc.BASE64Encoder;

import com.sun.ws.management.Message;
import com.sun.ws.management.addressing.Addressing;
import com.sun.ws.management.transport.ContentType;

public final class HttpClient {

private static final Logger LOG = Logger.getLogger(HttpClient.class
.getName());
private static PasswordAuthentication auth;

private HttpClient() {
}

static {
System.setProperty("java.security.krb5.conf", "C:\\Users\\eichbege\\wiseman-client\\client\\src\\krb5.conf");
System.setProperty("sun.security.krb5.debug", "false");
System.setProperty("java.security.auth.login.config", "C:\\Users\\eichbege\\wiseman-client\\client\\src\\login.conf");
}

static class MyAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
// I haven't checked getRequestingScheme() here, since for NTLM
// and Negotiate, the usrname and password are all the same.
System.err.println("Feeding username and password for " + getRequestingScheme());
return auth;
}
}


public static void setPasswordAuthentication(
final PasswordAuthentication pauth) {
auth = pauth;
}


public static void setTrustManager(final X509TrustManager trustManager)
throws NoSuchAlgorithmException, KeyManagementException {

final TrustManager[] tm = { trustManager };
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tm, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext
.getSocketFactory());
}

public static void setHostnameVerifier(final HostnameVerifier hv) {
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}

private static HttpURLConnection initConnection(final String to,
final ContentType ct, Object msg) throws IOException {
if (to == null) {
throw new IllegalArgumentException("Required Element is missing: "
+ Addressing.TO);
}

if (auth != null) {
//Authenticator.setDefault(new MyAuthenticator());

// String encodedUserPassword = new BASE64Encoder().encode((auth
// .getUserName()
// + ":" + new String(auth.getPassword())).getBytes());
// conn.setRequestProperty("Authorization", "Basic "
// + encodedUserPassword);
}

final URL dest = new URL(to);
URLConnection conn = null;
SpnegoHttpURLConnection spnego = null;
try {
spnego = new SpnegoHttpURLConnection("custom-client", auth.getUserName(), auth.getPassword().toString());
spnego.setRequestMethod("POST");
spnego.setRequestProperty("Content-Type",
ct == null ? ContentType.DEFAULT_CONTENT_TYPE.toString() : ct
.toString());
spnego.setRequestProperty("User-Agent", "https://wiseman.dev.java.net");
spnego.setRequestProperty("Accept", ContentType.ACCEPTABLE_CONTENT_TYPES);
conn = spnego.connect(dest, transfer(msg));
} catch (GSSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (PrivilegedActionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (LoginException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SOAPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//Authenticator.setDefault(new MyAuthenticator());
//conn = dest.openConnection();

//


final HttpURLConnection http = (HttpURLConnection) conn;
//http.setRequestMethod("POST");

return http;
}

// type of data can be Message or byte[], others will throw
// IllegalArgumentException
private static ByteArrayOutputStream transfer(final Object data)
throws IOException, SOAPException, JAXBException {
ByteArrayOutputStream os = null;
try {
os = new ByteArrayOutputStream();
if (data instanceof Message) {
((Message) data).writeTo(os);
} else if (data instanceof SOAPMessage) {
((SOAPMessage) data).writeTo(os);
} else if (data instanceof byte[]) {
os.write((byte[]) data);
} else {
throw new IllegalArgumentException("Type of data not handled: "
+ data.getClass().getName());
}
return os;
} finally {
if (os != null) {
os.close();
}
}
}

public static Addressing sendRequest(final SOAPMessage msg,
final String destination) throws IOException, SOAPException,
JAXBException {

if (LOG.isLoggable(Level.FINE))
LOG.fine("\n" + msg + "\n");
final HttpURLConnection http = initRequest(destination, ContentType
.createFromEncoding((String) msg
.getProperty(SOAPMessage.CHARACTER_SET_ENCODING)), msg);
//transfer(http, msg);
final Addressing response = readResponse(http);
if (LOG.isLoggable(Level.FINE)) {
if (response.getBody().hasFault())
LOG.fine("\n" + response + "\n");
else
LOG.fine("\n" + response + "\n");
}
return response;
}

public static Addressing sendRequest(final SOAPMessage msg,
final String destination, Entry... headers)
throws IOException, SOAPException, JAXBException {

if (LOG.isLoggable(Level.FINE))
LOG.fine("\n" + msg + "\n");
final HttpURLConnection http = initRequest(destination, ContentType
.createFromEncoding((String) msg
.getProperty(SOAPMessage.CHARACTER_SET_ENCODING)), msg);
// if (headers != null) {
// for (Entry entry : headers) {
// http.setRequestProperty(entry.getKey(), entry.getValue());
// }
// }
//
// transfer(http, msg);
final Addressing response = readResponse(http);
if (LOG.isLoggable(Level.FINE)) {
if (response.getBody().hasFault())
LOG.fine("\n" + response + "\n");
else
LOG.fine("\n" + response + "\n");
}
return response;
}

public static Addressing sendRequest(final Addressing msg,
final Entry... headers) throws IOException,
JAXBException, SOAPException {

if (LOG.isLoggable(Level.FINE))
LOG.fine("\n" + msg + "\n");
final HttpURLConnection http = initRequest(msg.getTo(), msg
.getContentType(), msg);

// if (headers != null) {
// for (Entry entry : headers) {
// http.setRequestProperty(entry.getKey(), entry.getValue());
// }
// }

//transfer(http, msg);
final Addressing response = readResponse(http);
if (LOG.isLoggable(Level.FINE)) {
if (response.getBody().hasFault())
LOG.fine("\n" + response + "\n");
else
LOG.fine("\n" + response + "\n");
}
response.setXmlBinding(msg.getXmlBinding());
return response;
}

public static HttpURLConnection createHttpConnection(String destination,
Object data) throws SOAPException, JAXBException, IOException {
final HttpURLConnection http = initRequest(destination, null, null);

return http;
}

private static HttpURLConnection initRequest(final String destination,
final ContentType contentType, Object msg) throws IOException {

final HttpURLConnection http = initConnection(destination, contentType, msg);
return http;
}

private static Addressing readResponse(final HttpURLConnection http)
throws IOException, SOAPException {

final InputStream is;
final int response = http.getResponseCode();
if (response == HttpURLConnection.HTTP_OK) {
is = http.getInputStream();
} else if (response == HttpURLConnection.HTTP_BAD_REQUEST
|| response == HttpURLConnection.HTTP_INTERNAL_ERROR) {
// read the fault from the error stream
is = http.getErrorStream();
} else {
final String detail = http.getResponseMessage();
throw new IOException(detail == null ? Integer.toString(response)
: detail);
}

final String responseType = http.getContentType();
final ContentType contentType = ContentType
.createFromHttpContentType(responseType);
if (contentType == null || !contentType.isAcceptable()) {
// dump the first 4k bytes of the response for help in debugging
if (LOG.isLoggable(Level.INFO)) {
final byte[] buffer = new byte[4096];
final int nread = is.read(buffer);
if (nread > 0) {
final ByteArrayOutputStream bos = new ByteArrayOutputStream(
buffer.length);
bos.write(buffer, 0, nread);
LOG.info("Response discarded: "
+ new String(bos.toByteArray()));
}
}
throw new IOException(
"Content-Type of response is not acceptable: "
+ responseType);
}

final Addressing addr;
try {
addr = new Addressing(is);
} finally {
if (is != null) {
is.close();
}
}

addr.setContentType(contentType);

return addr;
}

public static int sendResponse(final String to, final byte[] bits,
final ContentType contentType) throws IOException, SOAPException,
JAXBException {
final HttpURLConnection http = initConnection(to, contentType, bits);
return http.getResponseCode();
}

public static int sendResponse(final Addressing msg) throws IOException,
SOAPException, JAXBException {
final HttpURLConnection http = initConnection(msg.getTo(), msg
.getContentType(), msg);
return http.getResponseCode();
}
}

Supposedly you could achieve the same with the standard HTTP connection from Java 6 (see tutorial) but I couldn't get to work. So who knows... anyway the SPNEGO project's solution worked for me with an unencrypted setup on Windows Vista.

Monday, December 07, 2009

Spring and REST

After loving for more than two years Don Brown's fantastic REST plugin for Struts2 I decendent again into Spring's version of REST (see http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/) My simple porting of Don Brown's demo app to Spring failed again (I tried 12 months ago to use some work from carbon five) Anyway why, why does it not work I thought I had it figured out:



package edu.ucsd.extension.springmvchelloworld;

import java.util.Collection;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;



@Controller
public class OrdersController {

private Order model = new Order();
private Collection list;
private OrdersService ordersService = new OrdersService();

// GET /orders/1
@RequestMapping("{orderId}")
public String show(@PathVariable String id, Model model) {
System.out.println("Showing order " + id);
setId(id);
model.addAttribute("order", getModel());
return "show";
}

// GET /orders
@RequestMapping
public String index(Model model) {
list = ordersService.getAll();
model.addAttribute("orders", list);
return "orders-index";
}

// GET /orders/1/edit
@RequestMapping("/{orderId}/edit")
public String edit(@PathVariable String id, Model model) {
this.setId(id);
model.addAttribute("order", getModel());
return "orders-edit";
}

// GET /orders/new
@RequestMapping("/new")
public String editNew(Model model) {
model.addAttribute("order", new Order());
return "orders-editNew";
}

// GET /orders/1/deleteConfirm
@RequestMapping("/{orderId}/deleteConfirm")
public String deleteConfirm(@PathVariable String id) {
this.setId(id);
return "orders-deleteConfirm";
}

// DELETE /orders/1
@RequestMapping(value="/{orderId}", method=RequestMethod.DELETE)
public String destroy(@PathVariable String id) {
ordersService.remove(id);
//addActionMessage("Order removed successfully");
return "orders-index";
}

// POST /orders
@RequestMapping(method=RequestMethod.POST)
public ModelAndView create(Order model) {
ordersService.save(model);
return new ModelAndView("orders-show", "model", getModel());
}

// PUT /orders/1
@RequestMapping(value="/{orderId}", method=RequestMethod.PUT)
public String update(@PathVariable String id, Order model) {
ordersService.save(model);
return "order-index";
}

public void setId(String id) {
if (id != null) {
this.model = ordersService.get(id);
}
}

public Object getModel() {
return (list != null ? list : model);
}

}


Well, I guess there is some other trick I am missing.

BTW: the repository in the POM file needs to look like:



org.springsource.maven.snapshot
Springframework milestone
http://maven.springframework.org/milestone

Sunday, November 15, 2009

The Passionate Programmer



I have been reading the The Passionate Programmer: Creating a Remarkable Career in Software Development (Pragmatic Life) and I think it is an absolute must read. I stumbled upon it on Matt's blog (see http://raibledesigns.com/rd/entry/the_passionate_programmer_by_chad) and I immediately liked the book. Chad is doing an incredible job explaining how to stay relevant and increase your career in "computers". The most impressive pieces are when he explains how to function in a corporate environment. "Make your manager happy he controls your pay" I can't say often enough how important that is. He also prefers a role maintaining software over some new project because oddly enough it gives you more freedom. His comparisons with the music scene are refreshing however probably not for everyone...

Anyway for $15 it's a must read :-)

Friday, August 28, 2009

Springing in Action

I have been asked to develop an online class for UCSD Extension covering Spring -- I am really excited and I will try to get it going soon --

On another note I started a new job with HP Software working among other things with JSF -- soon I can say I have seen all the web frameworks in the world :-)

Friday, June 26, 2009

SDCodeCamp

Saturday, 6/27 I will be talking on Scala and Spring -- check out their home page (http://www.socalcodecamp.com/)