/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xd.integration.hadoop.config;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.Lifecycle;
import org.springframework.data.hadoop.store.DataStoreWriter;
import org.springframework.data.hadoop.store.codec.CodecInfo;
import org.springframework.data.hadoop.store.output.PartitionTextFileWriter;
import org.springframework.data.hadoop.store.output.TextFileWriter;
import org.springframework.data.hadoop.store.strategy.naming.FileNamingStrategy;
import org.springframework.data.hadoop.store.strategy.rollover.RolloverStrategy;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.messaging.Message;
import org.springframework.util.StringUtils;
import org.springframework.xd.integration.hadoop.partition.MessagePartitionStrategy;
/**
* A {@link FactoryBean} creating a {@link DataStoreWriter}. Created writer will be either
* {@link PartitionTextFileWriter} or {@link TextFileWriter} depending whether partition
* path expression is set.
*
* @author Janne Valkealahti
* @author Gary Russell
*/
public class StoreWriterFactoryBean implements InitializingBean, DisposableBean, FactoryBean<DataStoreWriter<?>>,
BeanFactoryAware, Lifecycle {
private volatile DataStoreWriter<?> storeWriter;
private volatile Configuration configuration;
private volatile Path basePath;
private volatile CodecInfo codec;
private volatile long idleTimeout;
private volatile long closeTimeout;
private volatile long flushTimeout;
private volatile boolean enableSync = false;
private volatile String inUseSuffix;
private volatile String inUsePrefix;
private volatile boolean overwrite = false;
private volatile String partitionExpression;
private volatile int fileOpenAttempts;
private volatile FileNamingStrategy fileNamingStrategy;
private volatile RolloverStrategy rolloverStrategy;
private volatile BeanFactory beanFactory;
private volatile EvaluationContext evaluationContext;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public DataStoreWriter<?> getObject() throws Exception {
return storeWriter;
}
@Override
public Class<?> getObjectType() {
return DataStoreWriter.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void destroy() throws Exception {
storeWriter = null;
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.evaluationContext == null) {
this.evaluationContext = IntegrationContextUtils.getEvaluationContext(this.beanFactory);
}
if (StringUtils.hasText(partitionExpression)) {
if (!(evaluationContext instanceof StandardEvaluationContext)) {
throw new RuntimeException("Expecting evaluationContext of type StandardEvaluationContext but was "
+ evaluationContext);
}
MessagePartitionStrategy<String> partitionStrategy = new MessagePartitionStrategy<String>(
partitionExpression, (StandardEvaluationContext) evaluationContext);
PartitionTextFileWriter<Message<?>> writer = new PartitionTextFileWriter<Message<?>>(configuration,
basePath,
codec,
partitionStrategy);
writer.setIdleTimeout(idleTimeout);
writer.setCloseTimeout(closeTimeout);
writer.setFlushTimeout(flushTimeout);
writer.setSyncable(enableSync);
writer.setInWritingPrefix(inUsePrefix);
writer.setInWritingSuffix(inUseSuffix);
writer.setOverwrite(overwrite);
writer.setFileNamingStrategyFactory(fileNamingStrategy);
writer.setRolloverStrategyFactory(rolloverStrategy);
if (beanFactory != null) {
writer.setBeanFactory(beanFactory);
}
if (fileOpenAttempts > 0) {
writer.setMaxOpenAttempts(fileOpenAttempts);
}
storeWriter = writer;
}
else {
TextFileWriter writer = new TextFileWriter(configuration, basePath, codec);
writer.setIdleTimeout(idleTimeout);
writer.setCloseTimeout(closeTimeout);
writer.setInWritingPrefix(inUsePrefix);
writer.setInWritingSuffix(inUseSuffix);
writer.setOverwrite(overwrite);
writer.setFlushTimeout(flushTimeout);
writer.setSyncable(enableSync);
writer.setFileNamingStrategy(fileNamingStrategy);
writer.setRolloverStrategy(rolloverStrategy);
if (beanFactory != null) {
writer.setBeanFactory(beanFactory);
}
if (fileOpenAttempts > 0) {
writer.setMaxOpenAttempts(fileOpenAttempts);
}
storeWriter = writer;
}
if (storeWriter instanceof InitializingBean) {
((InitializingBean) storeWriter).afterPropertiesSet();
}
}
@Override
public boolean isRunning() {
if (storeWriter instanceof Lifecycle) {
return ((Lifecycle) storeWriter).isRunning();
}
else {
return false;
}
}
@Override
public void start() {
if (storeWriter instanceof Lifecycle) {
((Lifecycle) storeWriter).start();
}
}
@Override
public void stop() {
if (storeWriter instanceof Lifecycle) {
((Lifecycle) storeWriter).stop();
}
}
public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
// used with partition writer spel if set
this.evaluationContext = evaluationContext;
}
/**
* Sets the hadoop configuration for the writer.
*
* @param configuration the new configuration
*/
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
/**
* Sets the base path for the writer.
*
* @param basePath the new base path
*/
public void setBasePath(Path basePath) {
this.basePath = basePath;
}
/**
* Sets the codec for the writer.
*
* @param codec the new codec
*/
public void setCodec(CodecInfo codec) {
this.codec = codec;
}
/**
* Sets the idle timeout for the writer.
*
* @param idleTimeout the new idle timeout
*/
public void setIdleTimeout(long idleTimeout) {
this.idleTimeout = idleTimeout;
}
/**
* Sets the close timeout for the writer.
*
* @param closeTimeout the new close timeout
*/
public void setCloseTimeout(long closeTimeout) {
this.closeTimeout = closeTimeout;
}
/**
* Sets the flush timeout for the writer.
*
* @param flushTimeout the new flush timeout
*/
public void setFlushTimeout(long flushTimeout) {
this.flushTimeout = flushTimeout;
}
/**
* Enables the syncable flag for the writer.
*
* @param enableSync the new syncable flag
*/
public void setEnableSync(boolean enableSync) {
this.enableSync = enableSync;
}
/**
* Sets the in use suffix for the writer.
*
* @param inUseSuffix the new in use suffix
*/
public void setInUseSuffix(String inUseSuffix) {
this.inUseSuffix = inUseSuffix;
}
/**
* Sets the in use prefix for the writer.
*
* @param inUsePrefix the new in use prefix
*/
public void setInUsePrefix(String inUsePrefix) {
this.inUsePrefix = inUsePrefix;
}
/**
* Sets the file overwrite flag for the writer.
*
* @param overwrite the new overwrite
*/
public void setOverwrite(boolean overwrite) {
this.overwrite = overwrite;
}
/**
* Sets the partition expression. This expression is used to determine
* if this factory creates an instance of {@link PartitionTextFileWriter}
* or {@link TextFileWriter}. Validity of this spel expression
* is not checked in this factory, thus any non empty string will
* result creation of {@link PartitionTextFileWriter}.
*
* @param partitionExpression the new partition expression
*/
public void setPartitionExpression(String partitionExpression) {
this.partitionExpression = partitionExpression;
}
/**
* Sets the file open attempts for the writer.
*
* @param fileOpenAttempts the new file open attempts
*/
public void setFileOpenAttempts(int fileOpenAttempts) {
this.fileOpenAttempts = fileOpenAttempts;
}
/**
* Sets the naming strategy.
*
* @param fileNamingStrategy the new naming strategy
*/
public void setNamingStrategy(FileNamingStrategy fileNamingStrategy) {
this.fileNamingStrategy = fileNamingStrategy;
}
/**
* Sets the rollover strategy.
*
* @param rolloverStrategy the new rollover strategy
*/
public void setRolloverStrategy(RolloverStrategy rolloverStrategy) {
this.rolloverStrategy = rolloverStrategy;
}
}