
org.jberet.support.io.CassandraItemReader Maven / Gradle / Ivy
/*
* Copyright (c) 2018 Red Hat, Inc. and/or its affiliates.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.jberet.support.io;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jberet.support._private.SupportLogger;
import org.jberet.support._private.SupportMessages;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.LocalDate;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.TypeCodec;
import jakarta.batch.api.BatchProperty;
import jakarta.batch.api.chunk.ItemReader;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import jakarta.inject.Named;
/**
* An implementation of {@code jakarta.batch.api.chunk.ItemReader} that reads data items from the Cassandra cluster.
*
* @see CassandraItemWriter
* @see CassandraBatchlet
* @see CassandraReaderWriterBase
*
* @since 1.3.0
*/
@Named
@Dependent
public class CassandraItemReader extends CassandraReaderWriterBase implements ItemReader {
/**
* The row number in the {@code ResultSet} to start reading. It's a positive integer starting from 1.
*/
@Inject
@BatchProperty
protected int start;
/**
* The row number in the {@code ResultSet} to end reading (inclusive). It's a positive integer starting from 1.
*/
@Inject
@BatchProperty
protected int end;
/**
* The query fetch size. As stated in {@code com.datastax.driver.core.Statement#setFetchSize(int)},
* the fetch size controls how much resulting rows will be retrieved simultaneously
* (the goal being to avoid loading too much results in memory for queries yielding large results).
* Please note that while value as low as 1 can be used,
* it is *highly* discouraged to use such a low value in practice as it will yield very poor performance.
* If in doubt, leaving the default is probably a good idea.
* Optional property, and defaults to null (not specified).
*/
@Inject
@BatchProperty
protected Integer fetchSize;
/**
* String keys used in target data structure for database columns. Optional property, and if not specified, it
* defaults to {@link #columnLabels}.
*
* For example, if {@link #cql} is
*
* SELECT NAME, ADDRESS, AGE FROM PERSON
*
* And you want to map the data to the following form:
*
* {"fn" = "Jon", "addr" = "1 Main st", "age" = 30}
*
* then {@code columnMapping} should be specified as follows in job xml:
*
* "fn, addr, age"
*/
@Inject
@BatchProperty
protected String[] columnMapping;
/**
* Whether to perform bean validation with Bean Validation API, if the {@link #beanType}
* is a custom POJO bean type. Optional property, and defaults to false
* (perform bean validation).
*/
@Inject
@BatchProperty
protected boolean skipBeanValidation;
/**
* The column names of the {@code ResultSet}
*/
protected String[] columnLabels;
/**
* The regular cql statement based on {@link #cql}
*/
protected Statement statement;
/**
* The {@code com.datastax.driver.core.ResultSet} from executing {@link #statement}
*/
protected com.datastax.driver.core.ResultSet resultSet;
/**
* The iterator of {@code com.datastax.driver.core.Row} based on {@link #resultSet}
*/
protected Iterator rowIterator;
/**
* The column definitions of the {@code ResultSet}
*/
protected ColumnDefinitions columnDefinitions;
/**
* For {@link #beanType} of custom POJO bean, this property defines a mapping
* between POJO bean's property name and its JavaBeans {@code java.beans.PropertyDescriptor}.
*/
protected Map propertyDescriptorMap;
/**
* The current row number.
*/
protected int currentRowNumber;
/**
* {@inheritDoc}
*/
@Override
public void open(final Serializable checkpoint) throws Exception {
if (session == null) {
initSession();
}
initBeanPropertyDescriptors();
if (statement == null) {
statement = new SimpleStatement(cql);
}
if (fetchSize != null) {
statement.setFetchSize(fetchSize);
}
resultSet = session.execute(statement);
rowIterator = resultSet.iterator();
columnDefinitions = resultSet.getColumnDefinitions();
if (columnMapping == null) {
if (beanType != List.class) {
final int columnCount = columnDefinitions.size();
columnLabels = new String[columnCount];
for (int i = 0; i < columnCount; ++i) {
columnLabels[i] = columnDefinitions.getName(i);
}
columnMapping = columnLabels;
}
} else if (columnMapping.length != columnDefinitions.size()) {
throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, Arrays.toString(columnMapping), "columnMapping");
}
if (start <= 0) {
start = 1;
}
if (end == 0) {
end = Integer.MAX_VALUE;
}
if (end < start) {
throw SupportMessages.MESSAGES.invalidReaderWriterProperty(null, String.valueOf(end), "end");
}
//readyPosition is the position before the first item to be read
int readyPosition = start - 1;
if (checkpoint != null) {
final int checkpointPosition = (Integer) checkpoint;
if (checkpointPosition > readyPosition) {
readyPosition = checkpointPosition;
}
}
for (; currentRowNumber < readyPosition && rowIterator.hasNext(); currentRowNumber++) {
rowIterator.next();
}
}
/**
* {@inheritDoc}
*/
@Override
public Object readItem() throws Exception {
if (currentRowNumber >= end) {
return null;
}
Object result = null;
if (rowIterator.hasNext()) {
final Row row = rowIterator.next();
if (beanType == List.class) {
final List