/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*/
package jlibs.nio.http;
import jlibs.nio.*;
import jlibs.nio.http.expr.Bean;
import jlibs.nio.http.expr.UnresolvedException;
import jlibs.nio.http.msg.Request;
import jlibs.nio.http.msg.Response;
import jlibs.nio.http.msg.parser.MessageParser;
import jlibs.nio.listeners.Task;
import java.io.Closeable;
import java.nio.channels.SelectionKey;
import java.util.IdentityHashMap;
import java.util.Map;
import static java.nio.channels.SelectionKey.OP_READ;
import static jlibs.nio.Debugger.HTTP;
/**
* @author Santhosh Kumar Tekuri
*/
public abstract class Exchange extends Task implements Bean, Closeable{
protected final ReadMessage readMessage;
protected final WriteMessage writeMessage;
protected Exchange(long maxHeadSize, MessageParser parser, int firstOp){
super(firstOp);
readMessage = new ReadMessage(maxHeadSize, parser);
writeMessage = new WriteMessage();
}
protected void reset(){
keepAlive = false;
error = null;
request = null;
response = null;
attachments = null;
}
@Override
protected int childTaskFinished(Task childTask, Throwable thr){
if(childTask instanceof ReadMessage){
in = ((jlibs.nio.Readable)in.channel()).in();
readMessageFinished(thr);
return OP_READ;
}else{
writeMessageFinished(thr);
return SelectionKey.OP_WRITE;
}
}
protected abstract void readMessageFinished(Throwable thr);
protected abstract void writeMessageFinished(Throwable thr);
@Override
protected void cleanup(Throwable thr){
readMessage.dispose();
writeMessage.dispose();
}
protected Request request;
protected Response response;
public Request getRequest(){ return request; }
public Response getResponse(){ return response; }
protected boolean keepAlive;
public final void resume(){
resume(null);
}
@Trace(condition=HTTP, args="$1")
public final void resume(Throwable thr){
if(thr!=null)
setError(thr);
in.channel().makeActive();
listener.process(in);
}
protected Throwable error = null;
public Throwable getError(){
return error;
}
protected abstract void setError(Throwable thr);
@Override
public void close(){
if(in!=null){
in.channel().close();
connectionStatus = error==null ? ConnectionStatus.CLOSED : ConnectionStatus.ABORTED;
in = null;
out = null;
}
}
public abstract TCPEndpoint getEndpoint();
public abstract Connection stealConnection();
protected ConnectionStatus connectionStatus;
public ConnectionStatus getConnectionStatus(){
return connectionStatus;
}
public int getRequestCount(){
return in==null ? 0 : in.channel().getTaskID();
}
@Override
public String toString(){
return getClass().getSimpleName()+"["+getEndpoint()+"]";
}
/*-------------------------------------------------[ Attachments ]---------------------------------------------------*/
private Map<Key, Object> attachments;
@SuppressWarnings("unchecked")
public <T> T attachment(Key<T> key){
if(attachments ==null)
return key.defaultValue;
else{
T value = (T)attachments.get(key);
return value==null ? key.defaultValue : value;
}
}
@SuppressWarnings("unchecked")
public <T> T attach(Key<T> key, T value){
if(attachments ==null)
attachments = new IdentityHashMap<>();
T oldValue = (T)attachments.put(key, value);
return oldValue==null ? key.defaultValue : value;
}
@SuppressWarnings("unchecked")
public <T> T detach(Key<T> key){
if(attachments ==null)
return key.defaultValue;
T value = (T)attachments.remove(key);
return value==null ? key.defaultValue : value;
}
/*-------------------------------------------------[ Bean ]---------------------------------------------------*/
@Override
@SuppressWarnings("StringEquality")
public Object getField(String name) throws UnresolvedException{
if(name=="request")
return getRequest();
else if(name=="response")
return getResponse();
else if(name=="scheme"){
TCPEndpoint endpoint = getEndpoint();
return endpoint==null ? null : endpoint.sslContext==null ? "http" : "https";
}else if(name=="host"){
TCPEndpoint endpoint = getEndpoint();
return endpoint==null ? null : endpoint.host;
}else if(name=="port"){
TCPEndpoint endpoint = getEndpoint();
return endpoint==null ? null : endpoint.port;
}else if(name=="connection_status")
return connectionStatus;
else if(name=="request_count")
return getRequestCount();
else if(name=="id")
return in==null ? null : in.channel().getExecutionID();
else if(name=="ssl_session"){
if(in==null)
return null;
Input in = this.in;
while(in instanceof InputFilter)
in = ((InputFilter)in).peer();
return in instanceof SSLSocket ? in : null;
}else
throw new UnresolvedException(name);
}
}