Author: chirino
Date: Mon Nov 23 02:08:51 2009
New Revision: 883211
URL: http://svn.apache.org/viewvc?rev=883211&view=rev
Log:
Beefed up io benchmarker
Added:
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/IOBenchmark.java
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/RateSampler.java
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/Combinator.java
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java
Modified:
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/FileDescriptor.java
activemq/sandbox/activemq-apollo/activemq-syscall/src/test/java/org/apache/activemq/syscall/FileDescriptorTest.java
Modified:
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/FileDescriptor.java
URL:
http://svn.apache.org/viewvc/activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/FileDescriptor.java?rev=883211&r1=883210&r2=883211&view=diff
==============================================================================
---
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/FileDescriptor.java
(original)
+++
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/FileDescriptor.java
Mon Nov 23 02:08:51 2009
@@ -115,7 +115,7 @@
}
public long read(NativeAllocation buffer) throws IOException {
- long rc = IO.write(fd, buffer.pointer(), buffer.length());
+ long rc = IO.read(fd, buffer.pointer(), buffer.length());
if (rc == -1) {
throw error();
}
@@ -131,7 +131,7 @@
}
public long read(long offset, NativeAllocation buffer) throws IOException {
- long rc = IO.pwrite(fd, buffer.pointer(), buffer.length(), offset);
+ long rc = IO.pread(fd, buffer.pointer(), buffer.length(), offset);
if (rc == -1) {
throw error();
}
Added:
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/IOBenchmark.java
URL:
http://svn.apache.org/viewvc/activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/IOBenchmark.java?rev=883211&view=auto
==============================================================================
---
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/IOBenchmark.java
(added)
+++
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/IOBenchmark.java
Mon Nov 23 02:08:51 2009
@@ -0,0 +1,927 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.activemq.syscall.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.activemq.syscall.Callback;
+import org.apache.activemq.syscall.FileDescriptor;
+import org.apache.activemq.syscall.FutureCallback;
+import org.apache.activemq.syscall.NativeAllocation;
+import org.apache.activemq.util.Combinator;
+import org.apache.activemq.util.IOExceptionSupport;
+import org.apache.activemq.util.IntrospectionSupport;
+import org.apache.activemq.util.MemoryPropertyEditor;
+import org.apache.activemq.util.cli.CommonsCLISupport;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
+
+import static java.lang.String.*;
+import static org.apache.activemq.syscall.jni.CLibrary.*;
+import static org.apache.activemq.syscall.jni.IO.*;
+import static org.apache.activemq.util.cli.OptionBuilder.*;
+
+/**
+ * This class is used to get a benchmark the raw disk performance.
+ */
+public class IOBenchmark {
+
+ private static final HashSet<String> OPS = new HashSet<String>();
+ private static String op(String v) {
+ OPS.add(v);
+ return v;
+ }
+ private static final String WRITE_OP = op("write");
+ private static final String READ_OP = op("read");
+
+ private static final HashSet<String> APIS = new HashSet<String>();
+ private static String api(String v) {
+ APIS.add(v);
+ return v;
+ }
+ private static final String JAVA_API = api("java");
+ private static final String NATIVE_BIO_API = api("native-bio");
+ private static final String NATIVE_AIO_DIRECT_API =
api("native-aio-direct");
+
+ boolean verbose;
+
+ String blockSize="4k";
+ int blockSizeInBytes=1024*4;
+
+ String fileSize="500M";
+ long blocks=128000;
+
+ String file = "benchmark.bin";
+ String report = "benchmark.html";
+ int sampleCount = 5;
+ double samplePeriod = 2;
+
+ String[] api = { JAVA_API, NATIVE_BIO_API, NATIVE_AIO_DIRECT_API };
+ String[] operation = { READ_OP, WRITE_OP };
+
+ // read|write options
+ boolean[] randomSeek={ false };
+
+ // read options
+ boolean[] keepDirect={ true };
+
+ // write options
+ boolean[] sync={ false };
+ boolean[] preallocate={ true };
+
+ private Options createOptions() {
+ Options options = new Options();
+
+ options.addOption("h", "help", false, "Display help information");
+ options.addOption("v", "verbose", false, "Verbose");
+
+ options.addOption(ob().id("d").name("file").arg("path")
+ .description("Path to the data file. Default: "+file).op());
+ options.addOption(ob().id("r").name("report").arg("path")
+ .description("Path to the report html that will be generated.
Default: "+report).op());
+ options.addOption(ob().id("fs").name("file-size").arg("count")
+ .description("Size of the data file. The larger you make it
the more likely that the OS will not be able to cache the data. Default:
"+fileSize).op());
+ options.addOption(ob().id("bs").name("block-size").arg("bytes")
+ .description("The size of each benchmarked io operation.
Default: "+blockSize).op());
+ options.addOption(ob().id("sc").name("sample-count").arg("number")
+ .description("How many samples should the bench mark take of
each run. Default: "+sampleCount).op());
+ options.addOption(ob().id("sp").name("sample-period").arg("seconds")
+ .description(format("How many seconds should elapse between
samples. Default: %.2f", samplePeriod)).op());
+
+
options.addOption(ob().id("a").name("api").arg("type,type").args(2).sperator(',')
+ .description("Which api style should get benchmarked. Pick
from: "+APIS+". Default: "+join(api, ",")).op());
+
options.addOption(ob().id("o").name("operation").arg("value,value").args(2).sperator(',')
+ .description("The type of io operation that should be
benchmarked. Default: "+join(operation, ",")).op());
+
+ // read|write options
+
options.addOption(ob().id("rs").name("random-seek").arg("true,false").args(2).sperator(',')
+ .description("Should a random seek be done before the io
operation? Affects: read, write. Default: "+join(randomSeek, ",")).op());
+
options.addOption(ob().id("kd").name("keep-direct").arg("true,false").args(2).sperator(',')
+ .description("Can direct/native operations skip converting
native buffers to java byte arrays? Affects: read, write. Default:
"+join(keepDirect, ",")).op());
+
+ // write options
+
options.addOption(ob().id("pr").name("preallocate").arg("true,false").args(2).sperator(',')
+ .description("Should the data file be preallocated before
running the benchmark? Affects: write. Default: "+join(preallocate, ",")).op());
+
options.addOption(ob().id("sy").name("sync").arg("true,false").args(2).sperator(',')
+ .description("Should a file sync be performed after each io
operation? Affects: write. Default: "+join(preallocate, ",")).op());
+
+ return options;
+ }
+
+
+ static private String join(boolean[] values, String seperator) {
+ boolean first=true;
+ StringBuffer sb = new StringBuffer();
+ for (Object v : values) {
+ if( !first ) {
+ sb.append(seperator);
+ }
+ first=false;
+ sb.append(v);
+ }
+ return sb.toString();
+ }
+
+ static private String join(Object[] values, String seperator) {
+ StringBuffer sb = new StringBuffer();
+ boolean first=true;
+ for (Object v : values) {
+ if( !first ) {
+ sb.append(seperator);
+ }
+ first=false;
+ sb.append(v);
+ }
+ return sb.toString();
+ }
+
+
+ public static void main(String[] args) {
+ String jv = System.getProperty("java.version").substring(0, 3);
+ if (jv.compareTo("1.5") < 0) {
+ System.err.println("This application requires jdk 1.5 or higher to
run, the current java version is " + System.getProperty("java.version"));
+ System.exit(-1);
+ return;
+ }
+
+ IOBenchmark app = new IOBenchmark();
+ System.exit(app.execute(args));
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Entry point for an embedded users who want to call us with
+ // via command line arguments.
+ ///////////////////////////////////////////////////////////////////
+ public int execute(String[] args) {
+ CommandLine cli = null;
+ try {
+ cli = new PosixParser().parse(createOptions(), args, true);
+ } catch (ParseException e) {
+ System.err.println( "Unable to parse command line options: " +
e.getMessage() );
+ displayHelp();
+ return 1;
+ }
+
+ if( cli.hasOption("h") ) {
+ displayHelp();
+ return 0;
+ }
+
+ IOBenchmark benchmark = new IOBenchmark();
+ args = CommonsCLISupport.setOptions(benchmark, cli);
+ try {
+ benchmark.benchmark();
+ return 0;
+ } catch (UsageException e) {
+ System.out.println("Invalid Usage: "+e.getMessage());
+ displayHelp();
+ return -1;
+ } catch (Throwable e) {
+ System.out.flush();
+ if (benchmark.verbose) {
+ System.err.println("ERROR:");
+ e.printStackTrace();
+ } else {
+ System.err.println("ERROR: " + e);
+ }
+ System.err.flush();
+ return -2;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static class UsageException extends Exception {
+ public UsageException(String msg) {
+ super(msg);
+ }
+ }
+
+ public void benchmark() throws IOException, UsageException {
+ for (String v : api) {
+ if( !APIS.contains(v) ) {
+ throw new UsageException("invalid api: "+v);
+ }
+ }
+ for (String v : operation) {
+ if( !OPS.contains(v) ) {
+ throw new UsageException("invalid operation: "+v);
+ }
+ }
+
+ long lv;
+ try {
+ MemoryPropertyEditor mpe = new MemoryPropertyEditor();
+ mpe.setAsText(blockSize);
+ lv = (Long) mpe.getValue();
+ } catch (Throwable e) {
+ throw new UsageException("invalid block-size: "+blockSize);
+ }
+ blockSizeInBytes = (int) lv;
+
+ try {
+ MemoryPropertyEditor mpe = new MemoryPropertyEditor();
+ mpe.setAsText(fileSize);
+ lv = (Long) mpe.getValue();
+ } catch (Throwable e) {
+ throw new UsageException("invalid file-size: "+fileSize);
+ }
+
+ blocks = lv/blockSizeInBytes;
+ if( (lv%blockSizeInBytes)>0 ) {
+ blocks++;
+ }
+
+
+ PrintWriter pw = null;
+ if( report!=null ) {
+ pw = new PrintWriter(new FileOutputStream(report));
+ }
+ writeReportHeader(pw);
+ int chartCounter=0;
+
+ for (String operation : this.operation) {
+ Combinator combinator = new Combinator();
+ if( WRITE_OP.equals(operation) ) {
+ combinator
+ .put("preallocate", array(preallocate))
+ .put("randomSeek", array(randomSeek))
+ .put("sync", array(sync));
+ } else if( READ_OP.equals(operation) ) {
+ combinator.put("randomSeek", array(randomSeek));
+ combinator.put("keepDirect", array(keepDirect));
+ } else {
+ throw new RuntimeException();
+ }
+ for (Map<String, Object> options : combinator.combinations()) {
+ String title = "operation: "+operation+", "+options;
+ System.out.println(title);
+ System.out.println(repeat('-', title.length()));
+
+ ArrayList<Benchmark> results = new
ArrayList<Benchmark>(this.api.length);
+ for (String apiName : this.api) {
+ Benchmark benchmark = createBenchmark(apiName);
+ benchmark.file = new File(file);
+ benchmark.operation = operation;
+ benchmark.apiName = apiName;
+ benchmark.options = options;
+ IntrospectionSupport.setProperties(benchmark, options);
+ benchmark.execute();
+ results.add(benchmark);
+ }
+ writeReportChart(pw, chartCounter++, results);
+ }
+ }
+
+ writeReportFooter(pw);
+ if( pw!=null) {
+ pw.close();
+ System.out.println("Benchmark report stored at: "+report);
+ }
+
+ }
+
+ static private Object[] array(boolean[] value) {
+ Object[] rc = new Object[value.length];
+ for (int i = 0; i < rc.length; i++) {
+ rc[i] = value[i];
+ }
+ return rc ;
+ }
+
+
+ private String repeat(char val, int length) {
+ char [] rc = new char[length];
+ Arrays.fill(rc, val);
+ return new String(rc);
+ }
+
+
+ private void writeReportHeader(PrintWriter pw) {
+ if(pw==null)
+ return;
+ pw.println("<html>");
+ pw.println(" <head>");
+ pw.println(" <script type='text/javascript'
src='http://www.google.com/jsapi'></script>");
+ pw.println(" <script type='text/javascript'>");
+ pw.println(" google.load('visualization', '1',
{'packages':['linechart']});");
+ pw.println(" </script>");
+ pw.println(" <style type='text/css'>");
+ pw.println(" body {font-family:Verdana; font-size:12px;
color:#666666;}");
+ pw.println(" * {margin:0; padding:0;}");
+ pw.println(" .title {text-align:center;}");
+ pw.println(" .chart-section {width:640px; padding: 10px; margin:
0px auto; clear: both;}");
+ pw.println(" .chart-props {width:120px; padding:0;
padding-top:5px; float:left; text-align:right; }");
+ pw.println(" .chart-graph {width: 500px; height: 200px;
float:right; }");
+ pw.println(" </style>");
+ pw.println(" </head>");
+ pw.println(" <body>");
+ pw.println(" <div class='title'> ");
+ pw.println(format(" File Size: "+fileSize+", Block Size:
"+blockSize+", Sample Period: %.2f, Samples: "+sampleCount, samplePeriod));
+ pw.println(" </div>");
+ }
+
+ private void writeReportChart(PrintWriter pw, int id, ArrayList<Benchmark>
data) {
+ if(pw==null)
+ return;
+ Benchmark d1 = data.get(0);
+
+ String titleX = String.format("seconds", samplePeriod);
+ String titleY = "operations / second";
+
+ pw.println(" <div class='chart-section'>");
+ pw.println(" <div class='chart-props'>");
+ pw.print( " <div>operation: "+d1.operation+"</div>");
+ for (Entry<String, Object> entry : new TreeMap<String,
Object>(d1.options).entrySet()) {
+ pw.print("<div>"+entry.getKey()+": "+entry.getValue()+"</div>");
+ }
+ pw.println();
+ pw.println(" </div>");
+ pw.println(" <div id='chart_"+id+"' class='chart-graph '></div>");
+ pw.println(" </div>");
+ pw.println(" <script type='text/javascript'>");
+ pw.println(" google.setOnLoadCallback(draw_chart_"+id+");");
+ pw.println(" function draw_chart_"+id+"() {");
+ pw.println(" var data = new google.visualization.DataTable();");
+ pw.println(" data.addColumn('string', 'Period');");
+
+ for (Benchmark d : data) {
+ pw.println(" data.addColumn('number', '"+d.apiName+"');");
+ }
+ pw.println(" data.addRows([");
+ for( int i=0; i < sampleCount; i++ ) {
+ if( i!=0 ) {
+ pw.println(",");
+ }
+ pw.print(String.format(" ['%.2f'", samplePeriod*(i+1)));
+ for (Benchmark d : data) {
+ double value = 0;
+ if( d.samples.size() >i ) {
+ value = d.samples.get(i);
+ }
+ pw.print(String.format(", %.2f",value));
+ }
+ pw.print("]");
+ }
+
+ pw.println(" ]);");
+ pw.println(" var chart = new
google.visualization.LineChart(document.getElementById('chart_"+id+"'));");
+ pw.println(" chart.draw(data, {legend: 'bottom',
smoothLine:true, titleX:'"+titleX+"', titleY:'"+titleY+"' });");
+ pw.println(" }");
+ pw.println(" </script>");
+
+ }
+
+ private void writeReportFooter(PrintWriter pw) {
+ if(pw==null)
+ return;
+ pw.println(" </body>");
+ pw.println("</html>");
+ }
+
+ private Benchmark createBenchmark(String api) {
+ if( JAVA_API.equals(api) )
+ return new JavaApi();
+ if( NATIVE_BIO_API.equals(api) )
+ return new NativeBioApi();
+ if( NATIVE_AIO_DIRECT_API.equals(api) ) {
+ return new NativeDirectAioApi();
+ }
+ throw new RuntimeException("Unsupported API: "+api);
+ }
+
+ abstract class Benchmark {
+
+ public Map<String, Object> options;
+ public File file;
+ public String apiName;
+ public String operation;
+ public boolean randomSeek;
+ public boolean sync;
+ public boolean preallocate;
+ public boolean keepDirect;
+ public ArrayList<Double> samples;
+
+ final public void execute() throws IOException {
+
+ boolean write;
+ if( WRITE_OP.equals(operation) ) {
+ write = true;
+ } else if( READ_OP.equals(operation) ) {
+ write = false;
+ } else {
+ throw new RuntimeException();
+ }
+
+ System.out.println(" api: "+apiName);
+
+ AtomicLong ioCount=new AtomicLong();
+ RateSampler sampler = new RateSampler(ioCount, sampleCount,
samplePeriod);
+ sampler.verbose = verbose;
+ try {
+
+ Callback<byte[]> callback=null;
+ if( write ) {
+ if( preallocate ) {
+ preallocate();
+ } else {
+ file.delete();
+ }
+ } else {
+ if( !file.exists() ) {
+ preallocate();
+ } else {
+ if( file.length() != blocks*blockSizeInBytes ) {
+ file.delete();
+ preallocate();
+ }
+ }
+ if(keepDirect) {
+ callback = new Callback<byte[]>() {
+ public void onSuccess(byte[] result) {
+ }
+ public void onFailure(Throwable exception) {
+ }
+ };
+ }
+ }
+
+ Random rand=null;
+ if( randomSeek ) {
+ rand = new Random((23<<16)/53);
+ }
+
+
+ init(write);
+ if( verbose ) {
+ System.out.print(" sampling: ");
+ }
+ sampler.start();
+ outer: while( true ) {
+ seek(0);
+ for( long i=0; i < blocks; i++) {
+ if( write ) {
+ if( keepDirect ) {
+ if( rand!=null ) {
+ write(rand.nextLong()%blocks);
+ } else {
+ write();
+ }
+ } else {
+ if( rand!=null ) {
+ write(rand.nextLong()%blocks, block());
+ } else {
+ write(block());
+ }
+ }
+ if( sync ) {
+ sync();
+ }
+ } else {
+ if( keepDirect ) {
+ if( rand!=null ) {
+ read(rand.nextLong()%blocks, callback);
+ } else {
+ read(callback);
+ }
+ } else {
+ if( rand!=null ) {
+ read(rand.nextLong()%blocks);
+ } else {
+ read();
+ }
+ }
+ }
+ ioCount.incrementAndGet();
+ if( !sampler.isAlive() ) {
+ break outer;
+ }
+ }
+ }
+ } finally {
+ if( verbose ) {
+ System.out.println(" done");
+ }
+ dispose();
+ }
+ samples = sampler.getSamples();
+ System.out.print(" samples (operations/second): ");
+ boolean first=true;
+ for (Double s : samples) {
+ if( !first ) {
+ System.out.print(", ");
+ }
+ first=false;
+ System.out.print(String.format("%.2f", s));
+ }
+ System.out.println();
+ System.out.println();
+
+ }
+
+ protected void preallocate() throws IOException {
+ // Pre-allocate the data file before we start sampling.
+ if( verbose ) {
+ System.out.println(" creating data file: ... ");
+ }
+ init(true);
+ for( long i=0; i < blocks; i++) {
+ write();
+ }
+ sync();
+ dispose();
+ }
+
+ abstract protected void init(boolean write) throws IOException;
+ abstract protected void dispose() throws IOException;
+ abstract protected void seek(long pos) throws IOException;
+ abstract protected void sync() throws IOException;
+
+ abstract protected void write() throws IOException;
+ protected void write(long pos) throws IOException {
+ seek(pos);
+ write();
+ }
+
+ abstract protected void write(byte []data) throws IOException;
+ protected void write(long pos, byte []data) throws IOException {
+ seek(pos);
+ write(data);
+ }
+
+ abstract protected void read() throws IOException;
+ abstract protected void read(Callback<byte[]> callback) throws
IOException;
+ protected void read(long pos) throws IOException {
+ seek(pos);
+ read();
+ }
+ protected void read(long pos, Callback<byte[]> callback) throws
IOException {
+ seek(pos);
+ read(callback);
+ }
+
+ }
+
+
+ final class NativeDirectAioApi extends Benchmark {
+
+ private FileDescriptor fd;
+ private NativeAllocation data;
+ private byte[] block;
+ private long offset;
+ private FutureCallback<Long> callback;
+
+ @Override
+ protected void init(boolean write) throws IOException {
+ block = block();
+ data = NativeAllocation.allocate(block);
+ if( write ) {
+ int oflags = O_CREAT | O_TRUNC | O_WRONLY;
+ int mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+ fd = FileDescriptor.open(file, oflags, mode);
+ } else {
+ int oflags = O_RDONLY;
+ fd = FileDescriptor.open(file, oflags);
+ }
+ fd.enableDirectIO();
+ }
+
+ @Override
+ protected void dispose() throws IOException {
+ if( data!=null )
+ data.free();
+ if( fd!=null )
+ fd.close();
+ }
+
+ @Override
+ protected void seek(long pos) throws IOException {
+ offset=pos;
+ }
+
+ @Override
+ protected void sync() throws IOException {
+ }
+
+ private FutureCallback<Long> nextCallback(final Callback<byte[]> next)
throws InterruptedIOException, IOException {
+ if( callback!=null ) {
+ try {
+ offset += callback.get();
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ } catch (ExecutionException e) {
+ throw IOExceptionSupport.create(e.getCause());
+ }
+ callback=null;
+ }
+ callback = new FutureCallback<Long>() {
+ @Override
+ public void onSuccess(Long result) {
+ if( next != null ) {
+ memmove(block, data.pointer(), data.length());
+ next.onSuccess(block);
+ }
+ super.onSuccess(result);
+ }
+ };
+ return callback;
+ }
+
+ @Override
+ protected void write() throws IOException {
+ write(offset);
+ }
+
+ @Override
+ protected void write(long offset) throws IOException {
+ fd.write(offset, data, nextCallback(null));
+ }
+
+ @Override
+ protected void read() throws IOException {
+ read(offset);
+ }
+
+ protected void read(long offset) throws IOException {
+ fd.read(offset, data, nextCallback(null));
+ }
+
+ @Override
+ protected void read(long offset, Callback<byte[]> callback) throws
IOException {
+ fd.read(offset, data, nextCallback(callback));
+ }
+
+ @Override
+ protected void read(Callback<byte[]> callback) throws IOException {
+ read(offset, callback);
+ }
+
+ @Override
+ protected void write(byte[] block) throws IOException {
+ write(offset, block);
+ }
+
+ @Override
+ protected void write(long offset, byte[] block) throws IOException {
+ FutureCallback<Long> callback = nextCallback(null);
+ memmove(data.pointer(), block, data.length());
+ fd.write(offset, data, callback);
+ }
+
+ }
+
+ final class NativeBioApi extends Benchmark {
+
+ private FileDescriptor fd;
+ private NativeAllocation data;
+ private byte[] block;
+
+ @Override
+ protected void init(boolean write) throws IOException {
+ block = block();
+ data = NativeAllocation.allocate(block);
+ if( write ) {
+ int oflags = O_CREAT | O_TRUNC | O_WRONLY;
+ int mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+ fd = FileDescriptor.open(file, oflags, mode);
+ } else {
+ int oflags = O_RDONLY;
+ fd = FileDescriptor.open(file, oflags);
+ }
+ }
+
+ @Override
+ protected void dispose() throws IOException {
+ if( data!=null )
+ data.free();
+ if( fd!=null )
+ fd.close();
+ }
+
+ @Override
+ protected void seek(long pos) throws IOException {
+ fd.seek(pos);
+ }
+
+ @Override
+ protected void sync() throws IOException {
+ fd.sync();
+ }
+
+ @Override
+ protected void write() throws IOException {
+ fd.write(data);
+ }
+
+ @Override
+ protected void read() throws IOException {
+ long count = fd.read(data);
+ if( count < data.length() ) {
+ throw new IOException("Expecting a full read.");
+ }
+ }
+
+ @Override
+ protected void read(Callback<byte[]> callback) throws IOException {
+ fd.read(data);
+ memmove(block, data.pointer(), data.length());
+ callback.onSuccess(block);
+ }
+
+ @Override
+ protected void write(byte[] block) throws IOException {
+ memmove(data.pointer(), block, data.length());
+ fd.write(data);
+ }
+
+ }
+
+ final class JavaApi extends Benchmark {
+
+ private RandomAccessFile raf;
+ private byte data[];
+
+ @Override
+ protected void init(boolean write) throws IOException {
+ data = block();
+ if( write ) {
+ raf = new RandomAccessFile(file, "rw");
+ } else {
+ raf = new RandomAccessFile(file, "r");
+ }
+ }
+
+ @Override
+ protected void dispose() throws IOException {
+ if( raf!=null )
+ raf.close();
+ }
+
+ @Override
+ protected void seek(long pos) throws IOException {
+ raf.seek(pos);
+ }
+
+ @Override
+ protected void sync() throws IOException {
+ raf.getFD().sync();
+ }
+
+ @Override
+ protected void write() throws IOException {
+ raf.write(data);
+ }
+
+ @Override
+ protected void read() throws IOException {
+ raf.readFully(data);
+ }
+
+ @Override
+ protected void read(Callback<byte[]> callback) throws IOException {
+ raf.readFully(data);
+ callback.onSuccess(data);
+ }
+
+ @Override
+ protected void write(byte[] data) throws IOException {
+ raf.write(data);
+ }
+
+ }
+
+ Random DATA_RANDOM = new Random();
+ private byte[] block() {
+ byte []data = new byte[blockSizeInBytes];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = (byte)('a'+(DATA_RANDOM.nextInt(26)));
+ }
+ return data;
+ }
+
+ private void displayHelp() {
+ System.err.flush();
+ String app = System.getProperty("diskbenchmark.application");
+ if( app == null ) {
+ try {
+ URL location =
getClass().getProtectionDomain().getCodeSource().getLocation();
+ String[] split = location.toString().split("/");
+ if( split[split.length-1].endsWith(".jar") ) {
+ app = split[split.length-1];
+ }
+ } catch (Throwable e) {
+ }
+ if( app == null ) {
+ app = getClass().getSimpleName();
+ }
+ }
+
+ // The commented out line is 80 chars long. We have it here as a
visual reference
+// p("
");
+ p();
+ p("Usage: "+ app +" [options]");
+ p();
+ p("Description:");
+ p();
+ pw(" "+app+" is a disk benchmarker.", 2);
+ p();
+
+ p("Options:");
+ p();
+ PrintWriter out = new PrintWriter(System.out);
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printOptions(out, 78, createOptions(), 2, 2);
+ out.flush();
+ p();
+ }
+
+
+ private void p() {
+ System.out.println();
+ }
+ private void p(String s) {
+ System.out.println(s);
+ }
+ private void pw(String message, int indent) {
+ PrintWriter out = new PrintWriter(System.out);
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printWrapped(out, 78, indent, message);
+ out.flush();
+ }
+
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+ public void setBlockSize(String blockSize) {
+ this.blockSize = blockSize;
+ }
+ public void setFileSize(String fileSize) {
+ this.fileSize = fileSize;
+ }
+ public void setBlocks(long blocks) {
+ this.blocks = blocks;
+ }
+ public void setFile(String file) {
+ this.file = file;
+ }
+ public void setReport(String report) {
+ this.report = report;
+ }
+ public void setSampleCount(int sampleCount) {
+ this.sampleCount = sampleCount;
+ }
+ public void setSamplePeriod(double samplePeriod) {
+ this.samplePeriod = samplePeriod;
+ }
+ public void setApi(String[] api) {
+ this.api = api;
+ }
+ public void setOperation(String[] operation) {
+ this.operation = operation;
+ }
+ public void setRandomSeek(boolean[] randomSeek) {
+ this.randomSeek = randomSeek;
+ }
+ public void setKeepDirect(boolean[] keepDirect) {
+ this.keepDirect = keepDirect;
+ }
+ public void setSync(boolean[] sync) {
+ this.sync = sync;
+ }
+ public void setPreallocate(boolean[] preallocate) {
+ this.preallocate = preallocate;
+ }
+
+}
Added:
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/RateSampler.java
URL:
http://svn.apache.org/viewvc/activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/RateSampler.java?rev=883211&view=auto
==============================================================================
---
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/RateSampler.java
(added)
+++
activemq/sandbox/activemq-apollo/activemq-syscall/src/main/java/org/apache/activemq/syscall/util/RateSampler.java
Mon Nov 23 02:08:51 2009
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.activemq.syscall.util;
+
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static java.util.concurrent.TimeUnit.*;
+
+public class RateSampler extends Thread {
+ private final AtomicReference<ArrayList<Double>> samples = new
AtomicReference<ArrayList<Double>>();
+ private final AtomicLong metric;
+ private final int count;
+ private final long period;
+ public boolean verbose;
+
+ public RateSampler(AtomicLong metric, int count, double periodInSecs) {
+ super("Sampler");
+ this.metric = metric;
+ this.count = count;
+ this.period = ns(periodInSecs);
+ setPriority(MAX_PRIORITY);
+ setDaemon(true);
+ }
+
+ static final long NANOS_PER_SECOND = NANOSECONDS.convert(1, SECONDS);
+
+ static private long ns(double v) {
+ return (long)(v*NANOS_PER_SECOND);
+ }
+
+
+ @Override
+ public void run() {
+ ArrayList<Double> samples = new ArrayList<Double>(count);
+ try {
+ long sleepMS = period/1000000;
+ int sleepNS = (int) (period%1000000);
+ long currentValue, now;
+ long lastValue = metric.get();
+ long lastTime = System.nanoTime();
+
+
+ for (int i = 0; i < count; i++) {
+ if( verbose ) {
+ System.out.print(".");
+ }
+ Thread.sleep(sleepMS,sleepNS);
+
+ now = System.nanoTime();
+ currentValue = metric.get();
+
+ double t = (now-lastTime);
+ t = t/NANOS_PER_SECOND;
+ t = (currentValue-lastValue)/t;
+ samples.add(t);
+
+ lastTime=now;
+ lastValue=currentValue;
+ }
+ } catch (InterruptedException e) {
+ } finally {
+ this.samples.set(samples);
+ }
+ }
+
+ public synchronized ArrayList<Double> getSamples() {
+ return samples.get();
+ }
+}
\ No newline at end of file
Modified:
activemq/sandbox/activemq-apollo/activemq-syscall/src/test/java/org/apache/activemq/syscall/FileDescriptorTest.java
URL:
http://svn.apache.org/viewvc/activemq/sandbox/activemq-apollo/activemq-syscall/src/test/java/org/apache/activemq/syscall/FileDescriptorTest.java?rev=883211&r1=883210&r2=883211&view=diff
==============================================================================
---
activemq/sandbox/activemq-apollo/activemq-syscall/src/test/java/org/apache/activemq/syscall/FileDescriptorTest.java
(original)
+++
activemq/sandbox/activemq-apollo/activemq-syscall/src/test/java/org/apache/activemq/syscall/FileDescriptorTest.java
Mon Nov 23 02:08:51 2009
@@ -78,7 +78,7 @@
NativeAllocation buffer = allocate(expected.length());
- int oflags = O_NONBLOCK | O_RDONLY;
+ int oflags = O_RDONLY;
FileDescriptor fd = FileDescriptor.open(file, oflags);
try {
@@ -92,5 +92,35 @@
assertEquals(expected, buffer.string() );
buffer.free();
+ }
+
+ @Test
+ public void read() throws IOException, InterruptedException,
ExecutionException, TimeoutException {
+ assumeThat(AIO.SUPPORTED, is(true));
+
+ String expected = "Hello World";
+
+ File file =
dataFile(FileDescriptorTest.class.getName()+".writeWithACallback.data");
+ writeFile(file, expected);
+
+ NativeAllocation buffer = allocate(6);
+
+ int oflags = O_RDONLY;
+ FileDescriptor fd = FileDescriptor.open(file, oflags);
+ try {
+
+ long size = fd.read(buffer);
+ assertEquals(6, size);
+ assertEquals(expected.substring(0, 6), buffer.string());
+
+ size = fd.read(buffer);
+ assertEquals(expected.length()-6, size);
+ assertEquals(expected.substring(6), buffer.view(0, size).string());
+
+ } finally {
+ fd.dispose();
+ }
+ buffer.free();
}
+
}
Added:
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/Combinator.java
URL:
http://svn.apache.org/viewvc/activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/Combinator.java?rev=883211&view=auto
==============================================================================
---
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/Combinator.java
(added)
+++
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/Combinator.java
Mon Nov 23 02:08:51 2009
@@ -0,0 +1,326 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.activemq.util;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Combinator objects are used to compute all the possible combinations given
a set of combination options.
+ * This class is generally use in conjunction with TestNG test cases generate
the @Factory and @DataProvider
+ * results.
+ *
+ * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
+ */
+public class Combinator {
+
+ private Combinator parent;
+ private ArrayList<Combinator> children = new ArrayList<Combinator>();
+
+ private LinkedHashMap<String, ComboOption> comboOptions = new
LinkedHashMap<String, ComboOption>();
+ private int annonymousAttributeCounter;
+
+ public Combinator() {
+ }
+
+ public Combinator(Combinator parent) {
+ this.parent = parent;
+ }
+
+ // For folks who like to use static imports to achieve a more fluent usage
API.
+ public static Combinator combinator() {
+ return new Combinator();
+ }
+
+ static class ComboOption {
+ final String attribute;
+ final LinkedHashSet<Object> values = new
LinkedHashSet<Object>();
+
+ public ComboOption(String attribute, Collection<Object>
options) {
+ this.attribute = attribute;
+ this.values.addAll(options);
+ }
+ }
+
+
+ public ArrayList<Combinator> all() {
+ ArrayList<Combinator> rc = new ArrayList<Combinator>();
+ root()._all(rc);
+ return rc;
+ }
+
+ private void _all(ArrayList<Combinator> rc) {
+ rc.add(this);
+ for (Combinator c : children) {
+ c._all(rc);
+ }
+ }
+
+ public Combinator put(String attribute, Object... options) {
+ ComboOption co = this.comboOptions.get(attribute);
+ if (co == null) {
+ this.comboOptions.put(attribute, new
ComboOption(attribute, Arrays.asList(options)));
+ } else {
+ co.values.addAll(Arrays.asList(options));
+ }
+ return this;
+ }
+
+ public Combinator and() {
+ Combinator combinator = new Combinator(this);
+ children.add(combinator);
+ return combinator;
+ }
+
+
+ public Combinator add(Object... options) {
+ put(""+(annonymousAttributeCounter++), options);
+ return this;
+ }
+
+// @SuppressWarnings("unchecked")
+// public void addFromContext(ApplicationContext applicationContext,
String name) {
+// List<Object> list = (List)applicationContext.getBean(name);
+// Object[] options = list.toArray();
+// add(options);
+// }
+//
+// public void addAllFromContext(ApplicationContext applicationContext,
String name) {
+// List<List<Object>> list =
(List)applicationContext.getBean(name);
+// for (List<Object> l : list) {
+// Object[] options = l.toArray();
+// add(options);
+// }
+// }
+
+
+ public Set<Map<String, Object>> combinations() {
+ return root()._combinations();
+ }
+
+ private Combinator root() {
+ Combinator c=this;
+ while( c.parent!=null ) {
+ c = c.parent;
+ }
+ return c;
+ }
+
+ private Set<Map<String, Object>> _combinations() {
+ LinkedHashSet<Map<String, Object>> rc = new LinkedHashSet<Map<String,
Object>>();
+ List<Map<String, Object>> expandedOptions = new ArrayList<Map<String,
Object>>();
+ expandCombinations(new ArrayList<ComboOption>(comboOptions.values()),
expandedOptions);
+ rc.addAll(expandedOptions);
+ for (Combinator c : children) {
+ rc.addAll(c._combinations());
+ }
+ return rc;
+ }
+
+ private void expandCombinations(List<ComboOption> optionsLeft,
List<Map<String, Object>> expandedCombos) {
+ if (!optionsLeft.isEmpty()) {
+ Map<String, Object> map;
+ if (comboOptions.size() == optionsLeft.size()) {
+ map = new LinkedHashMap<String, Object>();
+ expandedCombos.add(map);
+ } else {
+ map = expandedCombos.get(expandedCombos.size()
- 1);
+ }
+
+ LinkedList<ComboOption> l = new
LinkedList<ComboOption>(optionsLeft);
+ ComboOption comboOption = l.removeFirst();
+ int i = 0;
+ for (Iterator<Object> iter =
comboOption.values.iterator(); iter.hasNext();) {
+ Object value = iter.next();
+ if (i != 0) {
+ map = new LinkedHashMap<String,
Object>(map);
+ expandedCombos.add(map);
+ }
+ map.put(comboOption.attribute, value);
+ expandCombinations(l, expandedCombos);
+ i++;
+ }
+ }
+ }
+
+ /**
+ * Creates a bean for each combination of the type specified by clazz
arguement and uses setter/field
+ * injection to initialize the Bean with the combination values.
+ *
+ * @param <T>
+ * @param clazz
+ * @return
+ * @throws InstantiationException
+ * @throws IllegalAccessException
+ */
+ public <T> Object[] combinationsAsBeans(Class<T> clazz) throws
Exception {
+ Set<Map<String, Object>> combinations = combinations();
+ List<T> rc = new ArrayList<T>(combinations.size());
+ for (Map<String, Object> combination : combinations) {
+ T instance = clazz.newInstance();
+
+ for (Entry<String, Object> entry :
combination.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ try {
+ // Try setter injection..
+ Method method =
clazz.getMethod("set"+ucfc(key), value.getClass());
+ method.invoke(instance, new
Object[]{value});
+ } catch (Exception ignore) {
+ // Try property injection..
+ Field declaredField =
clazz.getDeclaredField(key);
+ declaredField.set(instance, value);
+ }
+ }
+ if( instance instanceof CombinationAware) {
+
((CombinationAware)instance).setCombination(combination);
+ }
+ rc.add(instance);
+ }
+ Object[] t = new Object[rc.size()];
+ rc.toArray(t);
+ return t;
+ }
+
+ public <T> Object[][] combinationsAsParameterArgBeans(Class<T> clazz)
throws Exception {
+ Object[] x = combinationsAsBeans(clazz);
+ Object[][]rc = new Object[x.length][];
+ for (int i = 0; i < rc.length; i++) {
+ rc[i] = new Object[] {x[i]};
+ }
+ return rc;
+ }
+
+ public interface BeanFactory<T> {
+ T createBean() throws Exception;
+ Class<T> getBeanClass();
+ }
+
+ public interface CombinationAware {
+
+ void setCombination(Map<String, Object> combination);
+ }
+
+
+ /**
+ * Creates a bean for each combination of the type specified by clazz
argument and uses setter/field
+ * injection to initialize the Bean with the combination values.
+ *
+ * @param clazz
+ * @return
+ * @throws InstantiationException
+ * @throws IllegalAccessException
+ */
+ public <T> T[] asBeans(BeanFactory<T> factory) throws Exception {
+ Set<Map<String, Object>> combinations = combinations();
+ List<Object> rc = new ArrayList<Object>(combinations.size());
+
+ Class<? extends Object> clazz=null;
+ for (Map<String, Object> combination : combinations) {
+ Object instance = factory.createBean();
+ if( clazz == null ) {
+ clazz = instance.getClass();
+ }
+ for (Entry<String, Object> entry :
combination.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ try {
+ // Try setter injection..
+ Method method =
clazz.getMethod("set"+ucfc(key), value.getClass());
+ method.invoke(instance, new
Object[]{value});
+ } catch (Exception ignore) {
+ // Try property injection..
+ setField(clazz, instance, key, value);
+ }
+ }
+
+ if( instance instanceof CombinationAware) {
+
((CombinationAware)instance).setCombination(combination);
+ }
+ rc.add(instance);
+ }
+
+ T[] t = toArray(factory, rc);
+ rc.toArray(t);
+ return t;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T[] toArray(BeanFactory<T> factory, List<Object> rc) {
+ return (T[]) Array.newInstance(factory.getBeanClass(), rc.size());
+ }
+
+ private void setField(Class<? extends Object> clazz, Object instance,
String key, Object value) throws NoSuchFieldException, IllegalAccessException {
+ while( clazz!= null ) {
+ try {
+ Field declaredField =
clazz.getDeclaredField(key);
+ declaredField.setAccessible(true);
+ declaredField.set(instance, value);
+ return;
+ } catch (NoSuchFieldException e) {
+ // Field declaration may be in a parent
class... keep looking.
+ clazz = clazz.getSuperclass();
+ }
+ }
+ }
+
+ public <T> Object[][] combinationsAsParameterArgBeans(BeanFactory<T>
factory) throws Exception {
+ Object[] x = asBeans(factory);
+ Object[][]rc = new Object[x.length][];
+ for (int i = 0; i < rc.length; i++) {
+ rc[i] = new Object[] {x[i]};
+ }
+ return rc;
+ }
+
+ /**
+ * Upper case the first character.
+ * @param key
+ * @return
+ */
+ static private String ucfc(String key) {
+ return key.substring(0,1).toUpperCase()+key.substring(1);
+ }
+
+ public Object[][] combinationsAsParameterArgs() {
+ Set<Map<String, Object>> combinations = combinations();
+ Object[][] rc = new Object[combinations.size()][];
+ int i=0;
+ for (Map<String, Object> combination : combinations) {
+ int j=0;
+ Object[] arg = new Object[combination.size()];
+ for (Object object : combination.values()) {
+ arg[j++] = object;
+ }
+ rc[i++] = arg;
+ }
+ return rc;
+ }
+
+}
Added:
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java
URL:
http://svn.apache.org/viewvc/activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java?rev=883211&view=auto
==============================================================================
---
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java
(added)
+++
activemq/sandbox/activemq-apollo/activemq-util/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java
Mon Nov 23 02:08:51 2009
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.activemq.util;
+
+import java.beans.PropertyEditorSupport;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Converts string values like "20 Mb", "1024kb", and "1g" to long values in
+ * bytes.
+ */
+public class MemoryPropertyEditor extends PropertyEditorSupport {
+ public void setAsText(String text) throws IllegalArgumentException {
+
+ Pattern p = Pattern.compile("^\\s*(\\d+)\\s*(b)?\\s*$",
Pattern.CASE_INSENSITIVE);
+ Matcher m = p.matcher(text);
+ if (m.matches()) {
+ setValue(Long.valueOf(Long.parseLong(m.group(1))));
+ return;
+ }
+
+ p = Pattern.compile("^\\s*(\\d+)\\s*k(b)?\\s*$",
Pattern.CASE_INSENSITIVE);
+ m = p.matcher(text);
+ if (m.matches()) {
+ setValue(Long.valueOf(Long.parseLong(m.group(1)) * 1024));
+ return;
+ }
+
+ p = Pattern.compile("^\\s*(\\d+)\\s*m(b)?\\s*$",
Pattern.CASE_INSENSITIVE);
+ m = p.matcher(text);
+ if (m.matches()) {
+ setValue(Long.valueOf(Long.parseLong(m.group(1)) * 1024 * 1024));
+ return;
+ }
+
+ p = Pattern.compile("^\\s*(\\d+)\\s*g(b)?\\s*$",
Pattern.CASE_INSENSITIVE);
+ m = p.matcher(text);
+ if (m.matches()) {
+ setValue(Long.valueOf(Long.parseLong(m.group(1)) * 1024 * 1024 *
1024));
+ return;
+ }
+
+ throw new IllegalArgumentException("Could convert not to a memory
size: " + text);
+ }
+
+ public String getAsText() {
+ Long value = (Long)getValue();
+ return value != null ? value.toString() : "";
+ }
+
+}