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