/*
* eID Applet Project.
* Copyright (C) 2008-2009 FedICT.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/
package be.fedict.eid.applet.shared.protocol;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.List;
import be.fedict.eid.applet.shared.annotation.HttpBody;
import be.fedict.eid.applet.shared.annotation.HttpHeader;
import be.fedict.eid.applet.shared.annotation.NotNull;
/**
* Transport component is responsible for governing the process of converting
* Java objects into data streams using a HTTP transport component.
*
* @author Frank Cornelis
*
*/
public class Transport {
private Transport() {
super();
}
/**
* Transfers the given data objects over the HTTP transport component.
*
* @param dataObject
* the data objects to transfer.
* @param httpTransmitter
* the transport component.
*/
public static void transfer(Object dataObject, HttpTransmitter httpTransmitter) {
/*
* Secure channel validation.
*/
if (false == httpTransmitter.isSecure()) {
throw new SecurityException("applet service connection not trusted");
}
// TODO: semantic integrity validation
Class<?> dataClass = dataObject.getClass();
Field[] fields = dataClass.getFields();
/*
* Input validation.
*/
try {
inputValidation(dataObject, fields);
} catch (Exception e) {
throw new IllegalArgumentException("error: " + e.getMessage(), e);
}
/*
* Add HTTP headers.
*/
Field bodyField = addHeaders(dataObject, httpTransmitter, fields);
/*
* Add HTTP body.
*/
addBody(dataObject, httpTransmitter, bodyField);
}
@SuppressWarnings("unchecked")
private static void addBody(Object dataObject, HttpTransmitter httpTransmitter, Field bodyField) {
if (null != bodyField) {
Object bodyValue;
try {
bodyValue = bodyField.get(dataObject);
} catch (Exception e) {
throw new RuntimeException("error reading field: " + bodyField.getName());
}
byte[] body;
if (bodyValue instanceof List<?>) {
List<String> bodyList = (List<String>) bodyValue;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(baos);
for (String bodyStr : bodyList) {
printStream.println(bodyStr);
}
body = baos.toByteArray();
} else {
body = (byte[]) bodyValue;
}
/*
* The Content-Length header is required for IIS 6 and 7.
*/
httpTransmitter.addHeader("Content-Length", Integer.toString(body.length));
httpTransmitter.setBody(body);
} else {
httpTransmitter.addHeader("Content-Length", "0");
}
}
private static Field addHeaders(Object dataObject, HttpTransmitter httpTransmitter, Field[] fields) {
Field bodyField = null;
for (Field field : fields) {
HttpBody httpBodyAnnotation = field.getAnnotation(HttpBody.class);
if (null != httpBodyAnnotation) {
if (null == bodyField) {
bodyField = field;
} else {
throw new RuntimeException("multiple @HttpBody fields detected");
}
}
HttpHeader httpHeaderAnnotation = field.getAnnotation(HttpHeader.class);
if (null == httpHeaderAnnotation) {
continue;
}
Object fieldValue;
try {
fieldValue = field.get(dataObject);
} catch (Exception e) {
throw new RuntimeException("error reading field: " + field.getName());
}
if (null != fieldValue) {
String httpHeaderName = httpHeaderAnnotation.value();
String httpHeaderValue;
if (String.class.equals(field.getType())) {
httpHeaderValue = (String) fieldValue;
} else if (Integer.TYPE.equals(field.getType()) || Integer.class.equals(field.getType())) {
httpHeaderValue = ((Integer) fieldValue).toString();
// TODO: make this more generic
} else if (Boolean.TYPE.equals(field.getType()) || Boolean.class.equals(field.getType())) {
httpHeaderValue = ((Boolean) fieldValue).toString();
} else if (field.getType().isEnum()) {
httpHeaderValue = ((Enum<?>) fieldValue).name();
} else {
throw new RuntimeException("unsupported field type: " + field.getType().getName());
}
httpTransmitter.addHeader(httpHeaderName, httpHeaderValue);
}
}
return bodyField;
}
private static void inputValidation(Object dataObject, Field[] fields)
throws IllegalArgumentException, IllegalAccessException {
for (Field field : fields) {
NotNull notEmptyAnnotation = field.getAnnotation(NotNull.class);
if (null == notEmptyAnnotation) {
continue;
}
Object fieldValue = field.get(dataObject);
if (null == fieldValue) {
throw new IllegalArgumentException("input validation error: empty field: " + field.getName());
}
}
}
}