KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > httpconnector > HttpConnectorImpl


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.httpconnector;
17
18 import org.apache.avalon.framework.configuration.Configuration;
19 import org.apache.avalon.framework.configuration.ConfigurationException;
20 import org.apache.avalon.framework.configuration.Configurable;
21 import org.apache.avalon.framework.service.Serviceable;
22 import org.apache.avalon.framework.service.ServiceManager;
23 import org.apache.avalon.framework.service.ServiceException;
24 import org.apache.avalon.framework.activity.Startable;
25 import org.apache.avalon.framework.activity.Initializable;
26 import org.apache.avalon.framework.logger.Logger;
27 import org.apache.avalon.framework.logger.LogEnabled;
28 import org.apache.cocoon.matching.helpers.WildcardHelper;
29 import org.apache.commons.logging.impl.AvalonLogger;
30 import org.outerj.daisy.repository.*;
31 import org.outerj.daisy.httpconnector.handlers.*;
32 import org.outerj.daisy.util.VersionHelper;
33 import org.mortbay.http.*;
34 import org.mortbay.http.handler.AbstractHttpHandler;
35 import org.mortbay.http.handler.SecurityHandler;
36 import org.mortbay.util.InetAddrPort;
37 import org.mortbay.log.LogFactory;
38 import org.mortbay.log.Factory;
39
40 import java.io.IOException JavaDoc;
41 import java.util.*;
42 import java.security.Principal JavaDoc;
43
44 /**
45  * @avalon.component version="1.0" name="httpconnector" lifestyle="singleton"
46  * @avalon.service type="org.outerj.daisy.httpconnector.HttpConnector"
47  */

48 public class HttpConnectorImpl implements HttpConnector, Configurable, Serviceable, Startable, Initializable, LogEnabled {
49     private int port;
50     private RepositoryManager repositoryManager;
51     private HttpServer server;
52     private HttpContext context;
53     private Logger requestErrorLogger;
54     private Logger logger;
55     private int uploadThreshold;
56     private int uploadMaxSize;
57     private String JavaDoc uploadTempdir;
58     private PathHandler[] pathHandlers;
59     private String JavaDoc versionString;
60
61     public void configure(Configuration configuration) throws ConfigurationException
62     {
63         port = configuration.getChild("port").getValueAsInteger();
64         Configuration uploadConf = configuration.getChild("upload");
65         uploadThreshold = uploadConf.getChild("threshold").getValueAsInteger(50000);
66         uploadMaxSize = uploadConf.getChild("maxsize").getValueAsInteger(-1);
67         uploadTempdir = uploadConf.getChild("tempdir").getValue(null);
68     }
69
70     public void enableLogging(Logger logger) {
71         this.logger = logger;
72         this.requestErrorLogger = logger.getChildLogger("request-errors");
73     }
74
75     /**
76      * @avalon.dependency key="repository-manager" type="org.outerj.daisy.repository.RepositoryManager"
77      */

78     public void service(ServiceManager serviceManager) throws ServiceException {
79         this.repositoryManager = (RepositoryManager)serviceManager.lookup("repository-manager");
80     }
81
82     public void initialize() throws Exception JavaDoc {
83         System.setProperty("org.mortbay.util.URI.charset", "UTF-8");
84         System.setProperty("org.mortbay.log.LogFactory.noDiscovery", "true");
85
86         versionString = VersionHelper.getVersionString(getClass().getClassLoader(), "org/outerj/daisy/repository/serverimpl/versioninfo.properties");
87         Factory factory = (Factory)LogFactory.getFactory();
88         factory.setAttribute("org.mortbay.*", new AvalonLogger(logger));
89
90         server = new HttpServer();
91         context = server.getContext(null, "");
92
93         SecurityConstraint securityConstraint = new SecurityConstraint();
94         securityConstraint.setAuthenticate(true);
95         securityConstraint.addRole("*");
96         securityConstraint.setName("daisy");
97         context.addSecurityConstraint("/", securityConstraint);
98
99         context.setRealm(new DaisyUserRealm());
100         context.setAuthenticator(new BasicAuthenticator());
101         context.addHandler(new SecurityHandler());
102
103         DaisyHandler daisyHandler = new DaisyHandler();
104         context.addHandler(daisyHandler);
105
106         server.addListener(new InetAddrPort(port));
107
108         initializePathHandlers();
109     }
110
111     public void start() throws Exception JavaDoc {
112         server.start();
113     }
114
115     public void stop() throws Exception JavaDoc {
116         server.stop();
117     }
118
119     public void addHandler(HttpHandler handler) throws Exception JavaDoc {
120         context.stop();
121         context.addHandler(handler);
122         context.start();
123     }
124
125     public void removeHandler(HttpHandler handler) {
126         context.removeHandler(handler);
127     }
128
129     private void initializePathHandlers() {
130         List pathHandlers = new ArrayList();
131         pathHandlers.add(new PathHandler(new UserInfoHandler()));
132         pathHandlers.add(new PathHandler(new DocumentHandler(requestErrorLogger, uploadThreshold, uploadMaxSize, uploadTempdir)));
133         pathHandlers.add(new PathHandler(new DocumentsHandler(requestErrorLogger, uploadThreshold, uploadMaxSize, uploadTempdir)));
134         pathHandlers.add(new PathHandler(new CommentsHandler()));
135         pathHandlers.add(new PathHandler(new CommentHandler()));
136         pathHandlers.add(new PathHandler(new UserCommentsHandler()));
137         pathHandlers.add(new PathHandler(new VersionHandler()));
138         pathHandlers.add(new PathHandler(new VersionsHandler()));
139         pathHandlers.add(new PathHandler(new LockHandler()));
140         pathHandlers.add(new PathHandler(new PartDataHandler()));
141         pathHandlers.add(new PathHandler(new AvailableVariantsHandler()));
142         pathHandlers.add(new PathHandler(new QueryHandler()));
143         pathHandlers.add(new PathHandler(new FacetedQueryHandler()));
144         pathHandlers.add(new PathHandler(new DistinctQueryHandler()));
145         pathHandlers.add(new PathHandler(new AclHandler()));
146         pathHandlers.add(new PathHandler(new PartTypeHandler()));
147         pathHandlers.add(new PathHandler(new PartTypeByNameHandler()));
148         pathHandlers.add(new PathHandler(new PartTypesHandler()));
149         pathHandlers.add(new PathHandler(new FieldTypeHandler()));
150         pathHandlers.add(new PathHandler(new FieldTypeByNameHandler()));
151         pathHandlers.add(new PathHandler(new FieldTypesHandler()));
152         pathHandlers.add(new PathHandler(new DocumentTypeHandler()));
153         pathHandlers.add(new PathHandler(new DocumentTypeByNameHandler()));
154         pathHandlers.add(new PathHandler(new DocumentTypesHandler()));
155         pathHandlers.add(new PathHandler(new CollectionHandler()));
156         pathHandlers.add(new PathHandler(new CollectionByNameHandler()));
157         pathHandlers.add(new PathHandler(new CollectionsHandler()));
158         pathHandlers.add(new PathHandler(new FilterDocumentTypesHandler()));
159         pathHandlers.add(new PathHandler(new FilterDocumentsHandler()));
160         pathHandlers.add(new PathHandler(new UsersHandler()));
161         pathHandlers.add(new PathHandler(new UserIdsHandler()));
162         pathHandlers.add(new PathHandler(new RolesHandler()));
163         pathHandlers.add(new PathHandler(new UserByLoginHandler()));
164         pathHandlers.add(new PathHandler(new UserByEmailHandler()));
165         pathHandlers.add(new PathHandler(new RoleByNameHandler()));
166         pathHandlers.add(new PathHandler(new UserHandler()));
167         pathHandlers.add(new PathHandler(new RoleHandler()));
168         pathHandlers.add(new PathHandler(new BranchHandler()));
169         pathHandlers.add(new PathHandler(new BranchByNameHandler()));
170         pathHandlers.add(new PathHandler(new BranchesHandler()));
171         pathHandlers.add(new PathHandler(new LanguageHandler()));
172         pathHandlers.add(new PathHandler(new LanguageByNameHandler()));
173         pathHandlers.add(new PathHandler(new LanguagesHandler()));
174         pathHandlers.add(new PathHandler(new AuthenticationSchemesHandler()));
175         pathHandlers.add(new PathHandler(new LinkExtractorsHandler()));
176
177         this.pathHandlers = (PathHandler[])pathHandlers.toArray(new PathHandler[pathHandlers.size()]);
178     }
179
180     private class DaisyHandler extends AbstractHttpHandler {
181         private static final String JavaDoc PREFIX = "/repository";
182         protected int[] versionCollectionPattern = WildcardHelper.compilePattern("/document/*/version");
183
184         public void handle(String JavaDoc pathInContext, String JavaDoc pathParams, HttpRequest request, HttpResponse response)
185                 throws HttpException, IOException JavaDoc {
186             String JavaDoc path = request.getPath();
187             if (!path.startsWith(PREFIX))
188                 return;
189             path = path.substring(PREFIX.length());
190
191             response.addField("X-Daisy-Version", versionString);
192             try {
193                 Repository repository = ((DaisyUserPrincipal)request.getUserPrincipal()).getRepository();
194                 HashMap matchMap = new HashMap();
195                 for (int i = 0; i < pathHandlers.length; i++) {
196                     if (pathHandlers[i].handle(path, matchMap, request, response, repository))
197                         return;
198                 }
199             } catch (BadRequestException e) {
200                 // doesn't need to be logged
201
HttpUtil.sendCustomError(e.getMessage(), HttpResponse.__400_Bad_Request, response);
202             } catch (Exception JavaDoc e) {
203                 requestErrorLogger.error("Error processing request " + path, e);
204                 HttpUtil.sendCustomError(e, HttpResponse.__202_Accepted, response);
205             } catch (Error JavaDoc e) {
206                 requestErrorLogger.error("Error processing request " + path, e);
207                 HttpUtil.sendCustomError(e, HttpResponse.__202_Accepted, response);
208             }
209         }
210     }
211
212     /**
213      * A runtime wrapper around a {@link RequestHandler}.
214      */

215     private static class PathHandler {
216         private final String JavaDoc path;
217         private final boolean isPattern;
218         private final RequestHandler requestHandler;
219         private final int[] compiledPattern;
220
221         public PathHandler(RequestHandler handler) {
222             this.path = handler.getPathPattern();
223             this.isPattern = path.indexOf("*") != -1;
224             this.requestHandler = handler;
225             if (isPattern)
226                 compiledPattern = WildcardHelper.compilePattern(path);
227             else
228                 compiledPattern = null;
229         }
230
231         /**
232          * @return true if this handler handled the request
233          */

234         public boolean handle(String JavaDoc requestPath, HashMap matchMap, HttpRequest request, HttpResponse response, Repository repository) throws Exception JavaDoc {
235             matchMap.clear();
236             if ((isPattern && WildcardHelper.match(matchMap, requestPath, compiledPattern)) || (!isPattern && path.equals(requestPath))) {
237                 requestHandler.handleRequest(matchMap, request, response, repository);
238                 return true;
239             }
240             return false;
241         }
242     }
243
244     private class DaisyUserRealm implements UserRealm {
245         public String JavaDoc getName() {
246             return "daisy";
247         }
248
249         public Principal JavaDoc authenticate(final String JavaDoc userRoleIdentification, final Object JavaDoc credentials, HttpRequest httpRequest) {
250             // userRoleIdentification follows the structure <login>@<roleId> in which the @<roleId> part
251
// is optional. Any '@'-symbols occuring in the <login> part must be escaped by doubling them
252
// The code below determines the <login> and <roleId> parts
253
StringBuffer JavaDoc login = new StringBuffer JavaDoc(userRoleIdentification.length());
254             StringBuffer JavaDoc roleIds = new StringBuffer JavaDoc(5);
255
256             final int STATE_IN_AT = 1;
257             final int STATE_IN_ROLE = 2;
258             final int STATE_IN_NAME = 3;
259
260             int state = STATE_IN_NAME;
261
262             for (int i = 0; i < userRoleIdentification.length(); i++) {
263                 char c = userRoleIdentification.charAt(i);
264                 switch (c) {
265                     case '@':
266                         if (state == STATE_IN_ROLE) {
267                             requestErrorLogger.error("Invalid username (user/role identification): " + userRoleIdentification);
268                             return null;
269                         } else if (state == STATE_IN_AT) {
270                             login.append('@');
271                             state = STATE_IN_NAME;
272                         } else {
273                             state = STATE_IN_AT;
274                         }
275                         break;
276                     default:
277                         if (state == STATE_IN_AT || state == STATE_IN_ROLE) {
278                             state = STATE_IN_ROLE;
279                             if ((c >= '0' && c <= '9') || c == ',') {
280                                 roleIds.append(c);
281                             } else {
282                                 requestErrorLogger.error("Invalid username (user/role identification), encountered non-digit in role ID: " + userRoleIdentification);
283                                 return null;
284                             }
285                         } else if (state == STATE_IN_NAME) {
286                             login.append(c);
287                         }
288                 }
289             }
290
291             Credentials daisyCredentials = new Credentials(login.toString(), (String JavaDoc)credentials);
292
293             try {
294                 Repository repository = repositoryManager.getRepository(daisyCredentials);
295                 if (roleIds.length() > 0) {
296                     long[] parsedRoleIds = parseRoleIds(roleIds.toString());
297                     if (parsedRoleIds.length > 0)
298                         repository.setActiveRoleIds(parsedRoleIds);
299                 }
300                 return new DaisyUserPrincipalImpl(repository);
301             } catch (RepositoryException e) {
302                 requestErrorLogger.error("Error authenticating user.", e);
303                 return null;
304             }
305         }
306
307         /**
308          *
309          * @param roleIdSpec a comma-separated list of role IDs
310          */

311         private long[] parseRoleIds(String JavaDoc roleIdSpec) {
312             List roleIdList = new ArrayList();
313             StringTokenizer tokenizer = new StringTokenizer(roleIdSpec, ",");
314             while (tokenizer.hasMoreTokens()) {
315                 roleIdList.add(tokenizer.nextToken());
316             }
317             long[] roleIds = new long[roleIdList.size()];
318             for (int i = 0; i < roleIds.length; i++)
319                 roleIds[i] = Long.parseLong((String JavaDoc)roleIdList.get(i));
320             return roleIds;
321         }
322
323         public void disassociate(Principal JavaDoc userPrincipal) {
324         }
325
326         public Principal JavaDoc pushRole(Principal JavaDoc userPrincipal, String JavaDoc string) {
327             return null;
328         }
329
330         public Principal JavaDoc popRole(Principal JavaDoc userPrincipal) {
331             return null;
332         }
333
334         public void logout(Principal JavaDoc userPrincipal) {
335         }
336
337         public Principal JavaDoc getPrincipal(String JavaDoc string) {
338             return null;
339         }
340
341         public boolean reauthenticate(Principal JavaDoc principal) {
342             return false;
343         }
344
345         public boolean isUserInRole(Principal JavaDoc principal, String JavaDoc string) {
346             return false;
347         }
348     }
349
350     private static class DaisyUserPrincipalImpl implements DaisyUserPrincipal {
351         private Repository repository;
352
353         public DaisyUserPrincipalImpl(Repository repository) {
354             this.repository = repository;
355         }
356
357         public String JavaDoc getName() {
358             return String.valueOf(repository.getUserId());
359         }
360
361         public Repository getRepository() {
362             return repository;
363         }
364     }
365 }
366
Popular Tags