1 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 ; 41 import java.util.*; 42 import java.security.Principal ; 43 44 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 uploadTempdir; 58 private PathHandler[] pathHandlers; 59 private String 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 78 public void service(ServiceManager serviceManager) throws ServiceException { 79 this.repositoryManager = (RepositoryManager)serviceManager.lookup("repository-manager"); 80 } 81 82 public void initialize() throws Exception { 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 { 112 server.start(); 113 } 114 115 public void stop() throws Exception { 116 server.stop(); 117 } 118 119 public void addHandler(HttpHandler handler) throws Exception { 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 PREFIX = "/repository"; 182 protected int[] versionCollectionPattern = WildcardHelper.compilePattern("/document/*/version"); 183 184 public void handle(String pathInContext, String pathParams, HttpRequest request, HttpResponse response) 185 throws HttpException, IOException { 186 String 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 HttpUtil.sendCustomError(e.getMessage(), HttpResponse.__400_Bad_Request, response); 202 } catch (Exception e) { 203 requestErrorLogger.error("Error processing request " + path, e); 204 HttpUtil.sendCustomError(e, HttpResponse.__202_Accepted, response); 205 } catch (Error e) { 206 requestErrorLogger.error("Error processing request " + path, e); 207 HttpUtil.sendCustomError(e, HttpResponse.__202_Accepted, response); 208 } 209 } 210 } 211 212 215 private static class PathHandler { 216 private final String 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 234 public boolean handle(String requestPath, HashMap matchMap, HttpRequest request, HttpResponse response, Repository repository) throws Exception { 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 getName() { 246 return "daisy"; 247 } 248 249 public Principal authenticate(final String userRoleIdentification, final Object credentials, HttpRequest httpRequest) { 250 StringBuffer login = new StringBuffer (userRoleIdentification.length()); 254 StringBuffer roleIds = new StringBuffer (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 )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 311 private long[] parseRoleIds(String 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 )roleIdList.get(i)); 320 return roleIds; 321 } 322 323 public void disassociate(Principal userPrincipal) { 324 } 325 326 public Principal pushRole(Principal userPrincipal, String string) { 327 return null; 328 } 329 330 public Principal popRole(Principal userPrincipal) { 331 return null; 332 } 333 334 public void logout(Principal userPrincipal) { 335 } 336 337 public Principal getPrincipal(String string) { 338 return null; 339 } 340 341 public boolean reauthenticate(Principal principal) { 342 return false; 343 } 344 345 public boolean isUserInRole(Principal principal, String 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 getName() { 358 return String.valueOf(repository.getUserId()); 359 } 360 361 public Repository getRepository() { 362 return repository; 363 } 364 } 365 } 366 | Popular Tags |