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