KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > repository > clientimpl > infrastructure > DaisyHttpClient


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.outerj.daisy.repository.clientimpl.infrastructure;
17
18 import org.apache.commons.httpclient.*;
19 import org.apache.xmlbeans.XmlObject;
20 import org.apache.xmlbeans.XmlOptions;
21 import org.outerj.daisy.repository.RepositoryException;
22 import org.outerj.daisy.repository.AuthenticationFailedException;
23 import org.outerj.daisy.xmlutil.LocalSAXParserFactory;
24 import org.outerx.daisy.x10.ErrorDocument;
25 import org.outerx.daisy.x10.CauseType;
26
27 import java.util.Map JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.lang.reflect.Method JavaDoc;
31 import java.lang.reflect.Constructor JavaDoc;
32 import java.lang.reflect.InvocationTargetException JavaDoc;
33
34 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
35
36 public class DaisyHttpClient {
37     private static Map JavaDoc xmlObjectParseMethodCache = new ConcurrentReaderHashMap();
38     private static final boolean validateResponses = false;
39     private HttpClient sharedHttpClient;
40     private HttpState httpState;
41     private HostConfiguration sharedHostConfiguration;
42
43     public DaisyHttpClient(HttpClient sharedHttpClient, HostConfiguration sharedHostConfiguration, HttpState httpState) {
44         this.sharedHttpClient = sharedHttpClient;
45         this.httpState = httpState;
46         this.sharedHostConfiguration = sharedHostConfiguration;
47     }
48
49     /**
50      * Executes the given method, and handles the response to take care of exceptions
51      * or non-OK responses, and optionally parses the response body according to the specified
52      * XmlObject class. If this method returns without throwing an exception, one can
53      * assume that the execution of the HTTP method was successful.
54      *
55      * @param xmlObjectResponseClass an Apache XmlBeans generated class (having a Factory inner class).
56      * @return the XmlObject resulting from the parsing of the response body, or null if no XmlObject
57      * class was specified.
58      */

59     public XmlObject executeMethod(HttpMethod method, Class JavaDoc xmlObjectResponseClass, boolean releaseConnection) throws RepositoryException {
60         try {
61             int statusCode;
62             try {
63                 statusCode = sharedHttpClient.executeMethod(sharedHostConfiguration, method, httpState);
64             } catch (Exception JavaDoc e) {
65                 throw new RepositoryException("Problems connecting to repository server.", e);
66             }
67
68             if (statusCode == HttpStatus.SC_OK) {
69                 if (xmlObjectResponseClass != null) {
70                     Method JavaDoc parseMethod = getParseMethod(xmlObjectResponseClass);
71                     XmlObject parseResult = null;
72                     try {
73                         parseResult = (XmlObject)parseMethod.invoke(null, new Object JavaDoc[] {method.getResponseBodyAsStream()});
74                         if (validateResponses)
75                             parseResult.validate();
76                     } catch (Exception JavaDoc e) {
77                         throw new RepositoryException("Error parsing reponse from repository server.", e);
78                     }
79
80                     return parseResult;
81                 } else {
82                     return null;
83                 }
84             } else {
85                 handleNotOkResponse(method);
86                 // handleNotOkResponse always throws an exception, thus...
87
throw new RuntimeException JavaDoc("This statement should be unreacheable.");
88             }
89         } finally {
90             if (releaseConnection)
91                 method.releaseConnection();
92         }
93     }
94
95     private static Method JavaDoc getParseMethod(Class JavaDoc xmlObjectClass) {
96         Object JavaDoc parseMethod = xmlObjectParseMethodCache.get(xmlObjectClass);
97         if (parseMethod != null) {
98             return (Method JavaDoc)parseMethod;
99         } else {
100             Class JavaDoc[] classes = xmlObjectClass.getClasses();
101             Class JavaDoc factoryClass = null;
102             for (int i = 0; i < classes.length; i++) {
103                 if (classes[i].getName().equals(xmlObjectClass.getName() + "$Factory")) {
104                     factoryClass = classes[i];
105                     break;
106                 }
107             }
108
109             if (factoryClass == null) {
110                 throw new RuntimeException JavaDoc("Missing Factory class in class " + xmlObjectClass.getName());
111             }
112
113             Method JavaDoc newParseMethod = null;
114             try {
115                 newParseMethod = factoryClass.getMethod("parse", new Class JavaDoc[] {java.io.InputStream JavaDoc.class});
116             } catch (NoSuchMethodException JavaDoc e) {
117                 throw new RuntimeException JavaDoc("Missing parse method on XmlObject Factory class for " + xmlObjectClass.getName(), e);
118             }
119
120             xmlObjectParseMethodCache.put(xmlObjectClass, newParseMethod);
121             return newParseMethod;
122         }
123     }
124
125     public static String JavaDoc getContentType(HttpMethod method) throws RepositoryException {
126         String JavaDoc contentType = null;
127         try {
128             if (method.getResponseHeader("Content-Type") != null)
129                 contentType = method.getResponseHeader("Content-Type").getValues()[0].getName();
130         } catch (HttpException e) {
131             throw new RepositoryException("Error getting Content-Type of the reponse.", e);
132         }
133         return contentType;
134     }
135
136     public void handleNotOkResponse(HttpMethod method) throws RepositoryException {
137         if ("text/xml".equals(getContentType(method))) {
138             // an error occured server side
139
ErrorDocument.Error errorXml = null;
140             try {
141                 XmlOptions xmlOptions = new XmlOptions().setLoadUseXMLReader(LocalSAXParserFactory.newXmlReader());
142                 ErrorDocument errorDocument = ErrorDocument.Factory.parse(method.getResponseBodyAsStream(), xmlOptions);
143                 errorXml = errorDocument.getError();
144             } catch (Exception JavaDoc e) {
145                 throw new RepositoryException("Error reading error response from repositoryserver", e);
146             }
147             if (errorXml.getDescription() != null) {
148                 throw new RepositoryException("Repository server answered with an error: " + errorXml.getDescription());
149             } else {
150                 CauseType causeXml = errorXml.getCause();
151                 if (causeXml.getExceptionData() != null)
152                     tryRestoreOriginalException(causeXml);
153                 Exception JavaDoc cause = restoreException(causeXml);
154                 throw new RepositoryException("Received exception from repository server.", cause);
155             }
156         } else {
157             if (method.getStatusCode() == 401) {
158                 throw new AuthenticationFailedException(((UsernamePasswordCredentials)httpState.getCredentials(null, null)).getUserName());
159             } else {
160                 throw new RepositoryException("Unexpected response from repositoryserver: " + method.getStatusCode() + " : " + HttpStatus.getStatusText(method.getStatusCode()));
161             }
162         }
163     }
164
165     private static Exception JavaDoc restoreException(CauseType causeXml) {
166         String JavaDoc message = causeXml.getException().getMessage();
167         String JavaDoc className = causeXml.getException().getType();
168
169         ArrayList JavaDoc stackTrace = new ArrayList JavaDoc();
170         CauseType.StackTrace.StackTraceElement[] stackTraceElements = causeXml.getStackTrace().getStackTraceElementArray();
171         for (int i = 0; i < stackTraceElements.length; i++) {
172             CauseType.StackTrace.StackTraceElement stackTraceElement = stackTraceElements[i];
173             stackTrace.add(new MyStackTraceElement(stackTraceElement.getClassName(), stackTraceElement.getFileName(), stackTraceElement.getLineNumber(), stackTraceElement.getMethodName(), stackTraceElement.getNativeMethod()));
174         }
175         MyStackTraceElement[] remoteStackTrace = (MyStackTraceElement[])stackTrace.toArray(new MyStackTraceElement[stackTrace.size()]);
176
177         DaisyPropagatedException exception = new DaisyPropagatedException(message, className, remoteStackTrace);
178
179         CauseType nestedCauseXml = causeXml.getCause();
180         if (nestedCauseXml != null) {
181             Exception JavaDoc cause = restoreException(nestedCauseXml);
182             exception.initCause(cause);
183         }
184
185         return exception;
186     }
187
188     /**
189      * This method handles exceptions which can be restored, ie RepositoryException's
190      * whose getState method returned a Map and have a constructor that takes a Map
191      * as argument.
192      *
193      * <p>If the exception could be restored, this method will throw it immediatelly,
194      * otherwise it will simply return. Only call this method if there is actually
195      * ExceptionData, otherwise this will throw a NPE.
196      */

197     private static void tryRestoreOriginalException(CauseType causeXml) throws RepositoryException {
198         String JavaDoc className = causeXml.getException().getType();
199         CauseType.ExceptionData exceptionData = causeXml.getExceptionData();
200
201         Map JavaDoc state = new HashMap JavaDoc();
202         CauseType.ExceptionData.Parameter[] parameters = exceptionData.getParameterArray();
203         for (int i = 0; i < parameters.length; i++) {
204             state.put(parameters[i].getName(), parameters[i].getValue());
205         }
206
207         Class JavaDoc clazz = null;
208         try {
209             clazz = DaisyHttpClient.class.getClassLoader().loadClass(className);
210         } catch (ClassNotFoundException JavaDoc e) {
211             return;
212         }
213
214         if (!RepositoryException.class.isAssignableFrom(clazz))
215             return;
216
217         Constructor JavaDoc constructor = null;
218         try {
219             constructor = clazz.getConstructor(new Class JavaDoc[] {Map JavaDoc.class});
220         } catch (NoSuchMethodException JavaDoc e) {
221             return;
222         }
223
224         RepositoryException restoredException = null;
225         try {
226             restoredException = (RepositoryException)constructor.newInstance(new Object JavaDoc[] {state});
227         } catch (InstantiationException JavaDoc e) {
228             // empty on purpose
229
} catch (IllegalAccessException JavaDoc e) {
230             // empty on purpose
231
} catch (InvocationTargetException JavaDoc e) {
232             // empty on purpose
233
}
234
235         if (causeXml.getCause() != null) {
236             Exception JavaDoc cause = restoreException(causeXml.getCause());
237             restoredException.initCause(cause);
238         }
239
240         if (restoredException != null) {
241             throw restoredException;
242         }
243     }
244 }
245
Popular Tags