Wednesday, October 21, 2009

Section 24.1.  What Is PDE Build?










24.1. What Is PDE Build?


At its heart, PDE Build is an Ant script generator. It takes in a collection of plug-ins and features, their manifests and build.properties files, and generates a set of Ant build scripts. These scripts are run to produce a build. The export operations you have been doing throughout this book use PDE Build under the covers.


PDE Build is quite flexible. It can consume hybrid mixes of plug-ins and features that are pre-built and those that remain to be built. Some may be included in the final output, while others may not. The output of a build can also vary from plug-ins in directories to update sites and Zip archives of JAR'd and signed plug-ins and features.


The build mechanism builds plug-ins and features, or cascades of the transitively included features and plug-ins starting at a root feature. Cross-platform building is also supported.


The main benefit of PDE Build is that it brings all this together into one relatively simple process. Developers express their normal runtime dependencies in manifest files and a mapping from development time structure to runtime structure in the feature and plug-in build.properties files. PDE Build does the rest.


Key to this process is the automatic generation of the build scripts. Using the input manifests and build.properties, PDE generates Ant scripts that copy identified files and compile identified code using a classpath derived by effectively flattening the plug-in dependency graph. The runtime classpath for a plug-in is defined as a complex graph of plug-in dependencies as described in its manifest file. The classes referenced at runtime are also needed at compile-time, so the compile-time classpath is similarly complex. PDE Build uses the Runtime's plug-in resolution and wiring mechanisms to derive the classpath for each plug-in being built.


As we mentioned above, you have already been using PDE Build if you followed along with the feature- or product-exporting examples. When you use these actions, you are under the covers, running PDE Build. The rest of this chapter explores the use of PDE Build in a release engineering setting, where reproducibility and automation are key concerns.












Section 22.3.  Distributed Builds










22.3. Distributed Builds


If you have a large project and more than one Macintosh at your disposal, you can see dramatically shorter build times with distributed builds. A Macintosh can volunteer idle time for distributed builds by opening the Distributed Builds panel of the Preferences window (Figure 22.1) and checking the first checkbox in the panel. Xcode processes on other computers or even the same computer can then send portions of their builds to the volunteer for compilation. The volunteer can choose to give the distributed-build background process low, medium, or high priority.



Figure 22.1. The Distributed Builds panel of the Xcode Preferences window. The upper checkbox makes this machine available to others for compiling modules. The lower checkbox makes this machine a host for distributed builds, either on every machine that advertises itself on the local Bonjour net as a distcc node or on only those listed machines you mark as trusted.

[View full size image]




If you want to take advantage of distributed builds, check the second checkbox in the Distributed Builds panel of the Preferences window. The list below the checkbox will then fill with the names of all the local net's computers that are advertising via Bonjour (formerly Rendezvous) that they can take distributed builds. You can choose to distribute work to all comers or only to the machines you indicate in the list are trusted.


Each machine participating in a distributed build must be running the same architecture and the same versions of Mac OS X, gcc, and Xcode. Xcode need not be running on the distributed machines.


No matter what your Distributed Builds settings, some tasks are always done on the home machine of the build. In particular, the home machine builds precompiled headers and does preprocessing before sending out compilation units. Because the home machine has work to do that no other machine can, it is wisest not to enable the local machine as sharable for distributed builds. Distributed compilation can't benefit from more than about six remotes; beyond that point, the local machine is spending all its time assembling preprocessed compilands for the remotes and becomes the limiting factor.


Remember that distributing the source and getting the compiled objects back takes time. Distributed builds may be a net loss in productivity if you use it on a network of less than 100Mb/second capacity. This rules out wireless and 10Base-T networks.


If you are dealing with a firewall, be sure that it passes the distributed-build ports of 3632 and 7264.












26.5 OpenGL in the Pop Framework



[ Team LiB ]










26.5 OpenGL in the Pop Framework


Look back at the code for CPopView::OnDraw(CDC* pDC)
that we gave in Section 24.4: Graphics in the Pop Framework. Here is a bulleted list showing some of the corresponding OpenGL calls made by the _pgraphics member of CPopView
if _pgraphics is of the type cGraphicsOpenGL*.



  • Wake up the graphics with


    _pgraphics->activate().

    This calls



    ::wglMakeCurrent( _pDC->GetSafeHdc(), _hRC )

  • Clear the graphics background with


    _pgraphics->clear(targetrect).

    This calls



    ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  • Install the projection and view matrices with these lines.


    _pviewpointcritter->loadProjectionMatrix();
    _pviewpointcritter->loadViewMatrix();

    These in turn call:



    ::glMatrixMode(GL_MODELVIEW)
    ::glLoadMatrixf(_pviewpointcritter->attitude().inverse());
    ::glMatrixMode(GL_PROJECTION)
    ::gluPerspective (fieldofviewangleindegrees, xtoyaspectratio,
    nearzclip, farzclip); //Values from _pviewpointcritter

  • Draw your game world and then the critters with


    pgame()->drawCritters(_pgraphics, _drawflags).

    This generates a variety of ::gl
    and ::glu
    calls like, in the case of polygons



    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glVertexPointer(...);
    ::glDrawArrays(...);

  • Send the graphics to your video display with


    _pgraphics->display(this, pDC)

    This calls



    ::glFinish();// Tell OpenGL to flush its pipeline
    ::SwapBuffers( _pDC->GetSafeHdc() ); // Now Swap the buffers






    [ Team LiB ]



    Section 10.5. XSLT Concepts










    10.5. XSLT Concepts


    When developing an XSL style sheet, I usually find myself using only two of the XSL functions shown earlier: the key() function and the generate-id() function, both of which are indispensable when doing something unique to XSL style sheets. I am referring to something called Muenchian grouping.


    Muenchian grouping, invented by Steve Muench, the XML Evangelist of the Oracle Corporation, is a method of grouping nodes based upon their values. Although I can describe how it works, it is probably a better idea to take a look at the example of Muenchian grouping shown in Listing 10-13. After that, we take it apart to see how it works.


    Listing 10-13. A Muenchian Grouping Example





    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />
    <xsl:key name="keyBook" match="book" use="series" />

    <xsl:template match="/">

    <xsl:element name="table">
    <xsl:attribute name="width">100%</xsl:attribute>

    <xsl:apply-templates select="//book[1]" mode="header" />
    <xsl:apply-templates select="//book[generate-id(.) = generate-
    id(key('keyBook',series)[1])]" />
    <xsl:apply-templates select="//book[string-length(series) =
    0]/series" />
    </xsl:element>

    </xsl:template>

    <xsl:template match="book">

    <xsl:variable name="key">
    <xsl:value-of select="series" />
    </xsl:variable>

    <xsl:apply-templates select="//series[node() = $key]" />

    </xsl:template>

    <xsl:template match="series">

    <xsl:element name="tr">
    <xsl:apply-templates select="parent::node()/*" mode="cell" />
    </xsl:element>

    </xsl:template>

    <xsl:template match="*" mode="cell">

    <xsl:element name="td">
    <xsl:attribute name="align">left</xsl:attribute>

    <xsl:value-of select="." />
    </xsl:element>

    </xsl:template>

    <xsl:template match="book" mode="header">

    <xsl:element name="tr">
    <xsl:apply-templates select="./*" mode="columnHeader" />
    </xsl:element>

    </xsl:template>

    <xsl:template match="*" mode="columnHeader">

    <xsl:variable
    name="lowerCase">qwertyuiopasdfghjklzxcvbnm</xsl:variable>
    <xsl:variable
    name="upperCase">QWERTYUIOPASDFGHJKLZXCVBNM</xsl:variable>

    <xsl:element name="th">
    <xsl:attribute name="width">33%</xsl:attribute>

    <xsl:value-of select="translate(name(.),$lowerCase,$upperCase)" />
    </xsl:element>

    </xsl:template>

    </xsl:stylesheet>




    The element that starts the whole ball rolling is the key element, which creates a cross-reference based upon the node specified by the use attribute. Using the series element as the key results in an index consisting of The Lord of the Rings and Lord Darcy, with the book The Way Station left out because its series element is null. This cross-reference is accessed using the key function, which accepts two parameters: the name from the key element and the node.


    Another function that plays an integral part in Muenchian grouping is the generate-id function. This function, well, generates a unique ID for every node in an XML document every time that the document is processed. So the XPath statement //book[generate-id(.) = generate-id(key('keyBook',series)[1])] locates the first element with each unique key from the cross-reference and applies the matching template. The matching template then uses the unique series to select the matching elements.


    It is all pretty basic XSLT and XPath stuff, although it does have a tendency to make grown men whimper like little scared puppies. If it doesn't, here is one that will put someone over the edge: Imagine trying to group based upon multiple criteria, such as author and series. Although it isn't done very often, and you'll probably never have to do it, I'll give you a hint: Concatenate the elements using the concat function.












    Initialization








    Initialization


    In the last section, you introduced the numberOfStudents field and initialized it to 0. This initialization is technically not requiredint fields are initialized to 0 by default. Explicitly initializing a field in this manner, while unnecessary, is useful to help explain the intent of the code.


    For now, you have two ways to initialize fields: You can initialize at the field level or you can initialize in a constructor. You could have initialized numberOfStudents in the CourseSession constructor:



    class CourseSession {
    private String department;
    private String number;
    private int numberOfStudents;

    CourseSession(String department, String number) {
    this.department = department;
    this.number = number;
    numberOfStudents = 0;
    }
    ...


    There is no hard-and-fast rule about where to initialize. I prefer initializing at the field level when possiblehaving the initialization and declaration in one place makes it easier to follow the code. Also, as you will learn later in this lesson, you can have more than one constructor; initializing at the field level saves you from duplicate initialization code in each constructor.


    You will encounter situations where you cannot initialize code at the field level. Initializing in the constructor may be your only alternative.








      Hour 8








       

       



      Hour 8








      1:

      Why would you want to include a .resx file in your project rather than just including the already compiled .resources file?


      A1:

      In case you need to make changes to the resources contained within the .resx file. You can't make changes to a compiled .resources file.


      2:

      What is an advantage of using managed resources compared to unmanaged resources? What are some disadvantages?


      A2:

      Managed resources are handled by the common language runtime and thus gain all the advantages that it entails. However, there is currently more work that needs to be done to Visual C++ .NET to make working with managed resources easier.


      3:

      Can you use the GetObject function within the ResourceManager class to retrieve a string resource?


      A3:

      Yes, you can. The GetString function is just a modified GetObject call that automatically performs the cast from the Object type to the String object. You can call GetObject and cast to a String object to retrieve a string resource.













       

       





      Top

      Digital Game Spaces











       < Day Day Up > 











      Digital Game Spaces



      Although games have been played in real-world spaces for millennia, the appearance of electronic and digital games in the last few decades have provided new kinds of game spaces: playgrounds that exist only on the screens of computer monitors and televisions. These game spaces take a multitude of forms, from blocky 2D grids to expansive 3D worlds. One useful taxonomy for describing the range of these digital spaces comes from Mark J. P. Wolf, in his essay "Space in the Video Game." Wolf lists eleven ways that video games operate to structure and represent space. We paraphrase these categories below, with examples from Wolf's essay:











      • Text-only space: text adventure games such as Zork and Planetfall





      • One contained screen: Pong, Space Invaders, Breakout





      • One screen with wraparound: Asteroids, Pac-Man (with teleporter wraparound)





      • Scrolling on one axis: Defender, Atari's Football arcade game





      • Scrolling on two axes: Gauntlet, Sim City





      • Adjacent spaces displayed one room at a time: Berzerk, Atari's Adventure





      • Scrolling with separate background layers: Zaxxon, Double Dragon





      • Limited 3D space: Tempest, Night Driver





      • Two spaces on one screen or two separate screens: Spy vs. Spy, Dactyl Nightmare





      • Full 3D spaces: Battlezone, DOOM, Tomb Raider





      • "Mapped" Spaces: Defender, Myst (both have a separate radar or map display) [9]




      Wolf 's categories are not without some conceptual problems. For example, there is a fundamental difference between a "two-and-a-half" dimensional space like DOOM and a more fully 3D space like Tomb Raider (in DOOM the player can only move in two dimensions and the objects are completely flat). Additionally, some of his categories, such as separate background scrolling layers and "mapped" spaces, seem to be fuzzier designations (most of the categories he lists could also incorporate a "map" element). Wolf's typology is certainly not the only way to conceptualize digital game spaces, but his categories are useful in pointing out the wealth of forms they take.






      Zork: Text-only space

      The structure of a digital game space always grows directly from the formal system that defines the game. However, the space that a player experiences is also a function of representation (how the space is displayed to the player) and interaction (how a player navigates through the space). These three ele-ments—formal structure, structure of display, and interactive structure—together constitute the experience of a digital game space. All three of them need to be designed in concert to achieve proper narrative effect in your game.


      For example, the feeling of zero-G drift in Asteroids is linked directly to the design of the game space. Rather than bouncing off the screen wall like a Pong ball, the player's ship moves right on through to the other side, evoking the illusion of endless movement through the darkness of space. The game's style of movement, emphasizing inertial drift and retro-rocket maneuvers, also heightens the feeling of space travel. Although the player's ship never leaves the screen, at the beginning of each wave, asteroids drift in from the edges. Once they appear, these asteroids follow the same wraparound logic as the player's ship. Other objects, such as the UFO saucers, don't ever wrap around, and simply disappear once they reach the edge of the screen. Curiously, these inconsistencies never break the game's spatial continuity. The fact that some objects remain constantly on screen (the player's ship and existing asteroids) whereas others seem to drift in from parts unknown (new asteroids and UFOs), adds up to a rich and multi-layered narrative of cosmic exploration and survival. Space creates narrative in all senses of Miller's definition: space helps define the "characters"(the game objects); space is the context in which narrative events occur; and space patterns narrative experience over time for the player.






      Asteroids: One screen with wraparound





      Tempest: Limited 3D space





      Tomb Raider: Full 3D space





      Double Dragon: Scrolling with separate background layers





      Spy vs. Spy: Two spaces on one screen





      Defender: "Mapped" space

      The design of a game space creates a context for narrative interaction, by structuring events in patterns of space, time, and causality. As Marsha Kinder notes in Playing with Power in Movies, Television, and Video Games, " Narrative creates a context for interpreting all perceptions. Narrative maps the world and its inhabitants, including one's own position within that grid."[10] The narrative play of games is always connected to an underlying grid of possibility, to goals and conflict, to uncertainty and the moment-to-moment action of the core mechanic. The space of a game is quite literally its space of play.


      In a fighting game such as Tekken, the space is tightly constrained, crowding the two fighters up against each other. There is nowhere to run and nowhere to hide. All you can do is fight your opponent, an action the design of the space explicitly encourages and facilitates. In contrast, the corridors and rooms of a Quake deathmatch space create a narrative of stealthy maneuvers, mad dashes to grab power-ups, and the surprise of sudden death. Tekken has no hidden spatial information. But in Quake, walls block players from seeing each other, dark lighting makes hiding in shadows possible, and the periodically appearing power-ups create uncertainty about when and where they can be found. In both Tekken and Quake, a player's "position on the grid," as Kinder puts it, is simultaneously a location in the space of the game and a position within the space of the game narrative. The formal, represented, and interactive spaces of games are also narrative spaces, contexts for interpreting the experience of a game as a story.


      When we frame experience as narrative, the events and actions of game play take on form and meaning within the game's representational universe. The dynamic properties of the space of Tekken and Quake are important in creating emergent narratives of conflict. But the spaces contain embedded narrative qualities as well. In Quake, the spaces embody the sets, props, and characters of pulp sci-fi horror. In Tekken, as with most fighting games, each arena is thematically linked to one of the game's characters. Space can therefore be used to express information about a character's persona or backstory, or to create narratives about defending one's home turf or invading an enemy's territory.







      [9]Mark J. P. Wolf, "Space in the Video Game." In The Medium of the Video Game, edited by Mark J. P. Wolf (Austin: University of Texas Press, 2002), p.53-70





      [10]MarshaKinder, Playing with Power in Movies, Television, and Video Games: From Muppet Babies to Teenage Mutant (Los Angeles: University of California Press, 1993), p. 2.



















       < Day Day Up > 



      Recipe&nbsp;2.4.&nbsp;Using Multiple Struts Configuration Files










      Recipe 2.4. Using Multiple Struts Configuration Files




      Problem



      You want to break
      apart a large struts-config.xml file into
      smaller files for improved organization and easier maintenance,
      particularly in a team environment.





      Solution



      Split your monolithic struts-config.xml into
      multiple configuration files. Each file must be well-formed and valid
      according to the struts-config XML DTD.
      Reference these files as the value for the config
      initialization parameter of the ActionServlet in
      the web.xml file as shown in Example 2-9.




      Example 2-9. Multiple config files (single module)

      <servlet>
      <servlet-name>action</servlet-name>
      <servlet-class>
      org.apache.struts.action.ActionServlet
      </servlet-class>
      <init-param>
      <param-name>config</param-name>
      <param-value>
      /WEB-INF/struts-config.xml,
      /WEB-INF/struts-config-2.xml
      </param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
      </servlet>





      When the ActionServlet is loaded, Struts will
      merge the results of the specified configuration files into a single
      in-memory configuration.





      Discussion



      For anything other than the most trivial applications, the
      struts-config.xml file tends to get large and
      unwieldy. Many applications may have action
      elements numbering in the hundreds. Combine this with the use of a
      locking source control system, and soon you will have developers in
      constant contention for the same file.



      Struts 1.1 introduced support for multiple configuration files. Each
      configuration file must be a valid XML file and conform to the
      struts-config XML
      Document Type Definition (DTD). You declare the set of files as the
      parameter value for the config
      ActionServlet initialization parameter in your
      web.xml file. You specify the files as a
      comma-separated list of file paths. At runtime, the files are merged
      in memory into a single configuration set. If duplicate elements
      existfor example, a form bean declaration with the same value
      for the name attributethe value read from
      the last configuration file listed takes precedence.



      Just because you are using multiple configuration files
      doesn't necessarily mean that
      you're using Struts modules. In fact, you can have
      multiple configuration files for one individual module. In the
      Solution above, the param-name element value of
      config dictates to the
      ActionServlet the path to the Struts configuration
      files for the default module. Additional modules are indicated by
      using a param-name value of
      config/module-name.
      Example 2-10 shows a Struts
      ActionServlet declaration from a
      web.xml file with a default module and two
      additional modules. The default module uses two configuration files:
      module1 uses only one configuration file, and
      module2 uses three configuration files.




      Example 2-10. Multiple config files (multiple modules)

      <servlet>
      <servlet-name>action</servlet-name>
      <servlet-class>
      org.apache.struts.action.ActionServlet
      </servlet-class>
      <init-param>
      <param-name>config</param-name>
      <param-value>
      /WEB-INF/struts-default-config.xml,
      /WEB-INF/struts-default-config-2.xml
      </param-value>
      </init-param>
      <init-param>
      <param-name>config/module1</param-name>
      <param-value>
      /WEB-INF/struts-module1-config.xml
      </param-value>
      </init-param>
      <init-param>
      <param-name>config/module2</param-name>
      <param-value>
      /WEB-INF/struts-module2-config.xml,
      /WEB-INF/struts-module2-config-2.xml,
      /WEB-INF/struts-module2-config-3.xml
      </param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
      </servlet>





      If you are doing team development, consider splitting your
      configuration files based on functional area, use cases, or user
      stories. Individual team members can focus on their areas of
      responsibility without bumping into the rest of the team.





      See Also



      Recipe 2.5 details the nuances of using
      Struts modules. Recipe 1.8 describes how to
      generate the struts configuration files automatically.












        Section 23.3.&nbsp; IP Statistics










        23.3. IP Statistics






























        The Linux kernel keeps several sets of statistics about different events and conditions that can be useful for accounting, debugging, or confirming compatibility with standards. In this chapter, we will only briefly see what statistics are kept by the IP protocol layer (without touching on the SNMP infrastructure) and how they are updated. In previous chapters, especially when describing the various functions, we saw a few cases where macros such as IP_INC_STATS were used to update the value of some counters.


        Let's start with the data structure that contains all of the counters associated with the IP protocol. It is called ip_statistics and is defined in net/ipv4/ip_input.c. It is a vector with two pointers, each one pointing to a vector of ipstats_mib[*] structures (defined in include/net/snmp.h), one per CPU. The allocation of such vectors is done in init_ipv4_mibs in net/ipv4/af_inet.c.

        [*] MIB, as mentioned earlier, stands for Management Information Base, and is used to refer to a collection of objects (typically counters).



        static int _ _init init_ipv4_mibs(void)
        {
        ...
        ip_statistics[0] = alloc_percpu(struct ipstats_mib);
        ip_statistics[1] = alloc_percpu(struct ipstats_mib);
        ...
        }



        The ipstats_mib structure is simply declared as an array of unsigned long fields of size _ _IPSTATS_MIB_MAX, which happens to be the size of the IPSTATS_MIB_XXX enumeration list in include/linux/snmp.h.


        Here is the meaning of the IPSTATS_MIB_XXX values, classified into four groups. For a more detailed description, you can refer to RFC 2011 for IPv4 and RFC 2465 for IPv6. The IPSTATS_MIX_XXX counters that are not used by IPv4 (with the exception of IPSTATS_MIB_INADDRERRORS) are not defined in RFC 2011.



        Fields related to received packets




        IPSTATS_MIB_INRECEIVES


        Number of packets received. This field does not distinguish between complete IP packets and fragments. It also includes both the ones that will be accepted and the ones that will be discarded for any reason (with the exception of those dropped because an interface in promiscuous mode delivered frames to ip_rcv that were not addressed to the receiving interface). It is updated at the beginning of ip_rcv.


        IPSTATS_MIB_INHDRERRORS


        Number of packets (fragments as well as nonfragmented packets) that were discarded because of corrupted IP headers. This field can be updated both in ip_rcv and in ip_rcv_finish for different reasons.


        IPSTATS_MIB_INTOOBIGERRORS


        Not used by IPv4. IPv6 uses it to count those ingress IP packets that cannot be forwarded because they would need to be fragmented (which is not an allowed operation for a router in IPv6, unlike IPv4).


        IPSTATS_MIB_INNOROUTES


        Not used at the moment. It is supposed to count those ingress packets that could not be forwarded because the local host does not have a valid route.


        IPSTATS_MIB_INADDRERRORS


        Not used at the moment by IPv4. IPv6 uses it to count those packets received with a wrong address type.


        IPSTATS_MIB_INUNKNOWNPROTOS


        Number of packets received with an unknown L4 protocol (i.e., no handler for the protocol was registered). This field is updated in ip_local_deliver_finish.


        IPSTATS_MIB_INTRUNCATEDPKTS


        The packet is truncated (i.e., it does not include a full IP header). It is used by IPv6, but not by IPv4.


        IPSTATS_MIB_INDISCARDS


        Number of packets discarded. This counter does not include the packets dropped because of header errors; it mainly includes memory allocation problems. This field is updated in ip_rcv and ip_rcv_finish.


        IPSTATS_MIB_INDELIVERS


        Number of packets successfully delivered to L4 protocol handlers. This field is updated in ip_local_deliver_finish.


        IPSTATS_MIB_INMCASTPKTS


        Number of received multicast packets. It is used by IPv6, but not by IPv4.


        Fields related to transmitted packets




        IPSTATS_MIB_OUTFORWDATAGRAMS


        Number of ingress packets that needed to be forwarded. This counter is actually incremented before the packets are transmitted and when they theoretically could still be discarded for some reason. Its value is updated in ip_forward_finish (and in ipmr_forward_finish for multicast).


        IPSTATS_MIB_OUTREQUESTS


        Number of packets that the system tried to transmit (successfully or not), not including forwarded packets. This field is updated in ip_ouput (and in ip_mc_output for multicast).


        IPSTATS_MIB_OUTDISCARDS


        Number of packets whose transmission failed. This field is updated in several places, including ip_append_data, ip_push_pending_frames, and raw_send_hdrinc.


        IPSTATS_MIB_OUTNOROUTES


        Number of locally generated packets discarded because there was no route to transmit them. Normally this field is updated after a failure of ip_route_output_flow. ip_queue_xmit is one of the functions that can update it.


        IPSTATS_MIB_OUTMCASTPKTS


        Number of transmitted multicast packets. Not used by IPv4 at the moment.


        Fields related to defragmentation




        IPSTATS_MIB_REASMTIMEOUT


        Number of packets that failed defragmentation because some of the fragments were not received in time. The value reflects the number of complete packets, not the number of fragments. This field is updated in ip_expire, which is the timer function executed when an IP fragment list is dropped due to a timeout. Note that this counter is not used as defined in the two RFCs mentioned at the beginning of this section.


        IPSTATS_MIB_REASMREQDS


        Number of fragments received (and therefore the number of attempted reassemblies). This field is updated in ip_defrag.


        IPSTATS_MIB_REASMFAILS


        Number of packets that failed the defragmentation. This field is updated in several places (_ _ip_evictor, ip_expire, ip_frag_reasm, and ip_defrag) for different reasons.


        IPSTATS_MIB_REASMOKS


        Number of packets successfully defragmented. This field is updated in ip_frag_reasm.


        Fields related to fragmentation




        IPSTATS_MIB_FRAGFAILS


        Number of failed fragmentation efforts. This field is updated in ip_fragment (and in ipmr_queue_xmit for multicast).


        IPSTATS_MIB_FRAGOKS


        Number of fragments transmitted. This field is updated in ip_fragment.


        IPSTATS_MIB_FRAGCREATES


        Number of fragments created. This field is updated in ip_fragment.


        The values of these counters are exported in the /proc/net/snmp file.


        Each CPU keeps its own accounting information about the packets it processes. Furthermore, it keeps two counters: one for events in interrupt context and the other for events outside interrupt context. Therefore, the ip_statistics array includes two elements per CPU, one for interrupt context and one for noninterrupt context. Not all of the events can happen in both contexts, but to make things easier and clearer, the vector has simply been defined of double in size; those elements that do not make sense in one of the two contexts are simply not to be used.


        Because some pieces of code can be executed both in interrupt context and outside interrupt context, the kernel provides three different macros to add an event to the IP statistics vector:



        #define IP_INC_STATS (field) SNMP_INC_STATS (ip_statistics, field)
        #define IP_INC_STATS_BH (field) SNMP_INC_STATS_BH (ip_statistics, field)
        #define IP_INC_STATS_USER(field) SNMP_INC_STATS_USER(ip_statistics, field)



        The first can be used in either context, because it checks internally whether it was called in interrupt context and updates the right element accordingly. The second and the third macros are to be used for events that happened in and outside interrupt context, respectively. The macros IP_INC_STATS, IP_INC_STATS_BH, and IP_INC_STATS_USER are defined in include/net/ip.h, and the three associated SNMP_INC_XXX macros are defined in include/net/snmp.h.












        22.9. Wrap-Up



        22.9. Wrap-Up


        In this chapter, you learned how to use
        the const_cast operator to remove the const qualification of a variable. We then showed how to use
        namespaces to ensure that every identifier in
        a program has a unique name and explained how they can help resolve naming
        conflicts. You saw several operator keywords for programmers whose keyboards do
        not support certain characters used in operator symbols, such as !,
        &, ^, ~ and |. Next, we
        showed how the mutable storage-class
        specifier enables a programmer to indicate that a data member should always be
        modifiable, even when it appears in an object that is currently being treated as
        a const. We also showed the mechanics of
        using pointers to class members and the ->* and .* operators. Finally, we introduced multiple inheritance
        and discussed problems associated with allowing a derived class to inherit the
        members of several base classes. As part of this discussion, we demonstrated how
        virtual inheritance can be used to solve those problems.


         


        11.1 Introduction



        [ Team LiB ]






        11.1 Introduction


        All the examples so far in this text have used numeric addresses for the hosts (e.g., 206.6.226.33) and numeric port numbers to identify the servers (e.g., port 13 for the standard daytime server and port 9877 for our echo server). We should, however, use names instead of numbers for numerous reasons: Names are easier to remember; the numeric address can change but the name can remain the same; and with the move to IPv6, numeric addresses become much longer, making it much more error-prone to enter an address by hand. This chapter describes the functions that convert between names and numeric values: gethostbyname and gethostbyaddr to convert between hostnames and IPv4 addresses, and getservbyname and getservbyport to convert between service names and port numbers. It also describes two protocol-independent functions: getaddrinfo and getnameinfo, which convert between hostnames and IP addresses and between service names and port numbers.






          [ Team LiB ]



          The Java Way of File Operations




          I l@ve RuBoard









          The Java Way of File Operations


          Interfaces and classes for dealing with files and other I/O types are in the java.io package. An interface is a class that contains abstract methods. Classes in java.io form a class hierarchy.


          The two main class types in java.io are text oriented (character streams) and binary oriented (byte streams). Subclasses of the Reader and Writer classes are text oriented; those of the InputStream and OutputStream classes are binary oriented.


          InputStream and OutputStream are abstract; that is, they can't be instantiated directly. To use an abstract class you must subclass it and instantiate the subclass. The subclasses of InputStream and OutputStream allow the reading of binary data to and from various types of input and output such as byte arrays (memory), files, and even network sockets.


          Streams can be chained to provide extra functionality. For example, you can buffer a FileInputStream by chaining it to a BufferedInputStream Then you can chain the BufferedInputStream to an ObjectInputStream to read in whole objects at one time. (This is similar to the pickle functionality in Python.)


          Java 1.1's binary data input streams are complemented by somewhat equivalent text input streams. The parents of these classes are the abstract classes Reader and Writer. Having an equivalent set of text-oriented character stream classes allows the conversion of Unicode text. Readers and Writers, like streams, can be chained together. For example, you can buffer a FileReader by chaining it to a BufferedReader. (Buffering will be explained shortly.)


          I/O Classes to Be Covered


          There are more than thirty I/O classes, not including interfaces and abstract classes. This seems like a lot, but if you understand how Reader, Writer, InputStream, and OutputStream work, you can easily understand the rest.


          Reader and Writer subclasses deal with character streams, that is, text. InputStream and OutputStream subclasses deal with binary streams. An easy way to remember this is, if you can read it, use Reader and Writer; if you can't, use InputStream and OutputStream.



          For Beginners: Understanding Streams


          Think of a stream as an abstract file. Just as you can read and write to a file, you can read and write to a stream. You might use a stream for reading and writing with an RS-232 serial connection, a TCP/IP connection, or a memory location (like a sequence). A stream is abstract, so if you know how to read and write to a file, you basically already know how to read and write to a memory location or an RS-232 serial connection or, for that matter, a Web site. This is the art and magic of polymorphism.










            I l@ve RuBoard



            Creating the User Module













            Creating the User Module

            The user module of the online discussion forum application allows end users to view and post discussions. The user module contains three files: UserPage.jsp, AddDiscussion.jsp, and ViewDiscussion.jsp.




            Creating the UserPage.jsp File


            The UserPage.jsp file of the application provides links that allow an end user to post a new discussion or view the existing discussion topics.



            Listing 7-10 shows the content of the UserPage.jsp file:




            Listing 7-10: The UserPage.jsp File







            <%@page import="TxtConverter.*"%>
            <%
            /*Call the constructor of DisplayString by passing the value of query parameter, lang*/
            DisplayString ds=new DisplayString((session.getAttribute("lang1")).toString());
            %>
            <html>
            <html>
            <head>
            <meta http-equiv="Content-Language" content="en-us">
            <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
            <title>User Options</title>
            </head>
            <body>
            <!--Display banner heading using getHeading() method of DisplayString class-->
            <center><h1 class="style1"><span class="style1"><font
            color="#E24907"><%= ds.getHeading()%></font> </h1></center>
            <p>&nbsp;</p>
            <!--Display page name using getUserOption() method of DisplayString class-->
            <p align="center"><b><font face="Arial" size="5" color="#0000FF"><%=
            ds.getUserOption() %> </font></b></p>
            <table border="0" width="215" cellspacing="0" cellpadding="0" height="1">
            <tr>
            <!--Display user ID label using getUser() method of DisplayString class-->
            <td width="95" bgcolor="#FFFFFF" height="1"><div align="center"><font
            color="#FF0000"><b><%= ds.getUser() %>
            </b></font></div></td>
            <td width="104" bgcolor="#FFFFFF" height="1">
            <div align="left"></div>
            <!--Display user ID value from session-->
            <div align="center"><font
            color="#FF0000"><%=session.getAttribute("userid")%></font></div></td>
            </tr>
            </table>
            <div align="justify">
            <table border="0" cellpadding="0" cellspacing="0" width="200" style="position: absolute; left: 653px; top: 128px;
            width: 259px" >
            <tr>
            <!--Display logout hyperlink-->
            <td height="18" bgcolor="#FFFFFF"><div align="center"><font
            color="#FFFFFF"><b><a href="DoLogout.jsp"><%=
            ds.getLogout()%></a></b></font></div></td>
            </tr>
            </table>
            </div>
            <p align="center">&nbsp;</p>
            <div align="justify">
            <table border="0" cellpadding="0" cellspacing="0" width="300" style="position: absolute;
            left: 322px; top: 248px; width:
            300">
            <tr>
            <!--Display text for hyperlink to AddDiscussion.jsp using getNewDiscussion() method of
            DisplayString class-->
            <td bgcolor="#FFFFFF"><div align="center"><i><b><a
            href="AddDiscussion.jsp"><%= ds.getNewDiscussion() %>
            </a></b></i></div></td>
            </tr>
            <tr>
            <!--Display text for hyperlink to ViewDiscussion.jsp using getViewDiscussion() method of
            DisplayString class-->
            <td bgcolor="#FFFFFF"><div align="center"><i><b><a
            href="ViewDiscussion.jsp"><%= ds.getViewDiscussion()%>
            </a></b></i></div></td>
            </tr>
            </table>
            </div>
            </body>
            </html>















            Download this listing.


            The above code creates the UserPage.jsp file. This code retrieves the value of the String, lang1, which represents an end user country stored in session, to create an object of the DisplayString class. The code calls the methods of the DisplayString class to provide the user interface. The user interface displays hyperlinks to access the AddDiscussion.jsp and ViewDiscussion.jsp files.



            Figure 7-7 shows the user interface of the UserPage JSP in French:






            Figure 7-7: User Interface of UserPage JSP in French




            Creating the AddDiscussion.jsp File


            The AddDiscussion JSP page provides fields that accept end user information about a new discussion. This information is stored in the discussionlist.xml file.



            Listing 7-11 shows the content of the AddDiscussion.jsp file:




            Listing 7-11: The AddDiscussion.jsp File







            <!--Import the necessary packages-->
            <%@page import="javax.xml.parsers.*, java.net.*, java.io.*, java.util.*, org.w3c.dom.*"%>
            <%@page
            import="javax.xml.transform.Transformer, javax.xml.transform.TransformerFactory,javax.xml.
            transform.TransformerException"%>
            <%@page
            import="javax.xml.transform.TransformerConfigurationException,
            javax.xml.transform.dom.DOMSource, javax.xml.transform.stream.StreamResult"%>
            <%@page import="javax.xml.transform.OutputKeys"%>
            <%@page import="TxtConverter.*"%>
            <%!String discussionpath;%>
            <%
            /*Call the constructor of DisplayString by passing the value of query parameter, lang*/
            DisplayString ds=new DisplayString((session.getAttribute("lang1")).toString());
            %>
            <html>
            <%
            /*Retrieve category, sub category, and discussion content from request object*/
            String tempCategory, tempSubCategory,tempDiscussion,tempuser;
            tempCategory=request.getParameter("txtCategory");
            tempDiscussion=request.getParameter("txtDiscussion");
            tempSubCategory=request.getParameter("txtSubCategory");
            if(!(tempCategory == null || tempDiscussion == null ||tempSubCategory == null))
            {
            try{
            /*Create a URL object to load properties file*/
            URL url=new URL("http://127.0.0.1:8080/seven/path.properties");
            /*Open URL connection*/
            URLConnection ucon=url.openConnection();
            /*Retrieve the discussionlistpath property*/
            Properties prop = new Properties();
            prop.load(ucon.getInputStream());
            discussionpath = prop.getProperty("discussionlistpath");
            }
            catch(Exception ex)
            {
            ex.printStackTrace();
            }
            /*Obtain a DocumentBuilder object by calling the newInstance()method*/
            DocumentBuilderFactory docbfact = DocumentBuilderFactory.newInstance();
            /*Call the newDocumentBuilder() method to obtain a DocumentBuilder object*/
            DocumentBuilder docb = docbfact.newDocumentBuilder();
            /*Parse the discussionlist XML document*/
            Document doc = docb.parse(new FileInputStream(discussionpath));
            /*Obtain Element object that represents the root element of an XML document*/
            Element doce = doc.getDocumentElement();
            /*Create a new child element*/
            Element newe = doc.createElement("query");
            /*Obtain user ID from session*/
            tempuser=session.getAttribute("userid").toString();
            /*Set category, sub category, discussion, and user ID values as attributes to the query Element*/
            newe.setAttribute("Category", tempCategory);
            newe.setAttribute("Discussion", tempDiscussion);
            newe.setAttribute("user",tempuser);
            newe.setAttribute("SubCategory", tempSubCategory);
            /*Append query Element to root Element*/
            doce.appendChild(newe);
            /*Obtain a TransformerFactory object*/
            TransformerFactory tFactory = TransformerFactory.newInstance();
            /*Create a Transformer object*/
            Transformer transformer = tFactory.newTransformer();
            /*Set output property of Transformer object*/
            transformer.setOutputProperty(OutputKeys.INDENT,"yes");
            /*Create a DOMSource that acts a transformation source*/
            DOMSource source = new DOMSource(doc);
            /*Create a StreamResult object that represents a stream to the XML document and acts as the
            /*destination of the transformation*/
            StreamResult result = new StreamResult(new PrintStream(new
            FileOutputStream(discussionpath)));
            /*Perform the transformation to transform the document object tree to the XML document*/
            transformer.transform(source, result);
            }
            %>
            <!--Display banner heading using resource bundle-->
            <Center><H1 class="style1"><font color="#E24907"><%=
            ds.getHeading()%></font></H1></Center>
            <br><br>
            <p align="center"><font size="5"><b>
            <!--Display page heading using resource bundle-->
            <%= ds.getPostNewQuery() %></b></font>
            </p>
            <table border="0" width="215" cellspacing="0" cellpadding="0" height="1">
            <tr>
            <!--Display user ID label using resource bundle-->
            <td width="95" bgcolor="#FFFFFF" height="1"><div align="center"><font
            color="#FF0000"><b><%= ds.getUser() %>
            </b></font></div></td>
            <td width="104" bgcolor="#FFFFFF" height="1">
            <!--Display user ID value from session-->
            <font color="#FF0000"><b>
            <%=session.getAttribute("userid")%></b></font></td>
            </tr>
            </table>
            <div align="justify">
            <table border="0" cellpadding="0" cellspacing="0" width="200" style="position: absolute;
            left: 707px; top: 129px; width: 208px">
            <tr>
            <!--Display text of hyperlink to DoLogout.jsp using resource bundle-->
            <td height="18" bgcolor="#FFFFFF"><font color="#FFFFFF"><b><a
            href="DoLogout.jsp"><%= ds.getLogout() %></a></b></font></td>
            </tr>
            <tr>
            <td height="18" bgcolor="#FFFFFF"><b>
            <!--Display text of hyperlink to UserPage.jsp using resource bundle-->
            <a href="UserPage.jsp">
            <%= ds.getGoMain() %></a></b></td>
            </tr>
            </table>
            </div>
            <!--Display HTML form to add new discussion-->
            <form action = AddDiscussion.jsp method ="post">
            <div align="center">
            <table width="544" border="1" cellpadding="0" cellspacing="0" bordercolor="#FF0000">
            <tr>
            <!--Display label for category field using resource bundle-->
            <td width="194" bgcolor="#FFFFFF"><div align="center"><%= ds.getCategory()
            %></div></td>
            <td width="450" bgcolor="#FFFFFF">
            <input name="txtCategory" size="30" >
            <select name ="txtCategory1" onChange="{form.txtCategory.value=''+this.value+'';}">
            <%
            java.util.HashMap hmap = new HashMap();
            try{
            /*Create a URL object to load properties file*/
            URL url=new URL("http://127.0.0.1:8080/seven/path.properties");
            /*Open URL connection*/
            URLConnection ucon=url.openConnection();
            /*Retrieve the discussionlistpath property*/
            Properties prop = new Properties();
            prop.load(ucon.getInputStream());
            discussionpath = prop.getProperty("discussionlistpath");
            }
            catch(Exception ex)
            {
            ex.printStackTrace();
            }
            /*Obtain a DocumentBuilder object by calling the newInstance()method*/
            DocumentBuilderFactory docbfact = DocumentBuilderFactory.newInstance();
            /*Call the newDocumentBuilder() method to obtain a DocumentBuilder object*/
            DocumentBuilder docb = docbfact.newDocumentBuilder();
            /*Parse the discussionlist XML document*/
            Document doc = docb.parse(new FileInputStream(discussionpath));
            /*Obtain query elemens of the XML document as a NodeList object*/
            NodeList nl1 = doc.getElementsByTagName("query");
            /*Iterate through the NodeList object*/
            for(int int_1 = 0; int_1 < nl1.getLength(); int_1 ++)
            {
            /*Obtain the Node object*/
            Node n=nl1.item(int_1);
            /*Retrieve attributes of the Node objects*/
            NamedNodeMap nnp = n.getAttributes();
            Object
            obj1=hmap.put((nnp.getNamedItem("Category")).getNodeValue(),(nnp.getNamedItem("Category")).getNodeValue());
            if(obj1==null)
            {
            %>
            <!--Populate category drop-down list with values of Category attributes-->
            <option
            value="<%=(nnp.getNamedItem("Category")).getNodeValue()%>"><%=(nnp.getNamedItem
            ("Category")).getNodeValue()%></option>
            <%
            }
            }
            %>
            </select>
            </td>
            </tr>
            <tr>
            <!--Display label for sub category field using resource bundle-->
            <td width="194" bgcolor="#FFFFFF"><div align="center"><%= ds.getSubCategory()
            %></div></td>
            <td width="450" bgcolor="#FFFFFF">
            <input name="txtSubCategory" size="30" >
            <select name ="txtSubCategory1"
            onChange="{form.txtSubCategory.value=txtSubCategory1.value;}">
            <%
            hmap = new HashMap();
            /*Obtain a DocumentBuilder object by calling the newInstance()method*/
            docbfact = DocumentBuilderFactory.newInstance();
            /*Call the newDocumentBuilder() method to obtain a DocumentBuilder object*/
            docb = docbfact.newDocumentBuilder();
            /*Parse the discussionlist XML document*/
            doc = docb.parse(new FileInputStream(discussionpath));
            /*Obtain query elemens of the XML document as a NodeList object*/
            nl1 = doc.getElementsByTagName("query");
            /*Iterate through the NodeList object*/
            for(int int_1 = 0; int_1 < nl1.getLength(); int_1 ++)
            {
            /*Obtain the Node object that represents elements of an XML document*/
            Node n=nl1.item(int_1);
            /*Retrieve attributes of the Node objects*/
            NamedNodeMap nnp = n.getAttributes();
            Object
            obj1=hmap.put((nnp.getNamedItem("SubCategory")).getNodeValue(),(nnp.getNamedItem
            ("SubCategory")).getNodeValue());
            if(obj1==null)
            {
            %>
            <!--Populate category drop-down list with values of SubCategory attributes-->
            <option
            value="<%=(nnp.getNamedItem("SubCategory")).getNodeValue()%>"><%=(nnp.getNamedItem
            ("SubCategory")).getNodeValue()%></option>
            <%
            }
            }
            %>
            </select>
            </td>
            </tr>
            <tr>
            <!--Display label for discussion field using resource bundle-->
            <td width="194" bgcolor="#FFFFFF"><div align="center"><%= ds.getDiscussion()
            %></div></td>
            <td width="338" bgcolor="#FFFFFF">
            <textarea name="txtDiscussion" rows="4" cols="50"></textarea>
            </td>
            </tr>
            </table>
            <p>
            <!--Display label for submit button using resource bundle-->
            <input type="submit" value=<%= ds.getSubmitQuery() %>>
            </p>
            </div>
            </form>















            Download this listing.




            The above code creates the AddDiscussion JSP file. The code retrieves the value of the String object, lang1, which is stored in session to create an object of the DisplayString class. The object of the DisplayString class retrieves country-specific data to provide the user interface of the AddDiscussion JSP page. When an end user specifies a topic, sub topic, and content of a discussion, and clicks the Submit button, the code parses the discussionlist.xml file to create an object tree of that file. The code adds a node that corresponds to the discussion in the object tree and transforms the object tree into an XML document. Figure 7-8 shows the user interface of AddDiscussion.jsp in French:






            Figure 7-8: User Interface of AddDiscussion.jsp in French




            Creating the ViewDiscussion.jsp File


            The ViewDiscussion.jsp file displays existing discussions and allows an end user to search for a specific discussion.



            Listing 7-12 shows the content of the ViewDiscussion.jsp file:




            Listing 7-12: The ViewDiscussion.jsp File







            <!--Import the necessary packages-->
            <%@page import="javax.xml.parsers.*, java.net.*, java.io.*, java.util.*, org.w3c.dom.*"%>
            <%@page
            import="javax.xml.transform.Transformer,javax.xml.transform.TransformerFactory, javax.xml.
            transform.TransformerException"%>
            <%@page
            import="javax.xml.transform.TransformerConfigurationException, javax.xml.transform.dom.
            DOMSource, javax.xml.transform.stream.StreamResult"%>
            <%@page import="javax.xml.transform.OutputKeys"%>
            <%@page import="TxtConverter.*"%>
            <%!String discussionpath;%>
            <%
            /*Call the constructor of DisplayString by passing the value of query parameter, lang*/
            DisplayString ds=new DisplayString((session.getAttribute("lang1")).toString());
            %>
            <%
            String tempCategory,tempSubCategory;
            /*Retrieve values of category and sub category of discussion from request object*/
            tempCategory = request.getParameter("txtCategory");
            tempSubCategory = request.getParameter("txtSubCategory");
            %>
            <Center><H1 class="style1"><font color="#E24907"><%=
            ds.getHeading()%></font></H1></Center>
            <br>
            <p align="center"><font size="5"><b>
            <%= ds.getQueryList() %> </b></font>
            </p>
            <table border="0" width="215" cellspacing="0" cellpadding="0" height="1">
            <tr>
            <td width="95" bgcolor="#FFFFFF" height="1"><div align="center"><font
            color="#FF0000"><b><%= ds.getUser() %>
            </b></font></div></td>
            <td width="104" bgcolor="#FFFFFF" height="1">
            <font color="#FF0000"><b>
            <%=session.getAttribute("userid")%> </b></font></td>
            </tr>
            </table>
            <div align="justify">
            <table border="0" cellpadding="0" cellspacing="0" width="200" style="position: absolute;
            left: 713px; top: 115px; width: 207px" >
            <tr>
            <!---->
            <td height="18" bgcolor="#FFFFFF"><font color="#FFFFFF"><b><a
            href="DoLogout.jsp"><%= ds.getLogout() %></a></b></font></td>
            </tr>
            <tr>
            <td height="18" bgcolor="#FFFFFF"><b>
            <a href="UserPage.jsp">
            <%= ds.getGoMain() %></a></b></td>
            </tr>
            </table>
            </div>
            <form action = ViewDiscussion.jsp method ="get">
            <Center>
            <table width="346" border="1" cellpadding="0" cellspacing="0" bordercolor="#FF0000">
            <tr>
            <td width="179" bgcolor="#FFFFFF"><%= ds.getCategory() %> </td>
            <td width="153" bgcolor="#FFFFFF">
            <select name="txtCategory" >
            <option value=""></option>
            <%
            HashMap hmap = new HashMap();
            try{
            /*Create a URL object to load properties file*/
            URL url=new URL("http://127.0.0.1:8080/seven/path.properties");
            /*Open URL connection*/
            URLConnection ucon=url.openConnection();
            /*Retrieve the discussionlistpath property*/
            Properties prop = new Properties();
            prop.load(ucon.getInputStream());
            discussionpath = prop.getProperty("discussionlistpath");
            }
            catch(Exception ex)
            {
            ex.printStackTrace();
            }
            /*Obtain a DocumentBuilder object by calling the newInstance()method*/
            DocumentBuilderFactory docbfact = DocumentBuilderFactory.newInstance();
            /*Call the newDocumentBuilder() method to obtain a DocumentBuilder object*/
            DocumentBuilder docb = docbfact.newDocumentBuilder();
            /*Parse the discussionlist XML document*/
            Document doc = docb.parse(new FileInputStream(discussionpath));
            /*Obtain query elemens of the XML document as a NodeList object*/
            NodeList nl1 = doc.getElementsByTagName("query");
            /*Iterate through the NodeList object*/
            for(int int_1 = 0; int_1 < nl1.getLength(); int_1 ++)
            {
            /*Obtain the Node object*/
            Node n=nl1.item(int_1);
            /*Retrieve attributes of the Node objects*/
            NamedNodeMap nnp = n.getAttributes();
            Object obj1 =
            hmap.put((nnp.getNamedItem("Category")).getNodeValue(),(nnp.getNamedItem("Category")).
            getNodeValue());
            if(obj1==null)
            {
            %>
            <!--Populate category drop-down list with values of Category attributes-->
            <option><%=(nnp.getNamedItem("Category")).getNodeValue()%></option>
            <%
            }
            }
            %>
            </select></td>
            </tr>
            <tr>
            <td width="179" height="26" bgcolor="#FFFFFF"><%= ds.getSubCategory() %> </td>
            <td width="153" bgcolor="#FFFFFF">
            <!--Display sub-category drop-down list-->
            <select name="txtSubCategory" >
            <option value=""></option>
            <%
            hmap = new HashMap();
            /*Obtain a DocumentBuilder object by calling the newInstance()method*/
            docbfact = DocumentBuilderFactory.newInstance();
            /*Call the newDocumentBuilder() method to obtain a DocumentBuilder object*/
            docb = docbfact.newDocumentBuilder();
            /*Parse the discussionlist XML document*/
            doc = docb.parse(new FileInputStream(discussionpath));
            /*Obtain query elemens of the XML document as a NodeList object*/
            nl1 = doc.getElementsByTagName("query");
            /*Iterate through the NodeList object*/
            for(int int_1 = 0; int_1 < nl1.getLength(); int_1 ++)
            {
            /*Obtain the Node object that represents elements of an XML document*/
            Node n=nl1.item(int_1);
            /*Retrieve attributes of the Node objects*/
            NamedNodeMap nnp = n.getAttributes();
            Object obj1 = hmap.put((nnp.getNamedItem("SubCategory")).getNodeValue(),
            (nnp.getNamedItem("SubCategory")).
            getNodeValue());
            if(obj1==null)
            {
            %>
            <!--Populate category drop-down list with values of SubCategory attributes-->
            <option
            value="<%=(nnp.getNamedItem("SubCategory")).getNodeValue()%>"><%=
            (nnp.getNamedItem("SubCategory")).getNodeValue()%></option>
            <%
            }
            }
            %>
            </select></td>
            </tr>
            </table>
            <p>
            <input type="submit" value= <%= ds.getViewDiscussion() %>">
            </p>
            </Center>
            </form>
            <div align="justify">
            <table width="300" border="1" cellpadding="0" cellspacing="0" bordercolor="#FF0000"
            style="position: absolute; left: 335px; top: 311px; width: 371px;">
            <tr bgcolor="#FFFFFF">
            <td colspan="3">
            <!--Call getExistingDiscussion() method of DisplayString object to display country
            specific data-->
            <p align="center"><i><font color="#FF0000"><b> <%=
            ds.getExistingDiscussion() %> </b></font></i></p>
            </td>
            </tr>
            <tr>
            <td width="147" bgcolor="#FFFFFF"><font color="#FF0000"><b>&nbsp;<%=
            ds.getCategory() %></b></font></td>
            <td width="147" bgcolor="#FFFFFF"><font color="#FF0000"><b><%=
            ds.getSubCategory() %></b></font></td>
            <td width="147" bgcolor="#FFFFFF"><font color="#FF0000"><b><%=
            ds.getDiscussion() %></b></font></td>
            </tr>
            <%
            hmap = new HashMap();
            /*Obtain a DocumentBuilder object by calling the newInstance()method*/
            docbfact = DocumentBuilderFactory.newInstance();
            /*Call the newDocumentBuilder() method to obtain a DocumentBuilder object*/
            docb = docbfact.newDocumentBuilder();
            /*Parse the discussionlist XML document*/
            doc = docb.parse(new FileInputStream(discussionpath));
            /*Obtain query elemens of the XML document as a NodeList object*/
            nl1 = doc.getElementsByTagName("query");
            /*Iterate through the NodeList object*/
            for(int int_1 = 0; int_1 < nl1.getLength(); int_1 ++)
            {/*If end user does not specifies any search criteria display all discussion data*/
            Node n=nl1.item(int_1);
            NamedNodeMap nnp = n.getAttributes();
            if(tempCategory == null && tempSubCategory == null )
            {
            %>
            <tr>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("Category")).getNodeValue()%></td>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("SubCategory")).getNodeValue()%></td>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("Discussion")).getNodeValue()%></td>
            </tr>
            <%
            }
            else
            {
            if (!(tempCategory.equals("")))
            {
            if ((nnp.getNamedItem("Category")).getNodeValue().equals(tempCategory))
            {
            /*If end user specifies a category,match end user category with the node value. For
            /* matching categories, display discussion data.*/
            %>
            <tr>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("Category")).getNodeValue()%></td>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("SubCategory")).getNodeValue()%></td>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("Discussion")).getNodeValue()%></td>
            </tr>
            <%
            }
            }
            else
            {
            if (!(tempSubCategory.equals("")))
            {
            if ((nnp.getNamedItem("SubCategory")).getNodeValue().equals(tempSubCategory))
            {
            /*If end user specifies both category and sub category,match end user sub
            category with the node value. For matching categories, display discussion data.*/
            %>
            <tr>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("Category")).getNodeValue()%></td>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("SubCategory")).getNodeValue()%></td>
            <td width="147"
            bgcolor="#FFFFFF"><%=(nnp.getNamedItem("Discussion")).getNodeValue()%></td>
            </tr>
            <%
            }
            }
            }
            }
            }
            %>
            </table>
            </div>















            Download this listing.


            The above code creates the ViewDiscussion JSP page. The code creates an object of the DisplayString class to display country-specific data. The parse() method of the DocumentBuilder object parses the discussionlist.xml file and retrieves values of the Category and SubCategory attributes. The Topic and Sub Topic drop-down lists display the attribute values. When an end user selects a topic or sub topic to view discussions, the code iterates through the object tree node of the discussionlist XML document to retrieve data of all matching discussions. The code displays the data in the country-specific user interface in a tabular format.



            Figure 7-9 shows the user interface provided by the ViewDiscussion JSP page in French:






            Figure 7-9: User Interface of the ViewDiscussion JSP Page in French











            Selecting the Data as Java Objects




















            Selecting the Data as Java Objects



            You have learned how to use JdbcTemplate to perform raw data operations, but there is too much code to write, and in most cases, writing code for various select statements results in massive code duplication. Also, the queryForList() method returns a List of Map instance because it is not aware of the columns being selected. Unfortunately you still need to write lots of code to retrieve the data using ResultSets and create appropriate domain objects.


            In Listing 8-14, we show the use of Spring JDBC support classes. We are going to start the discussion with the use of the MappingSqlQuery class that will process the result set and create appropriate domain objects. Subclasses of MappingSqlQuery must implement its mapRow method. This method gets the values from the underlying ResultSet and returns the appropriate domain object.




            Listing 8-14: MappingSqlQuery Usage






            package com.apress.prospring.ch10.spring;

            import java.sql.ResultSet;
            import java.sql.SQLException;
            import java.sql.Types;
            import java.util.List;

            import javax.sql.DataSource;

            import org.springframework.jdbc.core.SqlParameter;
            import org.springframework.jdbc.core.support.JdbcDaoSupport;
            import org.springframework.jdbc.object.MappingSqlQuery;

            import com.apress.prospring.ch10.TestDao;
            import com.apress.prospring.ch10.domain.Test;

            public class JdbcTestDao implements TestDao, InitializingBean {
            private DataSource dataSource;

            private static final String SELECT_BY_NAME_SQL =
            "select * from Test where Name=?";

            abstract class AbstractSelect extends MappingSqlQuery {

            public AbstractSelect(DataSource dataSource, String sql) {
            super(dataSource, sql);
            }

            protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Test test = new Test();

            test.setName(rs.getString("Name"));
            test.setTestId(rs.getInt("TestId"));

            return test;
            }
            }

            class SelectByName extends AbstractSelect {
            public SelectByName (DataSource dataSource) {
            super(dataSource, SELECT_BY_NAME_SQL);
            declareParameter(new SqlParameter(Types.VARCHAR));
            }
            }

            public void afterPropertiesSet() throws Exception {
            if (dataSource == null) {
            throw new BeanCreationException("Must set dataSource on UserDao");
            }
            // more initialization code
            }

            public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
            }
            }














            The code in Listing 8-14 looks like it can actually do some database work! Let's review what we have implemented.


            First, we have the AbstractSelect class that extends MappingSqlQuery. This class maps the ResultSet columns and returns the appropriate Test domain object. All query classes that select users inevitably return User objects, so we can use the AbstractSelect class as a super- class of all select classes. The SelectByName class exemplifies this approach. The constructor takes only a single DataSource parameter, because this class performs one specific SQL query. This particular query requires two VARCHAR parameters, username and password. We declare the parameters using a call to declareParameter().



            AbstractSelect is a superclass for all concrete Select implementations that no longer have to implement the mapRow() method to create an instance of the domain object. This implies that all select statements need to return the same set of columns in order for AbstractSelect.mapRow() to be able to create the domain object.


            You may be wondering why AbstractSelect is still marked as abstract; after all, it already implements the mapRow() abstract method. There is a simple explanation for this: we should not use the AbstractSelect class to perform any SQL query, because different queries may use different parameters, and by marking this class abstract, we are forcing ourselves to subclass AbstractSelect for every SQL query.


            A more important benefit, however, is that by implementing each individual query in its own class, we can take advantage of the fact that most databases precompile the queries and cache the compiled query plan. This may not be very critical in simple select * from Table queries similar to those that result in a full table scan, but it becomes more important as the queries get more complex.






            Note 

            Some databases do not use the precompiled plans. The rationale behind this is that the statistics used to create the query plan may become out of date very quickly as records are inserted into the database. If a table has 100 rows, for example, it is not worth the effort to use index operations, but if a table has



            1,000,000 rows, then the additional work of an Index Scan operation is well justified. It is not impossible to imagine that a query plan was created for a table of 100 rows and, in the meantime, the number of rows increased to a million. If this was the case, the database would be using a very inefficient query plan, and the query time saved by using the precompiled query plan would be insignificant.



            Listing 8-15 shows how to use the concept of the AbstractSelect class in a TestDao implementation.



            Listing 8-15: TestDao Interface






            package com.apress.prospring.ch10;

            import java.util.List;

            public interface TestDao {

            public List getByName(String name);

            }













            To implement the method declared in the TestDao interface, we are going to use the SelectByName inner class (see Listing 8-16). To use this class, we need to instantiate, and we will do so in the afterPropertiesSet() method declared in InitializingBean.




            Listing 8-16: Implementation of getByName






            package com.apress.prospring.data.jdbc;

            public class JdbcTestDao implements TestDao, InitializingBean {

            private DataSource dataSource;

            private static final String SELECT_BY_NAME_SQL =
            "select * from Test where Name=?";

            abstract class AbstractSelect extends MappingSqlQuery {

            public AbstractSelect(DataSource dataSource, String sql) {
            super(dataSource, sql);
            }

            protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Test test = new Test();

            test.setName(rs.getString("Name"));
            test.setTestId(rs.getInt("TestId"));

            return test;
            }
            }

            class SelectByName extends AbstractSelect {

            public SelectByName(DataSource dataSource) {
            super(dataSource, SELECT_BY_NAME_SQL);
            declareParameter(new SqlParameter(Types.VARCHAR));
            }
            }

            private SelectByName selectByName;

            public void afterPropertiesSet() throws Exception {
            if (dataSource == null) {
            throw new BeanCreationException("Must set dataSource on testDao");
            }

                    selectByName = new SelectByName(dataSource);
            }

                public List getByName(String name) {
                    return selectByName.execute(new Object[] { name });
                }
                
            }














            This code links all pieces of the puzzle together. We created an abstract subclass of Spring's MappingSqlQuery named AbstractSelect, which takes care of mapping the results retrieved from the database into a Test object. We then subclassed AbstractSelect in SelectByName to execute the SQL query that selects rows from the Test table to return a test whose name matches the parameters passed to the method. In the SelectByName constructor, we have declared that name is the only parameter and is stored as VARCHAR in the database table. To select the Test objects by name, we need to pass the parameter value to the execute() method in an Object[] array containing a single value. The execute() method returns a List of objects returned by the mapRow() method. Because SelectByName is a subclass of AbstractSelect, and because the AbstractSelect.mapRow() method returns an instance of the Test class, we can safely cast the Objects in the List to Tests.


            Keep in mind that the order in which the parameters are passed is critical to the execute() method: if we accidentally change the order of the parameters, we do not get the correct results.


            Let us step back and look at the code we have written. If this is the only DAO implementation class in the application, it is just fine the way it is. However, most applications have more than one DAO class. If this is the case, we need to create a DAO class that has a dataSource property and implements InitializingBean and the appropriate DAO interface. This has a nasty code smell and as a result, we need to refactor the code. Spring supports this situation by providing a JdbcDaoSupport class.


            The refactored JdbcTestDao class is shown in Listing 8-17.




            Listing 8-17: Refactored JdbcTestDao






            public class JdbcTestDao 
            extends JdbcDaoSupport implements TestDao, InitializingBean {

            private DataSource dataSource;

            private static final String SELECT_BY_NAME_SQL =
            "select * from Test where Name=?";

            abstract class AbstractSelect extends MappingSqlQuery {
            /* omitted for clarity */
            }

            class SelectByName extends AbstractSelect { /* omitted for clarity */ }

            private SelectByName selectByName;

                protected void initDao() throws Exception {
                    super.initDao();
                    selectByName = new SelectByName(getDataSource());
                }
                
            public void afterPropertiesSet() throws Exception {
            if (dataSource == null)
            throw new BeanCreationException("Must set dataSource on testDao");

            selectByName = new SelectByName(dataSource);
            }

            public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
            }

            public List getByName(String name) {
            return selectByName.execute(name);
            }
            }














            This code is much cleaner after we remove the stricken-out lines and add the lines in bold. We had to remove some of the code because JdbcDaoSupport implements InitializingBean's afterPropertiesSet as final, and it also has the dataSource property. Because we still need to instantiate the query inner classes, JdbcDaoSupport allows us to override the initDao() method, which is called after all properties have been set on JdbcDaoSupport. We can access the dataSource property by calling the getDataSource() method.


            The code we now have looks like it does not need any more refactoring, and indeed, that would be true if we were only selecting and updating data. Before we take a look at the inevitable catch with inserts, we will discuss data updates.