package com.yahoo.dtf.config; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import com.yahoo.dtf.config.DynamicProperty; import com.yahoo.dtf.exception.DTFException; import com.yahoo.dtf.exception.ParseException; import com.yahoo.dtf.streaming.DTFInputStream; import com.yahoo.dtf.streaming.StringInputStream; /** * * @dtf.feature Streaming Properties * @dtf.feature.group DTF Properties * * @dtf.feature.desc * <p> * This feature is extremely useful when you need to generate large amounts of * data but you can't afford to have it all stored in memory at one time during * execution because it would break your application. Referencing a streaming * property is as easy as referencing any other property and can be done so, * like so: * </p> * <pre>${dtf.stream([random|repeat|etc],[size],[additional arguments separated by commas])}</pre> * <p> * Internally these properties are not resolved into the data that they * represent but instead into an InputStream that can be read and immediately * sent to whatever OutputStream is awaiting data. To the test writer they're * used the same way any other property would be used but they allow the test * writer to generate hundreds of streams sending GB objects at the same time * while never hold more than a few MB (total) in memory. The main difference * when using these properties is in the way the property is handled within * the tag code where the developer can pick up an InputStream to read the data * in a way more efficient manner than being give a huge chunk of data in a * String. * </p> * * The Streaming properties are broken down into the following 2 types: * * <h2>Unstructured Stream Properties</h2> * <p> * There are a few unstructured stream types types already built into DTF which * allow you to generate random and repeated data patterns, these include: * </p> * <ul> * <li>{@dtf.link Random Stream Type}</li> * <li>{@dtf.link Repeat Stream Type}</li> * </ul> * * <h2>Structured Stream Properties</h2> * <p> * There are also structured data streaming types which generate data that are * structured documents such as XML or JSON. The currently available structured * stream types are: * </p> * <ul> * <li>{@dtf.link XML Stream Type}</li> * </ul> * * <h2>Using DTF Streams in your Code</h2> * Now to take advantage of streaming properties as the developer of a tag you * need to use a slightly different method when retrieving property data. This * means that instead of using <b>replaceProperties()</b> function you will have * to use the <b>replacePropertiesAsInputStream()</b> which if the underlying * property is a DTF streaming property it will return an InputStream that you * can use to get your data without having to house it all in memory. */ public class DTFStream implements DynamicProperty { public static final String DTF_STREAM = "dtf.stream"; private static HashMap<String, Class> _streams = new HashMap<String, Class>(); public static <T extends DTFInputStream> void registerStream(String name, Class<T> stream) throws DTFException { if ( _streams.containsKey(name) ) { throw new DTFException("Stream handler already exists with name [" + name + "]"); } _streams.put(name, stream); } public static ArrayList<String> getStreamNames() { ArrayList<String> names = new ArrayList<String>(); Iterator<String> iter = _streams.keySet().iterator(); while ( iter.hasNext() ) { names.add(iter.next()); } return names; } public static DTFInputStream getStringAsStream(String data) throws ParseException { DTFInputStream wrapper = new DTFInputStream(data.length(),new String[0]); wrapper.setInputStream(new StringInputStream(data)); return wrapper; } public DTFInputStream getValueAsStream(String args) throws ParseException { String[] parts = args.split(","); if ( args.length() < 1 ) throw new ParseException("Expecting more args for dtf.stream, got [" + args + "]"); String name = parts[0]; String arg1 = null; String[] arguments = new String[parts.length-2]; if ( arguments.length > 0 ) { for (int i = 2; i < parts.length; i++) { arguments[i-2] = parts[i]; } } if ( args.length() >= 2 ) arg1 = parts[1]; long size = 0; if ( arg1 != null ) { try { size = new Long(arg1); } catch (NumberFormatException e) { throw new ParseException("Size should be a long not [" + arg1 + "]"); } } Class<DTFInputStream> streamclass = _streams.get(name); if ( streamclass == null ) throw new ParseException("Unsupported stream [" + name + "]"); try { Constructor<DTFInputStream> c = streamclass.getConstructor(new Class[]{long.class,String[].class}); DTFInputStream stream = c.newInstance(new Object[]{size, arguments}); DTFInputStream wrapper = new DTFInputStream(size,arguments); wrapper.setInputStream(stream); return wrapper; } catch(NoSuchMethodException e ) { throw new ParseException("Error instantiating DTFInputStream.",e); } catch (IllegalArgumentException e) { throw new ParseException("Error instantiating DTFInputStream.",e); } catch (InstantiationException e) { throw new ParseException("Error instantiating DTFInputStream.",e); } catch (IllegalAccessException e) { throw new ParseException("Error instantiating DTFInputStream.",e); } catch (InvocationTargetException e) { throw new ParseException("Error instantiating DTFInputStream.",e); } } public String getValue(String args) throws ParseException { return getValueAsStream(args).getAsString(); } }