Friday, October 23, 2009

Chapter 14 Packages

Team-Fly
 

 

Oracle® PL/SQL® Interactive Workbook, Second Edition
By
Benjamin Rosenzweig, Elena Silvestrova
Table of Contents

Appendix D. 
Answers to Test Your Thinking Sections



Chapter 14 Packages

1)

Add a procedure to the student_api package called remove_student. This procedure accepts a student_id and returns nothing. Based on the student id passed in, it removes the student from the database. If the student does not exist or there is a problem removing the student (such as a foreign key constraint violation), then let the calling program handle it.

A1:


CREATE OR REPLACE PACKAGE student_api AS
v_current_date DATE;
PROCEDURE discount;
FUNCTION new_instructor_id
RETURN instructor.instructor_id%TYPE;
FUNCTION total_cost_for_student
(p_student_id IN student.student_id%TYPE)
RETURN course.cost%TYPE;
PRAGMA RESTRICT_REFERENCES
(total_cost_for_student, WNDS, WNPS, RNPS);
PROCEDURE get_student_info
(p_student_id IN student.student_id%TYPE,
p_last_name OUT student.last_name%TYPE,
p_first_name OUT student.first_name%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER);
PROCEDURE get_student_info
(p_last_name IN student.last_name%TYPE,
p_first_name IN student.first_name%TYPE,
p_student_id OUT student.student_id%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER);
PROCEDURE remove_student
(p_studid IN student.student_id%TYPE);
END student_api;

CREATE OR REPLACE PACKAGE BODY student_api AS
PROCEDURE discount IS
CURSOR c_group_discount IS
SELECT distinct s.course_no, c.description
FROM section s, enrollment e, course c
WHERE s.section_id = e.section_id
GROUP BY s.course_no, c.description,
e.section_id, s.section_id
HAVING COUNT(*) >=8;
BEGIN
FOR r_group_discount IN c_group_discount LOOP
UPDATE course
SET cost = cost * .95
WHERE course_no = r_group_discount.course_no;

DBMS_OUTPUT.PUT_LINE
('A 5% discount has been given to'||
r_group_discount.course_no||' '||
r_group_discount.description);
END LOOP;
END discount;

FUNCTION new_instructor_id
RETURN instructor.instructor_id%TYPE
IS
v_new_instid instructor.instructor_id%TYPE;
BEGIN
SELECT INSTRUCTOR_ID_SEQ.NEXTVAL
INTO v_new_instid
FROM dual;
RETURN v_new_instid;
EXCEPTION
WHEN OTHERS THEN
DECLARE
v_sqlerrm VARCHAR2(250) :=
SUBSTR(SQLERRM,1,250);
BEGIN
RAISE_APPLICATION_ERROR
(-20003, 'Error in instructor_id: '||
v_sqlerrm);
END;
END new_instructor_id;

FUNCTION get_course_descript_private
(p_course_no course.course_no%TYPE)
RETURN course.description%TYPE
IS
v_course_descript course.description%TYPE;
BEGIN
SELECT description
INTO v_course_descript
FROM course
WHERE course_no = p_course_no;
RETURN v_course_descript;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END get_course_descript_private;

FUNCTION total_cost_for_student
(p_student_id IN student.student_id%TYPE)
RETURN course.cost%TYPE
AS
v_cost course.cost%TYPE;
BEGIN
SELECT sum(cost)
INTO v_cost
FROM course c, section s, enrollment e
WHERE c.course_no = c.course_no
AND e.section_id = s.section_id
AND e.student_id = p_student_id;
RETURN v_cost;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END total_cost_for_student;

PROCEDURE get_student_info
(p_student_id IN student.student_id%TYPE,
p_last_name OUT student.last_name%TYPE,
p_first_name OUT student.first_name%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER)
IS
BEGIN
SELECT last_name, first_name, zip
INTO p_last_name, p_first_name, p_zip
FROM student
WHERE student.student_id = p_student_id;
p_return_code := 0;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE
('Student ID is not valid.');
p_return_code := -100;
p_last_name := NULL;
p_first_name := NULL;
p_zip := NULL;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE
('Error in procedure get_student_info');
END get_student_info;

PROCEDURE get_student_info
(p_last_name IN student.last_name%TYPE,
p_first_name IN student.first_name%TYPE,
p_student_id OUT student.student_id%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER)
IS
BEGIN
SELECT student_id, zip
INTO p_student_id, p_zip
FROM student
WHERE UPPER(last_name) = UPPER(p_last_name)
AND UPPER(first_name) = UPPER(p_first_name);
p_return_code := 0;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE
('Student name is not valid.');
p_return_code := -100;
p_student_id := NULL;
p_zip := NULL;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE
('Error in procedure get_student_info');
END get_student_info;

PROCEDURE remove_student
(p_studid IN student.student_id%TYPE)
IS
BEGIN
DELETE
FROM STUDENT
WHERE student_id = p_studid;
END;

BEGIN
SELECT trunc(sysdate, 'DD')
INTO v_current_date
FROM dual;
END student_api;
2)

Alter remove_student in the student_api package body to accept an additional parameter. This new parameter is a VARCHAR2 and is called p_ri. Make p_ri default to "R." The new parameter may contain a value of "R" or "C." If "R" is received, it represents DELETE RESTRICT and the procedure acts as it does now. If there are enrollments for the student, the delete is disallowed. If a "C" is received, it represents DELETE CASCADE. This functionally means that the remove_student procedure locates all records for the student in all of the CTA tables and removes them from the database before attempting to remove the student from the student table. Decide how to handle the situation where the user passes in a code other than "C" or "R."

A2:

Answer: Your answer should look similar to the following:


CREATE OR REPLACE PACKAGE student_api AS
v_current_date DATE;
PROCEDURE discount;
FUNCTION new_instructor_id
RETURN instructor.instructor_id%TYPE;
FUNCTION total_cost_for_student
(p_student_id IN student.student_id%TYPE)
RETURN course.cost%TYPE;
PRAGMA RESTRICT_REFERENCES
(total_cost_for_student, WNDS, WNPS, RNPS);
PROCEDURE get_student_info
(p_student_id IN student.student_id%TYPE,
p_last_name OUT student.last_name%TYPE,
p_first_name OUT student.first_name%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER);
PROCEDURE get_student_info
(p_last_name IN student.last_name%TYPE,
p_first_name IN student.first_name%TYPE,
p_student_id OUT student.student_id%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER);
PROCEDURE remove_student
(p_studid IN student.student_id%TYPE,
p_ri IN VARCHAR2 DEFAULT 'R');
END student_api;

CREATE OR REPLACE PACKAGE BODY student_api AS
PROCEDURE discount IS
CURSOR c_group_discount IS
SELECT distinct s.course_no, c.description
FROM section s, enrollment e, course c
WHERE s.section_id = e.section_id
GROUP BY s.course_no, c.description,
e.section_id, s.section_id
HAVING COUNT(*) >=8;
BEGIN
FOR r_group_discount IN c_group_discount LOOP
UPDATE course
SET cost = cost * .95
WHERE course_no = r_group_discount.course_no;

DBMS_OUTPUT.PUT_LINE
('A 5% discount has been given to'||
r_group_discount.course_no||' '||
r_group_discount.description);
END LOOP;
END discount;

FUNCTION new_instructor_id
RETURN instructor.instructor_id%TYPE
IS
v_new_instid instructor.instructor_id%TYPE;
BEGIN
SELECT INSTRUCTOR_ID_SEQ.NEXTVAL
INTO v_new_instid
FROM dual;
RETURN v_new_instid;
EXCEPTION
WHEN OTHERS THEN
DECLARE
v_sqlerrm VARCHAR2(250) :=
SUBSTR(SQLERRM,1,250);
BEGIN
RAISE_APPLICATION_ERROR
(-20003, 'Error in instructor_id: '||
v_sqlerrm);
END;
END new_instructor_id;

FUNCTION get_course_descript_private
(p_course_no course.course_no%TYPE)
RETURN course.description%TYPE
IS
v_course_descript course.description%TYPE;
BEGIN
SELECT description
INTO v_course_descript
FROM course
WHERE course_no = p_course_no;
RETURN v_course_descript;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END get_course_descript_private;

FUNCTION total_cost_for_student
(p_student_id IN student.student_id%TYPE)
RETURN course.cost%TYPE
IS
v_cost course.cost%TYPE;
BEGIN
SELECT sum(cost)
INTO v_cost
FROM course c, section s, enrollment e
WHERE c.course_no = c.course_no
AND e.section_id = s.section_id
AND e.student_id = p_student_id;
RETURN v_cost;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END total_cost_for_student;

PROCEDURE get_student_info
(p_student_id IN student.student_id%TYPE,
p_last_name OUT student.last_name%TYPE,
p_first_name OUT student.first_name%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER)
IS
BEGIN
SELECT last_name, first_name, zip
INTO p_last_name, p_first_name, p_zip
FROM student
WHERE student.student_id = p_student_id;
p_return_code := 0;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Student ID is not valid.');
p_return_code := -100;
p_last_name := NULL;
p_first_name := NULL;
p_zip := NULL;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE
('Error in procedure get_student_info');
END get_student_info;

PROCEDURE get_student_info
(p_last_name IN student.last_name%TYPE,
p_first_name IN student.first_name%TYPE,
p_student_id OUT student.student_id%TYPE,
p_zip OUT student.zip%TYPE,
p_return_code OUT NUMBER)
IS
BEGIN
SELECT student_id, zip
INTO p_student_id, p_zip
FROM student
WHERE UPPER(last_name) = UPPER(p_last_name)
AND UPPER(first_name) = UPPER(p_first_name);
p_return_code := 0;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE
('Student name is not valid.');
p_return_code := -100;
p_student_id := NULL;
p_zip := NULL;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE
('Error in procedure get_student_info');
END get_student_info;

PROCEDURE remove_student
-- the parameters student_id and p_ri give user an
-- option of cascade delete or restrict delete for
-- the given students records
(p_studid IN student.student_id%TYPE,
p_ri IN VARCHAR2 DEFAULT 'R')
IS
-- declare exceptions for use in procedure
enrollment_present EXCEPTION;
bad_pri EXCEPTION;
BEGIN
-- R value is for restrict delete option
IF p_ri = 'R' THEN
DECLARE
-- a variable is needed to test if the student
-- is in the enrollment table
v_dummy CHAR(1);
BEGIN
-- This is a standard existence check
-- If v_dummy is assigned a value via the
-- SELECT INTO, the exception
-- enrollment_present will be raised
-- If the v_dummy is not assigned a value, the
-- exception no_data_found will be raised
SELECT NULL
INTO v_dummy
FROM enrollment e
WHERE e.student_id = p_studid
AND ROWNUM = 1;

-- The rownum set to 1 prevents the SELECT
-- INTO statement raise to_many_rows
-- exception
-- If there is at least one row in enrollment
-- table with corresponding student_id, the
-- restrict delete parameter will disallow the
-- deletion of the student by raising
-- the enrollment_present exception
RAISE enrollment_present;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- The no_data_found exception is raised
-- when there are no students found in the
-- enrollment table Since the p_ri indicates
-- a restrict delete user choice the delete
-- operation is permitted
DELETE FROM student
WHERE student_id = p_studid;
END;
-- when the user enter "C" for the p_ri
-- he/she indicates a cascade delete choice
ELSIF p_ri = 'C' THEN
-- delete the student form the enrollment and
-- grade tables
DELETE
FROM enrollment
WHERE student_id = p_studid;
DELETE
FROM grade
WHERE student_id = p_studid;

-- delete from student table only after
corresponding
-- records have been removed from the other
tables because
-- the student table is the parent table
DELETE
FROM student
WHERE student_id = p_studid;
ELSE
RAISE bad_pri;
END IF;
EXCEPTION
WHEN bad_pri THEN
RAISE_APPLICATION_ERROR
(-20231, 'An incorrect p_ri value was '||
'entered. The remove_student procedure can '||
'only accept a C or R for the p_ri
parameter.');

WHEN enrollment_present THEN
RAISE_APPLICATION_ERROR
(-20239, 'The student with ID'||p_studid||
' exists in the enrollment table thus records'||
' will not be removed.');
END remove_student;

BEGIN
SELECT trunc(sysdate, 'DD')
INTO v_current_date
FROM dual;
END student_api;





    Team-Fly
     

     
    Top
     


    Section 1.1. What Is a Stored Program?










    1.1. What Is a Stored Program?



    A database stored programsometimes called a stored module or a stored routineis a computer program (a series of instructions associated with a name) that is stored within, and executes within, the database server. The source code and (sometimes) any compiled version of the stored program are almost always held within the database server's system tables as well. When the program is executed, it is executed within the memory address of a database server process or thread.


    There are three major types of
    MySQL stored

    programs:




    Stored procedures


    Stored procedures are the most common type of stored program. A stored procedure is a generic program unit that is executed on request and that can accept multiple input and output parameters.




    Stored functions


    Stored functions are similar to stored procedures
    , but their execution results in the return of a single value. Most importantly, a stored function can be used within a standard SQL statement, allowing the programmer to effectively extend the capabilities of the SQL language.




    Triggers


    Triggers are stored programs that are activated in response to, or are triggered by, an activity within the database. Typically, a trigger will be invoked in response to a DML operation (INSERT, UPDATE, DELETE) against a database table. Triggers can be used for data validation or for the automation of denormalization.



    Other databases offer additional types of stored programs
    , including packages and classes, both of which allow you to define or collect multiple procedures and functions within a single, named context. MySQL does not currently support such structuresin MySQL, each stored program is a standalone entity.



    Throughout this book, we are going to use the term stored programs to refer to stored procedures, functions, and triggers
    , and the term stored program language to refer to the language used to write these programs. Most of the facilities in the stored program language are applicable across procedures, functions, and triggers; however, both functions and triggers have strict limitations on the language features that may be used with them. Thus, we dedicate a chapter to each of these program types in which we explain these limitations.



    1.1.1. Why Use Stored Programs?






    Developers have a multitude of programming languages from which to choose. Many of these are not database languages, which means that the code written in these languages does not reside in, nor is it managed by, a database server. Stored programs offer some very important advantages over more general-purpose languages, including:


    • The use of stored programs can lead to a more secure database.

    • Stored programs offer a mechanism to abstract data access routines, which can improve the maintainability of your code as underlying data structures evolve.

    • Stored programs can reduce network traffic, because the program can work on the data from within the server, rather than having to transfer the data across the network.

    • Stored programs can be used to implement common routines accessible from multiple applicationspossibly using otherwise incompatible frameworksexecuted either within or from outside the database server.

    • Database-centric logic can be isolated in stored programs and implemented by programmers with more specialized, database experience.

    • The use of stored programs can, under some circumstances, improve the portability of your application.


    While this is an impressive list of advantages (many of which will be explored in greater detail in this book), we do not recommend that you immediately move all your application logic into stored programs. In today's rich and complex world of software technology, you need to understand the strengths and weaknesses of each possible element in your software configuration, and figure out how to maximize each element. We spend most of Chapter 12 evaluating how and where to apply MySQL stored programs.


    The bottom line is that, used correctly, stored programsprocedures, functions, and triggerscan improve the performance, security, maintainability, and reliability of your applications.


    Subsequent chapters will explore how to construct MySQL stored programs and use them to best advantage. Before plunging into the details, however, let's look at how the technology developed and take a quick tour of language capabilities.




    1.1.2. A Brief History of MySQL







    MySQL has its roots in an in-house (non-SQL) database system called Unireg
    used by the Swedish company TcX that was first developed in the 1980s and optimized for data warehousing. The author of Unireg, Michael "Monty" Widenius, added a SQL interface to Unireg in 1995, thus creating the first version of MySQL. David Axmark, from Detron HB
    , approached Monty proposing to release MySQL to the world under a "dual licensing" model that would allow widespread free use, but would still allow for commercial advantage. Together with Allan Larsson, David and Monty became the founders of the MySQL company.


    The first widely available version of MySQL was 3.11, which was released in mid-1996. Adoption of MySQL grew rapidlyparalleling the adoption of other related open source technologies. By the year 2005, MySQL could lay claim to over 6 million installations of the MySQL database.


    Version 3 of MySQL, while suitable for many types of applications (particularly read-intensive web applications), lacked many of the features normally considered mandatory in a relational database. For instance, transactions, views, and subqueries were not initially supported.


    However, the MySQL system was designed to support a particularly extensible data access architecture, in which the SQL layer was decoupled from the underlying data and file access layer. This allowed custom "storage engines" to be employed in place ofor in combination withthe native ISAM (Indexed Sequential Access Method)
    -based MySQL engine. The Berkeley-DB (BDB
    ) database
    (from Sleepycat Software
    ) was integrated as an optional storage engine in version 3.23.34 in early 2001. BDB provided MySQL with its initial transaction processing capability. At about the same time, the open source InnoDB storage engine became available and quickly became a natively available option for MySQL users.


    The 4.0 release in early 2002 fully incorporated the InnoDB option, making transactions easily available for all MySQL users, and also added improved replication capabilities. The 4.1 release in early 2004 built on the 4.0 release and includedamong many other improvementssupport for subqueries and Unicode character sets.


    With the 5.0 release of MySQL in late 2005, MySQL took an important step closer to functional parity with commercial RDBMS systems; it introduced stored
    procedures
    , functions, and triggers
    , the addition of a data dictionary (the SQL-standard INFORMATION_SCHEMA), and support for updateable views.


    The 5.1 release, scheduled for the second half of 2006, will add important factilities such as an internal scheduler, table partitioning, row-based replication, and many other significant enhancements.




    1.1.3. MySQL Stored Procedures, Functions, and Triggers






    MySQL chose to implement its stored program language within the MySQL server as a subset of the ANSI SQL:2003 SQL/PSM (Persistent Stored Module) specification. What a mouthful! Essentially, MySQL stored programsprocedures, functions, and triggerscomply with the only available open standard for these types of programsthe ANSI standard.


    Many MySQL and open source aficionados had been hoping for a stored program language implementation based on an open source language such as PHP or Python. Others anticipated a Java?-based implementation. However, by using the ANSI specificationthe same specification adopted within IBM's DB2 databaseMySQL has taken advantage of years of work done by the ANSI committee, which included representatives from all of the major RDBMS companies.


    The MySQL stored program language is a block-structured language (like Pascal) that includes familiar commands for manipulating variables, implementing conditional execution, performing iterative processing, and handling errors. Users of existing stored program languages, such as Oracle's PL/SQL

    or SQL Server's Transact-SQL, will find features of the language very familiar. Programmers familiar with other languages, such as PHP or Java, might consider the language somewhat simplistic, but they will find that it is easy to learn and that it is well matched to the common requirements of database programming.













    4.3 Servlet Life Cycle











     < Day Day Up > 





    4.3 Servlet Life Cycle



    Servlets use packages found in the Java Servlet API. When you write code for a Java servlet, you must use at least one of the following two packages: javax.servlet for any type of servlet and/or javax.servlet.http for servlets specific to HTTP.



    Servlets are usually created by extending from the javax.servlet.http.HttpServlet abstract class, which in turn extends the javax.servlet.GenericServlet abstract class, or from the GenericServlet class itself, which implements the javax.servlet.Servlet interface. Both the GenericServlet and the HttpServlet classes contain three methods that they inherit from the Servlet interface: init(), service(), and destroy(). These methods, used by the servlet to communicate with the servlet container, are called life-cycle methods. You work with these three methods in a slightly different way, depending on whether you are extending the GenericServlet class or the HttpServlet class. The init() and the destroy() methods have the same properties for the GenericServlet and the HttpServlet classes; the service() method must be handled differently when it is based on the GenericServlet class or on the HttpServlet class.



    The init() method is run only once after the servlet container loads the servlet class and the servlet is instantiated, regardless of how many clients access the servlet. This method is guaranteed to finish before any service() requests are processed. The servlet can be activated when the servlet container starts or when the first client accesses the servlet.



    A custom init() method is typically used to perform setup of servlet-wide resources only once rather than once per request, as in the service() method. For example, a customized init() can load Graphics Interchange Format (GIF) images once, whereas the servlet service() method returns the images multiple times in response to multiple client requests to the servlet. Further examples include initializing sessions with other network services or establishing access to persistent data, such as databases and files.



    The destroy() method is run only once when the servlet container stops the servlet and unloads it. Usually, servlets are unloaded when the servlet container is shut down. The default destroy() method can be accepted as is, without the need to override it, because it is not abstract. Servlet writers may, if they wish, override the destroy() call, providing their own custom destroy() method. A custom destroy() method is often used to manage servletwide resources. For example, the destroy() method can be used to release shared servletwide resources allocated in the init() method.



    The service() method is the heart of a servlet. The simplest servlets define only the service() method. Unlike the init() and destroy() methods, service() is called for each client request.



    The service() method must be handled differently when it is based on the GenericServlet class or on the HttpServlet class.



    • If the servlet is based on the GenericServlet class, the service() method is abstract, so it must be overriden. The service() method obtains information about the client request, prepares the response, and returns this response to the client. As multiple clients might access the service() method at the same time, multiple threads will be executing the code in the single instance of the servlet. Therefore, the service() method must include any required synchronization code.

    • If the servlet is based on the HttpServlet class�the majority of the cases�the service() method is not abstract. Therefore, it can be used as is. More commonly, the servlet overrides one or more of the methods doGet(), doPut(), doPost(), and doDelete(). The default implementation of service() calls one of these methods, based on the client request. For example, if the client request is an HTTP GET method, the servlet's service() method will invoke the doGet() method; if the request is an HTTP POST method, the service() method will invoke the doPost() method, and so on.



    It is through the service() method that the servlet and the servlet container exchange data. When it invokes the servlet's service() method, the servlet container also passes in two objects as parameters. These objects are known as the request and response objects, and their implementation depends on the servlet's superclass.



    • If the servlet is based on the GenericServlet class, the two objects are instances of the javax.servlet.ServletRequest and javax.servlet.ServletResponse interfaces, respectively.

    • If the servlet is based on the HttpServlet class, the two objects are instances of the javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse interfaces, respectively.



    The request and response objects encapsulate the data sent by the client, providing access to parameters and allowing the servlets to report status information, including any errors that occurred. The servlet container creates an instance for the request and response objects and passes them to the servlet. Both of these objects are used by the servlet container to exchange data with the servlet.



    • The servlet invokes methods on the request object in order to discover information about the client and server environments, as well as the parameter names and values. For example, the servlet can call methods on the request object to obtain all the data entered on a form on the client's Web browser and set by the HTTP GET and POST methods.

    • The servlet invokes methods from the response object to send the response information back to the servlet container, which then sends it to the client.



    The servlet life cycle involves a series of interactions among the client, the servlet container, and the servlet, as shown in Figure 4.3. The steps are as follows.





























    1. The servlet is loaded. This operation is typically performed when the first client accesses the servlet. In most servlet containers, options are provided to force the loading of the servlet when the servlet container starts up.

    2. The servlet container creates an instance of the servlet.

    3. The servlet container calls the servlet's init() method. This method is called only once during the lifetime of the servlet.

    4. A client request arrives at the servlet container, and the servlet container creates a request object: ServletRequest or HttpServletRequest.

    5. The servlet container creates a response object: ServletResponse or HttpServletResponse.

    6. The servlet container invokes the servlet's service() method.

    7. The service() method takes the request object as one of its two parameters.

    8. The service() method takes the response object as the other parameter.

    9. The service() method gets information about the request object and processes the request accessing other resources, such as databases and files.

    10. The service() method retrieves the necessary information from the resources accessed.

    11. The service() method uses methods of the response object.

    12. The service() method passes the response back to the servlet container.

    13. The servlet container passes the response back to the client.



    Figure 4.3. Servlet Life Cycle




    For additional client requests, the servlet container creates new request and response objects, invokes the service() method of the servlet, and passes the request and response objects as parameters. This loop is repeated for every client request but without the need to call the init() method every time. When the servlet container no longer needs the servlet�typically when the servlet container is shut down�the servlet container invokes the servlet destroy() method.



    As discussed earlier, a servlet based on the HttpServlet class generally overrides one or more of the HTTP methods doGet(), doPut(), doPost(), and doDelete(), and the default implementation of the service() method calls one of these methods, based on the client request. In this case, the servlet life cycle just described should take into account the additional call that the service() method makes to the HTTP method.













       < Day Day Up > 



      Test First Design and Refactoring










      Test First Design and Refactoring


      Test-driven development (TDD) has brought the concept of test first design to the fore-front. This approach has several benefits and hence we will write tests first in this book, whenever possible.


      Writing tests first takes a little bit of getting used to, and many times, you will wonder if you really have the time to write tests given the pressure of deliverables. However, I find it is a nicer way to code (after you get the hang of it), particularly because it helps me think of how to design/develop my classes. Also, if you factor in the time you spend unit testing and fixing defects discovered during functional and user testing, you will find that this style of working can actually save time in the end.


      Writing tests first has several benefits. For example, writing tests first ensures that you write only functional code that will actually be used; this is based on the assumption that you have written code to satisfy the unit tests, which themselves are based on the acceptance tests specified earlier in our business requirements. Second, if your code passes the unit and acceptance tests, you are done with that part of the code. Third, it can help you design your classes better because when you write the test first, you are experiencing firsthand how your actual classes/methods will be used. Last, test first can also help you to refactor with confidence because you can retest your refactored code quickly through JUnit unit tests to ensure that the refactored code works as the original version did (assuming there is little or no change to the external interface, as defined on refactoring.com).


      Although, unit testing is only one part of the overall testing that occurs in corporations, it is something that developers should always do. Other testing includes functional testing, user acceptance testing (UAT), system integration testing (also known as interface testing), stress/load testing, and more.


      We will use JUnit to implement our acceptance tests. The following are sample files to demonstrate our class-naming convention for test classes:


      • test/TimesheetListControllerTest.java

      • test/TimesheetManagerTest.java

      • test/ReminderEmailTest.java


      I have chosen to keep our JUnit test classes in a separate test package (that is, com.visualpatterns.timex.test) because I believe this is a cleaner design. However, I've also seen other developers keep the JUnit test classes in the same directory as the code being tested. For example, in this scenario, our TimesheetListControllerTest.java would be placed in our controller package.





      Personal Opinion: Early Environment Setup Is Essential


      After almost two decades of developing software, I am amazed how little some of the fundamental concepts have changed. Although the technologies have changed dramatically, underlying concepts such as environment setup, design, development, debugging, and so on remain fundamentally the same. One of things I have consistently found over the years is that the environment setup almost always involves more than people expect or plan for. So, getting the environment setup upfront is vital. Based on my personal experience, I like to recommend three things to my customers in regards to environment setup.


      First, get the minimal but completely functional environment set up up front (directory structure and build scripts, for example). This should be consistent for all members of a software development team. Second, get a simple end-to-end demo working up front (for example, a user interface to database round-trip demo). Third, environment setup generally takes longer than people expect, so factor in enough time up front. This is one reason many Agile projects consider this iteration 0 (zero) or cycle 0 (Jim Highsmith), because a demo or environment setup doesn't produce anything tangible from a customer perspective; it merely gets the environment setup for developers and testers to work in.














      Chapter 5: Object Description in Cryptography Using ASN.1















































      Chapter 5: Object Description in Cryptography Using ASN.1




      Overview



      At
      this point one thing probably has become obvious to you. There are a
      number of areas in cryptography that, in order for communication to
      take place, require two implementations to have the same idea about how
      algorithms are implemented as well as have a common language for
      exchanging required parameter information for those algorithms.


      This chapter introduces ASN.1, which is a language designed for just this purpose. By the end of this chapter, you should




      • Have enough knowledge to read and understand most ASN.1 modules related to cryptography




      • Understand the basic ASN.1 types and the relevant ASN.1 binary encoding rules




      • Understand how ASN.1 binary encoding can be used with the Java APIs for encoding AlgorithmParameters




      • Understand how public and private keys can be encoded as ASN.1 objects and re-created later




      Finally, you will see how to use the EncryptedPrivateKeyInfo class defined in the JCE, as well as see what is taking place when it does its job.







































      Section 23.7.&nbsp; What You Need to Know









      23.7. What You Need to Know













      So, do you really need to remember everything in this chapter? I certainly hope not, because I can't even remember it in my day-to-day work. Your database administrator, on the other hand, probably needs to know most of this stuff.


      In addition to satisfying healthy curiosity, my goal in presenting this material was to help allay any misgivings programmers might have about what happens under the hood. Whether or not you've ever had such concerns, there are a number of important points to remember about what goes on inside PL/SQL:


      • To avoid compilation overhead, programs you plan to use more than a few times should be put in stored programs rather than stored in files as anonymous blocks.

      • In addition to their unique ability to preserve state throughout a session, PL/SQL packages offer performance benefits. You should put most of your extensive application logic into package bodies.

      • When upgrading Oracle versions, new features in the PL/SQL compiler warrant thorough application testing. In some (probably rare) cases when upgrading to Oracle Database 10g, slight changes in execution order, resulting from freedoms exploited by the optimizing compiler, could affect application results.

      • While Oracle's automatic dependency management approach relieves a huge burden on developers, upgrading applications on a live production database should be undertaken with great care because of the need for object locking and package state reset.

      • If you use signature-based remote dependency checking in remote procedure calls or with a loopback-link synonym as a way to avoid invalidations, you should institute (manual) procedures to eliminate the possibility of the signature check returning a false negative (which would cause a runtime error).

      • Use definer rights to maximize performance and to help simplify the management and control of privileges on database tables. Use invoker rights only to address particular problems (for example, programs that use dynamic SQL and that create or destroy database objects).

      • Oracle's sophisticated approaches aimed at minimizing the machine resources needed to run PL/SQL occasionally benefit from a little help from developers and DBAsfor example, by explicitly freeing unused user memory or pinning objects in memory.

      • Where it makes sense to your application logic, use the cursor FOR loop idiom, rather than open/fetch loop/close, to take advantage of the automatic bulk binding feature in Oracle Database 10g.

      • When your program does need to open an explicit cursor in a PL/SQL program, be sure to close the cursor as soon as fetching is complete.

      • Native compilation of PL/SQL may not offer any performance advantages for SQL-intensive applications, but it can significantly improve the performance of compute-intensive programs.

      • Calling remote packages entails some special programming considerations if you want to take advantage of anything in the package other than procedures, functions, types, and subtypes.

      • Use program variables in embedded static SQL statements in PL/SQL, and bind variables in dynamic SQL statements, to avoid subverting Oracle's cursor sharing features.


      Now ... if you've made it through this entire chapter, I may one day come and bother you with my questions.









        3.3. Problem-Definition Prerequisite










         < Free Open Study > 







        3.3. Problem-Definition Prerequisite



        The first prerequisite you need to fulfill before beginning construction is a clear statement of the problem that the system is supposed to solve. This is sometimes called "product vision," "vision statement," "mission statement," or "product definition." Here it's called "problem definition." Since this book is about construction, this section doesn't tell you how to write a problem definition; it tells you how to recognize whether one has been written at all and whether the one that's written will form a good foundation for construction.







        If the "box" is the boundary of constraints and conditions, then the trick is to find the box…. Don't think outside the box�find the box.

        �Andy Hunt and Dave Thomas



        A problem definition defines what the problem is without any reference to possible solutions. It's a simple statement, maybe one or two pages, and it should sound like a problem. The statement "We can't keep up with orders for the Gigatron" sounds like a problem and is a good problem definition. The statement "We need to optimize our automated data-entry system to keep up with orders for the Gigatron" is a poor problem definition. It doesn't sound like a problem; it sounds like a solution.



        As shown in Figure 3-4, problem definition comes before detailed requirements work, which is a more in-depth investigation of the problem.





        Figure 3-4. The problem definition lays the foundation for the rest of the programming process


        [View full size image]




        The problem definition should be in user language, and the problem should be described from a user's point of view. It usually should not be stated in technical computer terms. The best solution might not be a computer program. Suppose you need a report that shows your annual profit. You already have computerized reports that show quarterly profits. If you're locked into the programmer mindset, you'll reason that adding an annual report to a system that already does quarterly reports should be easy. Then you'll pay a programmer to write and debug a time-consuming program that calculates annual profits. If you're not locked into the programmer mindset, you'll pay your secretary to create the annual figures by taking one minute to add up the quarterly figures on a pocket calculator.



        The exception to this rule applies when the problem is with the computer: compile times are too slow or the programming tools are buggy. Then it's appropriate to state the problem in computer or programmer terms.



        As Figure 3-5 suggests, without a good problem definition, you might put effort into solving the wrong problem.





        Figure 3-5. Be sure you know what you're aiming at before you shoot


        [View full size image]




        The penalty for failing to define the problem is that you can waste a lot of time solving the wrong problem. This is a double-barreled penalty because you also don't solve the right problem.













           < Free Open Study > 



          6.1 Overview








           

           










          6.1 Overview



          In a servlet-based Web application, you can specify application-wide initialization parameters�known as context initialization parameters�for your application in the deployment descriptor; for example, the following code fragment stores the value es-MX in a context initialization parameter:





          <!-- Specifying a default locale in WEB-INF/web.xml -->

          <web-app>

          <context-param>

          <param-name>

          javax.servlet.jsp.jstl.fmt.locale

          </param-name>



          <param-value>

          es-MX

          </param-value>



          </context-param>

          ...

          </web-app>



          You might suspect that the parameter name javax.servlet.jsp.jstl.fmt. locale serves some official purpose, and you'd be right�if you add that context initialization parameter to your deployment descriptor (WEB-INF/web.xml), as in the preceding code fragment, you will set the default locale for JSTL I18N and formatting actions. In the preceding code fragment, that locale is set to Mexican Spanish.



          It's often desirable to override settings for a particular scope; for example, you might want to temporarily override the javax.servlet.jsp.jstl.fmt.locale context initialization parameter for a page, request, or session, so JSTL lets you create scoped variables that override context initialization parameters. Those scoped variables are known as configuration variables, and they are most often created by JSTL actions; for example, you can use the <fmt:setLocale> action like this:





          <fmt:setLocale value='fr-CA' scope='request'/>



          The <fmt:setLocale> action in the preceding code fragment creates a French Canadian locale and stores it in a configuration variable in request scope. During the request, that configuration variable will take precedence over the context initialization parameter named javax.servlet.jsp.jstl.fmt.locale. When the request is over, the configuration variable is removed, and the default locale returns to the value specified in the context initialization parameter, assuming no other configuration variables of the same name exist in other scopes.



          The combination of context initialization parameter and configuration variable is defined by the JSTL specification as a configuration setting. Configuration settings let you specify default values with context initialization parameters and subsequently override those defaults with configuration variables with a JSTL action or a business component. Configuration settings are defined by static constants in the javax.servlet.jsp.jstl.core.Config class and a name that corresponds to that constant; for example, the configuration setting named javax.servlet.jsp.jstl.locale is defined by the FMT_LOCALE constant in the Config class.



          You use a configuration setting's name in deployment descriptors, but you use the static constants in business components, such as servlets or life-cycle listeners. Throughout this book, configuration settings are referred to by their constants; for example, the configuration setting named javax.servlet.jsp.jstl.fmt.locale is referred to as the FMT_LOCALE configuration setting.



          The FMT_LOCALE configuration setting discussed in this section is one of six configuration settings in JSTL 1.0. All of the JSTL 1.0 configuration settings are listed in Table 6.1.





































































          Table 6.1. JSTL 1.0 Configuration Settings


          Constant[a]





          Name[b]





          FMT_LOCALE





          j.s.j.j.fmt.locale





          FMT_FALLBACK_LOCALE





          j.s.j.j.fmt.fallbackLocale





          FMT_LOCALIZATION_CONTEXT





          j.s.j.j.fmt.localizationContext





          FMT_TIME_ZONE





          j.s.j.j.fmt.timeZone





          SQL_DATA_SOURCE





          j.s.j.j.sql.dataSource





          SQL_MAX_ROWS





          j.s.j.j.sql.maxRows





          [a] The constants are defined by the javax.servlet.jsp.jstl.core.Config class.



          [b] j.s.j.j = javax.servlet.jsp.jstl



          The first three configuration settings listed in Table 6.1 are used by JSTL I18N and formatting tags. The fourth, which lets you specify a time zone, is used only by formatting actions. The last two let you specify an SQL data source and a limit for database queries.



          In JSTL 1.0, four actions change configuration settings. Table 6.2 lists those actions and the configuration settings they modify.





















































          Table 6.2. JSTL 1.0 Actions That Modify Configuration Settings


          Action





          Configuration Setting





          <fmt:setLocale>





          FMT_LOCALE





          <fmt:setBundle>





          FMT_LOCALIZATION_CONTEXT





          <fmt:setTimeZone>





          FMT_TIME_ZONE





          <sql:setDataSource>





          SQL_DATA_SOURCE





          Notice that all of the actions in Table 6.2 are named setXXX, where XXX represents a configuration setting. The FMT_FALLBACK_LOCALE and SQL_MAX_ROWS configuration settings are not set by any JSTL actions; if you want to set them, you can specify them as context initialization parameters or you can set them in a business component.



          Configuration settings all work the same way, so we will focus mostly on one configuration setting�FMT_LOCALE�throughout the rest of this chapter. Learning about that configuration setting will help you understand how all configuration settings work.



          The FMT_LOCALE Configuration Setting



          This book documents JSTL configuration settings with tables like Table 6.3, which documents the FMT_LOCALE configuration setting





















































          Table 6.3. The FMT_LOCALE Configuration Setting


          Config Constant





          FMT_LOCALE





          Name





          javax.servlet.jsp.jstl.fmt.locale





          Type





          java.lang.String or java.util.Locale





          Set by





          <fmt:setLocale>, Deployment Descriptor, Config class





          Used by





          <fmt:bundle>, <fmt:setBundle>, <fmt:message>, <fmt:formatNumber>, <fmt:parseNumber>, <fmt:formatDate>, <fmt:parseDate>





          Each configuration setting has a type. All JSTL configuration settings can be specified with a string or an object; for example, you can specify the FMT_LOCALE configuration setting as a string, such as en-US or fr-CA, or as an instance of java.util.Locale.



          Table 6.3 also tells you which JSTL actions set the FMT_LOCALE configuration setting (only <fmt:setLocale>) and which actions use it (quite a few).





          Temporarily Overriding Configuration Settings



          Figure 6-1 shows a simple Web application that illustrates how the FMT_LOCALE configuration setting works. There are two JSP pages in the application. The first page, referred to as Page 1, is shown at startup (shown on the left in Figure 6-1). That page has a submit button that takes you to the second page, referred to as Page 2 (shown on the right in Figure 6-1).



          Figure 6-1. Using the FMT_LOCALE Configuration Setting





          The first thing you need to know to make sense of the application shown in Figure 6-1 is that French is specified as the default locale with a context initialization parameter in the deployment descriptor. That deployment descriptor is listed in Listing 6.1.





          Listing 6.1 WEB-INF/web.xml




          <?xml version="1.0" encoding="ISO-8859-1"?>



          <!DOCTYPE web-app

          PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

          "http://java.sun.com/j2ee/dtds/web-app_2.3.dtd">



          <web-app>

          <context-param>

          <param-name>javax.servlet.jsp.jstl.fmt.locale</param-name>

          <param-value>fr</param-value>

          </context-param>



          <welcome-file-list>

          <welcome-file>page_1.jsp</welcome-file>

          </welcome-file-list>

          </web-app>



          When the application shown in Figure 6-1 starts up, it displays Page 1 (page_1.jsp), which is designated as a welcome file in the deployment descriptor. That page uses a scriptlet to print the value of the FMT_LOCALE configuration setting, which is initially French (fr) because that's what was specified in the deployment descriptor. Listing 6.2 shows how that JSP page is implemented.



          The scriptlet that prints the value of the FMT_LOCALE configuration setting uses the static find method from the JSTL Config class to find the value of the FMT_LOCALE configuration setting. That method searches for a configuration variable in page, request, session, and application scopes, in that order; if it finds the configuration variable, it returns that variable's value. If the method does not find the configuration variable, it checks to see if a corresponding context initialization parameter exists; if so, it returns that parameter's value. In that way, scoped variables override context initialization parameters for configuration settings.[3]



          [3] See "The Config Class" on page 239 for more information about the Config class.





          Listing 6.2 page_1.jsp




          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

          <html>

          <head>

          <title>Page 1</title>

          </head>



          <body>

          <%@ taglib uri='http://java.sun.com/jstl/fmt' prefix='fmt'%>

          <%@ page import='javax.servlet.jsp.jstl.core.*' %>



          <h1>Page 1</h1>

          <%-- The following scriptlet prints the value of the

          FMT_LOCALE configuration setting --%>

          <% Object o = Config.find(pageContext, Config.FMT_LOCALE);

          out.print("Initial Locale Configuration Setting: <b>");

          out.print(o + "</b><br>");

          %>



          <fmt:setLocale value='en' scope='page'/>



          <p>

          <% o = Config.find(pageContext, Config.FMT_LOCALE);

          out.print("After &lt;fmt:setLocale value='en'&gt;");

          out.print(" Locale Configuration Setting: <b>" +

          o + "</b><br>");

          %>



          <form action='page_2.jsp'>

          <input type='submit' value='Go to Page 2'/>

          </form>

          </body>

          </html>



          After it prints the FMT_LOCALE configuration setting, the preceding JSP page uses the <fmt:setLocale> action to override that setting with English for page scope, like this:





          <fmt:setLocale value='en' scope='page'/>



          Subsequently, the JSP page prints the FMT_LOCALE configuration setting again, to verify that for Page 1, the default locale has changed from French to English.



          When you click on the submit button in Page 1, the browser loads Page 2, which is listed in Listing 6.3.





          Listing 6.3 page_2.jsp




          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

          <html>

          <head>

          <title>Page 2</title>

          </head>



          <body>

          <%@ page import='javax.servlet.jsp.jstl.core.*' %>



          <h1>Page 2</h1>

          <% Object o = Config.find(pageContext, Config.FMT_LOCALE);

          out.print("Locale Configuration Setting: <b>" +

          o + "</b><br>");

          %>

          </body>

          </html>



          Page 2 (page_2.jsp), listed above, prints the FMT_LOCALE configuration setting with the same scriplet used in Page 1.



          Clicking on the submit button in Page 1 generates a new request, so the English override of the context initialization parameter in Page 1�which was specified for page scope�goes out of scope. Because that override goes out of scope, the FMT_LOCALE configuration setting for Page 2 returns to French.



          What would happen if you changed this line of code from Page 1:





          <fmt:setLocale value='en' scope='page'/>



          to this:





          <fmt:setLocale value='en' scope='request'/>



          Will the output be different? No, because clicking on the Go to Page 2 button generates a new request, and the <fmt:setLocale> action in Page 1 sets the FMT_LOCALE configuration setting for the current request, so the setting once again goes out of scope. If you want to change the FMT_LOCALE configuration setting for Page 2, you can specify session or application scope for <fmt:setLocale>, like this:





          <fmt:setLocale value='en' scope='session'/>



          With the <fmt:setLocale> scope attribute set to session, as in the preceding code fragment, the FMT_LOCALE configuration setting will hold for Page 2, as shown in Figure 6-2.



          Figure 6-2. Overriding the FMT_LOCALE Configuration Setting in Session Scope


















             

             


            5.2 Chasing/Evading











             < Day Day Up > 







            5.2 Chasing/Evading



            To show how to implement potential-based chasing (or evading), we need only add a few bits of code to AIDemo2-2 (see Chapter 2 for more details). In that example program, we simulated the predator and prey units in a continuous environment. The function UpdateSimulation was responsible for handling user interaction and for updating the units every step through the game loop. We're going to add two lines to that function, as shown in Example 5-1.





            Lenard-Jones Potential Function



            The following equation shows the Lenard-Jones potential function:





            In solid mechanics, U represents the interatomic potential energy, which is inversely proportional to the separation distance, r, between molecules. A and B are parameters, as are the exponents m and n. To get the interatomic force between two molecules, we take the negative of the derivative of this potential function, which yields:





            Here, again, A, B, m, and n are parameters that are chosen to realistically model the forces of attraction and repulsion of the material under consideration. For example, these parameters would differ if a scientist were trying to model a solid (e.g., steel) versus a fluid (e.g., water). Note that the potential function has two terms: one involves -A/rn, while the other involves B/rm. The term involving A and n represents the attraction force component of the total force, while the term involving B and m represents the repulsive force component.



            The repulsive component acts over a relatively short distance, r, from the object, but it has a relatively large magnitude when r gets small. The negative part of the curve that moves further away from the vertical axis represents the force of attraction. Here, the magnitude of the force is smaller but it acts over a much greater range of separation, r.



            You can change the slope of the potential or force curve by adjusting the n and m parameters. This enables you to adjust the range over which repulsion or attraction dominates and affords some control over the point of transition. You can think of A and B as the strength of the attraction and repulsion forces, respectively, whereas the n and m represent the attenuation of these two force components.








            Example 5-1. Chase/evade demo UpdateSimulation




            void UpdateSimulation(void)

            {

            double dt = _TIMESTEP;

            RECT r;

            // User controls Craft1:

            Craft1.SetThrusters(false, false);



            if (IsKeyDown(VK_UP))

            Craft1.ModulateThrust(true);



            if (IsKeyDown(VK_DOWN))

            Craft1.ModulateThrust(false);



            if (IsKeyDown(VK_RIGHT))

            Craft1.SetThrusters(true, false);

            if (IsKeyDown(VK_LEFT))

            Craft1.SetThrusters(false, true);

            // Do Craft2 AI:

            .

            .

            .

            if(PotentialChase)

            DoAttractCraft2();

            // Update each craft's position:

            Craft1.UpdateBodyEuler(dt);

            Craft2.UpdateBodyEuler(dt);

            // Update the screen:

            .

            .

            .

            }








            As you can see, we added a check to see if the PotentialChase flag is set to true. If it is, we execute the AI for Craft2, the computer-controlled unit, which now uses the potential function. DoAttractCraft2 handles this for us. Basically, all it does is use the potential function to calculate the force of attraction (or repulsion) between the two units, applying the result as a steering force to the computer-controlled unit. Example 5-2 shows DoAttractCraft2.





            Example 5-2. DoAttractCraft2




            // Apply Lenard-Jones potential force to Craft2

            void DoAttractCraft2(void)

            {

            Vector r = Craft2.vPosition - Craft1.vPosition;

            Vector u = r;

            u.Normalize();

            double U, A, B, n, m, d;

            A = 2000;

            B = 4000;

            n = 2;

            m = 3;

            d = r.Magnitude()/Craft2.fLength;

            U = -A/pow(d, n) + B/pow(d, m);

            Craft2.Fa = VRotate2D( -Craft2.fOrientation, U * u);

            Craft2.Pa.x = 0;

            Craft2.Pa.y = Craft2.fLength / 2;

            Target = Craft1.vPosition;

            }








            The code within this function is a fairly straightforward implementation of the Lenard-Jones function. Upon entry, the function first calculates the displacement vector between Craft1 and Craft2. It does this by simply taking the vector difference between their respective positions. The result is stored in the vector r and a copy is placed in the vector u for use later. Note that u also is normalized.



            Next, several local variables are declared corresponding to each parameter in the Lenard-Jones function. The variable names shown here directly correspond to the parameters we discussed earlier. The only new parameter is d. d represents the separation distance, r, divided by the unit's length. This yields a separation distance in terms of unit lengths rather than position units. This is done for scaling purposes, as we discussed in Chapter 4.



            Aside from dividing r by d, all the other parameters are hardcoded with some constant values. You don't have to do it like this, of course; you could read those values in from some file or other source. We did it this way for clarity. As far as the actual numbers go, they were determined by tuning�i.e., they all were adjusted by trial and error until the desired results were achieved.



            The line that reads U = -A/pow(d, n) + B/pow(d, m); actually calculates the steering force that will be applied to the computer-controlled unit. We used the symbol U here, but keep in mind that what we really are calculating is a force. Also, notice that U is a scalar quantity that will be either negative or positive, depending on whether the force is an attractive or repulsive force. To get the force vector, we multiply it by u, which is a unit vector along the line of action connecting the two units. The result is then rotated to a local coordinate system connected to Craft2 so that it can be used as a steering force. This steering force is applied to the front end of Craft2 to steer it toward or away from the target, Craft1. That's all there is to it.



            Upon running this modified version of the chase program, we can see that the computer-controlled unit does indeed chase or evade the player unit depending on the parameters we've defined. Figure 5-2 shows some of the results we generated while tuning the parameters.





            Figure 5-2. Potential chase and evade







            In Figure 5-2 (A), the predator heads toward the prey and then loops around as the prey passes him by. When the predator gets too close, it turns abruptly to maintain some separation between the two units. In Figure 5-2 (B), we reduced the strength of the attraction component (we reduced parameter A somewhat), which yielded behavior resembling the interception algorithm we discussed in Chapter 2. In Figure 5-2 (C), we increased the strength of attraction and the result resembles the basic line-of-sight algorithm. Finally, in Figure 5-2 (D), we reduced the attraction force, increased the repulsion force, and adjusted the exponent parameters. This resulted in the computer-controlled unit running from the player.



            Adjusting the parameters gives you a great deal of flexibility when tuning the behavior of your computer-controlled units. Further, you need not use the same parameters for each unit. You can give different parameter settings to different units to lend some variety to their behavior�to give each unit its own personality, so to speak.



            You can take this a step further by combining this potential function approach with one of the other chase algorithms we discussed in Chapter 2. If you play around with AIDemo2-2, you'll notice that the menu selections for Potential Chase and the other chase algorithms are not mutually exclusive. This means you could turn on Potential Chase and Basic Chase at the same time. The results are very interesting. The predator relentlessly chases the prey as expected, but when it gets within a certain radius of the prey, it holds that separation�i.e., it keeps its distance. The predator sort of hovers around the prey, constantly pointing toward the prey. If the prey were to turn and head toward the predator, the predator would turn and run until the prey stops, in which case the predator would resume shadowing the prey. In a game, you could use this behavior to control alien spacecraft as they pursue and shadow the player's jet or spacecraft. You also could use such an algorithm to create a wolf or lion that stalks its victim, keeping a safe distance until just the right moment. You even could use such behavior to have a defensive player cover a receiver in a football game. Your imagination is the only limit here, and this example serves to illustrate the power of combining different algorithms to add variety and, hopefully, to yield some sort of emergent behavior.















               < Day Day Up > 



              2.4 The Big Picture




              I l@ve RuBoard









              2.4 The Big Picture


              This chapter has provided an overview of the entire process. The remainder of the book covers the details of what exactly needs to be done to build and compile Executable UML models.


              Chapter 3 and Chapter 4 address the topics of working out what needs to be modeled in the first place, addressed in this chapter as Section 2.1: The System Model.


              Chapter 5 and Chapter 6 cover the class diagram, addressed here as Section 2.2.1: Classes.


              Chapter 7 covers action semantics and action language (Section 2.2.3: Procedures) and Chapter 8 covers constraints on the class diagram.


              Chapter 9 covers the basics of statechart diagrams (Section 2.2.2: State Machines). Chapter 10 through Chapter 14 cover ways to build sets of communicating statechart diagrams.


              Chapter 15 covers model verification (Section 2.3.1: Model Verification).


              Chapter 16 covers model management.


              Chapter 17 and Chapter 18 cover weaving the models together so they will execute (Section 2.3.2: Model Compilation).








                I l@ve RuBoard



                Python Phrasebook: Essential Code and Commands








                Python Phrasebook: Essential Code and Commands
                By
                Brad Dayley
                ...............................................
                Publisher: Sams
                Pub Date: November 07, 2006
                Print ISBN-10: 0-672-32910-7
                Print ISBN-13: 978-0-672-32910-4

                Pages: 288
                 

                Table of Contents
                 | Index

                Python Phrasebook

                Brad Dayley

                Essential Code and Commands

                Python Phrasebook gives you the code
                phrases you need to quickly and effectively complete your
                programming projects in Python.

                Concise and Accessible

                Easy to carry and easy to uselets you
                ditch all those bulky books for one portable guide

                Flexible and Functional

                Packed with more than 100 customizable code
                snippetsso you can readily code functional Python in just
                about any situation

                Brad Dayley is a software engineer at
                Novell, Inc. He has been a system administrator and software
                developer on the Unix, Windows, Linux, and NetWare platforms for
                the past 14 years. Brad co-developed an advanced debugging course
                used to train engineers and customers and is the co-author of
                several Novell Press books.

                Programming / Python

                $16.99 USA / $20.99 CAN / £11.99 Net
                UK