Java experience sheet

Read files > 1 GB lazily

This reads big files (>200 MBs) sequentially and without loading the whole File in memory. This way we’re able to read text files on the Gigabyte level. This example was done reading from a remote SFTP server.

final ChannelSftp sftpClient = this.connect();
final InputStream is = sftpClient.get(file);
final InputStreamReader isReader
      = new InputStreamReader(is);  

try (final BufferedReader bffReader  
      = new BufferedReader(isReader)) {
  bffReader.lines()
        .forEach(this::doAction);
} catch(final IOException ex) {
  log.error("bla", ex);
}

Operate a Stream in batches

(This solution uses Google Guava)

I have a really big List<Object> with f.e. 600.000 entries and I’d like to be able to operate them in batches of n size.

public void handle() {
  final int batchSize = 100; // n
  final List<MyEntity> list = this.dao.findAll();
  Iterators.partition(list.iterator(), batchSize)
           .forEachRemaining(this::consumer);  
}

private void consumer(final List<MyEntity> batch) {
  // this will contain 100 Entities
  // do x
}

Reference(s)

https://stackoverflow.com/questions/30641383/java-8-stream-with-batch-processing

Iterate, operate and delete from a single List

This has the benefit we don’t need to create another List to contain the result of our operation. It comes really handy when we’ve to operate big Lists of items (+500.000) and we’re already near the limit of our VM.

/**
 * @param originalItems
 *          huge list
 * @param filteredItems
 *          map to filter them into
 */
private void filterAndRemove(
      final List<TYPE> originalItems,
      final Map<String, List<TYPE>> filteredItems)
{
  final ListIterator<TYPE> origIterator =
      originalItems.listIterator();
  for (int idx = 0; origIterator.hasNext(); idx++)
  {
    final TYPE item = origIterator.next();
    final String key = this.buildKey(item);
    filteredItems.put(key, item);
    origIterator.remove();
    this.clearMemoryEvery(idx, 1000);
  }
}

Convert checked into unchecked exception

(Test example in private Repo)

Use Unchecked.consumer()

private OrderRepo repo;

public File exportFile(String fileName) {
  File file = new File("export/" + fileName);
  try (Writer writer = new FileWriter(file)) {
    writer.write("ID;Date\n");
    repo.findByActiveTrue()
    .map(o -> o.getId() + ";"
    	+ o.getCreationDate())
    .forEach(Unchecked.consumer(writer::write));
  }
}

Reference

Conference (18:45)

Time

Obtain last day of a month

(This uses Java8 Time API)

final LocalDate invoiceDate =  
		position.getReferenceDate();
final int month = invoiceDate.getMonthValue();
final int year = invoiceDate.getYear();
final YearMonth yearMonth =   
		YearMonth.of(year, month);
final int lastDayOfMonth =   
		yearMonth.lengthOfMonth();

Mock LocalDate.now()

The key is to give a fixed clock to LocalDate (Java 8).
First of all we prepare the real clock to be injected.

// This goes into our Spring's config class.
@Bean
public Clock clock() {
  return Clock.systemDefaultZone();
}

We inject and use it

@Component
public class Whatever {

  @Autowired
  private Clock clock;

  public LocalDate obtainDate() {
    return LocalDate.now(clock);
  }

}

And inside the test class, we can just mock it

@Test
public void testLocalDate() {
  // Given
  final String date = "2019-07-03";
  final DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("yyyy-MM-dd");
  final LocalDateTime dateTime =
    LocalDateTime.parse(date, formatter);

  final Clock fixedClock = Clock.fixed(  
    dateTime.atStartOfDay(ZoneId.systemDefault()).toInstant(),  
    ZoneId.systemDefault());

  // the clock is now ready to be injected and / or used

  // When
  // Then
}

Reference(s)

https://stackoverflow.com/questions/22463062/how-to-parse-format-dates-with-localdatetime-java-8
https://stackoverflow.com/questions/32792000/how-can-i-mock-java-time-localdate-now

Testing

Inject SpringContext into IT (TestNG)

To inject Spring’s context into an Integration-Test. Instead of @RunWith(…) [JUnit]

@ContextConfiguration(classes = { SpringConfig.class })
public class MyAutowiredTestNGTest extends AbstractTestNGSpringContextTests {

	@Autowired
	private Object whatever;

	...

}

Inject SpringContext into IT (JUnit4)

A good recommendation is to have an AppTestConfig.class for your tests, independent of the real bean.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= { AppTestConfig.class })
public class MyAutowiredJUnit4Test  {

    // here may you set your own class to inject

    @Autowired
    private ApplicationContext context;

    ...

}

MergedAnnotations$SearchStrategy error

When trying to @Autowire a JUnit4 test and it gives this error back

java.lang.NoClassDefFoundError: org.springframework.core.annotation.MergedAnnotations$SearchStrategy

Check we have uniform Spring versions at POM, as they cannot differ between test and normal dependencies.

BDDAssertions

(This uses BDDCatchException, Mockito and AssertJ)

Mock an exception, which will be thrown and caught

// Given
BDDMockito.given(mock.method())  
		.willThrow(Exception.class);

// When
BDDCatchException.when(mainTestedClass).method();

// Then
BDDAssertions  
.assertThat(BDDCatchException.caughtException())  
.isNull();

Test an exception is thrown:

// Given
CrudService service = new CrudService() {};
String userName = "admin";

// When
BDDCatchException.when(service)  
.methodToExecute(userName);

// Then
BDDAssertions  
.then(BDDCatchException.caughtException())  
.isInstanceOf(NameOfExpectedException.class);

Test no exception is thrown:

...
// Then  
BDDAssertions  
.then(BDDCatchException.caughtException())  
.isNull();

Inject mock into ‘new’ operator

// Real class  
protected JSch createStubInstance() {
	neturn new JSch();
}

// Test class  
@Test
public void testWhatever() {  
  JSch jschMock = Mockito.mock(JSch.class);  

  BDDMockito.given(realClass.createStubInstance())  
	.willReturn(jschMock);
}

Javadoc

Link javadoc to a method of the same class

/*
* This method does the same as
* {@link #escape(Parameters) escape}
* but with lists
*/

Spring

Clean Dirty context

When we use Spring’s DI in an integration test, the context may be dirty between every test and must be cleaned. This is done with @DirtiesContext tag.

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(classes =  
	{ CoreConfig.class })  
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)  
public class ASCIISystemIT { ... }  

IntelliJ

Maven builds, but IntelliJ doesn’t find imports

  1. From IntelliJ execute a mvn clean install
  2. Execute maven goal mvn dependency:purge-local-repository
  3. Build
  4. Rebuild project

Reference(s)

https://stackoverflow.com/questions/37581523/maven-doesnt-find-dependency