tag:blogger.com,1999:blog-12491790083760898512024-02-08T16:01:33.863+01:00Riven :: code </> brain :: dump<br>share more code + it makes life hilariousRivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.comBlogger38125tag:blogger.com,1999:blog-1249179008376089851.post-39252358568269288532012-06-21T16:09:00.000+02:002012-06-21T23:17:03.166+02:00HotSpot bug in Java 7u4, 7u5, 7u6(rc) with StringBuilder optimisationI found an odd bug that seems to be related with the new Java7 optimisations regarding StringBuilder concatenation. The following code works, until HotSpot kicks in, and replaces the interpreted code with optimized code, after a few thousand iterations.
<br><br>
<b>Code that corrupts the concatenated string when compiled with Eclipse</b>:<br>
<pre style="color:#c80">
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("Java Version: " + System.getProperty("java.vm.version"));
long[] durations = new long[60];
for (int i = 0; true; i++) {
// this empty for-loop is required to reproduce this bug
for (long duration : durations) {
// do nothing
}
{
String s = "test";
int len = s.length();
s = s + s;
len = len + len;
s = s + s;
len = len + len;
s = s + s;
len = len + len;
if (s.length() != len) {
System.out.println("Failed at iteration: " + i);
System.out.println("Length mismatch: " + s.length() + " <> " + len);
System.out.println("Expected: \"" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "\"");
System.out.println("Actual: \"" + s + "\"");
System.exit(0);
}
}
}
}
}
</pre>
<br><br>
<b>Code that corrupts the concatenated string when compiled with Javac</b>:<br>
<pre style="color:#c80">
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("Java Version: " + System.getProperty("java.vm.version"));
long[] durations = new long[60];
for (int i = 0; true; i++) {
// this empty for-loop is required to reproduce this bug
for (long duration : durations) {
// do nothing
}
{
String s = "test";
int len = s.length();
s = new StringBuilder(String.valueOf(s)).append(s).toString();
len = len + len;
s = new StringBuilder(String.valueOf(s)).append(s).toString();
len = len + len;
s = new StringBuilder(String.valueOf(s)).append(s).toString();
len = len + len;
if (s.length() != len) {
System.out.println("Failed at iteration: " + i);
System.out.println("Length mismatch: " + s.length() + " <> " + len);
System.out.println("Expected: \"" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "\"");
System.out.println("Actual: \"" + s + "\"");
System.exit(0);
}
}
}
}
}
</pre>
<br><br>
Output:
<pre style="color:#0c0">
Java Version: 23.0-b21
Failed at iteration: 11983
Length mismatch: 16 <> 32
Expected: <span style="color:#f00">"testtesttesttesttesttesttesttest"</span>
Actual: <span style="color:#f00">"<span style="color:#f88">nullnull</span>testtest"</span>
</pre>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com2tag:blogger.com,1999:blog-1249179008376089851.post-10583702109521540882012-02-10T23:20:00.029+01:002012-02-13T19:42:43.370+01:00Bash script for simple port-knocking in iptablesThis is a simple bash script that allows you to add (multiple) port-knocking rules into iptables. Usage is explained below.<div class="code" style="height:auto"><pre><span style="color:#fc0">KNOCK_NAME</span>=$1
<span style="color:#fc0">PORT_1</span>=$2
<span style="color:#fc0">PORT_2</span>=$3
<span style="color:#fc0">PORT_3</span>=$4
<span style="color:#fc0">PORT_4</span>=$5
<span style="color:#fc0">OPEN_PORT</span>=$6
<span style="color:#fc0">OPEN_TIME</span>=$7
PHASE_1=p1_$KNOCK_NAME
PHASE_2=p2_$KNOCK_NAME
PHASE_3=p3_$KNOCK_NAME
PHASE_4=p4_$KNOCK_NAME
JUMP_TO_2=j2_$KNOCK_NAME
JUMP_TO_3=j3_$KNOCK_NAME
JUMP_TO_4=j4_$KNOCK_NAME
iptables -X $JUMP_TO_2
iptables -N $JUMP_TO_2
iptables -A $JUMP_TO_2 -m recent --name $PHASE_1 --remove
iptables -A $JUMP_TO_2 -m recent --name $PHASE_2 --set
iptables -X $JUMP_TO_3
iptables -N $JUMP_TO_3
iptables -A $JUMP_TO_3 -m recent --name $PHASE_2 --remove
iptables -A $JUMP_TO_3 -m recent --name $PHASE_3 --set
iptables -X $JUMP_TO_4
iptables -N $JUMP_TO_4
iptables -A $JUMP_TO_4 -m recent --name $PHASE_3 --remove
iptables -A $JUMP_TO_4 -m recent --name $PHASE_4 --set
iptables -A INPUT -m recent --update --name $PHASE_1
iptables -A INPUT -p <span style="color:#66c">udp</span> --dport <span style="color:#fc0">$PORT_1</span> -m recent --set --name $PHASE_1
iptables -A INPUT -p <span style="color:#66c">udp</span> --dport <span style="color:#fc0">$PORT_2</span> -m recent --rcheck --name $PHASE_1 -j $JUMP_TO_2
iptables -A INPUT -p <span style="color:#66c">udp</span> --dport <span style="color:#fc0">$PORT_3</span> -m recent --rcheck --name $PHASE_2 -j $JUMP_TO_3
iptables -A INPUT -p <span style="color:#66c">udp</span> --dport <span style="color:#fc0">$PORT_4</span> -m recent --rcheck --name $PHASE_3 -j $JUMP_TO_4
iptables -A INPUT -p <span style="color:#c66">tcp</span> --dport <span style="color:#fc0">$OPEN_PORT</span> -m recent --rcheck --seconds <span style="color:#fc0">$OPEN_TIME</span> --name $PHASE_4 -j ACCEPT
</pre></div>(credit: http://www.debian-administration.org/articles/268)<br />
<br />
In this example I'm actually using UDP ports, because they are easier to knock on: no attempt is made to actually connect and (thus) timeout. If you prefer TCP knocking, and you don't know how to adjust the script, click <a href="http://www.google.com/" target="_blank">here</a>.<br />
<br />
<br />
<div class="code" style="height:auto"><pre><span style="color:#6f6"># setup all rules you currently have</span>
... (important stuff!)
<span style="color:#6f6"># add (several) port knocking triggers</span>
./iptables-knocking.sh <span style="color:#6c6">mail2</span> <span style="color:#fc0">108 107 106 105</span> <span style="color:#66c">9992</span> <span style="color:#c66">15</span>
./iptables-knocking.sh <span style="color:#6c6">mail3</span> <span style="color:#fc0">101 102 103 104</span> <span style="color:#66c">9993</span> <span style="color:#c66">15</span>
| | | | | | -> timeout_to_close
name | | port3 | port_to_open
port1 | |
port2 port4
<span style="color:#6f6"># reject everything else</span>
iptables -A INPUT -s 0/0 -d 0/0 -j REJECT
</pre></div><br />
When working with IPTABLES, keep paying attention. It's WAY too easy to lock yourself out or 'ruin your uptime' one way or another. Needless to say, I am in no way responsible for any problems or damages that are the result of the usage of the above code. You have been warned.<br />
<br />
<br />
Next up, a trivial Java portknocking client:<br />
<div class="code" style="height:auto"><pre>public static void knockUDP(String hostname, int... ports) {
DatagramSocket socket = new DatagramSocket();
for(int port: ports) {
// account for a bit of packet loss
for(int i=0; i<4; i++) {
socket.send(new DatagramPacket(new byte[1], 1, new InetSocketAddress(hostname, port)));
}
// try to make it more likely the packets arrive in-order
Thread.sleep(100);
}
}
public static void knockTCP(String hostname, int... ports) {
for(int port: ports) {
Socket s = new Socket();
try {
s.connect(new InetSocketAddress(hostname, port), 100);
s.close(); // it appears we actually connected, which is not what we wanted....
} catch( IOException) {
// ignore, really!
}
}
}
knockUDP("your-public-server.com", 101, 102, 103, 104);
knockTCP("your-public-server.com", 101, 102, 103, 104);
</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com1tag:blogger.com,1999:blog-1249179008376089851.post-16918277804871462032012-02-10T23:05:00.013+01:002012-02-13T18:46:40.266+01:00Reverse SSH tunnel in plain english...Those tutorials explaining reverse SSH tunnels are ambiguous at best in their examples, often using the same port number twice, making it unclear whether it is a local port or a remote port. <br />
<br />
Yes, I'm done complaining, here's my attempt at being helpful:<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>MASTER_HOST<span style="color:#66c">=</span>where-your-ssh-server-is.com
MASTER_PORT<span style="color:#66c">=</span>22 <span style="color:#6c6"># likely</span>
MASTER_USER<span style="color:#66c">=</span>root <span style="color:#6c6"># not really!</span>
MASTER_LISTEN<span style="color:#66c">=</span>... <span style="color:#6c6"># whatever you want your public port to be</span>
TARGET_HOST<span style="color:#66c">=</span>192.168.... <span style="color:#6c6"># probably some local server</span>
TARGET_PORT<span style="color:#66c">=</span>25 <span style="color:#6c6"># let's imagine we tunnel a mailserver</span>
<span style="color:#6c6"># let's actually do something now!</span>
ssh -l <span style="color:#fc0">$MASTER_USER</span> -nNT -p <span style="color:#fc0">$MASTER_PORT</span> -R <span style="color:#fc0">$MASTER_LISTEN</span>:<span style="color:#fc0">$TARGET_HOST</span>:<span style="color:#fc0">$TARGET_PORT</span> <span style="color:#fc0">$MASTER_HOST</span>
Please ensure the line
GatewayPorts yes
exists in the file
/etc/ssh/sshd_config
or your tunnel will bind to localhost only.
</pre></div><br />
As you can see, the order of parameters is not quite intuitive, which might have brought you on this page in the first place. Enjoy.Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-29178754980209617382010-11-30T23:29:00.014+01:002011-02-13T16:43:34.045+01:00Java Continuations and Green Threads at native speedsContinuations, or green threads, are a concept mostly seen in scripting languages. Green threads are just like native threads, but you can run multiple of them on a single native thread. Java has no native support for continuations, so we have to transform (instrument) java bytecode with a library that saves localvars and the instruction pointer upon a GreenThread.yield(). Once the green thread is resumed, this information is used to rebuild the stacktrace, with all localvars intact.<br />
<br />
The library used is: <a href='http://l33tlabs.org/Continuations/'>http://l33tlabs.org/Continuations/</a> written by <a href='http://www.matthiasmann.de/'>Matthias Mann</a><br />
His library depends on ASM3 to parse and construct the bytecode: <a href='http://asm.ow2.org/'>http://asm.ow2.org/</a><br />
<br />
Through the Java Instrumentation Agent that I wrote for the previously mentioned library, there is no need to transform your byteclass ahead of time. The java agent will take care of this at runtime.<br />
<br />
<pre>java -javaagent:<a href='http://indiespot.net/files/temp/conti4.jar'>conti4.jar</a> -cp ./your.jars your.MainClass</pre><br />
<br />
I wrote a wrapper around the continuation library, to lower the bar a bit. I hope the code pretty much speaks for itself, and if not, feel free to ask questions. For additional convenience, I added some demo code and its fascinating output at the bottom.<br />
<br />
Enjoy!<br />
<div class="code" onClick="this.style.height='auto';"><pre>import de.matthiasmann.continuations.Coroutine;
import de.matthiasmann.continuations.CoroutineProto;
import de.matthiasmann.continuations.SuspendExecution;
import de.matthiasmann.continuations.Coroutine.State;
/*
* Created on Nov 30, 2010
*
* @author Riven
*/
public abstract class <span style='color:yellow'>GreenThread</span> implements CoroutineProto
{
public static final long EOF = -1L;
private final Coroutine coroutine;
public <span style='color:yellow'>GreenThread</span>()
{
this.coroutine = new Coroutine(this);
}
private long doSleep;
@Override
<span style='color:green'>// this is where you put your code</span>
public abstract void <span style='color:yellow'>coExecute</span>() throws SuspendExecution;
long step()
{
coroutine.run();
if (coroutine.getState() == State.FINISHED)
return EOF;
return this.doSleep;
}
public static void <span style='color:yellow'>yield</span>() throws SuspendExecution
{
GreenThread.sleep(0L);
}
public static void <span style='color:yellow'>sleep</span>(long ms) throws SuspendExecution
{
GreenThread thread = (GreenThread) Coroutine.getActiveCoroutine().getProto();
thread.doSleep = Math.max(ms, 0L);
Coroutine.yield();
}
}
---------------------------------------------------------------------
import java.util.Comparator;
import java.util.PriorityQueue;
/*
* Created on Nov 30, 2010
*
* @author Riven
*/
public class <span style='color:yellow'>GreenThreadQueue</span>
{
private final PriorityQueue<greenthreadwakeup> queue;
private final List<greenthreadwakeup> reschedule;
public <span style='color:yellow'>GreenThreadQueue</span>()
{
this.queue = new PriorityQueue<greenthreadwakeup>(16, new GreenThreadWakeupComparator());
this.reschedule = new ArrayList<greenthreadwakeup>();
}
public void <span style='color:yellow'>start</span>(<span style='yellow'>GreenThread</span> thread)
{
this.queue.add(new GreenThreadWakeup(thread, 0L));
}
public boolean <span style='color:yellow'>tick</span>(long now)
{
try
{
while (true)
{
GreenThreadWakeup wakeup = this.queue.peek();
if (wakeup == null)
return !this.reschedule.isEmpty(); // signal nothing more to do
if (wakeup.timestamp > now)
break;
if (this.queue.poll() != wakeup)
throw new IllegalStateException();
long sleep = wakeup.thread.step();
if (sleep == GreenThread.EOF)
continue;
wakeup.timestamp = now + sleep;
this.reschedule.add(wakeup);
}
return true;
}
finally
{
for (GreenThreadWakeup wakeup : this.reschedule)
{
this.queue.add(wakeup);
}
this.reschedule.clear();
}
}
static private class GreenThreadWakeup
{
public final GreenThread thread;
public long timestamp;
public GreenThreadWakeup(GreenThread thread, long timestamp)
{
this.thread = thread;
this.timestamp = timestamp;
}
}
static class GreenThreadWakeupComparator implements Comparator<greenthreadwakeup>
{
@Override
public int compare(GreenThreadWakeup o1, GreenThreadWakeup o2)
{
int val = Long.signum(o1.timestamp - o2.timestamp);
return (val == 0) ? 1 : val;
}
}
}
</pre></div><br />
<br />
<b>Demo code</b><br />
<div class="code" onClick="this.style.height='auto';"><pre>GreenThreadQueue queue = new GreenThreadQueue();
GreenThread thread1 = new GreenThread()
{
@Override
public void coExecute() throws SuspendExecution
{
for (int i = 0; i < 3; i++)
{
System.out.println("a" + i);
GreenThread.sleep(1500);
}
}
};
GreenThread thread2 = new GreenThread()
{
@Override
public void coExecute() throws SuspendExecution
{
for (int i = 0; i < 4; i++)
{
System.out.println("b" + i);
GreenThread.sleep(1300);
}
}
};
queue.start(thread1);
queue.start(thread2);
do
{
try { Thread.sleep(100); } catch(InterruptedException exc) { /* ignore */ }
}
while (queue.tick(System.currentTimeMillis()));
}
</pre>
</div><b>Fancy output (from 1 thread)</b>
<div class="code" onClick="this.style.height='auto';"><pre>a0
b0
b1
a1
b2
a2
b3
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com1tag:blogger.com,1999:blog-1249179008376089851.post-16256895915264869572010-11-24T23:42:00.092+01:002011-01-11T00:53:12.900+01:00Lost four words... (pro nouns)<img src="http://matthewsalomon.files.wordpress.com/2008/03/vogon_poetry2.jpg" align="right" hspace="16" vspace="8"><blockquote><script type='text/javascript'>function j(){ var arr = Array('s', 'w', 'e', 'j'); for(var i=1; i<=2; i++) {document.getElementById('j'+i).innerHTML = arr.reverse().join(''); arr.reverse(); }} setTimeout(j, 250);</script>
<b>It got cold while she called for juice from <span id='j1'></span>, not awaiting the tide and tied a tight knot. A scene never seen again, maybe missed due to the dew or mist on site by a dude with poor sight on one side. A phase etched on the edge of his face. Whether her patients had patience depended on the weather. So take a look at the sea and see: sow and pour it in a poor pore, with any luck, lock it. I'll weigh the way the rained isle was reigned. A pane hid me, then a pain hit me. During the war the witch wore a hat which was won by one bright bride. During the juring descent, she thought and fought the scent. Worn by the sun, I warn my son, as his eye tans tense, 'caus where regular wear hides hides, I knew a new gnu (its dead dad had that dept) that played ball, sipping a bowl for four hours, assuming it was ours, while the cued cheap cute sheep queued. Silence. Deaf by impending death. 'Hear here', she heard hurt. She waited, weighted by lead, led down the stairs by the wrath filled failed ref. Bye Susan, by all means, awe at the whirled world... Eight dodo's ate dough though.</b></blockquote><table cellpadding=8>
<tr><td valign=top><pre><font color=#404040>
cold/called
juice/<span id='j2'></span>
not/knot
tide/tight/tied
scene/seen
missed/mist
due/dew
site/side/sight
phase/face
etch/edge
</font></pre></td><td valign=top><pre><font color=#404040>
whether/weather
patience/patients
suck it/socket
rained/reighed
thought/fought
eight/ate
dodo/though/dough
so/sow
luck/lock
sea/see
pour/pore/poor
</font></pre></td><td valign=top><pre><font color=#404040>
war/wore
won/one
witch/which
bright/bride
worn/warn
sun/son
tans/tense
where/wear
knew/new/gnu
dead/dad/that/dept
descent/the scent
</font></pre></td><td valign=top><pre><font color=#404040>
ball/bowl
for/four
hours/ours
cued/cute/queued
cheap/sheep
plug/plaque
deaf/death
hear/here
heard/hurt
waited/weighted
</font></pre></td><td valign=top><pre><font color=#404040>
lead/led
wrath/ref
filled/failed
bye/by
all/awe
whirled/world
i'll/isle
way/weigh
pain/pane
hit/hid
</font></pre></td></tr>
</table>Disclaimer: English is not my native language, so certain words might sound the same for me, while they are clearly distinct for you gays.
<!--
serial, cereal
a raw roar came from the bode, as he rowed his boat
pray prey
shoot chute
be bee
blew blue
know no
none non
hi high
allowed aloud
through threw
roll role
bye by buy
the bored board prays praise for preys
-->Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com3tag:blogger.com,1999:blog-1249179008376089851.post-35934758950527520522010-08-19T19:15:00.008+02:002010-11-24T17:51:51.404+01:00Calculate PerlinNoise faster with an optimization to grad()A simple optimization to the gradient function makes the noise function twice as fast.<br />
<br />
<b>PerlinNoise.grad()</b><br />
<div class="code" style='height:auto'><pre>inline float grad(int hash, float x, float y, float z)
{
//float u = (h < 8) ? x : y;
//float v = (h < 4) ? y : ((h == 12 || h == 14) ? x : z);
//return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
switch(hash & 0xF)
{
case 0x0: return x + y;
case 0x1: return -x + y;
case 0x2: return x - y;
case 0x3: return -x - y;
case 0x4: return x + z;
case 0x5: return -x + z;
case 0x6: return x - z;
case 0x7: return -x - z;
case 0x8: return y + z;
case 0x9: return -y + z;
case 0xA: return y - z;
case 0xB: return -y - z;
case 0xC: return y + x;
case 0xD: return -y + z;
case 0xE: return y - x;
case 0xF: return -y - z;
default: return 0; // never happens
}
}
</pre>
</div><p></p><p></p><b>PerlinNoise.noise()</b> (click code to expand)
<div class="code" onClick="this.style.height='auto';"><pre>int perm[512];
void initPerlinNoise()
{
int i, permutation[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 };
for (i = 0; i < 256; i++)
perm[256 + i] = perm[i] = permutation[i];
}
inline float fade(float t)
{
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
}
inline float lerp(float t, float a, float b)
{
return a + t * (b - a);
}
inline float grad(int hash, float x, float y, float z)
{
//float u = (h < 8) ? x : y;
//float v = (h < 4) ? y : ((h == 12 || h == 14) ? x : z);
//return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
switch(hash & 0xF)
{
case 0x0: return x + y;
case 0x1: return -x + y;
case 0x2: return x - y;
case 0x3: return -x - y;
case 0x4: return x + x;
case 0x5: return -x + x;
case 0x6: return x - x;
case 0x7: return -x - x;
case 0x8: return y + x;
case 0x9: return -y + x;
case 0xA: return y - x;
case 0xB: return -y - x;
case 0xC: return y + z;
case 0xD: return -y + x;
case 0xE: return y - x;
case 0xF: return -y - z;
default: return 0; // never happens
}
}
static inline float __fastcall noise(float x, float y, float z)
{
jint ix,iy,iz,gx,gy,gz;
jint a0,b0,aa,ab,ba,bb;
float aa0,ab0,ba0,bb0;
float aa1,ab1,ba1,bb1;
float a1,a2,a3,a4,a5,a6,a7,a8;
float u,v,w,a8_5,a4_1;
ix = (jint)x; x-=ix;
iy = (jint)y; y-=iy;
iz = (jint)z; z-=iz;
gx = ix & 0xFF;
gy = iy & 0xFF;
gz = iz & 0xFF;
a0 = gy+perm[gx];
b0 = gy+perm[gx + 1];
aa = gz+perm[a0];
ab = gz+perm[a0 + 1];
ba = gz+perm[b0];
bb = gz+perm[b0 + 1];
aa0 = perm[aa]; aa1 = perm[aa + 1];
ab0 = perm[ab]; ab1 = perm[ab + 1];
ba0 = perm[ba]; ba1 = perm[ba + 1];
bb0 = perm[bb]; bb1 = perm[bb + 1];
a1 = grad(bb1, x-1, y-1, z-1);
a2 = grad(ab1, x , y-1, z-1);
a3 = grad(ba1, x-1, y , z-1);
a4 = grad(aa1, x , y , z-1);
a5 = grad(bb0, x-1, y-1, z );
a6 = grad(ab0, x , y-1, z );
a7 = grad(ba0, x-1, y , z );
a8 = grad(aa0, x , y , z );
u = fade(x);
v = fade(y);
w = fade(z);
a8_5 = lerp(v, lerp(u, a8, a7), lerp(u, a6, a5));
a4_1 = lerp(v, lerp(u, a4, a3), lerp(u, a2, a1));
return lerp(w, a8_5, a4_1);
}
</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com8tag:blogger.com,1999:blog-1249179008376089851.post-50505094206395784872010-07-23T19:28:00.021+02:002010-07-25T15:02:27.700+02:00Rhino ClassShutter replacement: SandboxShutterBecause the <tt>ClassShutter</tt> in Rhino doesn't usually provide enough information to make decisions on whether or not certain Java objects, fields or methods should be accessible, I wrote the <tt>SandboxShutter</tt> which will enable you to control accessibility for each field and each method of each Java object. The performance impact is negligible, because JavaScript itself is an order of magnitude slower than the injected checks. <br />
<br />
<br />
<b>The SandboxShutter interface:</b><br />
<div class="code" style='height:auto'><pre>public interface <span style='color:yellow'>SandboxShutter</span>
{
public boolean allowClassAccess(Class<?> type);
public boolean allowFieldAccess(Class<?> type, Object instance, String fieldName);
public boolean allowMethodAccess(Class<?> type, Object instance, String methodName);
public boolean allowStaticFieldAccess(Class<?> type, String fieldName);
public boolean allowStaticMethodAccess(Class<?> type, String methodName);
}
</pre></div><br />
<br />
<b>When the following javascript code is executed:</b><br />
<div class='code' style='height:auto'><pre>importPackage(Packages.my.game); <span style='color:#00c000'>// assuming the Java class my.game.Player exists</span>
var player = new Player("Jake");
player.gender = "female"; <span style='color:#00c000'>// this is a Java method!</span>
player.setGender("male"); <span style='color:#00c000'>// this the same Java method.</span>
player.age = 18; <span style='color:#00c000'>// this is a Java field</span>
player.age += 3;
var player = new Player("Jane");
player.gender = "male";
player.gender = "female";
player.age = 19;
player.age += 2;
var count = Player.PLAYER_COUNT;
Player.PLAYER_COUNT += 2;
</pre></div><br />
<br />
<b>The following SandboxShutter calls will be made:</b><br />
<div class='code' style='height:auto'><pre>allowClassAccess: my.game.Player
allowMethodAccess: my.game.Player.setGender() instance=Player@2346
allowFieldAccess: my.game.Player.age instance=Player@2346
allowMethodAccess: my.game.Player.setGender() instance=Player@54326
allowFieldAccess: my.game.Player.age instance=Player@54326
allowStaticFieldAccess: my.game.Player.PLAYER_COUNT
</pre></div>As shown, there will be at most one call for each field/method in each object, and one call per class. This allows you to control accessibility per Java object.<br />
<br />
<br />
<b>Create the SandboxContextFactory:</b><br />
<div class="code" onClick="this.style.height='auto';"><pre> public static class <span style='color:yellow'>SandboxContextFactory</span> extends ContextFactory
{
final SandboxShutter shutter;
public SandboxContextFactory(SandboxShutter shutter)
{
this.shutter = shutter;
}
@Override
protected Context makeContext()
{
Context cx = super.makeContext();
cx.setWrapFactory(new SandboxWrapFactory());
cx.setClassShutter(new ClassShutter()
{
private final Map<String, Boolean> nameToAccepted = new HashMap<String, Boolean>();
@Override
public boolean visibleToScripts(String name)
{
Boolean granted = this.nameToAccepted.get(name);
if (granted != null)
{
return granted.booleanValue();
}
Class< ? > staticType;
try
{
staticType = Class.forName(name);
}
catch (Exception exc)
{
this.nameToAccepted.put(name, Boolean.FALSE);
return false;
}
boolean grant = shutter.allowClassAccess(staticType);
this.nameToAccepted.put(name, Boolean.valueOf(grant));
return grant;
}
});
return cx;
}
class SandboxWrapFactory extends WrapFactory
{
@Override
public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj)
{
this.ensureReplacedClass(scope, obj, null);
return super.wrapNewObject(cx, scope, obj);
}
@Override
public Object wrap(Context cx, Scriptable scope, Object obj, Class< ? > staticType)
{
this.ensureReplacedClass(scope, obj, staticType);
return super.wrap(cx, scope, obj, staticType);
}
@Override
public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class< ? > staticType)
{
final Class< ? > type = this.ensureReplacedClass(scope, javaObject, staticType);
return new NativeJavaObject(scope, javaObject, staticType)
{
private final Map<String, Boolean> instanceMethodToAllowed = new HashMap<String, Boolean>();
@Override
public Object get(String name, Scriptable scope)
{
Object wrapped = super.get(name, scope);
if (wrapped instanceof BaseFunction)
{
String id = type.getName() + "." + name;
Boolean allowed = this.instanceMethodToAllowed.get(id);
if (allowed == null)
{
boolean allow = shutter.allowMethodAccess(type, javaObject, name);
this.instanceMethodToAllowed.put(id, allowed = Boolean.valueOf(allow));
}
if (!allowed.booleanValue())
{
return NOT_FOUND;
}
}
else
{
// NativeJavaObject + only boxed primitive types?
if (!shutter.allowFieldAccess(type, javaObject, name))
{
return NOT_FOUND;
}
}
return wrapped;
}
};
}
//
private final Set<Class< ? >> replacedClasses = new HashSet<Class< ? >>();
private Class< ? > ensureReplacedClass(Scriptable scope, Object obj, Class< ? > staticType)
{
final Class< ? > type = (staticType == null && obj != null) ? obj.getClass() : staticType;
if (!type.isPrimitive() && !type.getName().startsWith("java.") && this.replacedClasses.add(type))
{
this.replaceJavaNativeClass(type, scope);
}
return type;
}
private void replaceJavaNativeClass(final Class< ? > type, Scriptable scope)
{
Object clazz = Context.jsToJava(ScriptableObject.getProperty(scope, "Packages"), Object.class);
Object holder = null;
for (String part : Text.split(type.getName(), '.'))
{
holder = clazz;
clazz = ScriptableObject.getProperty((Scriptable) clazz, part);
}
NativeJavaClass nativeClass = (NativeJavaClass) clazz;
nativeClass = new NativeJavaClass(scope, type)
{
@Override
public Object get(String name, Scriptable start)
{
Object wrapped = super.get(name, start);
if (wrapped instanceof BaseFunction)
{
if (!shutter.allowStaticMethodAccess(type, name))
{
return NOT_FOUND;
}
}
else
{
// NativeJavaObject + only boxed primitive types?
if (!shutter.allowStaticFieldAccess(type, name))
{
return NOT_FOUND;
}
}
return wrapped;
}
};
ScriptableObject.putProperty((Scriptable) holder, type.getSimpleName(), nativeClass);
ScriptableObject.putProperty(scope, type.getSimpleName(), nativeClass);
}
}
}
</pre></div><br />
<br />
<b>Install the (global) SandboxContextFactory:</b><br />
<div class="code" style='height:auto'><pre> ContextFactory.initGlobal(new <span style='color:yellow'>SandboxContextFactory</span>(new <span style='color:yellow'>SandboxShutter</span>()
{
...
}));
<span style='color:#00c000'>// create and initialize Rhino Context</span>
Context cx = Context.enter();
Scriptable prototype = cx.initStandardObjects();
Scriptable topLevel = new ImporterTopLevel(cx);
prototype.setParentScope(topLevel);
Scriptable scope = cx.newObject(prototype);
scope.setPrototype(prototype);
<span style='color:#00c000'>// your scripts</span>
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com9tag:blogger.com,1999:blog-1249179008376089851.post-86249502228375353282010-07-20T19:58:00.004+02:002010-07-20T20:07:29.719+02:00Functions on Iterables :: Consume<div class="code" onClick="this.style.height='auto';"><pre>public class <span style='color:yellow'>Functional</span>
{
<span style='color:#00c000'>/**
* Consumes (removes) all items while iterating<t>
*/</span>
public static <T> Iterable<T> <span style='color:yellow'>consume</span>(final Iterable<T> iterable)
{
if (iterable == null)
throw new NullPointerException();
return new Iterable<T>()
{
@Override
public Iterator<T> iterator()
{
final Iterator<T> iterator = iterable.iterator();
return new Iterator<T>()
{
@Override
public boolean hasNext()
{
return iterator.hasNext();
}
@Override
public T next()
{
T result = iterator.next();
iterator.remove();
return result;
}
@Override
public void remove()
{
throw new NoSuchElementException("already removed");
}
};
}
};
}
</pre></div><br />
<br />
<br />
<br />
<b>To remove all short texts in a list:</b><br />
<div class="code" onClick="this.style.height='auto';"><pre>List<String> data = ...;
Filter<String> <span style='color:#00c0c0'>shortText</span> = new Filter<String>()
{
public boolean accept(String item) { return item == null || item.length() <= 3; }
}
for(String item: <span style='color:yellow'>consume</span>(<span style='color:yellow'>filter</span>(data, <span style='color:#00c0c0'>shortText</span>)))
{
System.out.println("By the time you see this, '"+item+"' has been removed.");
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com1tag:blogger.com,1999:blog-1249179008376089851.post-57120488158826752332010-07-20T19:35:00.008+02:002010-07-21T16:57:07.403+02:00Functions on Iterables :: Event Hooks<div class="code" onClick="this.style.height='auto';"><pre>public interface <span style='color:yellow'>Operator</span><t>
{
public void operate(T item);
}
public class <span style='color:yellow'>Functional</span>
{
<span style='color:#00c000'>/**
* Performs a callback on each element-visit, and each element removal
*/</span>
public static <T> Iterable<T> <span style='color:yellow'>eventHook</span>(final Iterable<T> iterable,
final Operator<T> onVisit,
final Operator<T> onRemove)
{
if (iterable == null)
throw new NullPointerException();
if (onVisit == null && onRemove == null)
throw new NullPointerException("must specify either onVisit, onRemove or both");
return new Iterable<T>()
{
@Override
public Iterator<T> iterator()
{
final Iterator<T> iterator = iterable.iterator();
return new Iterator<T>()
{
@Override
public boolean hasNext()
{
return iterator.hasNext();
}
@Override
public T next()
{
this.current = iterator.next();
if (onVisit != null)
onVisit.operate(this.current);
return this.current;
}
@Override
public void remove()
{
iterator.remove();
if (onRemove != null)
onRemove.operate(this.current);
}
//
private T current;
};
}
};
}
}
</pre></div><br />
<b>Example:</b><br />
<div class="code" onClick="this.style.height='auto';"><pre>Operator<String> onVisit= new Operator<String>()
{
public void operate(String text)
{
System.out.println("visited: "+text);
}
};
Operator<String> onRemove= new Operator<String>()
{
public void operate(String text)
{
System.out.println("removed: "+text);
}
};
Iterable<String> data = ...;
for(String item: <span style='color:yellow'>eventHook</span>(data, onVisit, onRemove))
{
if(item.length() > String.valueOf(Math.PI))
System.out.println("this text is longer than PI!");
}
</pre></div><br />
<br />
Once you created an Iterable (say, a filtered view on a List) you can iterate over it as often as you want. No need to create a new Iterable.<br />
<br />
<br />
With the support for closures in Java 7 a lot of the boilerplate code will disappear.Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com2tag:blogger.com,1999:blog-1249179008376089851.post-41160666200555484152010-07-20T19:31:00.007+02:002010-07-20T19:39:46.550+02:00Functions on Iterables :: Transformer<div class="code" onClick="this.style.height='auto';"><pre>public interface <span style='color:yellow'>Transformer</span><I, O>
{
public O transform(I item);
}
public class <span style='color:yellow'>Functional</span>
{
<span style='color:#00c000'>/**
* Transforms Iterable<I> into Iterable<O> using a Transformer<I, O>
*/</span>
public static <I, O> Iterable<O> <span style='color:yellow'>transform</span>(final Iterable<I> iterable,
final Transformer<I, O> transformer)
{
if (iterable == null)
throw new NullPointerException();
if (transformer == null)
throw new NullPointerException();
return new Iterable<O>()
{
@Override
public Iterator<O> iterator()
{
final Iterator<I> iterator = iterable.iterator();
return new Iterator<O>()
{
@Override
public boolean hasNext()
{
return iterator.hasNext();
}
@Override
public O next()
{
return transformer.transform(iterator.next());
}
@Override
public void remove()
{
iterator.remove();
}
};
}
};
}
}
</pre></div><br />
<b>Example:</b><br />
<div class="code" onClick="this.style.height='auto';"><pre>Transformer<String, Integer> textToNumber = new Transformer<String, Integer>()
{
public Integer transform(String text)
{
return Integer.valueOf(text);
}
};
Iterable<String> data = ...;
for(Integer number: <span style='color:yellow'>transform</span>(data, textToNumber))
{
System.out.println("number: "+number.intValue());
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-30510970620401312322010-07-20T19:24:00.002+02:002010-07-20T19:25:31.703+02:00Functions on Iterables :: FilterFunctional programming has its strengths when iterating over data. If you want a <i>view</i> on a subset data, you can make a filter, and iterate over your data only seeing the elements that the filter accepted:<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public interface <span style='color:yellow'>Filter</span><T>
{
public boolean <span style='color:yellow'>accept</span>(T value);
}
public class <span style='color:yellow'>Functional</span>
{
<span style='color:#00c000'>/**
* Filter items from the view of the returned Iterable<T>
*/</span>
public static <T> Iterable<T> <span style='color:yellow'>filter</span>(final Iterable<T> iterable, final Filter<T> filter)
{
if (iterable == null)
throw new NullPointerException();
if (filter == null)
throw new NullPointerException();
return new Iterable<T>()
{
@Override
public Iterator<T> iterator()
{
final Iterator<T> iterator = iterable.iterator();
return new Iterator<T>()
{
@Override
public boolean hasNext()
{
this.ensureReady();
return this.ready;
}
@Override
public T next()
{
this.ensureReady();
if (!this.ready)
throw new NoSuchElementException();
T result = this.current;
this.ready = false;
this.current = null;
return result;
}
@Override
public void remove()
{
iterator.remove();
}
//
private boolean ready = false;
private T current;
private void ensureReady()
{
while (!this.ready && iterator.hasNext())
{
T item = iterator.next();
if (!filter.accept(item))
continue;
this.ready = true;
this.current = item;
}
}
};
}
};
}
}
</pre></div><br />
<b>Example:</b><br />
<div class="code" onClick="this.style.height='auto';"><pre>Filter<String> nonNull = new Filter<String>()
{
public boolean accept(String value)
{
return value != null;
}
};
Iterable<String> data = ...;
for(String item: <span style='color:yellow'>filter</span>(data, nonNull))
{
System.out.println("item: "+item); <span style='color:#00c000'>// guaranteed not to be null</span>
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-61977083032606587182010-04-29T11:10:00.006+02:002010-04-29T11:26:03.214+02:00Histogram :: array basedFor any reasonably sized Histogram, you probably want to look at <a href="http://riven8192.blogspot.com/2009/08/histogram-map-based.html">this Histogram class based on a HashMap</a>. However, if your histograms are tiny, say, less than 8 elements occur frequently, this array based histogram is an order of magnitude faster.<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
public class <span style='color:yellow'>TinyHistogram</span><T>
{
private T[] items;
private int[] usage;
private int size;
public <span style='color:yellow'>TinyHistogram</span>()
{
this.items = (T[]) new Object[4];
this.usage = new int[4];
this.size = 0;
}
public int <span style='color:yellow'>put</span>(T item)
{
if (item == null)
throw new IllegalArgumentException();
int io = this.indexOfItem(item);
if (io != -1)
{
int result = ++this.usage[io];
this.swapIncreasedValue(io);
return result;
}
// add item to the end
if (this.size == this.items.length)
this.grow();
this.items[this.size] = item;
this.usage[this.size] = 1;
this.size += 1;
return 1;
}
public int <span style='color:yellow'>get</span>(T item)
{
if (item == null)
throw new IllegalArgumentException();
int io = this.indexOfItem(item);
if (io == -1)
return 0; // not -1, as the object occurred zero times
return this.usage[io];
}
public List<T> <span style='color:yellow'>topKeys</span>()
{
return this.topKeys(Integer.MAX_VALUE);
}
public List<T> <span style='color:yellow'>topKeys</span>(int amount)
{
int end = Math.min(this.size, amount);
// make a copy, the items are already sorted
List<T> tops = new ArrayList<T>();
for (int i = 0; i < end; i++)
tops.add(this.items[i]);
return tops;
}
public List<Pair<T, Integer>> <span style='color:yellow'>topEntries</span>()
{
return this.topEntries(Integer.MAX_VALUE);
}
public List<Pair<T, Integer>> <span style='color:yellow'>topEntries</span>(int amount)
{
int end = Math.min(this.size, amount);
// make a copy, the items are already sorted
List<Pair<T, Integer>> tops = new ArrayList<Pair<T, Integer>>();
for (int i = 0; i < end; i++)
tops.add(new Pair<T, Integer>(this.items[i], Integer.valueOf(this.usage[i])));
return tops;
}
public int <span style='color:yellow'>remove</span>(T item)
{
int io = this.indexOfItem(item);
if (io == -1)
throw new NoSuchElementException(String.valueOf(item));
int result = --this.usage[io];
if (result == 0)
return this.removeIndex(io);
this.swapDecreasedValue(io);
return result;
}
public int <span style='color:yellow'>reset</span>(T item)
{
int io = this.indexOfItem(item);
if (io == -1)
throw new NoSuchElementException(String.valueOf(item));
return this.removeIndex(io);
}
//
private final void swapDecreasedValue(int index)
{
while ((index + 1) < size && usage[index + 0] < usage[index + 1])
{
int a = index + 0;
int b = index + 1;
swapObj(items, a, b);
swapInt(usage, a, b);
index++;
}
}
private final void swapIncreasedValue(int index)
{
while (index > 0 && usage[index + 0] > usage[index - 1])
{
int a = index - 0;
int b = index - 1;
swapObj(items, a, b);
swapInt(usage, a, b);
index--;
}
}
private static final void swapInt(int[] array, int a, int b)
{
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
private static final void swapObj(Object[] array, int a, int b)
{
Object temp = array[a];
array[a] = array[b];
array[b] = temp;
}
private void grow()
{
this.items = Arrays.copyOf(this.items, this.items.length * 2);
this.usage = Arrays.copyOf(this.usage, this.usage.length * 2);
}
private int removeIndex(int index)
{
int count = this.usage[index];
int shift = --this.size - index;
System.arraycopy(this.items, index + 1, this.items, index, shift);
System.arraycopy(this.usage, index + 1, this.usage, index, shift);
return count;
}
private int indexOfItem(T item)
{
for (int i = 0; i < this.size; i++)
if (this.items[i] == item || this.items[i].equals(item))
return i;
return -1;
}
}</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-74327871703117424352010-04-29T10:58:00.001+02:002010-04-29T10:58:44.110+02:00Slicing direct buffers to workaround 4K overheadDirect <tt>ByteBuffers</tt> can have an unexpected massive overhead. For every allocation, a number of bytes equal to the system pagesize (typically 4K) is added. The reason for this is that <tt>MappedByteBuffers</tt> must be page-aligned. The code used behind the scenes in <tt>ByteBuffer.allocateDirect(int size)</tt> looks a little something like this:<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>private static ByteBuffer malloc(int size)
{
int pageSize = unsafe.getPageSize(); // typically 4096
long pointer = sun.misc.Unsafe.malloc(size + pageSize);
long base = (pointer + (pageSize-1)) / pageSize * pageSize;
ByteBuffer buffer = unsafe.createBufferAt(base);
return buffer;
}
</pre></div><br />
Code like this will have a massive overhead:<br />
<div class="code" onClick="this.style.height='auto';"><pre>for(int i=0; i<count; i++)
buffers[i] = ByteBuffer.allocateDirect(64);
</pre></div><br />
Instead use an approach like below:<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style='color:yellow'>DirectBufferProvider</span>
{
private static final int ALLOCATION_SIZE = 1024*1024;
private ByteBuffer currentBuffer = null;
public ByteBuffer <span style='color:yellow'>allocate</span>(int size)
{
if(size >= ALLOCATION_SIZE)
return ByteBuffer.allocateDirect(size);
if(currentBuffer == null || size > currentBuffer.remaining())
currentBuffer = ByteBuffer.allocateDirect(ALLOCATION_SIZE);
currentBuffer.limit(currentBuffer.position() + size);
ByteBuffer result = currentBuffer.slice();
currentBuffer.position(currentBuffer.limit());
currentBuffer.limit(currentBuffer.capacity());
return result;
}
private static DirectBufferProvider global_synced = new DirectBufferProvider();
public static ByteBuffer <span style='color:yellow'>allocateDirect</span>(int size)
{
synchronized(global_synced)
{
return global_synced.allocate(size);
}
}
}
</pre></div><br />
Note that unlike <tt>ByteBuffer.allocateDirect()</tt>, the code in <tt>DirectBufferProvider.allocate()</tt> is not threadsafe. The static (convenience) method is synchronized and should thus not be used in heavily multithreaded code. Using <tt>ThreadLocals</tt> is even worse, performance wise, so just make new <tt>DirectBufferProvider</tt> instances when you need them.Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-67759219502043083602010-02-23T16:56:00.037+01:002010-04-06T16:34:56.291+02:00Delaying security dialogs in Java until you need themDelaying security dialogs in Java until you need them, is a bit harder than it should be. By default, the dialog appears before the first line of code is executed, scaring off your casual visitor.<br />
<br />
If you only occasionally need to perform actions that require elevated privileges, you can delay the security dialog to the absolute last moment (for example: right before reading/writing a file). The trick is to keep most of your code in an unsigned JAR, and the code that requires elevated privileges into a signed JAR. Use <tt>Class.forName(String)</tt> to load the signed class, which will prompt the security dialog.<br />
<br />
Using an interface in the unsigned code, and the implementation in the signed code, you can keep your code tidy.<br />
<br />
<br />
Note:<br />
<blockquote><i><br />
The browser will remember the choice of the user, until a *restart* of the browser. To workaround this (when people declined), create a dozen tiny signed JARs (with a dozen different certificates, mind you) and use a roundrobin algorithm, using serverside code or javascript that generates the applet-archive attribute. After a dozen hits and rejections, you can be sure your visitor will never grant access to his system anyway.</i></blockquote><br />
Update:<br />
<blockquote><i><br />
To make it work in MSIE, both classes MUST be in separate packages.</i></blockquote><br />
<br />
<font color=red><b>IMPORTANT: Since 6u19 this doesn't work anymore. You not only get a rather confusing security dialog (clicking [YES] means deny access, clicking [NO] means allow access), the two classes end up in different classloaders that cannot access eachother, resulting in ClassNotFoundException / NoClassDefFoundException. Thanks Oracle, for making Java's user experience even more secure and crap at the same time.<br />
<a href="http://java.sun.com/javase/6/docs/technotes/guides/jweb/mixed_code.html" target="_blank">http://java.sun.com/javase/6/docs/technotes/guides/jweb/mixed_code.html</a></b></font><br />
<br />
<br />
On to the code, which is reasonably simple.<br />
<br />
<span style='color:#80C0E0'><b>Unsigned JAR:</b></span><br />
<div class="code" onClick="this.style.height='auto';"><pre>package <span style='color:cyan'>some.unsigned.stuff</span>;
public interface <span style='color:yellow'>SecureAccess</span>
{
public byte[] loadFile(File file);
public void storeFile(File file, byte[] data);
}
// Usage:
File file = new File("/home/silly/image.jpg");
Class< ? > clazz = Class.forName("<span style='color:cyan'>some.signed.stuff</span>.LocalSecureAccess");
SecureAccess access = (SecureAccess) clazz.newInstance();
byte[] data = <span style='color:yellow'>access.loadFile(file);</span>
</pre></div><br />
<span style='color:#80C0E0'><b>Signed JAR:</b></span><br />
<div class="code" onClick="this.style.height='auto';"><pre>package <span style='color:cyan'>some.signed.stuff</span>;
public class <span style='color:yellow'>LocalSecureAccess</span> implements SecureAccess
{
public byte[] loadFile(final File file)
{
return AccessController.doPrivileged(new PrivilegedAction<byte[]>()
{
@Override
public byte[] run()
{
return loadFileImpl(file);
}
});
}
@Override
public void storeFile(final File file, final byte[] data)
{
AccessController.doPrivileged(new PrivilegedAction<Object>()
{
@Override
public Object run()
{
storeFileImpl(file, data);
return null;
}
});
}
// implementation
static final int MAX_FILE_SIZE = 8 * 1024 * 1024; // prevent applet running out of memory
byte[] loadFileImpl(File file)
{
DataInputStream input = null;
try
{
long len = file.length();
if (len > MAX_FILE_SIZE)
throw new IllegalStateException("file too big: " + file);
byte[] data = new byte[(int) len];
input = new DataInputStream(new FileInputStream(file));
input.readFully(data);
input.close();
return data;
}
catch (IOException exc)
{
throw new IllegalStateException(exc);
}
finally
{
try { if(input!=null) input.close(); } catch(IOException exc) {}
}
}
void storeFileImpl(File file, byte[] data)
{
OutputStream output = null;
try
{
output = new FileOutputStream(file);
output.write(data);
output.flush();
}
catch (IOException exc)
{
throw new IllegalStateException(exc);
}
finally
{
try { if(output!=null) output.close(); } catch(IOException exc) {}
}
}
}
</pre></div><br />
<span style='color:#80C0E0'><b>Jar signing 101: (using DSA keys instead of RSA for Java 1.4 compatibility)</b></span><br />
<div class="code" onClick="this.style.height='auto';"><pre>PATH=%PATH%;path\to\JDK\bin
SET ALIAS=MY_ALIAS
SET PASS=MY_PASSWORD
SET JAR=my.jar
keytool -delete -storepass %PASS% -alias %ALIAS%
keytool -genkey -storepass %PASS% -keypass %PASS% -keyalg <span style='color:yellow'>DSA</span> -alias %ALIAS%
-dname "CN=full.domainname.com, OU=Your unit, O=Your Company,
L=Your city, ST=Your state, C=CA,
EMAILADDRESS=your@server.com DC=server, DC=com"
-validity 999 <span style='color:red'>(put all of this on one line)</span>
keytool -selfcert -storepass %PASS% -alias %ALIAS% -validity 999
keytool -exportcert -storepass %PASS% -alias %ALIAS% -rfc -file %ALIAS%.cer
jarsigner -storepass %PASS% -keypass %PASS% %JAR% %ALIAS%
pause
</pre></div><br />
<span style='color:#80C0E0'><b>Applet code: (nothing special)</b></span><br />
<div class="code" onClick="this.style.height='auto';"><pre> <applet
code="package/of/YourApplet.class"
archive="unsigned.jar,signed.jar"
width="640"
height="480">
no applet?
</applet>
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com1tag:blogger.com,1999:blog-1249179008376089851.post-82541590985425422302010-02-04T19:23:00.007+01:002010-02-04T19:39:54.869+01:00Image :: Java Animated GIFs (with transparant pixel disposal modes)It's a nightmare to find the code to create animated GIFs in Java. Once you found it, you notice you can't set the frame disposal, and transparent pixels will show pixels in the previous frames. The following code snippet solves this.<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style='color:yellow'>GifFrame</span>
{
public static final String NONE = "none";
public static final String DO_NOT_DISPOSE = "doNotDispose";
public static final String RESTORE_TO_BGCOLOR = "restoreToBackgroundColor";
public static final String RESTORE_TO_PREVIOUS = "restoreToPrevious";
public final BufferedImage img;
public final long delay; // in millis
public final String disposalMethod;
public GifFrame(BufferedImage img, long delay)
{
this(img, delay, NONE);
}
public GifFrame(BufferedImage img, long delay, String disposalMethod)
{
this.img = img;
this.delay = delay;
this.disposalMethod = disposalMethod;
}
}
public class <span style='color:yellow'>ImageUtil</span>
{
public static BufferedImage <span style='color:yellow'>convertRGBAToGIF</span>(BufferedImage src, int transColor)
{
BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics g = dst.getGraphics();
g.setColor(new Color(transColor));
g.fillRect(0, 0, dst.getWidth(), dst.getHeight());
{
IndexColorModel indexedModel = (IndexColorModel) dst.getColorModel();
WritableRaster raster = dst.getRaster();
int sample = raster.getSample(0, 0, 0);
int size = indexedModel.getMapSize();
byte[] rr = new byte[size];
byte[] gg = new byte[size];
byte[] bb = new byte[size];
indexedModel.getReds(rr);
indexedModel.getGreens(gg);
indexedModel.getBlues(bb);
IndexColorModel newModel = new IndexColorModel(8, size, rr, gg, bb, sample);
dst = new BufferedImage(newModel, raster, dst.isAlphaPremultiplied(), null);
}
dst.createGraphics().drawImage(src, 0, 0, null);
return dst;
}
public static void <span style='color:yellow'>saveAnimatedGIF</span>(OutputStream out, List<GifFrame> frames, int loopCount) throws Exception
{
ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
ImageOutputStream ios = ImageIO.createImageOutputStream(out);
iw.setOutput(ios);
iw.prepareWriteSequence(null);
int p = 0;
for (GifFrame frame : frames)
{
ImageWriteParam iwp = iw.getDefaultWriteParam();
IIOMetadata metadata = iw.getDefaultImageMetadata(new ImageTypeSpecifier(frame.img), iwp);
ImageUtil.configureGIFFrame(metadata, String.valueOf(frame.delay / 10L), p++, frame.disposalMethod, loopCount);
IIOImage ii = new IIOImage(frame.img, null, metadata);
iw.writeToSequence(ii, null);
}
iw.endWriteSequence();
ios.close();
}
private static void configureGIFFrame(IIOMetadata meta, String delayTime, int imageIndex, String disposalMethod, int loopCount)
{
String metaFormat = meta.getNativeMetadataFormatName();
if (!"javax_imageio_gif_image_1.0".equals(metaFormat))
{
throw new IllegalArgumentException("Unfamiliar gif metadata format: " + metaFormat);
}
Node root = meta.getAsTree(metaFormat);
Node child = root.getFirstChild();
while (child != null)
{
if ("GraphicControlExtension".equals(child.getNodeName()))
break;
child = child.getNextSibling();
}
IIOMetadataNode gce = (IIOMetadataNode) child;
gce.setAttribute("userDelay", "FALSE");
gce.setAttribute("delayTime", delayTime);
gce.setAttribute("disposalMethod", disposalMethod);
if (imageIndex == 0)
{
IIOMetadataNode aes = new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode ae = new IIOMetadataNode("ApplicationExtension");
ae.setAttribute("applicationID", "NETSCAPE");
ae.setAttribute("authenticationCode", "2.0");
byte[] uo = new byte[] { 0x1, (byte) (loopCount & 0xFF), (byte) ((loopCount >> 8) & 0xFF) };
ae.setUserObject(uo);
aes.appendChild(ae);
root.appendChild(aes);
}
try
{
meta.setFromTree(metaFormat, root);
}
catch (IIOInvalidTreeException e)
{
throw new Error(e);
}
}
}
</pre></div><br />
Usage:<br />
<div class="code" onClick="this.style.height='auto';"><pre>List<GifFrame> gifFrames = new ArrayList<GifFrame>();
for(BufferedImage image: images)
{
int transparantColor = 0xFF00FF; // purple
BufferedImage gif = convertRGBAToGIF(image, transparantColor);
long delay = 100; // every frame takes 100ms
String disposal = GifFrame.RESTORE_TO_BGCOLOR; // make transparent pixels not 'shine through'
gifFrames.add(new GifFrame(gif, delay, disposal));
}
int loopCount = 0; // loop indefinitely
saveAnimatedGIF(outputStream, gifFrames, loopCount);
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com8tag:blogger.com,1999:blog-1249179008376089851.post-4781546652229051362010-02-04T19:14:00.008+01:002010-02-04T19:17:04.086+01:00Image :: read/write TGA<div class="code" onClick="this.style.height='auto';"><pre>public static BufferedImage <span style="color:yellow">readTGA</span>(File file) throws IOException
{
if (!file.exists())
throw new FileNotFoundException(file.getAbsolutePath());
byte[] header = new byte[18];
int len = (int) file.length() - header.length;
if (len < 0)
throw new IllegalStateException("file not big enough to contain header: " + file.getAbsolutePath());
byte[] data = new byte[len];
RandomAccessFile raf = new RandomAccessFile(file, "r");
raf.read(header);
raf.read(data);
raf.close();
if ((header[0] | header[1]) != 0)
throw new IllegalStateException(file.getAbsolutePath());
if (header[2] != 2)
throw new IllegalStateException(file.getAbsolutePath());
int w = 0, h = 0;
w |= (header[12] & 0xFF) << 0;
w |= (header[13] & 0xFF) << 8;
h |= (header[14] & 0xFF) << 0;
h |= (header[15] & 0xFF) << 8;
boolean alpha;
if ((w * h) * 3 == data.length)
alpha = false;
else if ((w * h) * 4 == data.length)
alpha = true;
else
throw new IllegalStateException(file.getAbsolutePath());
if (!alpha && (header[16] != 24))
throw new IllegalStateException(file.getAbsolutePath());
if (alpha && (header[16] != 32))
throw new IllegalStateException(file.getAbsolutePath());
if ((header[17] & 15) != (alpha ? 8 : 0))
throw new IllegalStateException(file.getAbsolutePath());
BufferedImage dst = new BufferedImage(w, h, alpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
int[] pixels = ((DataBufferInt) dst.getRaster().getDataBuffer()).getData();
if (pixels.length != w * h)
throw new IllegalStateException(file.getAbsolutePath());
if (data.length != pixels.length * (alpha ? 4 : 3))
throw new IllegalStateException(file.getAbsolutePath());
if (alpha)
{
for (int i = 0, p = (pixels.length - 1) * 4; i < pixels.length; i++, p -= 4)
{
pixels[i] |= ((data[p + 0]) & 0xFF) << 0;
pixels[i] |= ((data[p + 1]) & 0xFF) << 8;
pixels[i] |= ((data[p + 2]) & 0xFF) << 16;
pixels[i] |= ((data[p + 3]) & 0xFF) << 24;
}
}
else
{
for (int i = 0, p = (pixels.length - 1) * 3; i < pixels.length; i++, p -= 3)
{
pixels[i] |= ((data[p + 0]) & 0xFF) << 0;
pixels[i] |= ((data[p + 1]) & 0xFF) << 8;
pixels[i] |= ((data[p + 2]) & 0xFF) << 16;
}
}
if ((header[17] >> 4) == 1)
{
// ok
}
else if ((header[17] >> 4) == 0)
{
// flip horizontally
for (int y = 0; y < h; y++)
{
int w2 = w / 2;
for (int x = 0; x < w2; x++)
{
int a = (y * w) + x;
int b = (y * w) + (w - 1 - x);
int t = pixels[a];
pixels[a] = pixels[b];
pixels[b] = t;
}
}
}
else
{
throw new UnsupportedOperationException(file.getAbsolutePath());
}
return dst;
}
public static void <span style="color:yellow">writeTGA</span>(BufferedImage src, File file) throws IOException
{
DataBuffer buffer = src.getRaster().getDataBuffer();
boolean alpha = src.getColorModel().hasAlpha();
byte[] data;
if (buffer instanceof DataBufferByte)
{
byte[] pixels = ((DataBufferByte) src.getRaster().getDataBuffer()).getData();
if (pixels.length != src.getWidth() * src.getHeight() * (alpha ? 4 : 3))
throw new IllegalStateException();
data = new byte[pixels.length];
for (int i = 0, p = pixels.length - 1; i < data.length; i++, p--)
{
data[i] = pixels[p];
}
}
else if (buffer instanceof DataBufferInt)
{
int[] pixels = ((DataBufferInt) src.getRaster().getDataBuffer()).getData();
if (pixels.length != src.getWidth() * src.getHeight())
throw new IllegalStateException();
data = new byte[pixels.length * (alpha ? 4 : 3)];
if (alpha)
{
for (int i = 0, p = pixels.length - 1; i < data.length; i += 4, p--)
{
data[i + 0] = (byte) ((pixels[p] >> 0) & 0xFF);
data[i + 1] = (byte) ((pixels[p] >> 8) & 0xFF);
data[i + 2] = (byte) ((pixels[p] >> 16) & 0xFF);
data[i + 3] = (byte) ((pixels[p] >> 24) & 0xFF);
}
}
else
{
for (int i = 0, p = pixels.length - 1; i < data.length; i += 3, p--)
{
data[i + 0] = (byte) ((pixels[p] >> 0) & 0xFF);
data[i + 1] = (byte) ((pixels[p] >> 8) & 0xFF);
data[i + 2] = (byte) ((pixels[p] >> 16) & 0xFF);
}
}
}
else
{
throw new UnsupportedOperationException();
}
byte[] header = new byte[18];
header[2] = 2; // uncompressed, true-color image
header[12] = (byte) ((src.getWidth() >> 0) & 0xFF);
header[13] = (byte) ((src.getWidth() >> 8) & 0xFF);
header[14] = (byte) ((src.getHeight() >> 0) & 0xFF);
header[15] = (byte) ((src.getHeight() >> 8) & 0xFF);
header[16] = (byte) (alpha ? 32 : 24); // bits per pixel
header[17] = (byte) ((alpha ? 8 : 0) | (1 << 4));
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.write(header);
raf.write(data);
raf.setLength(raf.getFilePointer()); // trim
raf.close();
}</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com6tag:blogger.com,1999:blog-1249179008376089851.post-56338312023274434182010-02-04T18:56:00.025+01:002010-12-22T01:16:03.860+01:00FastMath :: fast floor + ceilCalling Math.floor() simply takes too long. The problem with optimizing is that you can't simply cast the floatingpoint number to an integer, because that will result in invalid results for negative numbers. If we know our input values are in a specific range, we can safely add a certain (big) constant to the input, cast the guaranteed positive value to an integer and subtract the constant again.<br />
<br />
The code is ~9x faster than Math.floor(). Replacing the doubles with floats makes it faster, but the results are rather... random, so don't.<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style="color:yellow">FastMath</span>
{
private static final int BIG_ENOUGH_INT = 16 * 1024;
private static final double BIG_ENOUGH_FLOOR = BIG_ENOUGH_INT + 0.0000;
private static final double BIG_ENOUGH_ROUND = BIG_ENOUGH_INT + 0.5000;
private static final double BIG_ENOUGH_CEIL = BIG_ENOUGH_INT + 0.9999;
public static int <span style="color:yellow">fastFloor</span>(float x) {
return (int) (x + BIG_ENOUGH_FLOOR) - BIG_ENOUGH_INT;
}
public static int <span style="color:yellow">fastRound</span>(float x) {
return (int) (x + BIG_ENOUGH_ROUND) - BIG_ENOUGH_INT;
}
public static int <span style="color:yellow">fastCeil</span>(float x) {
return (int) (x + BIG_ENOUGH_CEIL) - BIG_ENOUGH_INT;
}
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com2tag:blogger.com,1999:blog-1249179008376089851.post-53371005211183675742009-10-26T15:55:00.007+01:002009-10-26T17:31:00.921+01:00Image :: read size of JPG/PNG/BMP/GIFIf you would believe Google, everybody is searching for code that reads the dimensions of an image,without loading all those colorful pixels. Oh well, without further ado....<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style="color:yellow">ImageUtil</span>
{
public static Dimension <span style="color:yellow">getImageDimension</span>(File file) throws IOException
{
return ImageUtil.getImageDimension(new FileInputStream(file));
}
public static Dimension <span style="color:yellow">getImageDimension</span>(InputStream in) throws IOException
{
DataInputStream dis = new DataInputStream(in);
try
{
int header = dis.readUnsignedShort();
if (header == 0x8950)
{
<span style="color:#00C000">// PNG</span>
dis.readFully(new byte[(8 - 2) + 4 + 4]); // thanks Abuse
return new Dimension(dis.readInt(), dis.readInt());
}
if (header == 0xffd8)
{
<span style="color:#00C000">// JPG (see below)</span>
}
else if (header == 0x424D)
{
<span style="color:#00C000">// BMP</span>
dis.readFully(new byte[16]);
int w = dis.read() | (dis.read() << 8) | (dis.read() << 16) | (dis.read() << 24);
int h = dis.read() | (dis.read() << 8) | (dis.read() << 16) | (dis.read() << 24);
return new Dimension(w, h);
}
else if (header == (('G' << 8) | ('I' << 0))) // GIF
{
<span style="color:#00C000">// GIF</span>
dis.readFully(new byte[4]);
int w = dis.read() | (dis.read() << 8);
int h = dis.read() | (dis.read() << 8);
return new Dimension(w, h);
}
else
{
throw new IllegalStateException("unexpected header: " + Integer.toHexString(header));
}
while (true) <span style="color:#00C000">// JPG is not so straight forward</span>
{
int marker = dis.readUnsignedShort();
switch (marker)
{
case 0xffd8: // SOI
case 0xffd0: // RST0
case 0xffd1: // RST1
case 0xffd2: // RST2
case 0xffd3: // RST3
case 0xffd4: // RST4
case 0xffd5: // RST5
case 0xffd6: // RST6
case 0xffd7: // RST7
case 0xffd9: // EOI
break;
case 0xffdd: // DRI
dis.readUnsignedShort();
break;
case 0xffe0: // APP0
case 0xffe1: // APP1
case 0xffe2: // APP2
case 0xffe3: // APP3
case 0xffe4: // APP4
case 0xffe5: // APP5
case 0xffe6: // APP6
case 0xffe7: // APP7
case 0xffe8: // APP8
case 0xffe9: // APP9
case 0xffea: // APPa
case 0xffeb: // APPb
case 0xffec: // APPc
case 0xffed: // APPd
case 0xffee: // APPe
case 0xffef: // APPf
case 0xfffe: // COM
case 0xffdb: // DQT
case 0xffc4: // DHT
case 0xffda: // SOS
dis.readFully(new byte[dis.readUnsignedShort() - 2]);
break;
case 0xffc0: // SOF0
case 0xffc2: // SOF2
dis.readUnsignedShort();
dis.readByte();
int height = dis.readUnsignedShort();
int width = dis.readUnsignedShort();
return new Dimension(width, height);
default:
throw new IllegalStateException("invalid jpg marker: " + Integer.toHexString(marker));
}
}
}
finally
{
dis.close();
}
}
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-45393884274848803352009-08-30T02:18:00.005+02:002009-08-30T02:21:45.719+02:00Graycode :: for analog stuff!THIS WAS FUN.<br />
<br />
Proved the math teacher wrong.<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style="color:yellow">Graycode</span>
{
public static int <span style="color:yellow">normalToGray</span>(int orig)
{
int gray = 0;
for (int n = 0; n < 31; n++)
gray |= (((orig + (1 << n)) >> (n + 1)) & 1) << n;
return gray;
}
public static int <span style="color:yellow">grayToNormal</span>(int gray)
{
int parity = 0;
int norm = 0;
for (int pos = 31; pos >= 0; pos--)
norm = (norm << 1) | (parity ^= ((gray >> pos) & 1));
return norm;
}
public static int <span style="color:yellow">changedBitInNextGrayCode</span>(int gray)
{
for (int i = 0; i < 31; i++)
if (parity(gray >> i) == 0)
return i + 1;
return -1;
}
private static int parity(int v)
{
int c = 0;
for (int i = 0; i < 31; i++)
c ^= (v & (1 << i)) >> i;
return c;
}
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-77934291290861754682009-08-30T02:02:00.003+02:002010-01-27T14:40:31.526+01:00Set :: binary operationsEverybody should have these. It's just a drag to rewrite it time and time again.<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style="color:yellow">SetUtil</span>
{
public static <T> Set<T> <span style="color:yellow">and</span>(Set<T> a, Set<T> b)
{
Set<T> c = new HashSet<T>();
// c.addAll(SetUtil.or(a, b));
// c.removeAll(SetUtil.xor(a, b));
c.addAll(a);
c.retainAll(b);
return c;
}
public static <T> Set<T> <span style="color:yellow">or</span>(Set<T> a, Set<T> b)
{
Set<T> c = new HashSet<T>();
c.addAll(a);
c.addAll(b);
return c;
}
public static <T> Set<T> <span style="color:yellow">xor</span>(Set<T> a, Set<T> b)
{
Set<T> a_minus_b = new HashSet<T>();
a_minus_b.addAll(a);
a_minus_b.removeAll(b);
Set<T> b_minus_a = new HashSet<T>();
b_minus_a.addAll(b);
b_minus_a.removeAll(a);
Set<T> c = new HashSet<T>();
c.addAll(a_minus_b);
c.addAll(b_minus_a);
return c;
}
}</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com2tag:blogger.com,1999:blog-1249179008376089851.post-90805753608866558302009-08-30T01:50:00.003+02:002009-08-30T01:59:01.620+02:00ArraySorter :: in-place, pre-calcThe point of this class is to pre-calculate the sort-order in advance, so that we're not comparing objects all the time, and then do a proper/fast sort. In-place, meaning not creating an auxillary array, like Arrays.sort() does. This class is thread safe, as in... synchronized.<br />
<br />
The 'chunked' version uses 'bucket sort' and then does a regular sort on the buckets, and merges the result back in the original array.<br />
<br />
The Sortable interface is expected to calculate the sort-index in calcSortIndex() and return that value each and every time in getSortIndex()<br />
<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public interface <span style="color:yellow">Sortable</span>
{
public void <span style="color:yellow">calcSortIndex</span>();
public int <span style="color:yellow">getSortIndex</span>();
}
public class <span style="color:yellow">ArraySorter</span>
{
public static final synchronized void <span style="color:yellow">fineSort</span>(Sortable[] p, int off, int len)
{
for (int i = 0; i < len; i++)
p[off + i].calcSortIndex();
ArraySorter.doFineSortImpl(p, off, len);
}
public static final synchronized void <span style="color:yellow">chunkedSort</span>(Sortable[] p, int off, int len, int min, int max, int chunks)
{
for (int i = 0; i < len; i++)
p[off + i].calcSortIndex();
ArraySorter.doChunkedSortImpl(p, off, len, min, max, chunks);
}
/**
* FINE
*/
private static final void doFineSortImpl(Sortable[] p, int off, int len)
{
if (len < 7)
{
for (int i = off; i < len + off; i++)
for (int j = i; j > off && p[j - 1].getSortIndex() > p[j].getSortIndex(); j--)
swap(p, j, j - 1);
return;
}
int m = off + (len >> 1);
if (len > 7)
{
int l = off;
int n = off + len - 1;
if (len > 40)
{
int s = len >>> 3;
l = med3(p, l, l + s, l + 2 * s);
m = med3(p, m - s, m, m + s);
n = med3(p, n - 2 * s, n - s, n);
}
m = med3(p, l, m, n);
}
int v = p[m].getSortIndex();
int a = off;
int b = a;
int c = off + len - 1;
int d = c;
while (true)
{
while (b <= c && p[b].getSortIndex() <= v)
{
if (p[b].getSortIndex() == v)
swap(p, a++, b);
b++;
}
while (c >= b && p[c].getSortIndex() >= v)
{
if (p[c].getSortIndex() == v)
swap(p, c, d--);
c--;
}
if (b > c)
break;
swap(p, b++, c--);
}
int s, n = off + len;
s = Math.min(a - off, b - a);
swapRange(p, off, b - s, s);
s = Math.min(d - c, n - d - 1);
swapRange(p, b, n - s, s);
if ((s = b - a) > 1)
doFineSortImpl(p, off, s);
if ((s = d - c) > 1)
doFineSortImpl(p, n - s, s);
}
private static final void swap(Sortable[] p, int a, int b)
{
Sortable q = p[a];
p[a] = p[b];
p[b] = q;
}
private static final void swapRange(Sortable[] p, int a, int b, int n)
{
Sortable q;
for (int i = 0; i < n; i++, a++, b++)
{
q = p[a];
p[a] = p[b];
p[b] = q;
}
}
private static final int med3(Sortable[] p, int a, int b, int c)
{
int a0 = p[a].getSortIndex();
int b0 = p[b].getSortIndex();
int c0 = p[c].getSortIndex();
return (a0 < b0 ? (b0 < c0 ? b : (a0 < c0 ? c : a)) : (b0 > c0 ? b : (a0 > c0 ? c : a)));
}
/**
* CHUNKED
*/
private static List<Sortable> less = new ArrayList<Sortable>();
private static List<Sortable>[] parts = new List[0];
private static List<Sortable> more = new ArrayList<Sortable>();
private static Sortable[] tmp = new Sortable[64];
private static final void doChunkedSortImpl(Sortable[] p, int off, int len, int min, int max, int chunks)
{
if (parts.length < chunks)
{
parts = new List[chunks];
for (int i = 0; i < parts.length; i++)
parts[i] = new ArrayList<Sortable>();
}
// sort-index is already calculated here
// distribute sortables over lists
for (int i = 0; i < len; i++)
{
Sortable s = p[off + i];
int index = s.getSortIndex();
if (index < min)
less.add(s);
else if (index >= max)
more.add(s);
else
{
float percent = (float) (s.getSortIndex() - min) / (max - min);
int arrayIndex = (int) (percent * chunks);
parts[arrayIndex].add(s);
}
}
// sort lists, overwrite P
tmp = less.toArray(tmp);
ArraySorter.doFineSortImpl(tmp, 0, less.size());
System.arraycopy(tmp, 0, p, off, less.size());
off += less.size();
for (int i = 0; i < chunks; i++)
{
if (parts[i].isEmpty())
continue;
tmp = parts[i].toArray(tmp);
ArraySorter.doFineSortImpl(tmp, 0, parts[i].size());
System.arraycopy(tmp, 0, p, off, parts[i].size());
off += parts[i].size();
}
tmp = more.toArray(tmp);
ArraySorter.doFineSortImpl(tmp, 0, more.size());
System.arraycopy(tmp, 0, p, off, more.size());
off += more.size();
// clear up all references
less.clear();
for (int i = 0; i < chunks; i++)
parts[i].clear();
more.clear();
}
}</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-33871352677026486052009-08-30T01:27:00.002+02:002009-08-30T01:33:43.285+02:00Histogram :: map-based<div class="code" onClick="this.style.height='auto';"><pre>public class <span style="color:yellow">Histogram</span><T>
{
private final Map<T, Integer> map;
public <span style="color:yellow">Histogram</span>()
{
this.map = new HashMap<T, Integer>();
}
public int <span style="color:yellow">put</span>(T t)
{
Integer count = this.map.get(t);
if (count == null)
count = Integer.valueOf(0);
count = Integer.valueOf(count.intValue() + 1);
this.map.put(t, count);
return count.intValue();
}
public int <span style="color:yellow">get</span>(T t)
{
Integer count = this.map.get(t);
if (count == null)
return 0;
return count.intValue();
}
public List<T> <span style="color:yellow">topKeys</span>(int amount)
{
//int threshold = Integer.MIN_VALUE;
List<T> kList = new ArrayList<T>();
List<integer> vList = new ArrayList<integer>();
for (Entry<T, Integer> e : this.map.entrySet())
{
//int val = e.getValue().intValue();
//if (val <= threshold)
{
//continue;
}
// when full, remove lowest
if (vList.size() == amount)
{
int index = indexOfMin(vList);
kList.remove(index);
vList.remove(index);
}
kList.add(e.getKey());
vList.add(e.getValue());
}
// bubble sort
for (int i = 0; i < vList.size(); i++)
{
for (int k = i + 1; k < vList.size(); k++)
{
if (vList.get(i).intValue() >= vList.get(k).intValue())
{
continue;
}
Integer a = vList.get(i);
Integer b = vList.get(k);
vList.set(k, a);
vList.set(i, b);
T x = kList.get(i);
T y = kList.get(k);
kList.set(k, x);
kList.set(i, y);
}
}
return kList;
}
public int <span style="color:yellow">remove</span>(T t)
{
Integer count = this.map.get(t);
if (count == null)
throw new NoSuchElementException(String.valueOf(t));
if (count.intValue() == 0)
throw new IllegalStateException("cannot remove");
count = Integer.valueOf(count.intValue() - 1);
if (count.intValue() == 0)
this.map.remove(t);
else
this.map.put(t, count);
return count.intValue();
}
public int <span style="color:yellow">reset</span>(T t)
{
Integer count = this.map.remove(t);
if (count == null)
return 0;
return count.intValue();
}
public int <span style="color:yellow">set</span>(T t, int val)
{
if (val < 0)
throw new IllegalArgumentException();
Integer count = this.map.get(t);
if (count == null)
{
if (val != 0)
this.map.put(t, Integer.valueOf(val));
return 0;
}
if (val == 0)
return this.map.remove(t).intValue();
return this.map.put(t, Integer.valueOf(val)).intValue();
}
public Set<T> <span style="color:yellow">keys</span>()
{
return this.map.keySet();
}
private static int indexOfMin(List<Integer> ids)
{
int minAt = 0;
for (int i = 1; i < ids.size(); i++)
if (ids.get(i).intValue() < ids.get(minAt).intValue())
minAt = i;
return minAt;
}
}</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-48938582250019152832009-08-30T01:09:00.005+02:002009-08-30T01:11:13.102+02:00Unsafe :: Pointers<div class="code" onClick="this.style.height='auto';"><pre> private final static Unsafe unsafe;
private static long addressOffset;
private static long positionOffset;
private static long limitOffset;
private static long capacityOffset;
public static final long WORD_SIZE_BITS, HEADER_SIZE;
public static final long BYTE_ARRAY_BASE_OFFSET;
public static final long SHORT_ARRAY_BASE_OFFSET;
public static final long INT_ARRAY_BASE_OFFSET;
public static final long LONG_ARRAY_BASE_OFFSET;
public static final long FLOAT_ARRAY_BASE_OFFSET;
public static final long DOUBLE_ARRAY_BASE_OFFSET;
public static final long OBJECT_ARRAY_BASE_OFFSET;
private static final Object[] holder = new Object[1];
static
{
try
{
ByteBuffer buffer = ByteBuffer.allocateDirect(1);
Field unsafeField = buffer.getClass().getDeclaredField("unsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe) unsafeField.get(buffer);
unsafeField.setAccessible(false);
addressOffset = getObjectFieldOffset(buffer, "address");
positionOffset = getObjectFieldOffset(buffer, "position");
limitOffset = getObjectFieldOffset(buffer, "limit");
capacityOffset = getObjectFieldOffset(buffer, "capacity");
buffer.flip();
buffer = null;
}
catch (Exception exc)
{
exc.printStackTrace();
throw new InternalError();
}
WORD_SIZE_BITS = unsafe.addressSize() * 8;
if (WORD_SIZE_BITS != 32 && WORD_SIZE_BITS != 64)
throw new IllegalStateException("WORD_SIZE: " + WORD_SIZE_BITS);
HEADER_SIZE = WORD_SIZE_BITS / 8 * 2;
BYTE_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new byte[4].getClass());
SHORT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new short[4].getClass());
INT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new int[4].getClass());
LONG_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new long[4].getClass());
FLOAT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new float[4].getClass());
DOUBLE_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new double[4].getClass());
OBJECT_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(new Object[4].getClass());
}
public static final long getObjectAddress(Object obj)
{
holder[0] = obj;
if (WORD_SIZE_BITS == 32)
return unsafe.getInt(holder, OBJECT_ARRAY_BASE_OFFSET);
if (WORD_SIZE_BITS == 64)
return unsafe.getLong(holder, OBJECT_ARRAY_BASE_OFFSET);
throw new IllegalStateException();
}
public static final Object getObjectAtAddress(long addr)
{
if (WORD_SIZE_BITS == 32)
unsafe.putInt(holder, OBJECT_ARRAY_BASE_OFFSET, (int) (addr & 0xFFFFFFFF));
if (WORD_SIZE_BITS == 64)
unsafe.putLong(holder, OBJECT_ARRAY_BASE_OFFSET, addr);
return holder[0];
}
public static final FloatBuffer createFloatBufferAt(long pntr, int len)
{
Native.zeroOut(pntr, len << 2);
FloatBuffer buf = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asFloatBuffer();
NativeHacks.setBufferProperties(buf, pntr, 0, len, len);
buf.clear();
return buf;
}
public static final float[] createFloatArrayAt(long pntr, int len)
{
NativeHacks.copyObjectHeaderTo(new float[0], pntr);
// write length
unsafe.putInt(pntr + HEADER_SIZE, len);
return (float[]) NativeHacks.fakePointerAsObject(pntr);
}
public static final void copyObjectHeaderTo(Object obj, long pntr)
{
for (int i = 0; i < HEADER_SIZE; i++)
unsafe.putByte(pntr + i, unsafe.getByte(obj, (long) i));
}
public static final Object fakePointerAsObject(long addr)
{
if (WORD_SIZE_BITS == 32)
unsafe.putInt(holder, OBJECT_ARRAY_BASE_OFFSET, (int) (addr & 0xFFFFFFFF));
else if (WORD_SIZE_BITS == 64)
unsafe.putLong(holder, OBJECT_ARRAY_BASE_OFFSET, addr);
else
throw new IllegalStateException();
return holder[0];
}</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-19686051335031560422009-08-25T03:48:00.006+02:002009-08-25T04:00:27.312+02:00Thread :: monitor CPU usageIf you want to monitor CPU usage, per thread, things couldn't get easier!<br />
<br />
<pre style="color:#808080">ThreadGroupMonitor gmonitor = new ThreadGroupMonitor();
while(true)
{
gmonitor.poll();
for(ThreadMonitor tmon: gmonitor.getAliveThreadMonitors())
{
double avg = tmon.getCpuTimeStats().avg(); // avg of last polls
double avg = tmon.getCpuTimeStats().avg(3); // avg of last 3 polls
double avg = tmon.getCpuTimeStats().avg(5); // avg of last 5 polls
}
for(ThreadMonitor tmon: gmonitor.getDeadThreadMonitors())
{
double total = tmon.getTotalCpuTime();
}
// sleep for a bit
}
</pre><br />
Even dead threads...<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class ThreadMonitor
{
private static ThreadMXBean tmxb;
static
{
tmxb = ManagementFactory.getThreadMXBean();
tmxb.setThreadCpuTimeEnabled(true);
}
//
private long tid;
private CyclicUsageHistory cpuTimeHistory;
private CyclicUsageHistory userTimeHistory;
private CyclicUsageHistory cpuUsageHistory;
private CyclicUsageHistory userUsageHistory;
public ThreadMonitor(long tid, int slots)
{
this.tid = tid;
this.cpuTimeHistory = new CyclicUsageHistory(slots);
this.userTimeHistory = new CyclicUsageHistory(slots);
this.cpuUsageHistory = new CyclicUsageHistory(slots);
this.userUsageHistory = new CyclicUsageHistory(slots);
}
public long getId()
{
return tid;
}
private double totalCpuTime;
private double totalUserTime;
public double getTotalCpuTime()
{
return this.totalCpuTime;
}
public double getTotalUserTime()
{
return this.totalUserTime;
}
public void poll()
{
// a time of -1 means not alive
double cpuTime = tmxb.getThreadCpuTime(this.tid) / 1000000000.0;
this.totalCpuTime += cpuTime < 0 ? 0 : cpuTime;
cpuTimeHistory.log(cpuTime < 0 ? 0 : cpuTime);
cpuUsageHistory.log(cpuTimeHistory.previous(0) - cpuTimeHistory.previous(1));
double userTime = tmxb.getThreadUserTime(this.tid) / 1000000000.0;
this.totalUserTime += userTime < 0 ? 0 : userTime;
userTimeHistory.log(userTime < 0 ? 0 : userTime);
userUsageHistory.log(userTimeHistory.previous(0) - userTimeHistory.previous(1));
}
public CyclicUsageHistory getCpuTimeStats()
{
return this.cpuUsageHistory;
}
public CyclicUsageHistory getUserTimeStats()
{
return this.userUsageHistory;
}
}
public class ThreadGroupMonitor
{
public ThreadGroupMonitor()
{
this(Thread.currentThread().getThreadGroup());
}
public ThreadGroupMonitor(ThreadGroup group)
{
this.group = group;
this.lastThreadIds = new long[0];
this.aliveId2mon = new HashMap<Long, ThreadMonitor>();
this.deadId2mon = new HashMap<Long, ThreadMonitor>();
}
//
private final ThreadGroup group;
public ThreadGroup getThreadGroup()
{
return group;
}
//
private int totalDeadThreadCount = 0;
public synchronized int getTotalDeadThreadCount()
{
return this.totalDeadThreadCount;
}
//
private int regularThreadCount = 0;
public synchronized int getRegularThreadCount()
{
return this.regularThreadCount;
}
//
private int deamonThreadCount = 0;
public synchronized int getDeamonThreadCount()
{
return this.deamonThreadCount;
}
//
private static final int default_slots = 3600;
private long[] lastThreadIds;
private Map<Long, ThreadMonitor> aliveId2mon;
private Map<Long, ThreadMonitor> deadId2mon;
public synchronized void poll()
{
Thread[] threads = this.findAllThreads();
long[] currThreadIds = this.findAllThreadIds(threads);
long[] newIds = this.findNewThreadIds(this.lastThreadIds, currThreadIds);
long[] deadIds = this.findDeadThreadIds(this.lastThreadIds, currThreadIds);
this.totalDeadThreadCount += deadIds.length;
for (long newId : newIds)
aliveId2mon.put(Long.valueOf(newId), new ThreadMonitor(newId, default_slots));
for (long deadId : deadIds)
deadId2mon.put(Long.valueOf(deadId), aliveId2mon.remove(Long.valueOf(deadId)));
for (ThreadMonitor mon : aliveId2mon.values())
mon.poll();
for (ThreadMonitor mon : deadId2mon.values())
mon.poll();
this.analyzeThreads(threads);
this.lastThreadIds = currThreadIds;
}
public synchronized double getAvgCpuTimeStats(int pollCount)
{
double sum = 0.0;
for (ThreadMonitor mon : aliveId2mon.values())
sum += mon.getCpuTimeStats().avg(pollCount);
return sum;
}
public synchronized double getAvgUserTimeStats(int pollCount)
{
double sum = 0.0;
for (ThreadMonitor mon : aliveId2mon.values())
sum += mon.getUserTimeStats().avg(pollCount);
return sum;
}
public Collection<threadmonitor> getAliveThreadMonitors()
{
return Collections.unmodifiableCollection(this.aliveId2mon.values());
}
public Collection<threadmonitor> getDeadThreadMonitors()
{
return Collections.unmodifiableCollection(this.deadId2mon.values());
}
private void analyzeThreads(Thread[] threads)
{
int deamonThreadCount = 0;
int regularThreadCount = 0;
for (Thread thread : threads)
{
if (!thread.isAlive())
continue;
if (thread.isDaemon())
deamonThreadCount++;
else
regularThreadCount++;
}
this.deamonThreadCount = deamonThreadCount;
this.regularThreadCount = regularThreadCount;
}
public Thread[] findAllThreads()
{
int threadCount;
Thread[] tempThreadArray = new Thread[8];
while ((threadCount = this.group.enumerate(tempThreadArray)) == tempThreadArray.length)
tempThreadArray = ArrayUtil.growTo(tempThreadArray, tempThreadArray.length * 2);
Thread[] threadArray = new Thread[threadCount];
System.arraycopy(tempThreadArray, 0, threadArray, 0, threadCount);
return threadArray;
}
private long[] findAllThreadIds(Thread[] threads)
{
long[] allThreadIds = new long[threads.length];
for (int i = 0; i < allThreadIds.length; i++)
allThreadIds[i] = threads[i].getId();
return allThreadIds;
}
private long[] findNewThreadIds(long[] lastThreads, long[] currThreads)
{
long[] newThreadIds = new long[currThreads.length];
int newThreadIndex = 0;
outer: for (int i = 0; i < currThreads.length; i++)
{
for (int k = 0; k < lastThreads.length; k++)
if (currThreads[i] == lastThreads[k])
continue outer;
newThreadIds[newThreadIndex++] = currThreads[i];
}
long[] ids = new long[newThreadIndex];
System.arraycopy(newThreadIds, 0, ids, 0, newThreadIndex);
return ids;
}
private long[] findDeadThreadIds(long[] lastThreads, long[] currThreads)
{
long[] deadThreadIds = new long[lastThreads.length];
int deadThreadIndex = 0;
outer: for (int i = 0; i < lastThreads.length; i++)
{
for (int k = 0; k < currThreads.length; k++)
if (lastThreads[i] == currThreads[k])
continue outer;
deadThreadIds[deadThreadIndex++] = lastThreads[i];
}
long[] ids = new long[deadThreadIndex];
System.arraycopy(deadThreadIds, 0, ids, 0, deadThreadIndex);
return ids;
}
}
public class CyclicUsageHistory
{
private final double[] values;
public CyclicUsageHistory(int slots)
{
this.values = new double[slots];
}
private int addIndex;
public void log(double value)
{
this.values[this.addIndex++ % this.values.length] = value;
}
//
public double previous()
{
return this.previous(0);
}
public double previous(int age)
{
int len = this.values.length;
return this.values[(((this.addIndex - 1 - age) % len) + len) % len];
}
//
public double max()
{
return this.max(this.values.length);
}
public double max(int slots)
{
int count = Math.min(this.values.length, Math.min(slots, this.addIndex - 1));
double max = 0.0;
for (int i = 0; i < count; i++)
if (this.previous(i) > max)
max = this.previous(i);
return max;
}
//
public double sum()
{
return this.sum(this.values.length);
}
public double sum(int slots)
{
int count = Math.min(this.values.length, Math.min(slots, this.addIndex - 1));
double sum = 0.0;
for (int i = 0; i < count; i++)
sum += this.previous(i);
return sum;
}
//
public double avg()
{
return this.avg(this.values.length);
}
public double avg(int slots)
{
int count = Math.min(this.values.length, Math.min(slots, this.addIndex - 1));
return this.sum(slots) / count;
}
//
public double nom()
{
return this.nom(this.values.length);
}
public double nom(int slots)
{
int count = Math.min(this.values.length, Math.min(slots, this.addIndex - 1));
if (count == 0)
return 0.0;
double[] arr = new double[count];
for (int i = 0; i < count; i++)
arr[i] = this.previous(i);
Arrays.sort(arr);
return arr[arr.length / 2];
}
}
</pre>
</div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0tag:blogger.com,1999:blog-1249179008376089851.post-55970443020089271852009-08-25T02:00:00.026+02:002009-08-25T03:03:35.844+02:00Swappable Jar ClassLoaderSo you have this pluggable stuff, and you want to support realtime loading of new classes. That's not so hard, but what if old classes try to load old classes, and the JAR has been replaced? Right! Native crash. Blehh.<br />
<br />
With DynamicJarClassLoader you can do this:<br />
<pre style="color:#808080">ClassLoader loader = new DynamicJarClassLoader(parent, file);
Class<?> clzz1 = loader.loadClass("my.package.MyClass");
// ...
// JAR is replaced
// ...
Class<?> clzz2 = loader.loadClass("my.package.MyClass");
// ...
clzz1.newInstance(); // loads old "other.package.OtherClass"
clzz2.newInstance(); // loads new "other.package.OtherClass"
</pre><br />
Let's say MyClass will also load "other.package.OtherClass" sooner or later. The classloader will keep that data loaded, so that <tt>clzz1</tt> and <tt>clzz2</tt> have access to their own version of OtherClass.<br />
<br />
Fancy stuff!<br />
<br />
<div class="code" onClick="this.style.height='auto';"><pre>public class <span style="color:yellow">DynamicJarClassLoader</span> extends DynamicClassLoader
{
private final File jar;
private long prevLastModified;
private final Set<string> resourceNames;
public <span style="color:yellow">DynamicJarClassLoader</span>(ClassLoader parent, File jar)
{
super(parent);
this.jar = jar;
this.prevLastModified = -1L;
this.resourceNames = new HashSet<string>();
this.ensureLatestClassLoader();
}
public File <span style="color:yellow">getJar</span>()
{
return this.jar;
}
public Set<string> <span style="color:yellow">getResourceNames</span>()
{
return Collections.unmodifiableSet(this.resourceNames);
}
private static final long file_idle_timeout = 3 * 1000;
@Override
public boolean <span style="color:yellow">isUpdated</span>()
{
long jarLastModified = this.jar.lastModified();
boolean willBeUpdated = jarLastModified != this.prevLastModified;
if (willBeUpdated && this.prevLastModified != -1L)
{
if (this.jar.lastModified() > System.currentTimeMillis() - file_idle_timeout)
{
Logger.notification("Pending new JAR file: %s", this.jar.getAbsolutePath());
willBeUpdated = false;
}
}
if (willBeUpdated)
{
Logger.notification("Loading new JAR file: %s", this.jar.getAbsolutePath());
this.prevLastModified = jarLastModified;
}
return willBeUpdated;
}
@Override
public ClassLoader <span style="color:yellow">createClassLoader</span>()
{
final Map<String, byte[]> resources;
this.resourceNames.clear();
try
{
resources = this.loadCompleteJarFile();
}
catch (IOException exc)
{
throw new IllegalStateException("Failed to load JAR file: " + this.jar.getAbsolutePath(), exc);
}
this.resourceNames.addAll(resources.keySet());
ClassLoader loader = new BytesClassLoader(this.getParent())
{
@Override
public byte[] readBytes(String name)
{
return resources.get(name);
}
};
return loader;
}
private final Map<String, byte[]> loadCompleteJarFile() throws IOException
{
Map<String, byte[]> map = new HashMap<String, byte[]>();
JarFile jf = new JarFile(this.jar);
Enumeration<jarentry> entries = jf.entries();
while (entries.hasMoreElements())
{
byte[] buf = null;
JarEntry entry = entries.nextElement();
if (!entry.isDirectory())
{
buf = new byte[(int) entry.getSize()];
InputStream in = jf.getInputStream(entry);
int off = 0;
while (off != buf.length)
{
int justRead = in.read(buf, off, buf.length - off);
if (justRead == -1)
throw new EOFException("Could not fully read JAR file entry: " + entry.getName());
off += justRead;
}
in.close();
}
map.put(entry.getName(), buf);
}
jf.close();
return map;
}
}
public abstract class DynamicClassLoader extends ClassLoader
{
private ClassLoader currentLoader;
public DynamicClassLoader(ClassLoader parent)
{
super(parent);
this.currentLoader = null;
}
//
public abstract boolean isUpdated();
public abstract ClassLoader createClassLoader();
//
@Override
public URL <span style="color:yellow">getResource</span>(String name)
{
this.ensureLatestClassLoader();
URL url = this.getParent().getResource(name);
if (url != null)
return url;
return this.currentLoader.getResource(name);
}
@Override
public Enumeration<url> <span style="color:yellow">getResources</span>(String name) throws IOException
{
this.ensureLatestClassLoader();
Enumeration<url> urls = this.getParent().getResources(name);
if (urls != null)
return urls;
return this.currentLoader.getResources(name);
}
@Override
public InputStream <span style="color:yellow">getResourceAsStream</span>(String name)
{
this.ensureLatestClassLoader();
InputStream in = this.getParent().getResourceAsStream(name);
if (in != null)
return in;
return this.currentLoader.getResourceAsStream(name);
}
public synchronized Class< ? > <span style="color:yellow">loadClass</span>(String name) throws ClassNotFoundException
{
this.ensureLatestClassLoader();
return this.currentLoader.loadClass(name);
}
//
private long lastChecked;
private long minCheckInterval = 0;
public void <span style="color:yellow">setMinCheckInterval</span>(long minCheckInterval)
{
this.minCheckInterval = minCheckInterval;
}
public final boolean <span style="color:yellow">checkForUpdate</span>()
{
long now = System.currentTimeMillis();
long elapsed = now - this.lastChecked;
if (elapsed < this.minCheckInterval)
{
// if we checked less than N ms ago,
// just assume the loader is not updated.
// otherwise we put a major strain on
// the file system (?) for no real gain
return false;
}
this.lastChecked = now;
return this.isUpdated();
}
//
public void <span style="color:yellow">ensureLatestClassLoader</span>()
{
if (this.checkForUpdate())
{
this.replaceClassLoader();
}
}
protected void replaceClassLoader()
{
this.currentLoader = this.createClassLoader();
// protected, so do stuff, if you wish
}
}
public abstract class BytesClassLoader extends ClassLoader
{
public BytesClassLoader(ClassLoader parent)
{
super(parent);
}
protected abstract byte[] readBytes(String path);
public synchronized Class< ? > loadClass(String name) throws ClassNotFoundException
{
Class< ? > found = this.findLoadedClass(name);
if (found != null)
{
return found;
}
String path = name.replace('.', '/').concat(".class");
byte[] raw = this.readBytes(path);
if (raw == null)
{
return this.getParent().loadClass(name);
}
return super.defineClass(name, raw, 0, raw.length);
}
@Override
public InputStream getResourceAsStream(String path)
{
byte[] raw = this.readBytes(path);
if (raw == null)
return null;
return new ByteArrayInputStream(raw);
}
@Override
public URL getResource(String name)
{
// who uses this anyway?
throw new UnsupportedOperationException();
}
@Override
public Enumeration<url> getResources(String name) throws IOException
{
// who uses this anyway?
throw new UnsupportedOperationException();
}
}
</pre></div>Rivenhttp://www.blogger.com/profile/12170038557837985530noreply@blogger.com0