hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rola...@apache.org
Subject svn commit: r393007 - in /jakarta/httpcomponents/httpasync/trunk/src: contrib/ contrib/org/ contrib/org/apache/ contrib/org/apache/http/ contrib/org/apache/http/async/ contrib/org/apache/http/async/contrib/ contrib/org/apache/http/async/contrib/routing...
Date Mon, 10 Apr 2006 18:04:02 GMT
Author: rolandw
Date: Mon Apr 10 11:04:00 2006
New Revision: 393007

URL: http://svn.apache.org/viewcvs?rev=393007&view=rev
Log:
bug 38943, take 3

Added:
    jakarta/httpcomponents/httpasync/trunk/src/contrib/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
  (with props)
    jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
  (with props)
Modified:
    jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AbstractHttpHandle.java
    jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AsyncHttpProcessor.java
    jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
    jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpHandle.java

Added: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java?rev=393007&view=auto
==============================================================================
--- jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
(added)
+++ jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
Mon Apr 10 11:04:00 2006
@@ -0,0 +1,190 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Copyright 2006 The Apache Software Foundation
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.async.contrib.routing;
+
+
+import java.util.LinkedList;
+
+import org.apache.http.async.HttpHandle;
+
+
+
+/**
+ * Example thread for asynchronous notification.
+ * This is an abstract base class used by the
+ * {@link NotifiedAsyncGet NotifiedAsyncGet} example.
+ * It is defined as a separate class to make re-use simpler for you.
+ *
+ * @author <a href="mailto:http-async at dubioso.net">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision$ $Date$
+ * 
+ * @since 4.0
+ */
+public abstract class NotifiedServiceThread extends Thread {
+
+    /** The shutdown indicator. */
+    private boolean keep_running;
+
+    /** The queue for passing handles. */
+    private final LinkedList handle_queue;
+
+    /** The monitor object for the {@link #handle_queue handle_queue}. */
+    private final Object handle_queue_monitor =
+        new String(super.toString()+".handle_queue_monitor");
+
+
+    /**
+     * Creates a new notified service thread.
+     */
+    protected NotifiedServiceThread() {
+
+        handle_queue = new LinkedList();
+        keep_running = true;
+    }
+
+
+    /**
+     * Main loop of this service thread.
+     * Calls {@link #processHandle processHandle} for each handle
+     * that is queued, in order.
+     */
+    public final void run() {
+
+        while (keep_running) {
+
+            HttpHandle handle = null;
+            synchronized (handle_queue_monitor) {
+
+                while (keep_running && handle_queue.isEmpty()) {
+                    try {
+                        handle_queue_monitor.wait();
+                    } catch (InterruptedException ix) {
+                        // ignore, we'll keep waiting in the loop if required
+                    }
+                } // while queue empty
+
+                if (!handle_queue.isEmpty())
+                    handle = (HttpHandle) handle_queue.removeFirst();
+
+            } // synchronized
+
+            if (keep_running && (handle != null)) {
+
+                try {
+                    processHandle(handle);
+
+                } catch (Exception x) {
+
+                    try {
+                        processException(handle, x);
+
+                    } catch (Exception e) {
+                        // This will only be called if the derived class
+                        // screwed up exception handling. We can't let this
+                        // fly through since the thread would die silently.
+                        System.out.println(getClass() + ":" + getName() +
+                                           ": exception handling failed");
+                        e.printStackTrace(System.out);
+                    }
+                }
+            } // if handle
+
+        } // while running
+
+    } // run
+
+
+    /**
+     * Shuts down this thread.
+     * The thread will be terminated gracefully.
+     */
+    public final void shutdown() {
+
+        keep_running = false;
+        this.interrupt();
+
+    } // shutdown
+
+
+    /**
+     * Queues a handle for processing by this thread.
+     *
+     * @param handle    the handle to process
+     */
+    public final void queueHandle(HttpHandle handle) {
+
+        if (handle == null)
+            throw new IllegalArgumentException("handle must not be null");
+
+        synchronized (handle_queue_monitor) {
+            handle_queue.add(handle);
+            handle_queue_monitor.notifyAll();
+        }
+
+    } // queueHandle
+
+
+    /**
+     * Called once for each handle routed to this thread.
+     *
+     * @param handle    the handle
+     *
+     * @throws Exception        in case of a problem
+     */
+    protected abstract void processHandle(HttpHandle handle)
+        throws Exception
+        ;
+
+
+    /**
+     * Called for exceptions thrown by {@link #processHandle processHandle}.
+     * This method can be overridden in derived classes for error handling.
+     * The default implementation prints the stack trace to System.out.
+     *
+     * @param handle    the handle for which processing failed
+     * @param dart      the exception raised while processing
+     */
+    protected void processException(HttpHandle handle, Exception dart) {
+
+        System.out.println(getClass() + ":" + getName() +
+                           ": exception while processing " + handle);
+        if (dart != null)
+            dart.printStackTrace(System.out);
+        else
+            System.out.println(getClass() + ":" + getName() +
+                               ": <exception is null>");
+
+    } // processException
+
+
+} // class NotifiedServiceThread

Propchange: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/NotifiedServiceThread.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java?rev=393007&view=auto
==============================================================================
--- jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
(added)
+++ jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
Mon Apr 10 11:04:00 2006
@@ -0,0 +1,521 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Copyright 2006 The Apache Software Foundation
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.async.contrib.routing;
+
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.Scheme;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpClientConnection;
+import org.apache.http.impl.DefaultHttpParams;
+import org.apache.http.impl.io.PlainSocketFactory;
+import org.apache.http.io.SocketFactory;
+import org.apache.http.message.HttpGet;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.async.HttpDispatcher;
+import org.apache.http.async.HttpHandle;
+import org.apache.http.async.HttpNotificationHandler;
+import org.apache.http.async.AsyncHttpProcessor;
+import org.apache.http.async.impl.SimpleHttpDispatcher;
+import org.apache.http.protocol.RequestConnControl;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.EntityUtils;
+
+
+
+/**
+ * Example for routing handles on asynchronous notification.
+ * Unlike the <code>NotifiedAsyncGet</code> example, this
+ * one uses multiple service threads. The notification
+ * {@link HttpNotificationHandler handler} decides which
+ * of these service threads is responsible to process a
+ * response or problem.
+ *
+ * @author <a href="mailto:http-async at dubioso.net">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision$ $Date$
+ * 
+ * @since 4.0
+ */
+public class RoutingAsyncGet {
+
+    /** The name of a context attribute for passing a throwable. */
+    public final static String PROBLEM_ATTRIBUTE = "problem";
+
+
+    /** The dispatcher. */
+    private HttpDispatcher http_dispatcher;
+
+    /** The notification handler. */
+    private NotifHandlerImpl notification_handler;
+
+    /** The notification counter. */
+    private int notification_counter;
+
+    /** The monitor object for the notification counter. */
+    private final Object notification_counter_monitor =
+        new String(getClass() + ".notification_counter_monitor");
+
+
+    /**
+     * Main entry point to this example.
+     *
+     * @param args        command line arguments
+     *
+     * @throws Exception        in case of a problem
+     */
+    public static void main(String[] args) throws Exception {
+
+        SocketFactory socketfactory = PlainSocketFactory.getSocketFactory();
+        Scheme.registerScheme("http", new Scheme("http", socketfactory, 80));
+
+        String[] targets = args;
+        if ((targets == null) || (targets.length < 1)) {
+            targets = new String[] {
+                "/",
+                "/servlets-examples/servlet/RequestInfoExample", 
+                "/somewhere%20in%20pampa"
+            };
+        }
+
+        RoutingAsyncGet example = new RoutingAsyncGet();
+
+        example.prepareExample();
+        example.executeExample(targets);
+        example.cleanupExample();
+
+    } // main
+
+
+    /**
+     * Default constructor for this example.
+     */
+    private RoutingAsyncGet() {
+
+        notification_counter = 0;
+
+    } // constructor
+
+
+    /**
+     * Prepare this example.
+     *
+     * @throws Exception        in case of a problem
+     */
+    private void prepareExample()
+        throws Exception {
+
+        http_dispatcher = createDispatcher();
+        System.out.println("dispatcher " + http_dispatcher + "\n");
+
+        notification_handler = createNotificationHandler();
+        System.out.println("notification handler " + notification_handler);
+        http_dispatcher.getDefaultContext().setAttribute
+            (HttpNotificationHandler.CTXT_NOTIFICATION_HANDLER,
+             notification_handler);
+
+    } // prepareExample
+
+
+    /**
+     * Execute a series of requests.
+     *
+     * @param targets   the URIs to request
+     *
+     * @throws Exception        in case of a problem
+     */
+    private void executeExample(String[] targets)
+        throws Exception {
+
+        if ((targets == null) || (targets.length < 1))
+            throw new IllegalArgumentException
+                ("targets must not be null nor empty");
+
+        HttpHost     host    = new HttpHost("localhost", 8080);
+        HttpHandle[] handles = new HttpHandle[targets.length];
+
+        for (int i = 0; i < targets.length; i++) {
+
+            HttpGet request = new HttpGet(targets[i]);
+            System.out.println(">> Request URI: " +
+                               request.getRequestLine().getUri());
+            handles[i] = http_dispatcher.sendRequest(request, host, null);
+            System.out.println(">> Handle: " + handles[i]);
+            System.out.println("==============");
+
+        } // for targets
+
+        // In this example, the array of handles is not needed at all since
+        // we'll get notifications for each handle. In a real application,
+        // you might want to track the handles. There probably is some
+        // application context for each request which needs tracking anyway.
+
+        // now wait until all handles have been processed by the threads...
+        synchronized (notification_counter_monitor) {
+
+            while (notification_counter < targets.length) {
+                try {
+                    notification_counter_monitor.wait();
+                } catch (InterruptedException ix) {
+                    // ignore, we'll just keep waiting in the loop
+                }
+                System.out.println("== main thread: notification counter is " +
+                                   notification_counter);
+            }
+
+        } // synchronized
+
+        System.out.println("\ndispatcher " + http_dispatcher + "\n");
+
+    } // executeExample
+
+
+    /**
+     * Clean up this example.
+     *
+     * @throws Exception        in case of a problem
+     */
+    private void cleanupExample()
+        throws Exception {
+
+        shutdownThreads(notification_handler);
+
+    } // cleanupExample
+
+
+    /**
+     * Instantiate a dispatcher.
+     *
+     * @return    the dispatcher
+     */
+    private final static HttpDispatcher createDispatcher() {
+
+        HttpParams params = new DefaultHttpParams(null);
+        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
+        HttpProtocolParams.setContentCharset(params, "UTF-8");
+        HttpProtocolParams.setUserAgent(params, "Jakarta-HttpComponents/1.1");
+        HttpProtocolParams.setUseExpectContinue(params, false);
+
+        HttpClientConnection conn = new DefaultHttpClientConnection();
+
+        AsyncHttpProcessor proc = new AsyncHttpProcessor();
+        proc.setParams(params);
+        // Required request interceptors
+        proc.addInterceptor(new RequestContent());
+        proc.addInterceptor(new RequestTargetHost());
+        // Recommended request interceptors
+        proc.addInterceptor(new RequestConnControl());
+        proc.addInterceptor(new RequestUserAgent());
+        // not supported: proc.addInterceptor(new RequestExpectContinue());
+
+        ConnectionReuseStrategy crs = new DefaultConnectionReuseStrategy();
+
+        HttpDispatcher hdp = new SimpleHttpDispatcher(conn, proc, crs);
+
+        return hdp;
+
+    } // createDispatcher
+
+
+    /**
+     * Start application threads and instantiates a notification handler.
+     *
+     * @return  the notification handler
+     */
+    private final NotifHandlerImpl createNotificationHandler() {
+
+        ResponseThread rspthread = new ResponseThread();
+        ProblemThread  plmthread = new ProblemThread();
+
+        rspthread.setDaemon(true);
+        rspthread.start();
+        plmthread.setDaemon(true);
+        plmthread.start();
+
+        return new NotifHandlerImpl(rspthread, plmthread);
+
+    } // createNotificationHandler
+
+
+    /**
+     * Stop the application threads.
+     *
+     * @param nhi       the notification handler implementation
+     */
+    private final static void shutdownThreads(NotifHandlerImpl nhi) {
+
+        nhi.response_target.shutdown();
+        nhi.problem_target.shutdown();
+
+    } // shutdownThreads
+
+
+    /**
+     * The application thread for responses.
+     */
+    public final class ResponseThread extends NotifiedServiceThread {
+
+        // default constructor
+
+        /**
+         * Called once for each handle routed to this thread.
+         *
+         * @param handle    the handle
+         *
+         * @throws Exception  in case of a problem
+         */
+        protected void processHandle(HttpHandle handle)
+            throws Exception {
+
+            HttpResponse response = handle.getResponse();
+            if (response == null) {
+                System.out.println("ERROR: response is null in handle!");
+            } else {
+                System.out.println
+                    ("<< Response: " + response.getStatusLine());
+                System.out.println
+                    (EntityUtils.toString(response.getEntity()));
+                /*
+                byte[] data = new byte[100];
+                int count = response.getEntity().getContent().read(data);
+                String s = new String(data, 0, count, "ISO-8859-1");
+                System.out.println(s);
+                */
+            }
+
+            // now that the handle is processed, we can update the counter
+            // this should really be done in a finally block:
+            synchronized (notification_counter_monitor) {
+                notification_counter++;
+                notification_counter_monitor.notifyAll();
+            }
+
+            handle.close();
+
+        } // processHandle
+
+    } // class ResponseThread
+
+
+    /**
+     * The application thread for problems.
+     */
+    public final class ProblemThread extends NotifiedServiceThread {
+
+        // default constructor
+
+        /**
+         * Called once for each handle routed to this thread.
+         *
+         * @param handle    the handle
+         */
+        protected void processHandle(HttpHandle handle) {
+
+            //@@@ There is a lot of test code here which is not relevant
+            //@@@ for the example. Remove that as test coverage increases.
+
+            System.out.println("<< Problem: " + handle);
+
+            if (handle.isLinked())
+                System.out.println("ERROR: problem handle still linked!");
+
+            // Get the problem from the context. This is application logic:
+            // The notification handler has put the problem in the context.
+            Throwable pfc = (Throwable) // get problem from context
+                handle.getContext().getAttribute(PROBLEM_ATTRIBUTE);
+
+            if (pfc == null)
+                System.out.println("ERROR: problem missing from context!");
+
+            // This is the alternative way of getting the problem
+            // in the application thread: trigger exception and catch.
+            // This is dispatcher logic, not application logic.
+            Throwable pfh = null; // problem from handle
+            try {
+                handle.checkError();
+            } catch (Throwable t) { // usually, you'd catch only Exception
+                pfh = t;
+            }
+
+            if (pfh == null)
+                System.out.println("ERROR: problem missing from handle!");
+
+            if (pfc != pfh) // this must be the very same exception
+                System.out.println
+                    ("ERROR: different problems in context and handle!");
+
+            if (pfc != null) {
+                System.out.println("--- problem from context ---");
+                pfc.printStackTrace(System.out);
+            }
+            if ((pfh != null) && (pfh != pfc )) {
+                System.out.println("--- problem from handle ---");
+                pfh.printStackTrace(System.out);
+            }
+
+            // now that the handle is processed, we can update the counter
+            // this should really be done in a finally block:
+            synchronized (notification_counter_monitor) {
+                notification_counter++;
+                notification_counter_monitor.notifyAll();
+            }
+
+            // handle is not linked anymore, no need to close it
+
+        } // processHandle
+
+    } // class ProblemThread
+
+
+
+    /**
+     * The notification handler.
+     * This class is public only to give you easier access to the JavaDoc.
+     */
+    public final static class NotifHandlerImpl
+        implements HttpNotificationHandler {
+
+        /** The thread to which to route responses. */
+        private final NotifiedServiceThread response_target;
+
+        /** The thread to which to route problems. */
+        private final NotifiedServiceThread problem_target;
+
+
+        /**
+         * Creates a new notification handler.
+         *
+         * @param rsp   the routing target for response notifications
+         * @param plm   the routing target for problem notifications
+         */
+        public NotifHandlerImpl(ResponseThread rsp, ProblemThread plm) {
+
+            if (rsp == null)
+                throw new IllegalArgumentException
+                    ("response handling thread must not be null");
+            if (plm == null)
+                throw new IllegalArgumentException
+                    ("problem handling thread must not be null");
+
+            response_target = rsp;
+            problem_target = plm;
+
+        } // constructor NotifHandlerImpl
+
+
+        /**
+         * Called when a response is available.
+         * Accessing the response entity is a big no-no during notification.
+         * We must not call:
+         * <ul>
+         * <li>{@link HttpHandle#getResponse handle.getResponse}
+         *      </li>
+         * <li>{@link HttpHandle#awaitResponse handle.awaitResponse}
+         *      </li>
+         * <li>{@link HttpResponse#getEntity response.getEntity}
+         *      </li>
+         * <li>{@link HttpResponse#setEntity response.setEntity}
+         *      </li>
+         * <li>{@link HttpHandle#checkError handle.checkError}
+         *      (should not)</li>
+         * </ul>
+         * Of course we're not supposed to modify the <code>response</code>
+         * at all, though that is not a strict requirement. But if we have
+         * to pass additional information to the application thread, we can
+         * simply put it in the {@link HttpHandle#getContext context}.
+         *
+         * @param handle        the handle for which the response is available.
+         * @param nqrsp         (not quite) the available response
+         */
+        public void notifyResponse(HttpHandle handle, HttpResponse nqrsp) {
+
+            System.out.println("== handler: notification for " + handle);
+
+            // we could check for example the response status code to
+            // select between multiple routing targets for responses
+
+            response_target.queueHandle(handle);
+
+        } // notifyResponse
+
+
+        /**
+         * Called when a problem is detected.
+         * We must not call:
+         * <ul>
+         * <li>{@link HttpHandle#getResponse handle.getResponse}
+         *      </li>
+         * <li>{@link HttpHandle#awaitResponse handle.awaitResponse}
+         *      </li>
+         * </ul>
+         * We should not call {@link HttpHandle#checkError handle.checkError}.
+         * The exception we could get from there is already passed as argument.
+         *
+         * @param handle    the handle for which a problem has occurred
+         * @param problem   the exception or throwable indicating the problem,
+         *                  never <code>null</code>
+         * @param nonfatal  <code>true</code> if the problem is non-fatal, or
+         *                  <code>false</code> if request processing must be
+         *                  aborted due to the problem
+         *
+         * @return Will be ignored for fatal problems.
+         *      In case of a non-fatal problem,
+         *      <code>true</code> if processing should resume or
+         *      <code>false</code> if processing should be aborted.
+         */
+        public boolean notifyProblem(HttpHandle handle, Throwable problem,
+                                     boolean nonfatal) {
+
+            System.out.println("== handler: problem notification for " +
+                               handle);
+
+            // provide additional information in the context
+            handle.getContext().setAttribute(PROBLEM_ATTRIBUTE, problem);
+
+            problem_target.queueHandle(handle);
+
+            return false;
+
+        } // notifyProblem
+
+
+    } // class NotifHandlerImpl
+
+
+} // class RoutingAsyncGet

Propchange: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpcomponents/httpasync/trunk/src/contrib/org/apache/http/async/contrib/routing/RoutingAsyncGet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AbstractHttpHandle.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AbstractHttpHandle.java?rev=393007&r1=393006&r2=393007&view=diff
==============================================================================
--- jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AbstractHttpHandle.java
(original)
+++ jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AbstractHttpHandle.java
Mon Apr 10 11:04:00 2006
@@ -39,6 +39,10 @@
 /**
  * Base class for implementations of {@link HttpHandle HttpHandle}.
  *
+ * @author <a href="mailto:http-async at dubioso.net">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
  * @version $Revision$ $Date$
  * 
  * @since 4.0
@@ -60,6 +64,9 @@
     /** The exception indicating an execution problem, if there was one. */
     private Throwable execution_problem;
 
+    /** The notification handler, or <code>null</code>. */
+    protected final HttpNotificationHandler notification_handler;
+
 
     /**
      * Create a new handle.
@@ -83,6 +90,12 @@
         http_context    = context;
         is_linked       = true;
 
+        // Look up the notification handler now.
+        // Later changes to the context will be ignored.
+        notification_handler = (HttpNotificationHandler)
+            context.getAttribute(HttpNotificationHandler.
+                                 CTXT_NOTIFICATION_HANDLER);
+
     } // constructor
 
 
@@ -177,7 +190,19 @@
 
 
     /**
-     * Checks the dispatcher.
+     * Obtain the notification handler.
+     *
+     * @return  the notification handler for the request, or
+     *          <code>null</code> if there is none
+     */
+    public final HttpNotificationHandler getNotificationHandler() {
+
+        return notification_handler;
+    }
+
+
+    /**
+     * Check the dispatcher.
      * Simply returns if the argument is the same as the one
      * given to the constructor.
      * This does not check whether the handle is {@link #isLinked linked}.

Modified: jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AsyncHttpProcessor.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AsyncHttpProcessor.java?rev=393007&r1=393006&r2=393007&view=diff
==============================================================================
--- jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AsyncHttpProcessor.java
(original)
+++ jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/AsyncHttpProcessor.java
Mon Apr 10 11:04:00 2006
@@ -44,9 +44,13 @@
 
 /**
  * HTTP processor for asynchronously dispatched requests.
- * This is the asynchronous equivalent to
+ * This is the asynchronous version of
  * {@link org.apache.http.protocol.HttpRequestExecutor HttpRequestExecutor}.
  *
+ * @author <a href="mailto:http-async at dubioso.net">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
  * @version $Revision$ $Date$
  * 
  * @since 4.0
@@ -89,15 +93,16 @@
                                               HttpContext context)
         throws HttpException, IOException {
 
+        if (context == null)
+            context = getContext(); // default context
+
         HttpExecutionContext hxc = new HttpExecutionContext(context);
-        hxc.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, 
-                         target);
+        hxc.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, target);
 
         // argument checking is done here...
         super.doPrepareRequest(request, hxc);
 
-        hxc.setAttribute(HttpExecutionContext.HTTP_REQUEST, 
-                         request);
+        hxc.setAttribute(HttpExecutionContext.HTTP_REQUEST, request);
 
         return hxc;
 

Modified: jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java?rev=393007&r1=393006&r2=393007&view=diff
==============================================================================
--- jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
(original)
+++ jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpDispatcher.java
Mon Apr 10 11:04:00 2006
@@ -66,14 +66,19 @@
  * <li>{@link #response_handle_monitor response_handle_monitor}</li>
  * </ol>
  *
+ * @author <a href="mailto:http-async at dubioso.net">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
  * @version $Revision$ $Date$
  * 
  * @since 4.0
  */
 public class SimpleHttpDispatcher extends AbstractHttpDispatcher
     implements HttpDispatcher {
-    // @@@ move some of this stuff to an abstract base class?
-    // @@@ the AsyncHttpProcessor and getDefaultContext method, for example
+    //@@@ move some of this stuff to an abstract base class?
+    //@@@ Not the AsyncHttpProcessor and getDefaultContext method, that
+    //@@@ would not allow for dispatching with different processors.
 
     /** The one and only connection. */
     private final HttpClientConnection client_connection;
@@ -114,7 +119,8 @@
      * For the simple dispatcher, there is at most one response handle.
      */
     private final Collection response_handles;
-  
+
+
 
     /**
      * Create a new simple HTTP dispatcher.
@@ -365,6 +371,7 @@
             while (background_thread == this) {
 
                 SimpleHttpHandle handle = null;
+                boolean          notify = true;
                 try {
                     handle = awaitRequestHandle();
                     if (handle != null) {
@@ -391,7 +398,15 @@
                                            handle.getRequest(),
                                            handle.getContext(),
                                            client_connection);
+/*@@@ hack to test problem notification
+if (response.getStatusLine().getStatusCode() != 200)
+    throw new IOException("fake IO problem");
+@@@ end of hack to test problem notification */
+
+                        // providing the response to the handle triggers the
+                        // response notification: disable problem notification
                         handle.provideResponse(response);
+                        notify = false;
 
                         if (response.getEntity() != null) {
                             awaitCompletion(handle);
@@ -402,7 +417,7 @@
                     } // if request handle
 
                 } catch (Throwable t) {
-                    cleanupOnProblem(handle, t);
+                    cleanupOnProblem(handle, t, notify);
                 }
             } // while thread not cancelled
 
@@ -413,13 +428,18 @@
          * Clean up after a problem was encountered.
          * The problem is indicated via the handle, if there is one.
          * The connection is shut down to establish a defined state.
+         * The handle is unlinked.
          *
          * @param handle    the handle for which there was a problem,
          *                  or <code>null</code> if there is none
          * @param dart      the exception indicating the problem
+         * @param notify    <code>true</code> if a problem notification should
+         *                  be triggered, or <code>false</code> if the response
+         *                  notification has already been triggered
          */
         private void cleanupOnProblem(SimpleHttpHandle handle,
-                                      Throwable dart) {
+                                      Throwable dart,
+                                      boolean notify) {
 
             //@@@ TODO: log the problem, in case it isn't picked up by the app
 
@@ -432,7 +452,10 @@
             synchronized (response_handle_monitor) {
                 response_handles.remove(handle);
             }
-            handle.provideProblem(dart);
+            synchronized (linked_handle_monitor) {
+                linked_handles.remove(handle);
+            }
+            handle.provideProblem(dart, notify);
 
             try {
                 client_connection.shutdown();

Modified: jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpHandle.java
URL: http://svn.apache.org/viewcvs/jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpHandle.java?rev=393007&r1=393006&r2=393007&view=diff
==============================================================================
--- jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpHandle.java
(original)
+++ jakarta/httpcomponents/httpasync/trunk/src/java/org/apache/http/async/impl/SimpleHttpHandle.java
Mon Apr 10 11:04:00 2006
@@ -36,10 +36,18 @@
 import org.apache.http.HttpException;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.async.AbstractHttpHandle;
+import org.apache.http.async.HttpNotificationHandler;
+
+
 
 /**
  * Handles for the {@link SimpleHttpDispatcher SimpleHttpDispatcher}.
  *
+ *
+ * @author <a href="mailto:http-async at dubioso.net">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
  * @version $Revision$ $Date$
  * 
  * @since 4.0
@@ -63,7 +71,16 @@
 
     /** The monitor for synchronizing access to the response. */
     private final Object response_monitor =
-    new String("SimpleHttpHandle.response_monitor");
+        new String("SimpleHttpHandle.response_monitor");
+
+    /**
+     * The thread used for a notification callback.
+     * <code>null</code> if no notification is in progress.
+     * This attribute should be read only while a lock on the
+     * {@link #response_monitor response_monitor} is held.
+     */
+    private Thread notification_thread;
+
 
 
     /**
@@ -92,6 +109,8 @@
     // non-javadoc, see interface HttpHandle
     public final HttpResponse getResponse() {
 
+        checkThreadAbuse();
+
         // if the response is pending post-processing,
         // use the calling thread to do it now
         synchronized (response_monitor) {
@@ -101,7 +120,7 @@
 
             } catch (Throwable t) {
                 response_object = null;
-                provideProblem(t);
+                internalProvideProblem(t, false, false);
             }
         } // synchronized response_monitor
 
@@ -114,6 +133,8 @@
     public final HttpResponse awaitResponse()
         throws HttpException, IOException, InterruptedException {
 
+        checkThreadAbuse();
+
         synchronized (response_monitor) {
             while (response_state == RESPONSE_STATE_MISSING) {
                 response_monitor.wait();
@@ -145,13 +166,18 @@
     public synchronized void close()
         throws HttpException, IOException {
 
+        checkThreadAbuse();
+        if (!isLinked())
+            return;
+
         setLinked(false);
         dispatcherCloseHandle(false);
 
         synchronized (response_monitor) {
             // handle remains useable if there is a response
             if (response_state == RESPONSE_STATE_MISSING) {
-                provideProblem(new IllegalStateException("closed"));
+                internalProvideProblem(new IllegalStateException("closed"),
+                                       false, false);
             }
         }
 
@@ -161,18 +187,23 @@
     // non-javadoc, see interface HttpHandle
     public void abort() {
 
+        if (!isLinked())
+            return;
+
         setLinked(false);
         try {
             dispatcherCloseHandle(true);
         } catch (Exception x) {
             // ignore: sanity checks will be passed, nothing else is expected
             // to fail, caller is not interested in handling problems anyway
+            //@@@ TODO: log problem - just in case there is an unexpected one
         }
 
         synchronized (response_monitor) {
             // handle no longer useable
             response_object = null;
-            provideProblem(new IllegalStateException("aborted"));
+            internalProvideProblem(new IllegalStateException("aborted"),
+                                   false, false);
         }
 
     } // abort
@@ -193,19 +224,78 @@
             response_monitor.notifyAll();
         }
 
+        notifyResponse();
+
     } // provideResponse
 
 
     /**
+     * Notify the callback handler about the response.
+     * Called from {@link #provideResponse provideResponse}.
+     */
+    private final void notifyResponse() {
+
+        if (notification_handler == null)
+            return;
+
+        HttpResponse notifyrsp =
+            new NotificationResponseWrapper(response_object);
+
+        try {
+            synchronized (response_monitor) {
+                // enable protection against thread abuse
+                notification_thread = Thread.currentThread();
+                notification_handler.notifyResponse(this, notifyrsp);
+            }
+
+        } catch (Throwable t) {
+            //@@@ TODO: log the problem
+//System.out.println("@@@ notification handler triggered exception");
+//t.printStackTrace(System.out); //@@@
+        } finally {
+            notification_thread = null;
+        }
+
+    } // notifyResponse
+
+
+    /**
      * Provide the problem for this handle.
      * A problem means there will be no {@link #provideResponse response}.
      * This method is called by the background thread of the
      * {@link SimpleHttpDispatcher SimpleHttpDispatcher}.
+     * The handle becomes {@link AbstractHttpHandle#isLinked unlinked}.
+     *
+     * @param dart      the exception indicating the problem,
+     *                  or <code>null</code> to indicate an unknown problem
+     * @param notify    <code>true</code> to trigger a callback to the
+     *                  notification handler, or
+     *                  <code>false</code> if there should be no callback.
+     *                  If <code>true</code>, the handle will also be unlinked.
+     */
+    public final void provideProblem(Throwable dart,
+                                     boolean notify) {
+
+        internalProvideProblem(dart, true, notify);
+    }
+
+
+    /**
+     * Internally provide the problem for this handle.
+     * A problem means there will be no {@link #provideResponse response}.
      *
-     * @param dart        the exception indicating the problem,
-     *                    or <code>null</code> to indicate an unknown problem
+     * @param dart      the exception indicating the problem,
+     *                  or <code>null</code> to indicate an unknown problem
+     * @param unlink    <code>true</code> to unlink this handle, or
+     *                  <code>false</code> to keep linkage unchanged
+     * @param notify    <code>true</code> to trigger a callback to the
+     *                  notification handler, or
+     *                  <code>false</code> if there should be no callback.
+     *                  If <code>true</code>, the handle will also be unlinked.
      */
-    public final void provideProblem(Throwable dart) {
+    private final void internalProvideProblem(Throwable dart,
+                                              boolean unlink,
+                                              boolean notify) {
 
         // don't throw an exception during exception handling!
         if (dart == null)
@@ -215,9 +305,46 @@
         synchronized (response_monitor) {
             response_state = RESPONSE_STATE_FINISHED;
             response_monitor.notifyAll();
+            if (unlink)
+                setLinked(false);
         }
 
-    } // provideProblem
+        if (notify)
+            notifyProblem();
+
+    } // internalProvideProblem
+
+
+    /**
+     * Notify the callback handler about a problem.
+     * Called from {@link #internalProvideProblem internalProvideProblem}.
+     * Unlike {@link HttpNotificationHandler#notifyProblem
+     *               HttpNotificationHandler.notifyProblem},
+     * this method does not have a return value nor boolean flag,
+     * since it is called only for fatal problems.
+     */
+    private final void notifyProblem() {
+
+        if (notification_handler == null)
+            return;
+
+        try {
+            synchronized (response_monitor) {
+                // enable protection against thread abuse
+                notification_thread = Thread.currentThread();
+                // ignore return value:
+                notification_handler.notifyProblem(this, getError(), false);
+            }
+
+        } catch (Throwable t) {
+            //@@@ TODO: log the problem
+//System.out.println("@@@ notification handler triggered exception");
+//t.printStackTrace(System.out); //@@@
+        } finally {
+            notification_thread = null;
+        }
+
+    } // notifyProblem
 
 
     /**
@@ -244,6 +371,38 @@
         } // synchronized response_monitor
 
     } // postprocessResponse
+
+
+    /**
+     * Check for abuse of a notification thread.
+     * Notification handlers are not allowed to call some methods of
+     * this handle, to prevent them from blocking background threads
+     * or triggering nested notifications.
+     * This method throws an exception if thread abuse is detected,
+     * and simply returns otherwise.
+     *
+     * @throws IllegalThreadStateException
+     *          if the calling thread is a background thread currently
+     *          executing a notification for this handle
+     */
+    private final void checkThreadAbuse()
+        throws IllegalThreadStateException {
+
+        // No need to synchronize access to the notification_thread here.
+        // If it is set to the current thread, then it has been set by the
+        // current thread, and operation within a thread is sequential.
+
+        if (notification_thread == null)
+            return;
+
+        if (notification_thread == Thread.currentThread()) {
+            final IllegalThreadStateException itsx =
+                new IllegalThreadStateException("notification thread abuse");
+            //@@@ TODO: log now, in case the caller catches the exception
+            throw itsx;
+        }
+
+    } // checkThreadAbuse
 
 
 } // class SimpleHttpHandle



Mime
View raw message