G
Guest
The Microsoft JVM seems to have a bug reading multiple URLs.
My test applet opens URLs in this sequence:
1. A URL that returns a basically empty HTML page
2. A URL that streams data for 2 seconds.
3. The same URL from step 1.
The problem is that #3 returns the tail end of #2's data instead of the
same data as #1, in spite of the fact that I close the InputStream,
call the garbage collector, and run the finalizers.
To see this in action, open
http://test.stream.wallst.com/JreBugTest.htm
and click the 'Go' button. Output will display in the applet and
also in the Java console.
This only occurs with Microsoft's JVM, not Sun's or others I've tried.
Am I missing something, or is there some other way to close a URLConnection?
Source code for the applet and isapi streaming dll are included in
the JAR file, and appended here as well:
---------- applet source ------------
import java.awt.*;
import java.applet.*;
import java.net.*;
import java.io.*;
public class datatest extends Applet
{
java.awt.Button button1 = new java.awt.Button();
StringBuffer m_s1 = new StringBuffer(),
m_s2 = new StringBuffer();
// Called when 'Go' button is pressed
class Listener implements java.awt.event.ActionListener {
public void actionPerformed(java.awt.event.ActionEvent event) {
m_s1.setLength(0); m_s2.setLength(0); repaint();
final String sBase = getCodeBase().getHost(), sFile = getCodeBase().getFile();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// Demonstrate different output from 2 calls to empty.htm
for (int i=0; i<2; ++i) {
System.gc(); System.runFinalization();
////////////////////////////////////////////////////////////////
// Get HTML data from empty.htm
////////////////////////////////////////////////////////////////
String sHtmlUrl = "http://" + sBase + sFile + "empty.htm";
System.out.println(getHtmlData(i==0 ? m_s1 : m_s2, sHtmlUrl));
////////////////////////////////////////////////////////////////
// Get partial stream data between 2 calls to empty.htm
////////////////////////////////////////////////////////////////
if (i == 0) {
String sStreamUrl = "http://" + sBase + sFile + "streamtest.dll";
StringBuffer s = new StringBuffer();
getHtmlData(s, sStreamUrl);
}
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
StringBuffer getHtmlData(StringBuffer sContent, String sUrl) {
URL url = null;
try { url = new URL(sUrl); }
catch (MalformedURLException e) { exceptionReport(e); }
URLConnection conn = null;
try { conn = url.openConnection(); }
catch (IOException e) { exceptionReport(e); }
java.io.InputStream in = null;
try {
in = conn.getInputStream();
int nextByte;
for (int i=0; i<15 && (nextByte = in.read()) != -1; ++i)
sContent.append((char)nextByte);
}
catch (IOException e) { exceptionReport(e); }
finally {
if (in != null)
try { in.close(); }
catch (IOException e) { exceptionReport(e); }
}
return sContent;
}
void exceptionReport(Throwable e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
public void init()
{
setLayout(null);
setSize(426,266);
button1.setLabel("Go");
add(button1);
button1.setBounds(36,12,60,24);
Listener lListener = new Listener();
button1.addActionListener(lListener);
}
public void paint(Graphics g) {
g.drawString("1st call to empty.htm: " + m_s1.toString(), 10, 120);
g.drawString("2nd call to empty.htm: " + m_s2.toString(), 10, 140);
g.drawString("Both should be: \"<html> <head> </head> <body>\"", 10, 160);
}
}
---------- isapi source ------------
#include "stdafx.h"
#include <stdio.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
pVer->dwExtensionVersion = HSE_VERSION;
strncpy(pVer->lpszExtensionDesc, "Data streaming ISA", HSE_MAX_EXT_DLL_NAME_LEN);
return TRUE;
}
BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
return TRUE;
}
void ProcessRequest(void* p)
{
EXTENSION_CONTROL_BLOCK* pECB = static_cast<EXTENSION_CONTROL_BLOCK*>(p);
// Send header
static const char szStatus[] = "200 OK",
szHeader[] = "Content-Type: application/octet-stream\r\n"
"Cache-Control: no-cache\r\n"
"Expires: Sat, 01 Jan 2000 00:00:00 GMT\r\n"
"\r\n";
static HSE_SEND_HEADER_EX_INFO header = { szStatus, szHeader, sizeof szStatus - 1, sizeof szHeader - 1, TRUE };
pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, (void*)&header, NULL, NULL);
char buffer[20];
for (int i=0; i<20; ++i)
{
sprintf(buffer, "%02d|", i);
DWORD dw = strlen(buffer);
pECB->WriteClient(pECB->ConnID, (void*)buffer, &dw, HSE_IO_SYNC);
Sleep(100);
}
pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL);
}
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK* pECB)
{
_beginthread(ProcessRequest, 0, pECB);
return HSE_STATUS_PENDING;
}
My test applet opens URLs in this sequence:
1. A URL that returns a basically empty HTML page
2. A URL that streams data for 2 seconds.
3. The same URL from step 1.
The problem is that #3 returns the tail end of #2's data instead of the
same data as #1, in spite of the fact that I close the InputStream,
call the garbage collector, and run the finalizers.
To see this in action, open
http://test.stream.wallst.com/JreBugTest.htm
and click the 'Go' button. Output will display in the applet and
also in the Java console.
This only occurs with Microsoft's JVM, not Sun's or others I've tried.
Am I missing something, or is there some other way to close a URLConnection?
Source code for the applet and isapi streaming dll are included in
the JAR file, and appended here as well:
---------- applet source ------------
import java.awt.*;
import java.applet.*;
import java.net.*;
import java.io.*;
public class datatest extends Applet
{
java.awt.Button button1 = new java.awt.Button();
StringBuffer m_s1 = new StringBuffer(),
m_s2 = new StringBuffer();
// Called when 'Go' button is pressed
class Listener implements java.awt.event.ActionListener {
public void actionPerformed(java.awt.event.ActionEvent event) {
m_s1.setLength(0); m_s2.setLength(0); repaint();
final String sBase = getCodeBase().getHost(), sFile = getCodeBase().getFile();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// Demonstrate different output from 2 calls to empty.htm
for (int i=0; i<2; ++i) {
System.gc(); System.runFinalization();
////////////////////////////////////////////////////////////////
// Get HTML data from empty.htm
////////////////////////////////////////////////////////////////
String sHtmlUrl = "http://" + sBase + sFile + "empty.htm";
System.out.println(getHtmlData(i==0 ? m_s1 : m_s2, sHtmlUrl));
////////////////////////////////////////////////////////////////
// Get partial stream data between 2 calls to empty.htm
////////////////////////////////////////////////////////////////
if (i == 0) {
String sStreamUrl = "http://" + sBase + sFile + "streamtest.dll";
StringBuffer s = new StringBuffer();
getHtmlData(s, sStreamUrl);
}
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
StringBuffer getHtmlData(StringBuffer sContent, String sUrl) {
URL url = null;
try { url = new URL(sUrl); }
catch (MalformedURLException e) { exceptionReport(e); }
URLConnection conn = null;
try { conn = url.openConnection(); }
catch (IOException e) { exceptionReport(e); }
java.io.InputStream in = null;
try {
in = conn.getInputStream();
int nextByte;
for (int i=0; i<15 && (nextByte = in.read()) != -1; ++i)
sContent.append((char)nextByte);
}
catch (IOException e) { exceptionReport(e); }
finally {
if (in != null)
try { in.close(); }
catch (IOException e) { exceptionReport(e); }
}
return sContent;
}
void exceptionReport(Throwable e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
}
public void init()
{
setLayout(null);
setSize(426,266);
button1.setLabel("Go");
add(button1);
button1.setBounds(36,12,60,24);
Listener lListener = new Listener();
button1.addActionListener(lListener);
}
public void paint(Graphics g) {
g.drawString("1st call to empty.htm: " + m_s1.toString(), 10, 120);
g.drawString("2nd call to empty.htm: " + m_s2.toString(), 10, 140);
g.drawString("Both should be: \"<html> <head> </head> <body>\"", 10, 160);
}
}
---------- isapi source ------------
#include "stdafx.h"
#include <stdio.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
pVer->dwExtensionVersion = HSE_VERSION;
strncpy(pVer->lpszExtensionDesc, "Data streaming ISA", HSE_MAX_EXT_DLL_NAME_LEN);
return TRUE;
}
BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
return TRUE;
}
void ProcessRequest(void* p)
{
EXTENSION_CONTROL_BLOCK* pECB = static_cast<EXTENSION_CONTROL_BLOCK*>(p);
// Send header
static const char szStatus[] = "200 OK",
szHeader[] = "Content-Type: application/octet-stream\r\n"
"Cache-Control: no-cache\r\n"
"Expires: Sat, 01 Jan 2000 00:00:00 GMT\r\n"
"\r\n";
static HSE_SEND_HEADER_EX_INFO header = { szStatus, szHeader, sizeof szStatus - 1, sizeof szHeader - 1, TRUE };
pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, (void*)&header, NULL, NULL);
char buffer[20];
for (int i=0; i<20; ++i)
{
sprintf(buffer, "%02d|", i);
DWORD dw = strlen(buffer);
pECB->WriteClient(pECB->ConnID, (void*)buffer, &dw, HSE_IO_SYNC);
Sleep(100);
}
pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL);
}
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK* pECB)
{
_beginthread(ProcessRequest, 0, pECB);
return HSE_STATUS_PENDING;
}