Friday, October 16, 2009

Chapter 5. Building Eclipse Projects Using Ant











 < Day Day Up > 









Chapter 5. Building Eclipse Projects Using Ant







Eclipse is great for building your code. But for more advanced

project development, there's still something

missing. For example, what if you want not only to compile several

files at once, but also to copy files over to other build

directories, create JAR files and Javadoc, create new directories,

delete previous builds, and create deployment packages all at once?





You can do that with a build tool like

Apache's Ant (http://ant.apache.org/).

Ant is a Java-based build tool that can perform all these tasks and

much more. You can download Ant and run it on the command line,

automating your build tasks to not only compile code, but to create

JAR files, move and create classes, delete and make directories, and

a great deal more.





The good news here is that Ant comes built into Eclipse, ready to

use. Ant is the premier build tool for Java development, and

we'll get an idea why in this chapter. As your

projects become more and more elaborate, Ant can automate dozens of

tasks that you'd otherwise need to perform manually.

When you have things set up to run with Ant, all

you've got to do is point and click to perform a

complete build without having to take dozens of separate steps, which

can save many steps omitted in error over the development process.





The fact that Ant comes built into Eclipse means that

it's easier to use for us than for all those

developers who use it on the command line. To see how this works,

we'll start with a quick example.



















     < Day Day Up > 



    10.7. static Class Members



    10.7. static Class Members


    There is an important exception to the
    rule that each object of a class has its own copy of all the data members of the
    class. In certain cases, only one copy of a variable should be shared by all
    objects of a class. A static data member is used for
    these and other reasons. Such a variable represents "class-wide" information
    (i.e., a property of the class shared by all instances, not a property of a
    specific object of the class). The declaration of a static member begins with keyword static. Recall that the versions of class GradeBook
    in Chapter
    7 use static data members to store
    constants representing the number of grades that all GradeBook objects
    can hold.


    Let us further motivate the need for static class-wide data with an example. Suppose that we have a video
    game with Martians and other space creatures. Each Martian tends to be brave and willing to attack other space
    creatures when the Martian is aware that there are
    at least five Martians present. If fewer than five are present, each
    Martian becomes cowardly. So each Martian needs to know the
    martianCount. We could endow each instance of class Martian
    with martianCount as a data member. If we do, every Martian will have a separate copy of the data member. Every time
    we create a new Martian, we'll have to update the data member martianCount in all
    Martian objects. Doing this would require
    every Martian object to have, or have access
    to, handles to all other Martian objects in
    memory. This wastes space with the redundant copies and wastes time in updating
    the separate copies. Instead, we declare martianCount to be
    static. This makes martianCount class-wide data. Every
    Martian can access martianCount as if it
    were a data member of the Martian, but only one copy of the
    static variable martianCount is maintained by
    C++. This saves space. We save time by having the Martian constructor
    increment static variable martianCount and having the
    Martian destructor decrement martianCount. Because there is only one copy, we do not have to
    increment or decrement separate copies of martianCount for each
    Martian object.



    Performance Tip 10.3








    Use static data members to save storage when a single copy of the
    data for all objects of a class will
    suffice.



    Although they may seem like global variables, a class's
    static data members have class scope. Also, static members can
    be declared public, private or protected. A
    fundamental-type static data member is initialized by default to
    0. If you want a different initial value, a static data member
    can be initialized once (and only once). A
    const static data member of int or enum type can be initialized in its declaration in the class
    definition. However, all other static data
    members must be defined at file scope (i.e., outside the body of the class
    definition) and can be initialized only in those definitions. Note that
    static data members of class types (i.e.,
    static member objects) that have
    default constructors need not be initialized because their default constructors
    will be called.


    A class's private and protected static members are normally accessed through
    public member functions of the class or through
    friends of the class. (In Chapter
    12, we'll see that a class's private and protected static
    members can also be accessed through protected member functions of the
    class.) A class's static members exist even when
    no objects of that class exist. To access a public static class member when no objects of the class exist, simply
    prefix the class name and the binary scope resolution operator (::) to the name of the data member. For example, if our
    preceding variable martianCount is public, it can be accessed
    with the expression Martian::martianCount when there are no
    Martian objects. (Of course, using public data is
    discouraged.)


    A class's public static class
    members can also be accessed through any object of that class using the object's
    name, the dot operator and the name of the member (e.g.,
    myMartian.martianCount). To access a
    private or protected static class
    member when no objects of the class exist, provide a public static member function and call the function by prefixing its name
    with the class name and binary scope resolution operator. (As we'll see in Chapter
    12, a protected static member function can
    serve this purpose, too.) A static member
    function is a service of the class, not of a
    specific object of the class.



    Software Engineering Observation 10.11








    A class's static data members
    and static member functions exist and can be
    used even if no objects of that class have been
    instantiated.



    The program of Figs. 10.21–10.23 demonstrates a private
    static
    data member called count (Fig. 10.21, line 21) and a public
    static
    member function called getCount (Fig. 10.21, line 15). In Fig. 10.22, line 14
    defines and initializes the data member count to zero at file
    scope
    and lines 18–21 define static member function
    getCount. Notice that neither line 14 nor line 18
    includes keyword static, yet both lines refer to
    static class members. When static is
    applied to an item at file scope, that item becomes known only in that file. The
    static members of the class need to be
    available from any client code that accesses the file, so we cannot declare them
    static in the .cpp file—we declare them static only
    in the .h file. Data member count
    maintains a count of the number of objects of class Employee that have been instantiated. When objects of class
    Employee exist, member count
    can be referenced through any member function of an Employee object—in
    Fig. 10.22,
    count is referenced by both line 33 in the
    constructor and line 48 in the destructor. Also, note that since count
    is an int, it could have been initialized in the
    header file in line 21 of Fig 10.21











    Fig. 10.21. Employee class definition with a
    static data member to track the number of Employee objects in
    memory.


     

     1   // Fig. 10.21: Employee.h
    2 // Employee class definition.
    3 #ifndef EMPLOYEE_H
    4 #define EMPLOYEE_H
    5
    6 class Employee
    7 {
    8 public:
    9 Employee( const char * const, const char * const ); // constructor
    10 ~Employee(); // destructor
    11 const char *getFirstName() const; // return first name
    12 const char *getLastName() const; // return last name
    13
    14 // static member function
    15 static int getCount(); // return number of objects instantiated
    16 private:
    17 char *firstName;
    18 char *lastName;
    19
    20 // static data
    21 static int count; // number of objects instantiated
    22 }; // end class Employee
    23
    24 #endif













    Fig. 10.22. Employee class member-function
    definitions.


     

     1   // Fig. 10.22: Employee.cpp
    2 // Employee class member-function definitions.
    3 #include <iostream>
    4 using std::cout;
    5 using std::endl;
    6
    7 #include <cstring> // strlen and strcpy prototypes
    8 using std::strlen;
    9 using std::strcpy;
    10
    11 #include "Employee.h" // Employee class definition
    12
    13 // define and initialize static data member at file scope
    14 int Employee::count = 0; // cannot include keyword static
    15
    16 // define static member function that returns number of
    17 // Employee objects instantiated (declared static in Employee.h)
    18 int Employee::getCount()
    19 {
    20 return count;
    21 } // end static function getCount
    22
    23 // constructor dynamically allocates space for first and last name and
    24 // uses strcpy to copy first and last names into the object
    25 Employee::Employee( const char * const first, const char * const last )
    26 {
    27 firstName = new char[ strlen( first ) + 1 ]; // create space
    28 strcpy( firstName, first ); // copy first into object
    29
    30 lastName = new char[ strlen( last ) + 1 ]; // create space
    31 strcpy( lastName, last ); // copy last into object
    32
    33 count++; // increment static count of employees
    34
    35 cout << "Employee constructor for " << firstName
    36 << ' ' << lastName << " called." << endl;
    37 } // end Employee constructor
    38
    39 // destructor deallocates dynamically allocated memory
    40 Employee::~Employee()
    41 {
    42 cout << "~Employee() called for " << firstName
    43 << ' ' << lastName << endl;
    44
    45 delete [] firstName; // release memory
    46 delete [] lastName; // release memory
    47
    48 count--; // decrement static count of employees
    49 } // end ~Employee destructor
    50
    51 // return first name of employee
    52 const char *Employee::getFirstName() const
    53 {
    54 // const before return type prevents client from modifying
    55 // private data; client should copy returned string before
    56 // destructor deletes storage to prevent undefined pointer
    57 return firstName;
    58 } // end function getFirstName
    59
    60 // return last name of employee
    61 const char *Employee::getLastName() const
    62 {
    63 // const before return type prevents client from modifying
    64 // private data; client should copy returned string before
    65 // destructor deletes storage to prevent undefined pointer
    66 return lastName;
    67 } // end function getLastName





    Common Programming Error 10.10








    It is a compilation
    error to include keyword static in the definition of a
    static data members at file
    scope.



    In Fig. 10.22, note the use of the
    new operator (lines 27 and 30) in the Employee constructor to dynamically allocate the correct amount of
    memory for members firstName and lastName. If
    the new operator is unable to fulfill the
    request for memory for one or both of these character arrays, the program will
    terminate immediately. In Chapter
    16, we'll provide a better mechanism for dealing
    with cases in which new is unable to allocate
    memory.


    Also note in Fig. 10.22
    that the implementations of functions getFirstName (lines 52–58) and
    getLastName (lines 61–67) return pointers to const character data. In this implementation, if the client wishes
    to retain a copy of the first name or last name, the client is responsible for
    copying the dynamically allocated memory in the Employee object after
    obtaining the pointer to const character data
    from the object. It is also possible to implement getFirstName and
    getLastName, so the client is required to
    pass a character array and the size of the array to each function. Then the
    functions could copy the first or last name into the character array provided by
    the client. Once again, note that we could have used class string here
    to return a copy of a string object to the caller
    rather than returning a pointer to the private data.


    Figure
    10.23 uses static member function
    getCount to determine the number of
    Employee objects currently instantiated. Note
    that when no objects are instantiated in the program, the
    Employee::getCount() function call is issued
    (lines 14 and 38). However, when objects are instantiated, function
    getCount can be called through either of
    the objects, as shown in the statement in lines 22–23, which uses pointer
    e1Ptr to invoke function getCount. Note that using
    e2Ptr->getCount() or Employee::getCount() in line 23 would
    produce the same result, because getCount always accesses the same
    static member count.













    Fig. 10.23. static data member tracking the
    number of objects of a class.


     

     1   // Fig. 10.23: fig10_23.cpp
    2 // static data member tracking the number of objects of a class.
    3 #include <iostream>
    4 using std::cout;
    5 using std::endl;
    6
    7 #include "Employee.h" // Employee class definition
    8
    9 int main()
    10 {
    11 // use class name and binary scope resolution operator to
    12 // access static number function getCount
    13 cout << "Number of employees before instantiation of any objects is "
    14 << Employee::getCount() << endl; // use class name
    15
    16 // use new to dynamically create two new Employees
    17 // operator new also calls the object's constructor
    18 Employee *e1Ptr = new Employee( "Susan", "Baker" );
    19 Employee *e2Ptr = new Employee( "Robert", "Jones" );
    20
    21 // call getCount on first Employee object
    22 cout << "Number of employees after objects are instantiated is "
    23 << e1Ptr->getCount();
    24
    25 cout << "\n\nEmployee 1: "
    26 << e1Ptr->getFirstName() << " " << e1Ptr->getLastName()
    27 << "\nEmployee 2: "
    28 << e2Ptr->getFirstName() << " " << e2Ptr->getLastName() << "\n\n";
    29
    30 delete e1Ptr; // deallocate memory
    31 e1Ptr = 0; // disconnect pointer from free-store space
    32 delete e2Ptr; // deallocate memory
    33 e2Ptr = 0; // disconnect pointer from free-store space
    34
    35 // no objects exist, so call static member function getCount again
    36 // using the class name and the binary scope resolution operator
    37 cout << "Number of employees after objects are deleted is "
    38 << Employee::getCount() << endl;
    39 return 0;
    40 } // end main



    Number of employees before instantiation of any objects is 0
    Employee constructor for Susan Baker called.
    Employee constructor for Robert Jones called.
    Number of employees after objects are instantiated is 2

    Employee 1: Susan Baker
    Employee 2: Robert Jones

    ~Employee() called for Susan Baker
    ~Employee() called for Robert Jones
    Number of employees after objects are deleted is 0




    Software Engineering Observation 10.12








    Some
    organizations specify in their software engineering standards that all calls to
    static member functions be made using the
    class name rather than an object
    handle.



    A member function should be declared
    static if it does not access non-static data members or
    non-static member functions of the class. Unlike non-static
    member functions, a static member function does not have a
    this pointer, because static data members and static
    member functions exist independently of any objects of a class. The
    this pointer must refer to a specific object
    of the class, and when a static member
    function is called, there might not be any objects of its class in memory.



    Common Programming Error 10.11








    Using the this pointer in a
    static member function is a compilation
    error.




    Common Programming Error 10.12








    Declaring a static member
    function const is a compilation error. The
    const qualifier indicates that a function cannot
    modify the contents of the object in which it operates, but static member functions exist and operate independently of any
    objects of the class.



    Lines 18–19 of Fig. 10.23 use operator new
    to dynamically allocate two Employee objects.
    Remember that the program will terminate immediately if it is unable to allocate
    one or both of these objects. When each Employee object is allocated,
    its constructor is called. When delete is used in lines
    30 and 32 to deallocate the Employee objects, each object's destructor
    is called.



    Error-Prevention Tip 10.2








    After deleting
    dynamically allocated memory, set the pointer that referred to that memory to
    0. This disconnects the pointer from the
    previously allocated space on the free store. This space in memory could still
    contain information, despite having been deleted. By setting the pointer to
    0, the program loses any access to that
    free-store space, which, in fact, could have already been reallocated for a
    different purpose. If you didn't set the pointer to 0, your code could inadvertently access this new
    information, causing extremely subtle, nonrepeatable logic
    errors.





     


    Creating the Object











    Creating the Object


    With a better understanding of the XHR and different request models, we can now focus on creating the object. Creating the request object is trivial in comparison to the power that is unleashed when applying it to a project.


    To create the request object, you must check to see if the browser uses the XHR or the ActiveX object. The primary difference between the objects is the browsers that use them. Windows Internet Explorer (IE) 5 and above use the ActiveX object, whereas Mozilla, Firefox, Netscape, Opera, and Safari use the native JavaScript XHR object. The second difference is the way in which we create each object: Windows IE requires the name of the object to be passed as a parameter to the ActiveX constructor, whereas the other browsers provide us with the native JavaScript object, which only we need to instantiate:


    function makeRequest(url)
    {
    if(window.XMLHttpRequest)
    {
    request = new XMLHttpRequest();
    }
    else if(window.ActiveXObject)
    {
    request = new ActiveXObject("Msxml2.XMLHTTP");
    }

    sendRequest(url);
    }


    As you can see from the code sample, the object creation is really a very simple task. We create a method named makeRequest to handleyou guessed itmaking the request and to decipher what type of object the browser uses by creating a condition that checks for the native XHR object. If this object is not available, we check for the ActiveXObject. After the correct object type has been identified for the current browser, the correct object is instantiated and a request object is created. This object can now be used to access all the properties and methods listed in Tables 2.1 and 2.2, which are available to the XHR object.


    Table 2.1. A List of XHR Properties and Corresponding Definitions

    Properties

    Definitions

    onreadystatechange

    An event handler that fires when the state of the request object changes.

    readyState

    Returns number values that indicate the current state of the object. These values are listed in Table 2.3.

    responseText

    String version of the response from the server.

    responseXML

    DOM-compatible document object of the response from the server.

    status

    Status code of the response from the server.

    statusText

    A status message returned as a string.




    Table 2.2. A List of XHR Methods and Corresponding Definitions

    Methods

    Definitions

    Abort()

    Cancels the current HTTP request.

    getAllResponseHeaders()

    Retrieves the values of all the HTTP headers.

    geTResponseHeader("label")

    Retrieves the value of a specified HTTP header from the response body.

    Open("method", "URL"[, asyncFlag[, "userName"[, "password"]]])

    Initializes an MSXML2.XMLHTTP or Microsoft.XMLHTTP request, and specifies the method, URL, and authentication information for the request.

    Send(content)

    Sends an HTTP request to the server and receives a response.

    SetRequestHeader("label", "value")

    Specifies the value of an HTTP header based on the label.



    These two tables might look like they have only a small number of options, but as you will find in the following chapters, they pack a lot of power when used with back-end code, a database, and a dynamic front end.












    Chapter 2. Metaphors for a Richer Understanding of Software Development










     < Free Open Study > 







    Chapter 2. Metaphors for a Richer Understanding of Software Development



    cc2e.com/0278



    Contents



    • The Importance of Metaphors page 9

    • How to Use Software Metaphors page 11

    • Common Software Metaphors page 13



    Related Topic



    • Heuristics in design: "Design Is a Heuristic Process" in Section 5.1



    Computer science has some of the most colorful language of any field. In what other field can you walk into a sterile room, carefully controlled at 68°F, and find viruses, Trojan horses, worms, bugs, bombs, crashes, flames, twisted sex changers, and fatal errors?



    These graphic metaphors describe specific software phenomena. Equally vivid metaphors describe broader phenomena, and you can use them to improve your understanding of the software-development process.



    The rest of the book doesn't directly depend on the discussion of metaphors in this chapter. Skip it if you want to get to the practical suggestions. Read it if you want to think about software development more clearly.














       < Free Open Study > 



      Specifying a Server Configuration









      Specifying a Server Configuration


      JBoss is not only powerful, it's also very configurable. At the center of JBoss is a JMX microkernel that manages the MBeans that control the various services in the server. The JBoss documentation describes the microkernel as a spine, and the analogy fits well. JBoss in its minimum configuration is like the brainstem sitting at the top of the spine. It provides all the core autonomic features (to steal some thunder from IBM's autonomic computing initiative). The spinal column is the attachment point for all the MBeans that want to talk to the brainstem and to each other over the spinal cord. This architecture provides a strong backbone to build on, but at the same time it can work at its most minimal state and still do amazing things. It makes JBoss useful for everything from very small, embedded applications all the way up to full-blown enterprise server applications, all on the same spinal column.


      Having a flexible architecture is pointless unless you can make use of that flexibility. JBoss provides several different configurations that range from a barebones server with no J2EE capabilities to a superserver with more services than any single application could possibly use. Figure 1-4 shows the three standard server configurations.



      Developer's Notebook 1-4. Server configurations



      Here are descriptions of the server configurations:



      Note: There is a huge difference in the size of these configurations. The minimal configuration can start up in just a few seconds, whereas it can take nearly a minute for the all configuration to completely start on a typical developer machine.


      minimal


      This configuration provides the bare services you might need in the simplest application: logging, JNDI naming services, and URL deployment scanning. You'd use this setup in tight memory situations, or when you want to use JMX to control exactly which MBeans are loaded, and when and how they are loaded. This configuration doesn't provide support for EJBs, web applications, JMS, or any other high-level services. It's just a raw microkernel waiting to be molded.


      default


      This is a lightweight J2EE configuration; it's the one most people use. It provides most of the common J2EE services, but it doesn't include IIOP, JAXR, or clustering services, for example. These services can be added easily to this configuration.


      all


      This configuration includes everything in JBoss. If you need any extra services not provided in the default configuration, this is the place to start. It's often easier to start from the all configuration and take out unwanted services than to start with the default configuration and add the desired services.



      Note: The all configuration includes everthing but proverbial kitchen sink.



      How do I do that?


      You specify a particular configuration using the -c command to the run script. To run the minimal configuration, for example, use -c minimal.


      Here is an example:



      Note: You can also use --configuration = minimal, if you prefer to use long configuration names.


      [bin]$ ./run.sh -c minimal











        18.12. String Stream Processing



        18.12. String Stream Processing


        In addition to standard stream I/O
        and file stream I/O, C++ stream I/O includes capabilities for inputting from,
        and outputting to, strings in memory.
        These capabilities often are referred to as in-memory I/O or string stream processing.


        Input from a string is supported by
        class istringstream.
        Output to a string is supported by class ostringstream. The class names
        istringstream and ostringstream are actually aliases defined
        by the typedefs


        typedef basic_istringstream< char > istringstream;
        typedef basic_ostringstream< char > ostringstream;


        Class templates basic_istringstream and
        basic_ostringstream provide the same functionality as classes
        istream and ostream plus other
        member functions specific to in-memory formatting. Programs that use in-memory
        formatting must include the <sstream> and
        <iostream> header files.


        One application of these techniques is
        data validation. A program can read an entire line at a time from the input
        stream into a string. Next, a validation
        routine can scrutinize the contents of the string and correct (or repair) the data, if necessary. Then the
        program can proceed to input from the string, knowing that the input data is in the proper
        format.


        Outputting to a string is a nice
        way to take advantage of the powerful output formatting capabilities of C++
        streams. Data can be prepared in a string
        to mimic the edited screen format. That string
        could be written to a disk file to preserve the screen image.


        An ostringstream object uses a string object to store the output data. The str member function of class
        ostringstream returns a copy of that string.


        Figure 18.11 demonstrates an
        ostringstream object. The program creates ostringstream object
        outputString (line 15) and uses the stream insertion
        operator to output a series of strings and numerical values to the
        object.













        Fig. 18.11. Using a dynamically allocated
        ostringstream object.


         

         1   // Fig. 18.11: Fig18_11.cpp
        2 // Using a dynamically allocated ostringstream object.
        3 #include <iostream>
        4 using std::cout;
        5 using std::endl;
        6
        7 #include <string>
        8 using std::string;
        9
        10 #include <sstream> // header file for string stream processing
        11 using std::ostringstream; // stream insertion operators
        12
        13 int main()
        14 {
        15 ostringstream outputString; // create ostringstream instance
        16
        17 string string1( "Output of several data types " );
        18 string string2( "to an ostringstream object:" );
        19 string string3( "\n double: " );
        20 string string4( "\n int: " );
        21 string string5( "\naddress of int: " );
        22
        23 double double1 = 123.4567;
        24 int integer = 22;
        25
        26 // output strings, double and int to ostringstream outputString
        27 outputString << string1 << string2 << string3 << double1
        28 << string4 << integer << string5 << &integer;
        29
        30 // call str to obtain string contents of the ostringstream
        31 cout << "outputString contains:\n" << outputString.str();
        32
        33 // add additional characters and call str to output string
        34 outputString << "\nmore characters added";
        35 cout << "\n\nafter additional stream insertions,\n"
        36 << "outputString contains:\n" << outputString.str() << endl;
        37 return 0;
        38 } // end main



        outputString contains:
        Output of several data types to an ostringstream object:
        double: 123.457
        int: 22
        address of int: 0012F540

        after additional stream insertions,
        outputString contains:
        Output of several data types to an ostringstream object:
        double: 123.457
        int: 22
        address of int: 0012F540
        more characters added



        Lines 27–28 output string string1, string
        string2
        , string string3, double double1, string
        string4
        , int integer, string string5 and the address of
        int integer—all to outputString in
        memory. Line 31 uses the stream insertion operator and the call
        outputString.str() to display a copy of the
        string created in lines 27–28. Line 34
        demonstrates that more data can be appended to the string in memory by simply issuing another stream insertion
        operation to outputString. Lines 35–36 display string
        outputString
        after appending additional characters.


        An istringstream object inputs
        data from a string in memory to program
        variables. Data is stored in an istringstream
        object as characters. Input from the istringstream object works identically to input from any file. The end of
        the string is interpreted by the istringstream object as
        end-of-file.


        Figure
        18.12 demonstrates input from an istringstream object. Lines 15–16
        create string input containing the data and istringstream
        object inputString constructed to contain the data in string
        input
        . The string input contains the data


        Input test 123 4.7 A


        which, when read as input to the program, consist of two
        strings ("Input" and "test"), an int (123),
        a double (4.7) and a char ('A'). These
        characters are extracted to variables string1, string2,
        integer, double1 and character in line 23.













        Fig. 18.12. Demonstrating input from an
        istringstream object.


         

         1   // Fig. 18.12: Fig18_12.cpp
        2 // Demonstrating input from an istringstream object.
        3 #include <iostream>
        4 using std::cout;
        5 using std::endl;
        6
        7 #include <string>
        8 using std::string;
        9
        10 #include <sstream>
        11 using std::istringstream;
        12
        13 int main()
        14 {
        15 string input( "Input test 123 4.7 A" );
        16 istringstream inputString( input );
        17 string string1;
        18 string string2;
        19 int integer;
        20 double double1;
        21 char character;
        22
        23 inputString >> string1 >> string2 >> integer >> double1 >> character;
        24
        25 cout << "The following items were extracted\n"
        26 << "from the istringstream object:" << "\nstring: " << string1
        27 << "\nstring: " << string2 << "\n int: " << integer
        28 << "\ndouble: " << double1 << "\n char: " << character;
        29
        30 // attempt to read from empty stream
        31 long value;
        32 inputString >> value;
        33
        34 // test stream results
        35 if ( inputString.good() )
        36 cout << "\n\nlong value is: " << value << endl;
        37 else
        38 cout << "\n\ninputString is empty" << endl;
        39
        40 return 0;
        41 } // end main



        The following items were extracted
        from the istringstream object:
        string: Input
        string: test
        int: 123
        double: 4.7
        char: A

        inputString is empty



        The data is then output in lines 25–28.
        The program attempts to read from inputString again in line 32. The
        if condition in line 35 uses function good (Section
        15.8) to test if any data remains. Because no
        data remains, the function returns false and the else part of
        the if...else statement is executed.


         


        Virtual Hosting with Tomcat 4.x





























        Chapter 19 -
        Shared Tomcat Hosting
        byVivek Chopra, Ben Galbraithet al.
        Wrox Press 2003































        Virtual Hosting with Tomcat 4.x


        Virtual host configuration in Tomcat 4.x is different from that in Tomcat 3.x in a number of ways. Mostly, this has been because Tomcat 4.x was rewritten from scratch and the whole architecture of the various components of Tomcat has changed.


        One common factor between the two configurations is that the configuration in Apache for transferring requests for servlets and JSP pages to the Tomcat process has more or less remained the same while using the mod_jk adapter. In addition, Tomcat 4.x can also use mod_webapp, which we will see later.


        We first take a look at configuring Tomcat 4.x as a standalone server implementing virtual hosts.




        Tomcat 4.x As a Standalone Server


        Virtual host definitions in Tomcat 4.x have to be provided in the server configuration file, server.xml itself. Once these additions have been done, we simply need to restart Tomcat to use the virtual host definitions.



        Editing server.xml


        The default sample server.xml file of the Tomcat 4.x build contains two services - one for the standalone server, and one for the server that cooperates with an Apache web server using the WARP protocol. We can remove the definition for the second service and reuse that of the first service.


        The top-level <Service> element for the standalone server would look like the following:





        <Server port="8005" shutdown="SHUTDOWN" debug="0">
        <Service name="Tomcat-Standalone">

        </Service>
        </Server>


        The rest of the configuration would go inside this <Service> container element. We then add the Connectors to be used for this service. Since this is a standalone server, we only need the HTTP/1.1 connector to communicate with the outside world.


        We add the following Connector definition inside the <Service> element.




        <Connector className="org.apache.catalina.connector.http.HttpConnector"
        port="8080"
        minProcessors="5"
        maxProcessors="75"
        enableLookups="true"
        acceptCount="10"
        debug="0"
        connectionTimeout="60000"/>


        This sets up Tomcat to listen to port 8080 for incoming web requests.


        We now add the <Engine> element to the <Service> by adding the following lines just after the <Connector> element and inside the <Service> element:




        <Engine name="Standalone" defaultHost="europa.dom" debug="0">

        </Engine>


        This specifies an engine for the service that processes incoming requests from the connectors. After any request has been received by the connector and passed on to the engine, the engine would then take a look at the HTTP headers, especially the Host:tag, and determine which of the virtual host definitions that it handles would receive the request. If none of the virtual host seems to match the request headers, the engine passes on the request to a default host. The name of the default virtual host is specified in the attribute defaultHost. The value of this attribute must match a <Host> definition in the engine.


        For our purposes, we see that the virtual host definition of europa.dom is served by default when a web request is made using the IP address (instead of a host name).


        We next add the virtual host definition of europa.dom to the engine. We add the following content inside the <Engine> element:




        <Host name="europa.dom" debug="0"
        appBase="/home/sites/europa.dom/webapps"
        unpackWARs="true">
        </Host>



        This defines a virtual host entry for europa.dom in Tomcat. We further add some logging functionality to this virtual host by placing the following content within the <Host> element:




        <Valve className="org.apache.catalina.valves.AccessLogValve"
        directory="/home/sites/europa.dom/logs"
        prefix="europa_access."
        suffix=".log"
        pattern="common" />

        <Logger className="org.apache.catalina.logger.FileLogger"
        directory="/home/sites/europa.dom/logs"
        prefix="europa_catalina."
        suffix=".log"
        timestamp="true"/>


        This defines two logging services for this virtual host. The <Logger> element is covered in Chapter 5.


        We now add the contexts to serve for this virtual host, inside the <Host> element.




        <Context path="" docBase="ROOT" debug="0"/>
        <Context path="/shop" docBase="shop" debug="0" />


        We have added two contexts here. The first one is the default context with an empty context path. This context either has to be defined explicitly or provided automatically by Tomcat (that is, without you defining it here) if you have a web application called ROOT in the appBase of the virtual host.


        As an example of how new web applications other than the default one have to be added to the site, we have also added the web application called shop which uses the /shop context path.


        One nice thing about context definitions in Tomcat 4.x is that it provides automatic contexts in case you haven't defined them in the host definition. To provide this functionality, Tomcat looks at directories inside the appBase directory. If these directories follow the web application structure, specifically if they contain a WEB-INF/web.xml file in them, Tomcat automatically provides contexts with context paths equal to the name of the directory under appBase.




        Remember that the default parameters of these contexts are picked up from $CATALINA_HOME/conf/web.xml.



        If you need to override some global parameters to these contexts, you need to place them within the <Context></Context> elements. Examples would be, logging for this context in a separate file, context parameters, resource definitions, and so on.


        This completes the virtual host definition for europa.dom. For the virtual host callisto.dom, we add another virtual host entry similar to that of europa.dom:


        We save this file as $CATALINA_HOME/conf/server.xml and restart the Tomcat service.


        We first check the test JSP file in the europa.dom virtual host using the URL http://europa.dom:8080/test.jsp:






        We then check out the callisto.dom using the URL http://callisto.dom:8080/test.jsp:





        We then check whether the default host setting of the <Engine> element is working properly. For this we use a host name other than the ones specified explicitly as <Host> definitions. Let's try accessing using the IP address 10.0.0.1:





        As we can see, Tomcat serves the contents of the europa.dom virtual host, as defined in the default virtual host entry of the engine.


        Now that we have Tomcat 4.0 working as a standalone server for the virtual hosts, let's make it work along with Apache.








        Tomcat 4.0 with Apache


        As we saw in the Tomcat 3.3 Apache configuration section, Apache communicates with Tomcat using connectors like mod_jk. In Tomcat 4.x, support for another kind of adapter called mod_webapp has also been added. While we will concentrate on using mod_jk in this section, as it is considered to be more stable than the new mod_webapp, we will touch mod_webapp briefly at the end.


        We can use the same mod_jk connector in both versions of Tomcat (it's the same at the Apache end after all). However, if you rely on Tomcat to automatically generate the mod_jk.conf file, the procedure for Tomcat 4.x is a bit different to the one in Tomcat 3.3 and will be explained shortly.



        Tomcat 4.0 with the AJP 1.3 Connector


        To use Tomcat along with the AJP connectors of Apache, we need to modify the previously used server.xml file.



        First of all we change the name of the <Service> element to reflect the fact that Tomcat is now working along with Apache. While this is not strictly necessary, it helps in distinguishing between multiple configuration files of Tomcat lying in your machine:




        [...]
        <Service name="Tomcat-Apache-mod_jk">
        [...]


        We then replace the HTTP/1.1 <Connector> definition with the given AJP1.3 <Connector> definition instead.




        The HTTP connector could be left in place if you still want Tomcat to handle web requests directly at port 8080.





        <Connector className="org.apache.ajp.tomcat4.Ajp13Connector"
        port="8009"
        minProcessors="5"
        maxProcessors="75"
        acceptCount="10"
        debug="0"/>


        That is all we have to do to configure Tomcat to communicate with the Apache mod_jk adapter. However, to make our configuration of Apache easier, we can ask Tomcat to auto-generate the required Apache configuration files.





        Auto Generating modjk.conf


        To auto-generate the Apache configuration files, we have to add appropriate <ApacheConfig> listeners at various points in the server.xml file.


        We start by generating the global mod_jk configuration, which is done by adding the following just after the <Service> opening element in the server.xml file:




        <Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
        modJk="/usr/local/apache/libexec/mod_jk.so"
        jkDebug="info"
        workersConfig="/usr/local/tomcat/conf/jk/workers.properties"
        jkLog="/usr/local/tomcat/logs/mod_jk.log"
        noRoot="false"
        forwardAll="false" />


        You would notice the familiar noRoot and forwardAll attributes from the <ApacheConfig> element of Tomcat 3.3. They have similar functions here:




        • The modJk attribute contains the path to the mod_jk connector module for Apache. This path would be used in the auto-generated output to inform Apache where to load the module from.





        • jkDebug attribute indicates the level of logging which could be one of debug, info, error, or emerg. Not setting this attribute would cause logging to be disabled.





        • workersConfig contains the path to the worker.properties file that needs to be mentioned in the generated file for the connector to find the workers to send the request to. jkLog contains the path to the file to be used for logging.





        To generate the <VirtualHost> sections we add the following just after the opening element of all the <Host> entries.




        <Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
        append="true" />


        The final server.xml file should look like this:




        <Server port="8005" shutdown="SHUTDOWN" debug="0">
        <Service name="Tomcat-Apache-modjk">
        <Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
        modJk="/usr/local/apache/libexec/mod_jk.so"
        jkDebug="info"
        workersConfig="/usr/local/tomcat/build/conf/jk/workers.properties"
        jkLog="/usr/local/tomcat/logs/mod_jk.log"
        noRoot="false"
        forwardAll="false" />

        <Connector className="org.apache.ajp.tomcat4.Ajp13Connector"
        port="8009"
        minProcessors="5"
        maxProcessors="75"
        acceptCount="10"
        debug="0"/>

        <Engine name="Standalone" defaultHost="europa.dom" debug="0">

        <!-- Virtual host definition for europa.dom -->
        <Host name="europa.dom" debug="0"
        appBase="/home/sites/europa.dom/webapps"
        unpackWARs="true">
        <Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
        append="true" />
        <Valve className="org.apache.catalina.valves.AccessLogValve"
        directory="/home/sites/europa.dom/logs"
        prefix="europa_access."
        suffix=".log"
        pattern="common"/>

        <Logger className="org.apache.catalina.logger.FileLogger"
        directory="/home/sites/europa.dom/logs"
        prefix="europa_catalina."
        suffix=".log"
        timestamp="true"/>
        <Context path="" docBase="ROOT" debug="0"/>

        <Context path="/shop" docBase="shop" debug="0" />

        </Host>

        <!-- Virtual host definition for callisto.dom -->
        <Host name="callisto.dom"
        debug="0"
        appBase="/home/sites/callisto.dom/webapps"
        unpackWARs="true">
        <Listener className="org.apache.ajp.tomcat4.config.ApacheConfig"
        append="true" />

        <Valve className="org.apache.catalina.valves.AccessLogValve"
        directory="/home/sites/callisto.dom/logs"
        prefix="callisto_access."
        suffix=".log"
        pattern="common"/>

        <Logger className="org.apache.catalina.logger.FileLogger"
        directory="/home/sites/callisto.dom/logs"
        prefix="callisto_catalina."
        suffix=".log"
        timestamp="true"/>
        <Context path="" docBase="ROOT" debug="0"/>

        <Context path="/shop" docBase="shop" debug="0" />

        </Host>
        </Engine>
        </Service>
        </Server>


        Restart Tomcat to get the auto-generated mod_jk.conf file. It will be in $CATALINA_HOME/conf/auto.


        While we can use the auto-generated files directly in our Apache configuration with the Include directive, we should remember that this time Tomcat generates these files every time it starts. So any changes made to this file will be lost. In cases where you would like to make any custom changes to the virtual host definitions, for example, adding the Apache ACL to certain file locations, we should instead write this configuration file by hand. This is in contrast to Tomcat 3.3, which generates the file only when asked to do so. Of course, in Tomcat 4.0.x, once you have got the generated mod_jk.conf, you can always remove the ApacheConfig <Listener> elements from the server.xml file. This way when Tomcat restarts it won't regenerate the file.


        In either case, the final mod_jk.conf file should look like the following:




        <IfModule !mod_jk.c>
        LoadModule jk_module /usr/local/apache/libexec/mod_jk.so
        </IfModule>

        JkWorkersFile "/usr/local/tomcat/build/conf/jk/workers.properties"
        JkLogFile "/usr/local/tomcat/build/logs/mod_jk.log"

        JkLogLevel emerg

        NameVirtualHost *

        <VirtualHost *>
        ServerName europa.dom
        DocumentRoot "/home/sites/europa.dom/webapps/ROOT"
        <Directory "/home/sites/europa.dom/webapps/ROOT">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        JkMount /servlet ajp13
        JkMount /servlet/* ajp13
        JkMount /*.jsp ajp13
        JkMount /shop ajp13
        JkMount /shop/* ajp13

        Alias /shop "/home/sites/europa.dom/webapps/shop"

        <Directory "/home/sites/europa.dom/webapps/shop">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/shop/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/shop/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        JkMount /shop/servlet ajp13
        JkMount /shop/servlet/* ajp13
        JkMount /shop/*.jsp ajp13
        </VirtualHost>

        <VirtualHost *>
        ServerName callisto.dom

        DocumentRoot "/home/sites/callisto.dom/webapps/ROOT"
        <Directory "/home/sites/callisto.dom/webapps/ROOT">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        JkMount /servlet ajp13
        JkMount /servlet/* ajp13
        JkMount /*.jsp ajp13

        Alias /shop "/home/sites/callisto.dom/webapps/shop"

        <Directory "/home/sites/callisto.dom/webapps/shop">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/shop/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/shop/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        JkMount /shop/servlet ajp13
        JkMount /shop/servlet/* ajp13
        JkMount /shop/*.jsp ajp13
        </VirtualHost>


        Restart Tomcat and Apache and check the test files for the virtual domains again.





        Tomcat 4.0 with mod_webapp


        Setting up the new Apache-Tomcat mod_webapp connector is not much different from setting up the mod_jk connector, with the important difference that Tomcat does not have a module like <ApacheConfig> for automatic generation of mod_webapp Apache configuration files. Even though Tomcat's automatic generation of mod_jk.conf is convenient, commercial shared hosting providers won't rely on it too much, because of the myriad different directives that they would like to put in the <VirtualHost> entry. They would rather have their own automated scripts to generate the mod_jk.conf files with their additions. Given that, the syntax for writing a mod_jk configuration and a mod_webapp configuration is not much different


        If you want a comparison between the two connectors, see Chapter 11.




        Configuring Apache

        To configure Apache for mod_webapp, instead of the mod_jk-specific commands in the Apache configuration file, we add the mod_webapp directives. Instead of the LoadModule and AddModule directives of mod_jk, we add:




        LoadModule webapp_module libexec/mod_webapp.so
        AddModule mod_webapp.c


        Also, replace the other global Jk* directives with:




        WebAppConnection tomcat_warp warp localhost:8008


        This defines a WARP connection with the name tomcat_warp for communicating with the Tomcat server on the same host at port 8008. We now replace all the JkMount directives with WebAppDeploy directives. WebAppDeploy directives are of the form:




        WebAppDeploy <webappname> <warpconnection> <contextpath>


        Thus the new Apache configuration file, which we would name as mod_webapp.conf, would look like this:




        LoadModule webapp_module libexec/mod_webapp.so
        AddModule mod_webapp.c

        WebAppConnection tomcat_warp warp localhost:8008

        NameVirtualHost *

        <VirtualHost *>
        ServerName europa.dom

        WebAppDeploy ROOT tomcat_warp /
        WebAppDeploy shop tomcat_warp /shop

        DocumentRoot "/home/sites/europa.dom/webapps/ROOT"

        <Directory "/home/sites/europa.dom/webapps/ROOT">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        Alias /shop "/home/sites/europa.dom/webapps/shop"
        <Directory "/home/sites/europa.dom/webapps/shop">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/shop/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/shop/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        </VirtualHost>

        <VirtualHost *>
        ServerName callisto.dom

        WebAppDeploy ROOT tomcat_warp /
        WebAppDeploy shop tomcat_warp /shop

        DocumentRoot "/home/sites/callisto.dom/webapps/ROOT"

        <Directory "/home/sites/callisto.dom/webapps/ROOT">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        Alias /shop "/home/sites/callisto.dom/webapps/shop"

        <Directory "/home/sites/callisto.dom/webapps/shop">
        Options Indexes FollowSymLinks
        DirectoryIndex index.jsp index.html index.htm
        </Directory>

        <Location "/shop/WEB-INF/*">
        AllowOverride None
        deny from all
        </Location>

        <Location "/shop/META-INF/*">
        AllowOverride None
        deny from all
        </Location>

        </VirtualHost>


        Note how the default context is sent to the ROOT webapp. Another point to be noted is that in this configuration, Tomcat would serve even the static files of the web application. Also, when Tomcat serves the default context of the virtual host through WARP protocol, the <Directory> directives are ignored, and hence the <Directory> directives given above are strictly redundant. However, the <Location> directives are still processed by Apache. These are in a way redundant too. Without these <Location> directives, any requests of files in META-INF and WEB-INF directories would be passed by mod_webapp to Tomcat, which according to the Servlet API specification would reject such requests. Having the <Location> ACL directives in the Apache configuration actually stops such requests before they are passed to Tomcat.


        This configuration file should be added to the Apache configuration using the Include directive, replacing the previous mod_jk configuration.





        Configuring Tomcat 4.x

        For configuring Tomcat to receive requests only through the WARP protocol of mod_webapp, we need to modify the server.xml file. In the last used server.xml, we have to replace the AJP <Connector> elements with WARP <Connector> ones. Also, since <ApacheConfig> can't handle WARP-based configurations, we remove all the <Listener> elements previously used to auto-generate Apache configuration files. The final server.xml file would look like this.




        <Server port="8005" shutdown="SHUTDOWN" debug="0">
        <Service name="Tomcat-Apache-warp">

        <Connector className="org.apache.catalina.connector.warp.WarpConnector"
        port="8008" minProcessors="5" maxProcessors="75"
        acceptCount="10" debug="0"/>

        <Engine className="org.apache.catalina.connector.warp.WarpEngine"
        name="Apache" defaultHost="europa.dom" debug="0">

        <!-- Virtual host definition for europa.dom -->
        <Host name="europa.dom" debug="0"
        appBase="/home/sites/europa.dom/webapps"
        unpackWARs="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve"
        directory="/home/sites/europa.dom/logs"
        prefix="europa_access."
        suffix=".log"
        pattern="common"/>

        <Logger className="org.apache.catalina.logger.FileLogger"
        directory="/home/sites/europa.dom/logs"
        prefix="europa_catalina."
        suffix=".log"
        timestamp="true"/>
        <Context path="" docBase="ROOT" debug="0"/>

        <Context path="/shop" docBase="shop" debug="0" />

        </Host>

        <!-- Virtual host definition for callisto.dom -->
        <Host name="callisto.dom" debug="0"
        appBase="/home/sites/callisto.dom/webapps"
        unpackWARs="true">

        <Valve className="org.apache.catalina.valves.AccessLogValve"
        directory="/home/sites/callisto.dom/logs"
        prefix="callisto_access."
        suffix=".log"
        pattern="common"/>

        <Logger className="org.apache.catalina.logger.FileLogger"
        directory="/home/sites/callisto.dom/logs"
        prefix="callisto_catalina."
        suffix=".log"
        timestamp="true"/>

        <Context path="" docBase="ROOT" debug="0"/>

        <Context path="/shop" docBase="shop" debug="0" />

        </Host>
        </Engine>
        </Service>
        </Server>


        We should now restart Apache and Tomcat and check the functioning of the two virtual hosts.