001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Portions Copyright 2013-2015 ForgeRock AS 025 */ 026package org.opends.server.protocols.http; 027 028import static org.forgerock.opendj.adapter.server3x.Converters.*; 029import static org.forgerock.opendj.ldap.ByteString.*; 030import static org.forgerock.opendj.ldap.LdapException.*; 031import static org.forgerock.opendj.ldap.spi.LdapPromiseImpl.*; 032 033import java.util.LinkedHashSet; 034import java.util.concurrent.atomic.AtomicInteger; 035 036import javax.servlet.http.HttpServletResponse; 037 038import org.forgerock.i18n.slf4j.LocalizedLogger; 039import org.forgerock.opendj.ldap.AbstractAsynchronousConnection; 040import org.forgerock.opendj.ldap.ByteString; 041import org.forgerock.opendj.ldap.ConnectionEventListener; 042import org.forgerock.opendj.ldap.IntermediateResponseHandler; 043import org.forgerock.opendj.ldap.LdapPromise; 044import org.forgerock.opendj.ldap.ResultCode; 045import org.forgerock.opendj.ldap.SearchResultHandler; 046import org.forgerock.opendj.ldap.requests.AbandonRequest; 047import org.forgerock.opendj.ldap.requests.AddRequest; 048import org.forgerock.opendj.ldap.requests.BindRequest; 049import org.forgerock.opendj.ldap.requests.CompareRequest; 050import org.forgerock.opendj.ldap.requests.DeleteRequest; 051import org.forgerock.opendj.ldap.requests.ExtendedRequest; 052import org.forgerock.opendj.ldap.requests.ModifyDNRequest; 053import org.forgerock.opendj.ldap.requests.ModifyRequest; 054import org.forgerock.opendj.ldap.requests.SearchRequest; 055import org.forgerock.opendj.ldap.requests.SimpleBindRequest; 056import org.forgerock.opendj.ldap.requests.UnbindRequest; 057import org.forgerock.opendj.ldap.responses.BindResult; 058import org.forgerock.opendj.ldap.responses.CompareResult; 059import org.forgerock.opendj.ldap.responses.ExtendedResult; 060import org.forgerock.opendj.ldap.responses.Result; 061import org.forgerock.opendj.ldap.spi.LdapPromiseImpl; 062import org.opends.server.core.AbandonOperation; 063import org.opends.server.core.AbandonOperationBasis; 064import org.opends.server.core.AddOperation; 065import org.opends.server.core.AddOperationBasis; 066import org.opends.server.core.BindOperation; 067import org.opends.server.core.BindOperationBasis; 068import org.opends.server.core.BoundedWorkQueueStrategy; 069import org.opends.server.core.CompareOperation; 070import org.opends.server.core.CompareOperationBasis; 071import org.opends.server.core.DeleteOperation; 072import org.opends.server.core.DeleteOperationBasis; 073import org.opends.server.core.ExtendedOperation; 074import org.opends.server.core.ExtendedOperationBasis; 075import org.opends.server.core.ModifyDNOperation; 076import org.opends.server.core.ModifyDNOperationBasis; 077import org.opends.server.core.ModifyOperation; 078import org.opends.server.core.ModifyOperationBasis; 079import org.opends.server.core.QueueingStrategy; 080import org.opends.server.core.SearchOperation; 081import org.opends.server.core.SearchOperationBasis; 082import org.opends.server.core.UnbindOperation; 083import org.opends.server.core.UnbindOperationBasis; 084import org.opends.server.protocols.ldap.AbandonRequestProtocolOp; 085import org.opends.server.protocols.ldap.AddRequestProtocolOp; 086import org.opends.server.protocols.ldap.BindRequestProtocolOp; 087import org.opends.server.protocols.ldap.CompareRequestProtocolOp; 088import org.opends.server.protocols.ldap.DeleteRequestProtocolOp; 089import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp; 090import org.opends.server.protocols.ldap.LDAPMessage; 091import org.opends.server.protocols.ldap.ModifyDNRequestProtocolOp; 092import org.opends.server.protocols.ldap.ModifyRequestProtocolOp; 093import org.opends.server.protocols.ldap.ProtocolOp; 094import org.opends.server.protocols.ldap.SearchRequestProtocolOp; 095import org.opends.server.protocols.ldap.UnbindRequestProtocolOp; 096import org.opends.server.types.AuthenticationInfo; 097import org.opends.server.types.DisconnectReason; 098import org.opends.server.types.Operation; 099 100/** 101 * Adapter class between LDAP SDK's {@link org.forgerock.opendj.ldap.Connection} 102 * and OpenDJ server's 103 * {@link org.opends.server.protocols.http.HTTPClientConnection}. 104 */ 105public class SdkConnectionAdapter extends AbstractAsynchronousConnection 106{ 107 108 /** The tracer object for the debug logger. */ 109 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 110 111 /** The HTTP client connection being "adapted". */ 112 private final HTTPClientConnection clientConnection; 113 114 /** 115 * The next message ID (and operation ID) that should be used for this 116 * connection. 117 */ 118 private final AtomicInteger nextMessageID = new AtomicInteger(0); 119 120 /** The queueing strategy used for this connection. */ 121 private final QueueingStrategy queueingStrategy; 122 123 /** 124 * Whether this connection has been closed by calling {@link #close()} or 125 * {@link #close(UnbindRequest, String)}. 126 */ 127 private boolean isClosed; 128 129 /** 130 * Constructor. 131 * 132 * @param clientConnection 133 * the HTTP client connection being "adapted" 134 */ 135 public SdkConnectionAdapter(HTTPClientConnection clientConnection) 136 { 137 this.clientConnection = clientConnection; 138 this.queueingStrategy = 139 new BoundedWorkQueueStrategy(clientConnection.getConnectionHandler() 140 .getCurrentConfig().getMaxConcurrentOpsPerConnection()); 141 } 142 143 private <R> LdapPromise<R> enqueueOperation(Operation operation) 144 { 145 return enqueueOperation(operation, null); 146 } 147 148 @SuppressWarnings({ "rawtypes", "unchecked" }) 149 private <R> LdapPromise<R> enqueueOperation(Operation operation, SearchResultHandler entryHandler) 150 { 151 final LdapPromiseImpl<R> promise = newLdapPromiseImpl(operation.getMessageID()); 152 153 try 154 { 155 operation.setInnerOperation(this.clientConnection.isInnerConnection()); 156 157 HTTPConnectionHandler connHandler = this.clientConnection.getConnectionHandler(); 158 if (connHandler.keepStats()) 159 { 160 connHandler.getStatTracker().updateMessageRead( 161 new LDAPMessage(operation.getMessageID(), toRequestProtocolOp(operation))); 162 } 163 164 // need this raw cast here to fool the compiler's generic type safety 165 // Problem here is due to the generic type R on enqueueOperation() 166 clientConnection.addOperationInProgress(operation, (LdapPromiseImpl) promise, entryHandler); 167 queueingStrategy.enqueueRequest(operation); 168 } 169 catch (Exception e) 170 { 171 logger.traceException(e); 172 clientConnection.removeOperationInProgress(operation.getMessageID()); 173 // TODO JNR add error message?? 174 promise.handleException(newLdapException(ResultCode.OPERATIONS_ERROR, e)); 175 } 176 177 return promise; 178 } 179 180 private ProtocolOp toRequestProtocolOp(Operation operation) 181 { 182 if (operation instanceof AbandonOperation) 183 { 184 final AbandonOperation op = (AbandonOperation) operation; 185 return new AbandonRequestProtocolOp(op.getIDToAbandon()); 186 } 187 else if (operation instanceof AddOperation) 188 { 189 final AddOperation op = (AddOperation) operation; 190 return new AddRequestProtocolOp(op.getRawEntryDN(), 191 op.getRawAttributes()); 192 } 193 else if (operation instanceof BindOperation) 194 { 195 final BindOperation op = (BindOperation) operation; 196 return new BindRequestProtocolOp(op.getRawBindDN(), 197 op.getSASLMechanism(), op.getSASLCredentials()); 198 } 199 else if (operation instanceof CompareOperation) 200 { 201 final CompareOperation op = (CompareOperation) operation; 202 return new CompareRequestProtocolOp(op.getRawEntryDN(), op 203 .getRawAttributeType(), op.getAssertionValue()); 204 } 205 else if (operation instanceof DeleteOperation) 206 { 207 final DeleteOperation op = (DeleteOperation) operation; 208 return new DeleteRequestProtocolOp(op.getRawEntryDN()); 209 } 210 else if (operation instanceof ExtendedOperation) 211 { 212 final ExtendedOperation op = (ExtendedOperation) operation; 213 return new ExtendedRequestProtocolOp(op.getRequestOID(), op 214 .getRequestValue()); 215 } 216 else if (operation instanceof ModifyDNOperation) 217 { 218 final ModifyDNOperation op = (ModifyDNOperation) operation; 219 return new ModifyDNRequestProtocolOp(op.getRawEntryDN(), op 220 .getRawNewRDN(), op.deleteOldRDN(), op.getRawNewSuperior()); 221 } 222 else if (operation instanceof ModifyOperation) 223 { 224 final ModifyOperation op = (ModifyOperation) operation; 225 return new ModifyRequestProtocolOp(op.getRawEntryDN(), op 226 .getRawModifications()); 227 } 228 else if (operation instanceof SearchOperation) 229 { 230 final SearchOperation op = (SearchOperation) operation; 231 return new SearchRequestProtocolOp(op.getRawBaseDN(), op.getScope(), op 232 .getDerefPolicy(), op.getSizeLimit(), op.getTimeLimit(), op 233 .getTypesOnly(), op.getRawFilter(), op.getAttributes()); 234 } 235 else if (operation instanceof UnbindOperation) 236 { 237 return new UnbindRequestProtocolOp(); 238 } 239 throw new RuntimeException("Not implemented for operation " + operation); 240 } 241 242 @Override 243 public LdapPromise<Void> abandonAsync(AbandonRequest request) 244 { 245 final int messageID = nextMessageID.getAndIncrement(); 246 return enqueueOperation(new AbandonOperationBasis(clientConnection, messageID, messageID, 247 to(request.getControls()), request.getRequestID())); 248 } 249 250 @Override 251 public LdapPromise<Result> addAsync(AddRequest request, IntermediateResponseHandler intermediateResponseHandler) 252 { 253 final int messageID = nextMessageID.getAndIncrement(); 254 return enqueueOperation(new AddOperationBasis(clientConnection, messageID, messageID, to(request.getControls()), 255 valueOfObject(request.getName()), to(request.getAllAttributes()))); 256 } 257 258 @Override 259 public void addConnectionEventListener(ConnectionEventListener listener) 260 { 261 // not useful so far 262 } 263 264 @Override 265 public LdapPromise<BindResult> bindAsync(BindRequest request, 266 IntermediateResponseHandler intermediateResponseHandler) 267 { 268 final int messageID = nextMessageID.getAndIncrement(); 269 String userName = request.getName(); 270 byte[] password = ((SimpleBindRequest) request).getPassword(); 271 return enqueueOperation(new BindOperationBasis(clientConnection, messageID, messageID, to(request.getControls()), 272 "3", ByteString.valueOfUtf8(userName), ByteString.wrap(password))); 273 } 274 275 @Override 276 public void close(UnbindRequest request, String reason) 277 { 278 AuthenticationInfo authInfo = this.clientConnection.getAuthenticationInfo(); 279 if (authInfo != null && authInfo.isAuthenticated()) 280 { 281 final int messageID = nextMessageID.getAndIncrement(); 282 final UnbindOperationBasis operation = new UnbindOperationBasis( 283 clientConnection, messageID, messageID, to(request.getControls())); 284 operation.setInnerOperation(this.clientConnection.isInnerConnection()); 285 286 // run synchronous 287 operation.run(); 288 } 289 else 290 { 291 this.clientConnection.disconnect(DisconnectReason.UNBIND, false, null); 292 } 293 294 // At this point, we try to log the request with OK status code. 295 // If it was already logged, it will be a no op. 296 this.clientConnection.log(HttpServletResponse.SC_OK); 297 298 isClosed = true; 299 } 300 301 @Override 302 public LdapPromise<CompareResult> compareAsync(CompareRequest request, 303 IntermediateResponseHandler intermediateResponseHandler) 304 { 305 final int messageID = nextMessageID.getAndIncrement(); 306 return enqueueOperation(new CompareOperationBasis(clientConnection, messageID, messageID, 307 to(request.getControls()), valueOfObject(request.getName()), 308 request.getAttributeDescription().getAttributeType().getOID(), 309 request.getAssertionValue())); 310 } 311 312 @Override 313 public LdapPromise<Result> deleteAsync(DeleteRequest request, 314 IntermediateResponseHandler intermediateResponseHandler) 315 { 316 final int messageID = nextMessageID.getAndIncrement(); 317 return enqueueOperation(new DeleteOperationBasis(clientConnection, messageID, messageID, 318 to(request.getControls()), valueOfObject(request.getName()))); 319 } 320 321 @Override 322 public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(ExtendedRequest<R> request, 323 IntermediateResponseHandler intermediateResponseHandler) 324 { 325 final int messageID = nextMessageID.getAndIncrement(); 326 ExtendedOperation op = new ExtendedOperationBasis( 327 clientConnection, messageID, messageID, to(request.getControls()), request.getOID(), request.getValue()); 328 op.setAuthorizationEntry(clientConnection.getAuthenticationInfo().getAuthorizationEntry()); 329 return enqueueOperation(op); 330 } 331 332 /** 333 * Return the queueing strategy used by this connection. 334 * 335 * @return The queueing strategy used by this connection 336 */ 337 public QueueingStrategy getQueueingStrategy() 338 { 339 return queueingStrategy; 340 } 341 342 @Override 343 public boolean isClosed() 344 { 345 return isClosed; 346 } 347 348 @Override 349 public boolean isValid() 350 { 351 return this.clientConnection.isConnectionValid(); 352 } 353 354 @Override 355 public LdapPromise<Result> modifyAsync(ModifyRequest request, 356 IntermediateResponseHandler intermediateResponseHandler) 357 { 358 final int messageID = nextMessageID.getAndIncrement(); 359 return enqueueOperation(new ModifyOperationBasis(clientConnection, messageID, messageID, 360 to(request.getControls()), to(request.getName()), 361 toModifications(request.getModifications()))); 362 } 363 364 @Override 365 public LdapPromise<Result> modifyDNAsync(ModifyDNRequest request, 366 IntermediateResponseHandler intermediateResponseHandler) 367 { 368 final int messageID = nextMessageID.getAndIncrement(); 369 return enqueueOperation(new ModifyDNOperationBasis(clientConnection, messageID, messageID, 370 to(request.getControls()), to(request.getName()), to(request 371 .getNewRDN()), request.isDeleteOldRDN(), to(request 372 .getNewSuperior()))); 373 } 374 375 @Override 376 public void removeConnectionEventListener(ConnectionEventListener listener) 377 { 378 // not useful so far 379 } 380 381 @Override 382 public LdapPromise<Result> searchAsync(final SearchRequest request, 383 final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) 384 { 385 final int messageID = nextMessageID.getAndIncrement(); 386 return enqueueOperation(new SearchOperationBasis(clientConnection, messageID, messageID, 387 to(request.getControls()), to(request.getName()), 388 request.getScope(), request.getDereferenceAliasesPolicy(), 389 request.getSizeLimit(), request.getTimeLimit(), 390 request.isTypesOnly(), toSearchFilter(request.getFilter()), 391 new LinkedHashSet<String>(request.getAttributes())), entryHandler); 392 } 393 394 @Override 395 public String toString() 396 { 397 return this.clientConnection.toString(); 398 } 399}