SyntaxHighlighter

Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Thursday, September 17, 2015

Spring YARN and Hortonworks Sandbox

Spring YARN and Hortonworks Sandbox


after much trial and error, here's some notes on getting Spring YARN to submit a remote job to Hortonworks sandbox...
  1. if working "remotely" (launching YARN job from one machine into the sandbox) make sure your local /etc/hosts is set to resolve sandbox.hortonworks.com on your target machine IP
  2. the ResourceManager port on HDP is 8050, not the default 8032 (spring.hadoop.resourceManagerPort: 8050)
  3. when running the job, use "HADOOP_USER_NAME=hdfs java -jar [your jar file]


Wednesday, July 9, 2014

HttpSession Failover - Spring Session

HttpSession Failover - Spring Session

Spring Session 1.0.0M1 was announced recently and it's a great answer to HttpSession failover.  


essentially it allows you to architect this;

- multiple instances of a webapp connected by redis (or any other bus) to share httpsession objects

the trick to remember is that you no longer need sticky sessions; just hit any server you like as the session data is replicated and shared to all members talking to the same redis server.

put this on CloudFoundry and you're really cooking; scale up and down the web apps without any worries about HttpSession loss and stickiness


(and to see it in a really really rough test, check out redis-session.cfapps.io)

Thursday, March 13, 2014

CloudFoundry and a "normal" WAR - How to bind to a datasource on CloudFoundry

CloudFoundry and a "normal" WAR - How to bind to a datasource on CloudFoundry

so here's an interesting one; really want to use "cloud" and i have existing non-Spring WAR.  how can we bind to a datasource in a "flexible" way that doesn't require too much coding or rebuilding as a Spring App?

1. import a package from Spring (it looks like we're adding Spring, but there's really no influence, just one class)

 
  
   org.springframework.maven.milestone
   Spring Maven Milestone Repository
   http://repo.spring.io/milestone
  
 


  
   org.springframework.cloud
   cloudfoundry-connector
   0.9.5
  
  
   org.springframework.cloud
   spring-service-connector
   0.9.5
  
2. provide a way to bind to a CloudFoundry datasource

package io.pivotal.poc.simple.service;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.cloud.CloudException;
import org.springframework.cloud.config.java.AbstractCloudConfig;

public class CloudConfiguration extends AbstractCloudConfig {

 private static final Logger logger = Logger.getLogger(CloudConfiguration.class);
 
 public CloudConfiguration() {
  BeanFactory factory = new DefaultListableBeanFactory();
  try {
   this.setBeanFactory(factory);
  }
  catch (CloudException e) {
   logger.warn("no cloud",e);//TODO clean up message
  }
 }
 
 public DataSource inventoryDataSource() {
  ServiceConnectionFactory connectionFactory = connectionFactory();
  if (connectionFactory != null) {
   return new ServiceConnectionFactory().dataSource("twitter-pgsql");//TODO clean up hard coding
  }//end if
  return null;
 }
 
}

3. provide the datasource as a servlet context attribute

package io.pivotal.poc.simple.listener;

import io.pivotal.poc.simple.service.CloudConfiguration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.log4j.Logger;

@WebListener
public class DatasourceListener implements ServletContextListener {
 
 private static final Logger logger = Logger.getLogger(DatasourceListener.class);

 private CloudConfiguration cloudConfiguration = new CloudConfiguration();
 
 public static final String DATASOURCE_ATTRIBUTE = "dataSource";
 
 @Override
 public void contextInitialized(ServletContextEvent sce) {
  if (!bindCloudDataSource(sce)) {
   logger.warn("not running in the cloud");;
   bindLocalDataSource(sce);
  }//end if
 }

 @Override
 public void contextDestroyed(ServletContextEvent sce) { }

 //creates a "local" (in-memory) binding for testing purposes
 private void bindLocalDataSource(ServletContextEvent sce) {
  //create a database
  BasicDataSource factory = new BasicDataSource();
  factory.setDriverClassName("org.h2.Driver");//TODO - remove hardcoding
  factory.setUrl("jdbc:h2:mem:a");
  factory.setUsername("sa");
  factory.setPassword("");
  //set
  sce.getServletContext().setAttribute(DATASOURCE_ATTRIBUTE, factory);
 }
 
 //runs the cloud configuration component
 private boolean bindCloudDataSource(ServletContextEvent sce) {
  DataSource dataSource = cloudConfiguration.inventoryDataSource();
  if (dataSource != null) {
   sce.getServletContext().setAttribute(DATASOURCE_ATTRIBUTE, dataSource);
   return true;
  }//end if
  return false;
 }
}
3. you're done

a couple of assumptions;
- not using a Dependency Injection container (at all)
- using servlets/etc to pull a datasource object from the servlet context

caveat; this is very crude and will be refined as it gets tested out

Saturday, August 31, 2013

Spring Application Context Events - ContextStartedEvent versus ContextRefreshedEvent

a recent question from a colleague spurred me to clarify the difference that between ContextStartedEvent and ContextRefreshedEvent.

ContextStartedEvent should be called at the completion of an ApplicationContext being started up.  however, when using it in different context scenarios, it doesn't appear to be triggered.  for instance, when using in JUnit in conjunction with SpringJUnit4ClassRunner, the event doesn't get fired.

ContextRefreshedEvent gets fired in almost every circumstance (haven't yet encountered one that doesn't).  so when an ApplicationContext starts up, ContextStartedEvent sometimes gets called, but ContextRefreshedEvent always gets called.  also, depending on the implementation of the ApplicationContext, refreshed is only called once with BeanFactory preventing refreshing, so it becomes analogous to Started.

Note: the principal ApplicationContexts used to formulate this are;
- ClassPathXmlApplicationContext
- AnnotationConfigApplicationContext
- GenericApplicationContext
- WebApplicationContext


Tuesday, August 20, 2013

Spring Context PropertyPlaceholder and ActiveProfiles

Spring Context PropertyPlaceholder and ActiveProfiles

so here's a little design requirement that i ran into; i want to use the < context:property-placeholder /> with different configurations in my application context dependent on the Active Profile.  that is, in "production" i want it to read from a properties file, but for testing, i want to set all the values via properties object defined in Spring.

normally, with ActiveProfiles you can declare a bean definition in the regular bean namespace then define it again (with whatever is required) in the beans (profile) tag.  this doesn't work with the < context:property-placeholder /> as it doesn't have an id attribute.  (there's a bunch more sophisticated/accurate/clever reasons as to why, but that's the most readily identifiable).  as such, the profile won't override it, but instead create a second one.  no good for the design.

so here's the solution; default profile.  essentially we create all our beans, then create a default profile and inside it, declare our production < context:property-placeholder />. to declare our test or alternative profile, we just do the same again.



 
  
 

 
  
 
 
 
  
  
  
   hello world
  
 





the default profile is the profile picked up by "default" when the application context is started.  as such, we don't need to specify it and it is akin to not wrapping anything in profiles.  it helps us in this case where we don't want a particular element to be referenced at all if we have a profile in play.

Monday, July 15, 2013

Spring Batch In-Memory Configuration

Spring Batch In-Memory Configuration


here's a snippet to illustrate configuration of Spring Batch Infrastructure (jobRepository, jobExplorer, jobLauncher) purely in-memory for typical JUnit Integration testing.



 
  
 

 
  
 
 
 
  
  
 

 
 
 




it gives you the basics to support a simple unit test with the following setup;

package de.incompleteco.spring.batch.job;

import static org.junit.Assert.assertFalse;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SimpleJobConfig.class)
public class SimpleJobConfigTest {

 @Resource
 private Job job;
 
 @Resource
 private JobLauncher jobLauncher;
 
 @Resource
 private JobExplorer jobExplorer;
 
 @Test
 public void test() throws Exception  {
  //run the job
  JobExecution execution = jobLauncher.run(job,new JobParametersBuilder().toJobParameters());
  //monitor
  while (jobExplorer.getJobExecution(execution.getId()).isRunning()) {
   Thread.sleep(100);
  }//end while
  //load
  execution = jobExplorer.getJobExecution(execution.getId());
  //check
  assertFalse(execution.getStatus().isUnsuccessful());
 }


Saturday, July 13, 2013

Spring Integration JMS to Database

Spring Integration JMS to Database


this is a quick design to persist a very basic JMS message directly to a database.  this pattern could be extended to a use case where you want to very quickly (and in an ordered fashion) consume messages from a JMS queue, persist them quickly into a staging table, then raise a subsequent event for secondary processing of the messages.

anyway, here's the consumption part in Spring Integration.



 
 
 
 
  
  
 
  
   
    
    
    
   
  
 
 



now here's a sample message

MSFT,100.00,1373761697932

here's the resource configuration



 
  
 

 
  
 
 
 
   
 


and finally a JUnit test

package de.incompleteco.spring.integration;

import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import javax.annotation.Resource;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;
import javax.sql.DataSource;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/META-INf/spring/*-context.xml"})
public class MessagingIntegrationTest {

 @Resource
 private ConnectionFactory connectionFactory;
 
 @Resource
 private Queue queue;
 
 @Resource
 private DataSource dataSource;
 
 @Before
 public void before() throws Exception {
  new JdbcTemplate(dataSource).execute("delete from target_table");
 }
 
 @Test
 public void test() throws Exception {
  //check in the database
  int count = new JdbcTemplate(dataSource).queryForObject("select count(*) from target_table",Integer.class);
  //check
  assertTrue(count == 0);  
  
  //read in a message and send it
  BufferedReader reader = new BufferedReader(new InputStreamReader(new DefaultResourceLoader().getResource("classpath:/data/msft.msg").getInputStream()));
  final String message = reader.readLine();
  //send the message
  new JmsTemplate(connectionFactory).send(queue,new MessageCreator() {

   @Override
   public Message createMessage(Session session) throws JMSException {
    return session.createTextMessage(message);
   } });
  //wait and check
  Thread.sleep(100);
  //check in the database
  count = new JdbcTemplate(dataSource).queryForObject("select count(*) from target_table",Integer.class);
  //check
  assertTrue(count == 1);
 }

}


one thing to note is that with Spring XD on it's way, this consumption becomes a little redundant; Spring XD does it better :)

Friday, July 12, 2013

Exposing Active Profiles as a JNDI value in Tomcat

Exposing Active Profiles as a JNDI value in Tomcat

here's a quick one for exposing Active Profiles in JNDI for Tomcat.  the use case is when you don't want to set an Active Profile in web.xml (or webappcontextinitializer) but you need them for Tomcat.  (say you're developing against a Tomcat container, but deploying against something else)

so, here's the class

package de.incompleteco.spring.tomcat;

import java.util.Enumeration;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;


public class ActiveProfilesObjectFactory implements ObjectFactory {

	static final String VALUE = "value";
	static final String DELIMITER = ",";
	
	@Override
	public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable < ?, ? > environment) throws Exception {
		//init
		String values = null;
		//retrieve
		Reference reference = (Reference) obj;
		Enumeration < RefAddr > addresses = reference.getAll();
		while (addresses.hasMoreElements()) {
			RefAddr address = addresses.nextElement();
			String attributeName = address.getType();
			String attributeValue = address.getContent().toString();
			if (attributeName.equalsIgnoreCase(VALUE)) {
				values = attributeValue;
				break;
			}//end if
		}//end while
		if (values != null) {
			return values.split(DELIMITER);
		}//end if
		return null;//default --> no active profiles
	}

}


and here's how you would access it in Tomcat.  (remember, the location is [your app]/META-INF/context.xml)




	
		

Tuesday, July 9, 2013

Loading Multiple Spring Application Contexts with their own ClassLoader

Loading Multiple Spring Application Contexts with their own ClassLoader


here's a quick snippet to support creating an application context with it's own classloader.

package de.incompleteco.spring.context;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.DefaultResourceLoader;

public class ApplicationContextClassLoader {

 public ClassLoader getClassLoader(String location) throws Exception {
  File f = new File(location);
  return new URLClassLoader(new URL[]{f.toURI().toURL()});
 }

 public ApplicationContext getContext(String location,String... contextPaths) throws Exception {
  return getContext(null,location,contextPaths);
 } 
 
 public ApplicationContext getContext(ApplicationContext parentContext,String location,String... contextPaths) throws Exception {
  //init
  GenericApplicationContext context = new GenericApplicationContext(); 
  //build the app context
  if (parentContext != null) {
   context = new GenericApplicationContext(parentContext);
  }//end if
  XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
  //get the classloader
  ClassLoader loader = getClassLoader(location);
  //set the loader
  reader.setBeanClassLoader(loader);
  reader.setResourceLoader(new DefaultResourceLoader(loader));
  //get the bean definitions
  reader.loadBeanDefinitions(contextPaths);
  //init
  context.refresh();
  //return
  return context;
 }
 
}


this supports XML configuration but annotation can be swapped in/out as the case may require.  i'm sure there are prettier ways and i'll tighten it up when i get the chance

Friday, July 5, 2013

Spring Batch - looping over multiple files

Spring Batch - looping over multiple files


here's a quick solution to a scenario coming from this question.  essentially, an array of file names will be passed in as a job parameter and the job needs to process each of these files.  one option is to have the 'looping' part managed by the job orchestrator (it loops, gets a single file, then kicks off the job for that file) the other is to pass the array in, and then loop internally in the job.

which option to choose would be dependent on other aspects of the use case, but here's an example of looping internally in the job

here's the job definition;



 
  
   
   
  
  
   
    
   
  
  
   
    
      
  
 
 

 
  
  
 
 
 


 
 
 
 
 
 
 



now here's the decider code;

package de.incompleteco.spring.batch.decider;

import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.JobExecutionDecider;

public class FileDecision implements JobExecutionDecider {

 public static final String INPUT_FILE = "input.file";
 public static final String INPUT_FILES = "input.files";
 public static final String DELIMITER = ",";
 
 private Queue < String > inputFiles;
 
 @Override
 public FlowExecutionStatus decide(JobExecution jobExecution,StepExecution stepExecution) {
  //check if the jobExecution has the input.file in it's context
  if (!jobExecution.getExecutionContext().containsKey(INPUT_FILE)) {
   //build the queue
   inputFiles = new LinkedBlockingQueue < String >(Arrays.asList(jobExecution.getJobParameters().getString(INPUT_FILES).split(DELIMITER)));
  }//end if
  //pop and add
  String file = inputFiles.poll();
  if (file != null) {
   jobExecution.getExecutionContext().put(INPUT_FILE, file);
   return FlowExecutionStatus.UNKNOWN;
  }//end if
  //return 'done'
  return FlowExecutionStatus.COMPLETED;
 }

}


our helper writer;

package de.incompleteco.spring.batch.item.writer;

import java.util.List;

import org.springframework.batch.item.ItemWriter;

public class SystemOutItemWriter implements ItemWriter < String > {

 @Override
 public void write(List items) throws Exception {
  for (String item : items) {
   System.out.println("this is what was received:" + item);
  }//end for
 }

}


and our resources.



 
 
 
  
 
 
 

 
  
  
 
 
 



finally, our unit test to see if it all works;

package de.incompleteco.spring.batch;

import static org.junit.Assert.assertFalse;

import java.io.File;
import java.io.FileWriter;

import javax.annotation.Resource;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import de.incompleteco.spring.batch.decider.FileDecision;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/META-INF/spring/*-context.xml")
public class MultipleFileProcessIntegrationTest {

 @Rule
 public TemporaryFolder folder = new TemporaryFolder();
 
 @Resource
 private Job job;
 
 @Resource
 private JobLauncher jobLauncher;
 
 @Resource
 private JobExplorer jobExplorer;
 
 @Test
 public void test() throws Exception {
  //somewhere to hold the filenames
  StringBuilder builder = new StringBuilder();
  //create 3 files
  for (int i=0;i<3 add="" content:="" content="" file="" filename="" filewriter="" i="" if="" some="" test="" testfile="" the="" txt="" write="" writer.close="" writer.flush="" writer.write="" writer="new"> 0) {
    builder.append(FileDecision.DELIMITER);
   }//end if
   builder.append(file.getAbsolutePath());
   //show it
   System.out.println(file.getAbsolutePath());
  }//end loop
  
  
  //now build the job parameters
  JobParameters parameters = new JobParametersBuilder().addString(FileDecision.INPUT_FILES,builder.toString()).toJobParameters();
  //execution
  JobExecution execution = jobLauncher.run(job,parameters);
  //check
  while (jobExplorer.getJobExecution(execution.getId()).isRunning()) {
   Thread.sleep(100);
  }//end while
  //load
  execution = jobExplorer.getJobExecution(execution.getId());
  //check
  assertFalse(execution.getStatus().isUnsuccessful());
 }
 
}

.

Thursday, May 30, 2013

Spring Integration - Calling a Service based on a Header Value

Spring Integration - Calling a Service based on a Header Value


here's a situation you may have encountered in Spring Integration, based on a header value, you want to invoke a certain service.  but, you've got a whole bunch of the services and putting in a header value router with an endpoint for each one is a bit of a pain, especially if they all inherit from the same Interface.

so, here's a quick solution to simplify your Spring Integration configuration;

here's the XML,



 
 
 
  
 

 
 
 
 
  

 




here's our wrapper bean - it's responsible for exposing the BeanFactory


package de.incompleteco.spring.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class BeanFactoryWrapper implements BeanFactoryAware {

 private org.springframework.beans.factory.support.DefaultListableBeanFactory beanFactory;
 
 public Object getBean(String name) throws BeansException {
  return beanFactory.getBean(name);
 }

 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  this.beanFactory = (DefaultListableBeanFactory) beanFactory;
 }

}

and finally, a simple test to see if it works

package de.incompleteco.spring.integration;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import de.incompleteco.spring.integration.service.ASomeService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/META-INF/spring/*-context.xml"})
public class ContextIntegrationTest {

 @Autowired
 @Qualifier("input.channel")
 private DirectChannel channel;
 
 @Autowired
 private ASomeService aSomeService;
 
 @Test
 public void test() throws Exception {
  assertNull(aSomeService.getPayload());
  //create a message with the header
  Message instruction = MessageBuilder.withPayload("hello world").setHeader("serviceName", "aSomeService").build();
  //send
  channel.send(instruction);
  //wait
  Thread.sleep(100);
  //now check 
  assertNotNull(aSomeService.getPayload());
 }
 
}

Wednesday, May 29, 2013

Spring Batch file processing

Spring Batch file processing

here's a simple little config to process a file;

first the job setup;



 
  
   
    
   
  
  
   
    
   
  
 

 
  
  
  
 
 
 
  
  
 
 
 
 
  
  
 
 
 
  
 
 
 
  
  
   
  
  
 



then the batch resources;



 

 
  
 
 
  
  
 
 
 
  
   
   
  
  
  
  
  
   
  
 



now, the entity we're going to load to;

package de.incompleteco.spring.batch.domain;

import java.io.Serializable;

public class SimpleEntity implements Serializable {

 private static final long serialVersionUID = 1L;

 private String col1;
 
 private String col2;
 
 private String col3;

 public String getCol1() {
  return col1;
 }

 public void setCol1(String col1) {
  this.col1 = col1;
 }

 public String getCol2() {
  return col2;
 }

 public void setCol2(String col2) {
  this.col2 = col2;
 }

 public String getCol3() {
  return col3;
 }

 public void setCol3(String col3) {
  this.col3 = col3;
 }
 
 
 
}


and here's an integration test to make it all work;

package de.incompleteco.spring.batch;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.FileOutputStream;

import javax.sql.DataSource;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/META-INF/spring/*-context.xml"})
@ActiveProfiles("junit")
public class FileJobIntegrationTest {

 @Autowired
 private Job job;
 
 @Autowired
 private JobLauncher jobLauncher;
 
 @Autowired
 private JobExplorer jobExplorer;
 
 @Autowired
 private DataSource dataSource;
 
 private int recordCount = 1000000;
 
 private String fileName = System.getProperty("java.io.tmpdir") + File.separator + "test.csv";
 
 @Before
 public void before() throws Exception {
  if (new File(fileName).exists()) {
   new File(fileName).delete();
  }//end if
 }
 
 @Test
 public void test() throws Exception {
  //create a file
  FileOutputStream fos = new FileOutputStream(fileName);
  fos.write("col1,col2,col3".getBytes());
  fos.flush();
  for (int i=0;i<=recordCount;i++) {
   fos.write(new String(i + "," + (i+1) + "," + (i+2) + "\n").getBytes());
   fos.flush();//flush it
  }//end for
  fos.close();
  //lets get the size of the file
  long length = new File(fileName).length();
  System.out.println("file size: " + ((length / 1024) / 1024));
  //execute the job
  JobParameters jobParameters = new JobParametersBuilder().addString("fileName",fileName).toJobParameters();
  JobExecution execution = jobLauncher.run(job,jobParameters);
  //monitor
  while (jobExplorer.getJobExecution(execution.getId()).isRunning()) {
   Thread.sleep(1000);
  }//end while
  //load again
  execution = jobExplorer.getJobExecution(execution.getId());
  //test
  assertEquals(ExitStatus.COMPLETED.getExitCode(),execution.getExitStatus().getExitCode());
  //lets see what's in the database
  int count = new JdbcTemplate(dataSource).queryForObject("select count(*) from simple_entity", Integer.class);
  //test
  assertTrue(count == recordCount);
 }
 
}


now, testing locally, the file generated by the test is ~ 19MB and the heap goes up, but does not exhaust at 1,000,000 records.  it's a neat little example of processing a big file, quickly and cleanly into a database.

Monday, May 13, 2013

Spring Reactor - it's out - and here's a brief sample

Spring Reactor - it's out - and here's a brief sample

Spring Reactor is out (http://www.springsource.org/node/22606) and here's my first shot at using it with the annotations;

https://github.com/willschipp/spring-reactor-annotations

here's our domain object;

package de.incompleteco.spring.reactor.domain;

import java.io.Serializable;
import java.util.Date;

public class LogEvent implements Serializable {

 private static final long serialVersionUID = 1L;

 private String source;
 
 private Date timestamp;
 
 private String message;

 public String getSource() {
  return source;
 }

 public void setSource(String source) {
  this.source = source;
 }

 public Date getTimestamp() {
  return timestamp;
 }

 public void setTimestamp(Date timestamp) {
  this.timestamp = timestamp;
 }

 public String getMessage() {
  return message;
 }

 public void setMessage(String message) {
  this.message = message;
 }

 @Override
 public String toString() {
  return "LogEvent [source=" + source + ", timestamp=" + timestamp
    + ", message=" + message + "]";
 }
 
 
}



here's the class that's going to do all the work.  that is, it will process the event given to it by Reactor;

package de.incompleteco.spring.reactor.command;

import reactor.fn.Consumer;
import reactor.fn.Event;
import reactor.spring.context.annotation.On;
import de.incompleteco.spring.reactor.domain.LogEvent;

public class SimpleLogEventConsumer implements Consumer < Event < LogEvent > > {
 
 @On(reactor="log.event.reactor",selector="log.event") 
 public void accept(Event < LogEvent > event) {
  System.out.println(Thread.currentThread().getId() + " " + event.getData());
 }

}


and here's the configuration for it (using @Configuration)


package de.incompleteco.spring.reactor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import reactor.core.Reactor;
import reactor.fn.Consumer;
import reactor.fn.Event;
import reactor.spring.context.ConsumerBeanPostProcessor;
import reactor.spring.context.ReactorFactoryBean;
import de.incompleteco.spring.reactor.command.SimpleLogEventConsumer;
import de.incompleteco.spring.reactor.domain.LogEvent;

@Configuration
public class AppConfig {

 @Bean
 public Reactor reactor() throws Exception {
  ReactorFactoryBean factory = new ReactorFactoryBean();
  factory.setName("log.event.reactor");
  //return
  return factory.getObject();
 }
 
 @Bean
 public Consumer> consumer() {
  return new SimpleLogEventConsumer();
 }
 
 @Bean
 public ConsumerBeanPostProcessor consumerBeanPostProcessor() {
  return new ConsumerBeanPostProcessor();
 }
}

Wednesday, April 24, 2013

Spring Batch - Parallel Item Writer

Spring Batch - Parallel Item Writer

here's a quick solution i put together for stackoverflow and writing to many itemwriters in parallel

here's the class

package de.incompleteco.spring.batch.item.support;

import java.util.List;

import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.CompositeItemWriter;
import org.springframework.core.task.TaskExecutor;
import org.springframework.util.Assert;

import de.incompleteco.spring.domain.SimpleEntity;

public class ParallelCompositeItemWriter extends CompositeItemWriter< SimpleEntity > {

 private List< ItemWriter< ? super SimpleEntity > > delegates;
 
 private TaskExecutor taskExecutor;
 
 @Override
 public void write(final List< ? extends SimpleEntity > item) throws Exception {
  for (final ItemWriter< ? super SimpleEntity > writer : delegates) {
   taskExecutor.execute(new Runnable()  {
    @Override
    public void run() {
     try {
      writer.write(item);
     } catch (Throwable t) {
      rethrow(t);
     } 
    }
    
    private void rethrow(Throwable t) {
     if (t instanceof RuntimeException) {
      throw (RuntimeException) t;
     }
     else if (t instanceof Error) {
      throw (Error) t;
     }
     throw new IllegalStateException(t);
    }  
   });
  }//end for
 }


 public void setTaskExecutor(TaskExecutor taskExecutor) {
  this.taskExecutor = taskExecutor;
 }
 
 @Override
 public void setDelegates(List < ItemWriter < ? super SimpleEntity > > delegates) {
  this.delegates = delegates;
  super.setDelegates(delegates);
 }

 @Override
 public void afterPropertiesSet() throws Exception {
  super.afterPropertiesSet();
  Assert.notNull(taskExecutor,"Task executor needs to be set");
 }
 
}


and here's a sample configuration to run it

 
  
   
    
   
  
 

 
  
 
 
 
  
  
 
 
 
  
   
   
   
    
   
  
  
   
   
   
    
   
    
 
 
 
  
   
  
  
   
    
  
   
    
 
 
 
 

 
     
     
     
     
     
         
             jdbc:h2:mem:a;DB_CLOSE_DELAY=-1
         
     
  
 
 
     
     
     
     
     
         
             jdbc:h2:mem:b;DB_CLOSE_DELAY=-1
         
     
   
 
 
  
 
 
 
  
 
 

 
 
 

 
     
     
 

there's a couple of things to note;
- Bitronix JTA is used to support XA transactions across multiple databases
- the jdbc stuff is pretty rubbish and needs to be done better

Thursday, April 18, 2013

A quick note about Spring Profiles

A quick note about Spring Profiles


Spring Profiles are cool little tools to enable you to keep your configuration together and yet cater for different environments.  (such as testing datasources versus production datasources).  The neat thing to note is that the "" tag(s) are always at the bottom of the XML, forcing bean definitions inside them to overwrite any that came before.



    
    

    
    
            
        
    




in the example above, it allows you to define two beans with the same name, and have the "local" configuration automatically overwrite the production.

here's the stackoverflow question where i try and put this together http://stackoverflow.com/questions/16062922/using-jndi-datasource-with-spring-batch-admin/16080997

Wednesday, April 3, 2013

Spring OpenSource and Pivotal

Spring Integration HTTP Endpoint and Id's

Spring Integration HTTP Endpoint and Id's

a quick note - if you're using Spring Integration HTTP Gateways (more than one) in an application context, give them unique Id's.

    


otherwise you'll get some strange bean exceptions on startup.

Friday, March 29, 2013

Spring Batch - Running only one Job Instance at a time

Spring Batch - Running only one Job Instance at a time

an occasional use case in Spring Batch is the need to have only one instance of a Job running at any given time.  this might be due to restrictions on source data or some other competing aspect.  an extension of this use case is that you don't want to lose the incoming 'job start', you want to 'queue it up' for next time.

here's a simple solution composed in Spring Integration that supports the above ideal and uses the new Retry components from Spring Integration 2.2.  It's not the only way of putting a solution together for it, but it allows the separation of Job (Spring Batch) from Job Orchestration (not Spring Batch)

first - the class that's going to verify if a job is running or not (and throw and exception if it is)

package de.incompleteco.spring.batch.service;

public interface JobStatus {

 /**
  * verifies if the job name is running anywhere in the job explorer
  * @param jobName
  * @throws Exception
  */
 public void verifyNotRunning(String jobName) throws Exception;
 
}



and the implementation

package de.incompleteco.spring.batch.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer;

public class SimpleJobStatus implements JobStatus {

 private List jobNames;
 
 private JobExplorer jobExplorer;

 public SimpleJobStatus() {
  jobNames = new ArrayList();
 }
 
 @Override
 public void verifyNotRunning(String jobName) throws Exception {
  //check if it's a known job
  if (jobNames.contains(jobName)) {
   //check if it's running
   Set executions = jobExplorer.findRunningJobExecutions(jobName);
   if (executions != null && !executions.isEmpty()) {
    //it's running somewhere
    throw new Exception("the job " + jobName + " is curretly running");
   }//end if
  }//end if
 }

 public void setJobNames(List jobNames) {
  this.jobNames = jobNames;
 }

 public void setJobExplorer(JobExplorer jobExplorer) {
  this.jobExplorer = jobExplorer;
 }

}


here's the Spring Integration using the retry to handle the exception and try again



 
 
 
  
   
    
     
      
       
        
         
        
       
       
        
       
      
     
    
   
  
  
 
 
 

 
  
 




so, now there are two things to note;
- the 'queue' (retry loop) is JVM bound - if the JVM dies, the 'queue' does too
- the 'verify' is based on the Job Explorer - it the Job Explorer is database, then it will go across JVM, if not, it's only for this one.

Framework Performance Comparison

Framework Performance Comparison


http://www.techempower.com/blog/2013/03/28/framework-benchmarks/

a very interesting performance test on different frameworks and different environments.  looks like i need to start looking at netty and gemini


Thursday, March 28, 2013

Spring Integration - Intercepting to Raise Events

Spring Integration - Intercepting to Raise Events

in Spring Integration, it is possible to use it to raise events to external systems and leverage all the existing components.  to do this, we can leverage the publishing-interceptor, a component in Spring Integration that uses AOP to capture a method and send off information about it.

the neat thing about this is that you can add a Message Driven Architecture layer to your existing application, allowing secondary or tertiary events to be raised from existing Spring Applications (such as audit).  this can be done either with annotations (@Publisher) or through xml

here's the link to the documentation for it

http://static.springsource.org/spring-integration/reference/html/message-publishing.html