001package com.pingidentity.ds.plugin;
002
003import com.unboundid.directory.sdk.common.operation.UpdatableAddRequest;
004import com.unboundid.directory.sdk.common.operation.UpdatableAddResult;
005import com.unboundid.directory.sdk.common.types.ActiveOperationContext;
006import com.unboundid.directory.sdk.common.types.LogSeverity;
007import com.unboundid.directory.sdk.common.types.UpdatableEntry;
008import com.unboundid.directory.sdk.ds.api.Plugin;
009import com.unboundid.directory.sdk.ds.config.PluginConfig;
010import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
011import com.unboundid.directory.sdk.ds.types.PreParsePluginResult;
012import com.unboundid.ldap.sdk.*;
013import com.unboundid.util.args.*;
014
015import java.util.concurrent.atomic.AtomicLong;
016
017public class UniqueMod7IDGenerator extends Plugin {
018
019    public static final String ARG_NAME_ATTRIBUTE_NAME = "attribute-name";
020    public static final String ARG_NAME_EXTERNAL_SERVER = "external-server";
021    public static final String ARG_NAME_MIN_CX = "min-connections";
022    private static final String ARG_NAME_MAX_CX = "max-connections";
023    private static final String ARG_NAME_SEED = "seed";
024    public static final String ARG_NAME_BASE = "base";
025    public static final String ARG_NAME_SCOPE = "scope";
026    public static final String ARG_NAME_FILTER_ATTRIBUTE = "filter-attribute";
027    public static final String ARG_NAME_MAX_RETRIES = "max-retries";
028
029
030    private String attributeName;
031    private LDAPConnectionPool ldapConnectionPool;
032    private Integer seed;
033    private DN base;
034    private SearchScope scope;
035    private String filterAttr;
036    private AtomicLong id;
037    private Integer maxRetries;
038    private DirectoryServerContext serverContext;
039
040    @Override
041    public String getExtensionName() {
042        return "Unique mod7 UID Generator";
043    }
044
045    @Override
046    public String[] getExtensionDescription() {
047        return new String[] {"generates a unique mod7 uid attribute value"};
048    }
049
050    @Override
051    public void defineConfigArguments(ArgumentParser parser) throws ArgumentException {
052        StringArgument attributeNameArg = new StringArgument(null, ARG_NAME_ATTRIBUTE_NAME,false,1,"{attribute-name}","the name of the attribute (default: uid)","uid");
053        parser.addArgument(attributeNameArg);
054        StringArgument externalServerArgument = new StringArgument(null, ARG_NAME_EXTERNAL_SERVER, false, 1,
055                "{external-server}", "The name of the external server to issue the persistent search request to.");
056        parser.addArgument(externalServerArgument);
057        IntegerArgument minConnections = new IntegerArgument(null, ARG_NAME_MIN_CX,false,1,"{min-connections}","The minimum number of connection to maintain in the pool (default: 1)",1);
058        parser.addArgument(minConnections);
059        IntegerArgument maxConnections = new IntegerArgument(null, ARG_NAME_MAX_CX,false,1,"{max-connections}","The maximum number of connection to maintain in the pool (default: 10)",10);
060        parser.addArgument(maxConnections);
061        IntegerArgument seedArg = new IntegerArgument(null, ARG_NAME_SEED,false,1,"{seed}","The seed to use for generation",89000000);
062        parser.addArgument(seedArg);
063        DNArgument baseArg = new DNArgument(null, ARG_NAME_BASE,false,1,"{base}","The base to issue searches against", DN.NULL_DN);
064        parser.addArgument(baseArg);
065        ScopeArgument scopeArg = new ScopeArgument(null, ARG_NAME_SCOPE,false,"{scope}","The scope to use to locate entries (default: sub)", SearchScope.SUB);
066        parser.addArgument(scopeArg);
067        StringArgument filterArg=new StringArgument(null, ARG_NAME_FILTER_ATTRIBUTE,false,1,"{filter-attribute}","The filter attribute to use to locate entries (default: uid )","uid");
068        parser.addArgument(filterArg);
069        IntegerArgument retriesArg = new IntegerArgument(null, ARG_NAME_MAX_RETRIES,false,1,"{max-retries}","Maximum number of retries when looking up an entry",10);
070        parser.addArgument(retriesArg);
071    }
072
073    @Override
074    public void initializePlugin(DirectoryServerContext serverContext, PluginConfig config, ArgumentParser parser) throws LDAPException {
075        attributeName = parser.getStringArgument(ARG_NAME_ATTRIBUTE_NAME).getValue();
076
077        this.serverContext = serverContext;
078        LDAPConnectionOptions options = new LDAPConnectionOptions();
079        options.setUseSynchronousMode(true);
080        options.setUseTCPNoDelay(true);
081        options.setConnectTimeoutMillis(5000);
082        options.setResponseTimeoutMillis(OperationType.SEARCH,23000L);
083        options.setResponseTimeoutMillis(OperationType.ADD,31000L);
084        options.setResponseTimeoutMillis(OperationType.MODIFY,37000L);
085        options.setResponseTimeoutMillis(57000L);
086        LDAPConnection ldapConnection = serverContext.getLDAPExternalServerConnection(parser.getStringArgument(ARG_NAME_EXTERNAL_SERVER).getValue(), options);
087        Integer minCx = parser.getIntegerArgument(ARG_NAME_MIN_CX).getValue();
088        Integer maxCx = parser.getIntegerArgument(ARG_NAME_MAX_CX).getValue();
089        ldapConnectionPool = new LDAPConnectionPool(ldapConnection,minCx,maxCx);
090        seed= parser.getIntegerArgument(ARG_NAME_SEED).getValue();
091        base = parser.getDNArgument(ARG_NAME_BASE).getValue();
092        scope = parser.getScopeArgument(ARG_NAME_SCOPE).getValue();
093        filterAttr = parser.getStringArgument(ARG_NAME_FILTER_ATTRIBUTE).getValue();
094        id = new AtomicLong(seed);
095        maxRetries = parser.getIntegerArgument(ARG_NAME_MAX_RETRIES).getValue();
096    }
097
098    private synchronized String generateValue(){
099        Integer retries = 0;
100        l("starting unique ID generation");
101        while (retries++<maxRetries){
102            try {
103                Long thisID = id.incrementAndGet();
104                String filter = filterAttr+"="+thisID;
105                l("Remote System checking for: "+filter);
106                SearchResult searchResult = ldapConnectionPool.search(base.toString(), scope, filter, "1.1");
107                serverContext.logMessage(LogSeverity.DEBUG,"Remote System result code: "+searchResult.getResultCode());
108                if ( ResultCode.SUCCESS.equals(searchResult.getResultCode()) && searchResult.getEntryCount() == 0 ) {
109                    l("finishing unique ID generation");
110                    return String.format("%d",id.addAndGet(id.get() % 7 ));
111                }
112            } catch (LDAPSearchException e) {
113                l("Unique ID generation: Error with remote system "+e.getDiagnosticMessage());
114            }
115        }
116        l("failed unique ID generation");
117        return null;
118    }
119
120    @Override
121    public PreParsePluginResult doPreParse(ActiveOperationContext operationContext, UpdatableAddRequest request, UpdatableAddResult result) {
122        l("calling generateValue");
123        String value = generateValue();
124        l("generateValue() +> "+value);
125        if (value == null ) {
126            result.setDiagnosticMessage("Could not obtain value probably due to issue on remote system");
127            result.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
128            return new PreParsePluginResult(false,false,true,true);
129        } else {
130            request.getEntry().setAttribute(new Attribute(attributeName,value));
131            return PreParsePluginResult.SUCCESS;
132        }
133    }
134
135    private void l(String s){
136        System.out.println(s);
137        serverContext.logMessage(LogSeverity.INFO, s);
138    }
139}