001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.server.core.number; 021 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.UUID; 029import java.util.concurrent.atomic.AtomicInteger; 030 031import org.apache.directory.api.ldap.model.constants.SchemaConstants; 032import org.apache.directory.api.ldap.model.entry.Attribute; 033import org.apache.directory.api.ldap.model.entry.DefaultEntry; 034import org.apache.directory.api.ldap.model.entry.DefaultModification; 035import org.apache.directory.api.ldap.model.entry.Entry; 036import org.apache.directory.api.ldap.model.entry.Modification; 037import org.apache.directory.api.ldap.model.entry.ModificationOperation; 038import org.apache.directory.api.ldap.model.exception.LdapException; 039import org.apache.directory.api.ldap.model.exception.LdapOtherException; 040import org.apache.directory.api.ldap.model.name.Dn; 041import org.apache.directory.api.ldap.model.schema.MatchingRule; 042import org.apache.directory.server.core.api.DirectoryService; 043import org.apache.directory.server.core.api.entry.ClonedServerEntry; 044import org.apache.directory.server.core.api.interceptor.BaseInterceptor; 045import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; 046import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext; 047import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 048import org.apache.directory.server.core.api.partition.Partition; 049import org.apache.directory.server.core.api.partition.PartitionTxn; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053 054/** 055 * An interceptor to increment any attribute with integer matching rule 056 * 057 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 058 */ 059public class NumberIncrementingInterceptor extends BaseInterceptor 060{ 061 /** A {@link Logger} for this class */ 062 private static final Logger LOG = LoggerFactory.getLogger( NumberIncrementingInterceptor.class ); 063 064 /** the DN of the holder entry */ 065 private Dn numberHolder; 066 067 /** a map of integer attribute and it's present value */ 068 private Map<String, AtomicInteger> incMap = new HashMap<>(); 069 070 071 @Override 072 public void init( DirectoryService directoryService ) throws LdapException 073 { 074 super.init( directoryService ); 075 076 numberHolder = new Dn( schemaManager, "ou=autoIncDataHolder,ou=system" ); 077 078 Partition systemPartition = directoryService.getSystemPartition(); 079 080 LookupOperationContext lookupContext = new LookupOperationContext( directoryService.getAdminSession(), numberHolder, SchemaConstants.ALL_ATTRIBUTES_ARRAY ); 081 lookupContext.setPartition( systemPartition ); 082 083 Entry entry; 084 085 try ( PartitionTxn partitionTxn = systemPartition.beginReadTransaction() ) 086 { 087 lookupContext.setTransaction( partitionTxn ); 088 089 entry = systemPartition.lookup( lookupContext ); 090 } 091 catch ( IOException ioe ) 092 { 093 throw new LdapOtherException( ioe.getMessage(), ioe ); 094 } 095 096 if ( entry == null ) 097 { 098 //FIXME make sure this entry addition gets replicated 099 entry = new DefaultEntry( schemaManager ); 100 entry.setDn( numberHolder ); 101 entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 102 entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.EXTENSIBLE_OBJECT_OC ); 103 entry.add( SchemaConstants.OU_AT, numberHolder.getRdn().getValue() ); 104 entry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 105 entry.add( SchemaConstants.ENTRY_CSN_AT, directoryService.getCSN().toString() ); 106 107 AddOperationContext addContext = new AddOperationContext( directoryService.getAdminSession() ); 108 addContext.setDn( numberHolder ); 109 addContext.setEntry( new ClonedServerEntry( entry ) ); 110 addContext.setPartition( systemPartition ); 111 PartitionTxn partitionTxn = null; 112 113 try 114 { 115 partitionTxn = systemPartition.beginWriteTransaction(); 116 addContext.setTransaction( partitionTxn ); 117 118 LOG.debug( "Adding container entry to hold numeric attribute values" ); 119 systemPartition.add( addContext ); 120 partitionTxn.commit(); 121 } 122 catch ( LdapException le ) 123 { 124 if ( partitionTxn != null ) 125 { 126 try 127 { 128 partitionTxn.abort(); 129 } 130 catch ( IOException ioe ) 131 { 132 throw new LdapOtherException( ioe.getMessage(), ioe ); 133 } 134 } 135 136 throw le; 137 } 138 catch ( IOException ioe ) 139 { 140 try 141 { 142 partitionTxn.abort(); 143 } 144 catch ( IOException ioe2 ) 145 { 146 throw new LdapOtherException( ioe2.getMessage(), ioe2 ); 147 } 148 149 throw new LdapOtherException( ioe.getMessage(), ioe ); 150 } 151 } 152 else 153 { 154 for ( Attribute at : entry ) 155 { 156 MatchingRule mr = at.getAttributeType().getEquality(); 157 158 if ( ( mr != null ) && SchemaConstants.INTEGER_MATCH_MR_OID.equals( mr.getOid() ) ) 159 { 160 int t = Integer.parseInt( at.getString() ); 161 incMap.put( at.getId(), new AtomicInteger( t ) ); 162 } 163 } 164 } 165 } 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 @Override 172 public void add( AddOperationContext addContext ) throws LdapException 173 { 174 LOG.debug( ">>> Entering into the number incrementing interceptor, addRequest" ); 175 176 if ( addContext.isReplEvent() ) 177 { 178 // Nope, go on. 179 next( addContext ); 180 return; 181 } 182 183 Entry entry = addContext.getEntry(); 184 185 List<Attribute> lst = new ArrayList<>(); 186 187 for ( String oid : incMap.keySet() ) 188 { 189 Attribute at = entry.get( oid ); 190 if ( at != null ) 191 { 192 lst.add( at ); 193 } 194 } 195 196 if ( lst.isEmpty() ) 197 { 198 next( addContext ); 199 return; 200 } 201 202 for ( Attribute at : lst ) 203 { 204 int stored = incMap.get( at.getId() ).get(); 205 at.clear(); 206 at.add( String.valueOf( stored + 1 ) ); 207 } 208 209 // Ok, we are golden. 210 next( addContext ); 211 212 ModifyOperationContext bindModCtx = new ModifyOperationContext( directoryService.getAdminSession() ); 213 bindModCtx.setDn( numberHolder ); 214 bindModCtx.setPushToEvtInterceptor( true ); 215 216 List<Modification> mods = new ArrayList<>(); 217 218 for ( Attribute at : lst ) 219 { 220 AtomicInteger ai = incMap.get( at.getId() ); 221 ai.set( ai.get() + 1 ); 222 223 Modification mod = new DefaultModification(); 224 mod.setOperation( ModificationOperation.REPLACE_ATTRIBUTE ); 225 mod.setAttribute( at ); 226 227 mods.add( mod ); 228 } 229 230 bindModCtx.setModItems( mods ); 231 232 directoryService.getPartitionNexus().modify( bindModCtx ); 233 234 LOG.debug( "Successfully updated numeric attribute in {}", numberHolder ); 235 } 236}