/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.sdk.examples.server.store; import static org.opends.sdk.ErrorResultException.newErrorResult; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.NavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.glassfish.grizzly.TransportFactory; import org.opends.sdk.*; import org.opends.sdk.ldif.LDIFEntryReader; import org.opends.sdk.requests.*; import org.opends.sdk.responses.*; import com.sun.opends.sdk.tools.PerfToolTCPNIOTransportFactory; /** * An LDAP directory server which exposes data contained in an LDIF file. This * is implementation is very simple and is only intended as an example: * * This example takes the following command line parameters: * *
 *  <listenAddress> <listenPort> [<ldifFile>]
 * 
*/ public final class Main { /** * Proxy server connection factory implementation. */ private static final class Store implements ServerConnectionFactory { private final class ServerConnectionImpl implements ServerConnection { private ServerConnectionImpl(final LDAPClientContext clientContext) { // Nothing to do. } /** * {@inheritDoc} */ @Override public void handleAbandon(final Integer requestContext, final AbandonRequest request) throws UnsupportedOperationException { // Not implemented. } /** * {@inheritDoc} */ @Override public void handleAdd(final Integer requestContext, final AddRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: controls. entryLock.writeLock().lock(); try { DN dn = request.getName(); if (entries.containsKey(dn)) { resultHandler.handleErrorResult(ErrorResultException .newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS, "The entry " + dn.toString() + " already exists")); } DN parent = dn.parent(); if (!entries.containsKey(parent)) { resultHandler.handleErrorResult(ErrorResultException .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The parent entry " + parent.toString() + " does not exist")); } else { entries.put(dn, request); resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS)); } } finally { entryLock.writeLock().unlock(); } } /** * {@inheritDoc} */ @Override public void handleBind(final Integer requestContext, final int version, final BindRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { if (request.getAuthenticationType() != ((byte) 0x80)) { // TODO: SASL authentication not implemented. resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "non-SIMPLE authentication not supported: " + request.getAuthenticationType())); } else { // TODO: always succeed. resultHandler.handleResult(Responses .newBindResult(ResultCode.SUCCESS)); } } /** * {@inheritDoc} */ @Override public void handleCompare(final Integer requestContext, final CompareRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: } /** * {@inheritDoc} */ @Override public void handleConnectionClosed(final Integer requestContext, final UnbindRequest request) { // Nothing to do. } /** * {@inheritDoc} */ @Override public void handleConnectionDisconnected(final ResultCode resultCode, final String message) { // Nothing to do. } /** * {@inheritDoc} */ @Override public void handleConnectionError(final Throwable error) { // Nothing to do. } /** * {@inheritDoc} */ @Override public void handleDelete(final Integer requestContext, final DeleteRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: controls. entryLock.writeLock().lock(); try { // TODO: check for children. DN dn = request.getName(); if (!entries.containsKey(dn)) { resultHandler.handleErrorResult(ErrorResultException .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString() + " does not exist")); } else { entries.remove(dn); resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS)); } } finally { entryLock.writeLock().unlock(); } } /** * {@inheritDoc} */ @Override public void handleExtendedRequest( final Integer requestContext, final ExtendedRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: not implemented. resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "Extended request operation not supported")); } /** * {@inheritDoc} */ @Override public void handleModify(final Integer requestContext, final ModifyRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: controls. // TODO: read lock is not really enough since concurrent updates may // still occur to the same entry. entryLock.readLock().lock(); try { DN dn = request.getName(); Entry entry = entries.get(dn); if (entry == null) { resultHandler.handleErrorResult(ErrorResultException .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString() + " does not exist")); } Entry newEntry = new LinkedHashMapEntry(entry); for (Modification mod : request.getModifications()) { ModificationType modType = mod.getModificationType(); if (modType.equals(ModificationType.ADD)) { // TODO: Reject empty attribute and duplicate values. newEntry.addAttribute(mod.getAttribute(), null); } else if (modType.equals(ModificationType.DELETE)) { // TODO: Reject missing values. newEntry.removeAttribute(mod.getAttribute(), null); } else if (modType.equals(ModificationType.REPLACE)) { newEntry.replaceAttribute(mod.getAttribute()); } else { resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "Modify request contains an unsupported modification type")); return; } } entries.put(dn, newEntry); resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS)); } finally { entryLock.readLock().unlock(); } } /** * {@inheritDoc} */ @Override public void handleModifyDN(final Integer requestContext, final ModifyDNRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: not implemented. resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "ModifyDN request operation not supported")); } /** * {@inheritDoc} */ @Override public void handleSearch(final Integer requestContext, final SearchRequest request, final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { // TODO: controls, limits, etc. entryLock.readLock().lock(); try { DN dn = request.getName(); Entry baseEntry = entries.get(dn); if (baseEntry == null) { resultHandler.handleErrorResult(ErrorResultException .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The entry " + dn.toString() + " does not exist")); } SearchScope scope = request.getScope(); if (scope.equals(SearchScope.BASE_OBJECT)) { sendEntry(request, resultHandler, baseEntry); } else if (scope.equals(SearchScope.SINGLE_LEVEL)) { sendEntry(request, resultHandler, baseEntry); NavigableMap subtree = entries.tailMap(dn, false); for (Entry entry : subtree.values()) { DN childDN = entry.getName(); if (childDN.isChildOf(dn)) { if (!sendEntry(request, resultHandler, entry)) { // Caller has asked to stop sending results. break; } } else if (!childDN.isSubordinateOrEqualTo(dn)) { // The remaining entries will be out of scope. break; } } } else if (scope.equals(SearchScope.WHOLE_SUBTREE)) { NavigableMap subtree = entries.tailMap(dn); for (Entry entry : subtree.values()) { DN childDN = entry.getName(); if (childDN.isSubordinateOrEqualTo(dn)) { if (!sendEntry(request, resultHandler, entry)) { // Caller has asked to stop sending results. break; } } else { // The remaining entries will be out of scope. break; } } } else { resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "Search request contains an unsupported search scope")); return; } resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS)); } finally { entryLock.readLock().unlock(); } } private boolean sendEntry(SearchRequest request, SearchResultHandler resultHandler, Entry entry) { // TODO: check filter, strip attributes. return resultHandler.handleEntry(Responses.newSearchResultEntry(entry)); } } private final ConcurrentSkipListMap entries; private final ReentrantReadWriteLock entryLock = new ReentrantReadWriteLock(); private Store(final ConcurrentSkipListMap entries) { this.entries = entries; } /** * {@inheritDoc} */ @Override public ServerConnection handleAccept( final LDAPClientContext clientContext) throws ErrorResultException { return new ServerConnectionImpl(clientContext); } } /** * Main method. * * @param args * The command line arguments: listen address, listen port, ldifFile */ public static void main(final String[] args) { if (args.length != 3) { System.err.println("Usage: listenAddress listenPort ldifFile"); System.exit(1); } // Use the same transport factory as the tools. TransportFactory.setInstance(new PerfToolTCPNIOTransportFactory()); // Parse command line arguments. final String localAddress = args[0]; final int localPort = Integer.parseInt(args[1]); // Read the LDIF. InputStream ldif; try { ldif = new FileInputStream(args[2]); } catch (final FileNotFoundException e) { System.err.println(e.getMessage()); System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue()); return; } final LDIFEntryReader reader = new LDIFEntryReader(ldif); ConcurrentSkipListMap entries = new ConcurrentSkipListMap(); try { while (reader.hasNext()) { Entry entry = reader.readEntry(); entries.put(entry.getName(), entry); } } catch (final IOException e) { System.err.println(e.getMessage()); System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); return; } finally { try { reader.close(); } catch (final IOException ignored) { // Ignore. } } // Quickly sanity check that every entry (except root entries) have a // parent. boolean isValid = true; for (DN dn : entries.keySet()) { if (dn.size() > 1) { DN parent = dn.parent(); if (!entries.containsKey(parent)) { System.err.println("The entry \"" + dn.toString() + "\" does not have a parent"); isValid = false; } } } if (!isValid) { System.exit(1); } // Create listener. final LDAPListenerOptions options = new LDAPListenerOptions() .setBacklog(4096); LDAPListener listener = null; try { listener = new LDAPListener(localAddress, localPort, new Store(entries), options); System.out.println("Press any key to stop the server..."); System.in.read(); } catch (final IOException e) { System.out .println("Error listening on " + localAddress + ":" + localPort); e.printStackTrace(); } finally { if (listener != null) { listener.close(); } } } private Main() { // Not used. } }