DefaultReservedValueService implements ReservedValueService { @Override @Transactional public List<ReservedValue> reserve( TextPattern textPattern, int numberOfReservations, Map<String, String> values, Date expires ) throws ReserveValueException, TextPatternGenerationException { long startTime = System.currentTimeMillis(); int attemptsLeft = 10; List<ReservedValue> resultList = new ArrayList<>(); TextPatternSegment generatedSegment = getGeneratedSegment( textPattern ); String key = textPatternService.resolvePattern( textPattern, values ); String valueKey = ( generatedSegment != null ? key.replaceAll( Pattern.quote( generatedSegment.getRawSegment() ), "%" ) : key ); ReservedValue reservedValue = new ReservedValue( textPattern.getOwnerObject().name(), textPattern.getOwnerUid(), key, valueKey, expires ); if ( !hasEnoughValuesLeft( reservedValue, TextPatternValidationUtils.getTotalValuesPotential( generatedSegment ), numberOfReservations ) ) { throw new ReserveValueException( "Not enough values left to reserve " + numberOfReservations + " values." ); } if ( generatedSegment == null && numberOfReservations == 1 ) { reservedValue.setValue( key ); return reservedValueStore.reserveValues( reservedValue, Lists.newArrayList( key ) ); } List<String> usedGeneratedValues = new ArrayList<>(); int numberOfValuesLeftToGenerate = numberOfReservations; try { while ( attemptsLeft-- > 0 && numberOfValuesLeftToGenerate > 0 ) { if ( System.currentTimeMillis() - startTime >= GENERATION_TIMEOUT ) { throw new TimeoutException( "Generation and reservation of values took too long" ); } List<String> resolvedPatterns = new ArrayList<>(); List<String> generatedValues = new ArrayList<>(); int maxGenerateAttempts = 10; while ( generatedValues.size() < numberOfValuesLeftToGenerate && maxGenerateAttempts-- > 0 ) { generatedValues.addAll( generateValues( textPattern, key, numberOfReservations - resultList.size() ) ); generatedValues.removeAll( usedGeneratedValues ); } usedGeneratedValues.addAll( generatedValues ); for ( int i = 0; i < numberOfReservations - resultList.size(); i++ ) { resolvedPatterns.add( textPatternService.resolvePattern( textPattern, ImmutableMap.<String, String>builder() .putAll( values ) .put( generatedSegment.getMethod().name(), generatedValues.get( i ) ) .build() ) ); } resultList.addAll( reservedValueStore.reserveValues( reservedValue, resolvedPatterns ) ); numberOfValuesLeftToGenerate = numberOfReservations - resultList.size(); } } catch ( TimeoutException ex ) { log.warn( String.format( "Generation and reservation of values for %s wih uid %s timed out. %s values was reserved. You might be running low on available values", textPattern.getOwnerObject().name(), textPattern.getOwnerUid(), resultList.size() ) ); } return resultList; } DefaultReservedValueService( TextPatternService textPatternService, ReservedValueStore reservedValueStore,
        SequentialNumberCounterStore sequentialNumberCounterStore ); @Override @Transactional List<ReservedValue> reserve( TextPattern textPattern, int numberOfReservations, Map<String, String> values, Date expires ); @Override @Transactional boolean useReservedValue( TextPattern textPattern, String value ); @Override @Transactional(readOnly = true) boolean isReserved( TextPattern textPattern, String value ); @Override @Transactional void removeExpiredReservations(); @Override @Transactional void deleteReservedValueByUid( String uid );  }
@Test public void testReserveReserveASingleSimpleValueWhenNotUsed() throws Exception { List<ReservedValue> res = reservedValueService.reserve( simpleTextPattern, 1, new HashMap<>(), future ); assertEquals( "FOOBAR", res.get( 0 ).getValue() ); assertEquals( 1, reservedValueStore.getCount() ); }
@Test public void testReserveReserveASingleSimpleValueWhenUsed() { reservedValueStore.reserveValues( simpleReservedValue, Lists.newArrayList( "FOOBAR" ) ); List<ReservedValue> res = null; try { res = reservedValueService.reserve( simpleTextPattern, 1, new HashMap<>(), future ); } catch ( Exception e ) { assertTrue( e instanceof ReserveValueException ); assertEquals( "Could not reserve value: Not enough values left to reserve 1 values.", e.getMessage() ); } assertNull( res ); assertEquals( 1, reservedValueStore.getCount() ); }
@Test public void testReserveReserveATwoSimpleValuesShouldFail() { List<ReservedValue> res = null; try { res = reservedValueService.reserve( simpleTextPattern, 2, new HashMap<>(), future ); } catch ( Exception e ) { assertTrue( e instanceof ReserveValueException ); assertEquals( "Could not reserve value: Not enough values left to reserve 2 values.", e.getMessage() ); } assertNull( res ); assertEquals( 0, reservedValueStore.getCount() ); }
@Test public void testReserveReserveMultipleRandomValues() throws Exception { reservedValueService.reserve( simpleRandomTextPattern, 3, new HashMap<>(), future ); List<ReservedValue> all = reservedValueStore.getAll(); assertEquals( 3, all.stream() .filter( ( rv ) -> rv.getValue().indexOf( "TEST-" ) == 0 && rv.getValue().length() == 8 ) .count() ); assertEquals( 3, all.size() ); }
@Test public void testReserveReserveASequentialValueWhenNotUsed() throws Exception { List<ReservedValue> res = reservedValueService.reserve( simpleSequentialTextPattern, 1, new HashMap<>(), future ); assertEquals( 1, res.stream().filter( ( rv ) -> rv.getValue().indexOf( "TEST-" ) == 0 && rv.getValue().length() == 7 ).count() ); assertEquals( 1, reservedValueStore.getCount() ); }
@Test public void testReserveReserveMultipleSequentialValueWhenNotUsed() throws Exception { List<ReservedValue> res = reservedValueService.reserve( simpleSequentialTextPattern, 50, new HashMap<>(), future ); assertEquals( 50, res.stream().filter( ( rv ) -> rv.getValue().indexOf( "TEST-" ) == 0 && rv.getValue().length() == 7 ).count() ); assertEquals( 50, reservedValueStore.getCount() ); }
@Test public void testReserveReserveMultipleSequentialValueWhenSomeExists() throws Exception { List<ReservedValue> reserved = reservedValueService .reserve( simpleSequentialTextPattern, 50, new HashMap<>(), future ); assertEquals( 50, reserved.stream().filter( ( rv ) -> rv.getValue().indexOf( "TEST-" ) == 0 && rv.getValue().length() == 7 ).count() ); assertEquals( 50, reservedValueStore.getCount() ); List<ReservedValue> res = reservedValueService.reserve( simpleSequentialTextPattern, 25, new HashMap<>(), future ); assertTrue( ListUtils.intersection( reserved, res ).isEmpty() ); assertEquals( 25, res.stream().filter( ( rv ) -> rv.getValue().indexOf( "TEST-" ) == 0 && rv.getValue().length() == 7 ).count() ); assertEquals( 75, reservedValueStore.getCount() ); }
@Test public void testReserveReserveTooManySequentialValuesWhenNoneExists() throws Exception { thrown.expect( ReserveValueException.class ); thrown.expectMessage( "Could not reserve value: Not enough values left to reserve 101 values." ); reservedValueService.reserve( simpleSequentialTextPattern, 101, new HashMap<>(), future ); }
@Test public void testReserveReserveTooManySequentialValuesWhenSomeExists() throws Exception { assertEquals( 99, reservedValueService.reserve( simpleSequentialTextPattern, 99, new HashMap<>(), future ).size() ); thrown.expect( ReserveValueException.class ); thrown.expectMessage( "Could not reserve value: Not enough values left to reserve 1 values." ); reservedValueService.reserve( simpleSequentialTextPattern, 1, new HashMap<>(), future ); }
@Test public void testReserveReserveStringValueWithValues() throws Exception { Map<String, String> map = new HashMap<>(); map.put( "ORG_UNIT_CODE", "OSLO" ); List<ReservedValue> result = reservedValueService.reserve( simpleStringPattern, 1, map, future ); assertEquals( 1, result.size() ); assertEquals( "TEST-OS", result.get( 0 ).getValue() ); }