Same Reference, Different Objects

P

Paul Carey

Hi

I'm seeing behaviour that I really don't understand and was hoping
someone could explain it to me. I've written a class that extends
HttpServlet and implements HttpSessionAttributeListener. The class
contains a single instance variable, attributes, which is an ArrayList
that's initialised in its declaration. What I don't understand is that
attributes appears to be a completely different object depending on
whether it's used in a HttpServlet method it overrides, or a
HttpSessionAttributeListener method it implements.

The logs look me this:

doGet: session 1a302615671063879637250 ArrayList attributes: size is
0, hashCode is 1
attributeAdded: (frustrated, true) added to session
1a302615671063879637250
ArrayList attributes: size is 3, hashCode is 1588958425
name: org.apache.struts.action.LOCALE value: en
name: APP_DATE_FORMAT value: M/d/yy
name: frustrated value: true
doGet: session 1a302615671063879637250 ArrayList attributes: size is
0, hashCode is 1

The code should be fairly clear. Any explanations at what I'm seeing
and why would be much appreciated.
Thanks in advance
Paul

public class SessionAttributeDetails extends HttpServlet implements
HttpSessionAttributeListener
{
private ArrayList attributes = new ArrayList();

public void attributeAdded( HttpSessionBindingEvent sbe )
{
attributes.add( sbe );
System.out.println( "attributeAdded: (" + sbe.getName() + ", "
+ sbe.getValue() + ") added to session " + sbe.getSession().getId() );
System.out.println( "\tArrayList attributes: size is " +
attributes.size() + ", hashCode is " + attributes.hashCode() );

for( Iterator itr = attributes.iterator(); itr.hasNext(); ) {
sbe = (HttpSessionBindingEvent) itr.next();
System.out.println( "\tname: " + sbe.getName() + " value:
" + sbe.getValue() );
}
}

public void attributeRemoved( HttpSessionBindingEvent sbe )
{
attributes.remove( sbe );
System.out.println( "attributeRemoved: (" + sbe.getName() + ",
" + sbe.getValue() + ") removed from session " +
sbe.getSession().getId() );
System.out.println( "\tArrayList attributes: size is " +
attributes.size() + ", hashCode is " + attributes.hashCode() );
}

public void attributeReplaced( HttpSessionBindingEvent sbe )
{
}

public void doGet( HttpServletRequest request, HttpServletResponse
response ) throws IOException, ServletException
{
System.out.println( "doGet: session " +
request.getSession().getId() + " ArrayList attributes: size is " +
attributes.size() +
", hashCode is " + attributes.hashCode() );

HttpSession session = request.getSession();
session.setAttribute( "frustrated", "true" );

...
}
}



By modifying the code slightly, setting attributes to null and
initialising it in the init method like so:

private ArrayList attributes = null;

public void init() throws ServletException
{
attributes = new ArrayList();
attributes.add( "definately not null" );
System.out.println( "init: attributes is " + attributes + "
hashCode is " + attributes.hashCode() );
}

the logs will then contain the very helpful

init: attributes is [definately not null] hashCode is -1048832968
attributeAdded: attributes is null
 
J

Jeff

I don't believe there is any guarantee that the
server software you are using will only create
a single instance of your class. Regardless of
your development environment, the production server
could be multithreaded or could even be running in a
clustered environment on several machines.

The 'attributes' variable should be stored
somewhere in the servlet's context or perhaps
in the session if you will be collecting different
values for each user.

getServletContext().setAttribute("name", value);

or

request.getSession().setAttribute("name", value);



Paul Carey said:
Hi

I'm seeing behaviour that I really don't understand and was hoping
someone could explain it to me. I've written a class that extends
HttpServlet and implements HttpSessionAttributeListener. The class
contains a single instance variable, attributes, which is an ArrayList
that's initialised in its declaration. What I don't understand is that
attributes appears to be a completely different object depending on
whether it's used in a HttpServlet method it overrides, or a
HttpSessionAttributeListener method it implements.

The logs look me this:

doGet: session 1a302615671063879637250 ArrayList attributes: size is
0, hashCode is 1
attributeAdded: (frustrated, true) added to session
1a302615671063879637250
ArrayList attributes: size is 3, hashCode is 1588958425
name: org.apache.struts.action.LOCALE value: en
name: APP_DATE_FORMAT value: M/d/yy
name: frustrated value: true
doGet: session 1a302615671063879637250 ArrayList attributes: size is
0, hashCode is 1

The code should be fairly clear. Any explanations at what I'm seeing
and why would be much appreciated.
Thanks in advance
Paul

public class SessionAttributeDetails extends HttpServlet implements
HttpSessionAttributeListener
{
private ArrayList attributes = new ArrayList();

public void attributeAdded( HttpSessionBindingEvent sbe )
{
attributes.add( sbe );
System.out.println( "attributeAdded: (" + sbe.getName() + ", "
+ sbe.getValue() + ") added to session " + sbe.getSession().getId() );
System.out.println( "\tArrayList attributes: size is " +
attributes.size() + ", hashCode is " + attributes.hashCode() );

for( Iterator itr = attributes.iterator(); itr.hasNext(); ) {
sbe = (HttpSessionBindingEvent) itr.next();
System.out.println( "\tname: " + sbe.getName() + " value:
" + sbe.getValue() );
}
}

public void attributeRemoved( HttpSessionBindingEvent sbe )
{
attributes.remove( sbe );
System.out.println( "attributeRemoved: (" + sbe.getName() + ",
" + sbe.getValue() + ") removed from session " +
sbe.getSession().getId() );
System.out.println( "\tArrayList attributes: size is " +
attributes.size() + ", hashCode is " + attributes.hashCode() );
}

public void attributeReplaced( HttpSessionBindingEvent sbe )
{
}

public void doGet( HttpServletRequest request, HttpServletResponse
response ) throws IOException, ServletException
{
System.out.println( "doGet: session " +
request.getSession().getId() + " ArrayList attributes: size is " +
attributes.size() +
", hashCode is " + attributes.hashCode() );

HttpSession session = request.getSession();
session.setAttribute( "frustrated", "true" );

...
}
}



By modifying the code slightly, setting attributes to null and
initialising it in the init method like so:

private ArrayList attributes = null;

public void init() throws ServletException
{
attributes = new ArrayList();
attributes.add( "definately not null" );
System.out.println( "init: attributes is " + attributes + "
hashCode is " + attributes.hashCode() );
}

the logs will then contain the very helpful

init: attributes is [definately not null] hashCode is -1048832968
attributeAdded: attributes is null
 
W

Wayne Berke

Paul said:
Hi

I'm seeing behaviour that I really don't understand and was hoping
someone could explain it to me. I've written a class that extends
HttpServlet and implements HttpSessionAttributeListener. The class
contains a single instance variable, attributes, which is an ArrayList
that's initialised in its declaration. What I don't understand is that
attributes appears to be a completely different object depending on
whether it's used in a HttpServlet method it overrides, or a
HttpSessionAttributeListener method it implements.

The logs look me this:

doGet: session 1a302615671063879637250 ArrayList attributes: size is
0, hashCode is 1
attributeAdded: (frustrated, true) added to session
1a302615671063879637250
ArrayList attributes: size is 3, hashCode is 1588958425
name: org.apache.struts.action.LOCALE value: en
name: APP_DATE_FORMAT value: M/d/yy
name: frustrated value: true
doGet: session 1a302615671063879637250 ArrayList attributes: size is
0, hashCode is 1

The code should be fairly clear. Any explanations at what I'm seeing
and why would be much appreciated.
Thanks in advance
Paul

public class SessionAttributeDetails extends HttpServlet implements
HttpSessionAttributeListener
{
private ArrayList attributes = new ArrayList();

public void attributeAdded( HttpSessionBindingEvent sbe )
{
attributes.add( sbe );
System.out.println( "attributeAdded: (" + sbe.getName() + ", "
+ sbe.getValue() + ") added to session " + sbe.getSession().getId() );
System.out.println( "\tArrayList attributes: size is " +
attributes.size() + ", hashCode is " + attributes.hashCode() );

for( Iterator itr = attributes.iterator(); itr.hasNext(); ) {
sbe = (HttpSessionBindingEvent) itr.next();
System.out.println( "\tname: " + sbe.getName() + " value:
" + sbe.getValue() );
}
}

public void attributeRemoved( HttpSessionBindingEvent sbe )
{
attributes.remove( sbe );
System.out.println( "attributeRemoved: (" + sbe.getName() + ",
" + sbe.getValue() + ") removed from session " +
sbe.getSession().getId() );
System.out.println( "\tArrayList attributes: size is " +
attributes.size() + ", hashCode is " + attributes.hashCode() );
}

public void attributeReplaced( HttpSessionBindingEvent sbe )
{
}

public void doGet( HttpServletRequest request, HttpServletResponse
response ) throws IOException, ServletException
{
System.out.println( "doGet: session " +
request.getSession().getId() + " ArrayList attributes: size is " +
attributes.size() +
", hashCode is " + attributes.hashCode() );

HttpSession session = request.getSession();
session.setAttribute( "frustrated", "true" );

...
}
}

By modifying the code slightly, setting attributes to null and
initialising it in the init method like so:

private ArrayList attributes = null;

public void init() throws ServletException
{
attributes = new ArrayList();
attributes.add( "definately not null" );
System.out.println( "init: attributes is " + attributes + "
hashCode is " + attributes.hashCode() );
}

the logs will then contain the very helpful

init: attributes is [definately not null] hashCode is -1048832968
attributeAdded: attributes is null

Try printing the value of "this" at the two points in question to
confirm that it is indeed the same reference.

Wayne
 
P

Paul Carey

I don't believe there is any guarantee that the
server software you are using will only create
a single instance of your class. Regardless of
your development environment, the production server
could be multithreaded or could even be running in a
clustered environment on several machines.
Try printing the value of "this" at the two points in question to
confirm that it is indeed the same reference.


Thanks. The server container must have created multiple instances of
my class as the "this" reference is indeed different for methods
invoked by HttpSessionAttributeListener from those which override
HttpServlet.
 
P

Paul Carey

Jeff said:
I don't believe there is any guarantee that the
server software you are using will only create
a single instance of your class. Regardless of
your development environment, the production server
could be multithreaded or could even be running in a
clustered environment on several machines.

From the Servlet 2.3 spec
"For a servlet not hosted in a distributed environment (the default),
the servlet container must use only one instance per servlet
declaration."

As my deployment descriptor does not contain the </distributable> tag,
can I not then assume that there should only be a single instance of
my class as it is a servlet?
 
R

Roedy Green

As my deployment descriptor does not contain the </distributable> tag,
can I not then assume that there should only be a single instance of
my class as it is a servlet?

That is not the same thing as guaranteeing only one thread at a time
uses it though.
 
S

soft-eng

From the Servlet 2.3 spec
"For a servlet not hosted in a distributed environment (the default),
the servlet container must use only one instance per servlet
declaration."

As my deployment descriptor does not contain the </distributable> tag,
can I not then assume that there should only be a single instance of
my class as it is a servlet?

Yes, you should report this as a bug to the vendor of the application
server you are using.
 
A

Adam Maass

Paul Carey said:
"Jeff" <[email protected]> wrote in message

From the Servlet 2.3 spec
"For a servlet not hosted in a distributed environment (the default),
the servlet container must use only one instance per servlet
declaration."

As my deployment descriptor does not contain the </distributable> tag,
can I not then assume that there should only be a single instance of
my class as it is a servlet?

There will only be one instance that gets the HttpServlet calls. The
HttpSessionAttributeListener interface is meant for different purposes, so
I'm not surprised that the container is creating a different instance to
handle the HttpSessionAttributeListener calls.

In effect, the container doesn't care that the HttpSessionAttributeListener
implementation class also happens to implement HttpServlet.


-- Adam Maass
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,119
Latest member
IrmaNorcro
Top