Previous     Contents     Index     DocHome     Next     
iPlanet Application Server 6.0 Programmer's Guide (C++)



Appendix A   Implementation Tips


This appendix includes the following tips:



System Configuration Tips

Install the Web server, the iPlanet Application Server, and the database on different machines so that they will not have to compete for resources. The two servers use Web Connector to communicate.

When using the Web Connector, remember that the CGI variables or NSAPI/ISAPI variables are set on the machine where Web Connector is running, not on the iPlanet Application Server machine. When changing the configuration of Web Connector, make sure to update the registry on the correct machine.



Memory Management Tips



When resources are no longer needed, you must explicitly free them by calling the Release( ) method. However, even if you are extremely careful about reference counting, it is likely that some memory will not be released properly. Over time, such memory leaks will consume the available resources on your machine. Therefore, when using C++, it is advisable to use a tool capable of memory use analysis, such as Purify 4.1 by Pure Atria.



Database and Query Tips



To improve database performance:

  • Design the database schema carefully.

  • Tune the database cache sizes and the number of connections.

Query files are easier to maintain than queries written in code using method calls. You can use the Query Designer to set up queries visually, and let the Query Designer automatically generate the query file. You can also write query files using a text editor.

Avoid queries with more than two or three joins, as this will degrade performance. To improve performance, consider denormalizing the database. Denormalization results in duplicate data in the database, but simplifies queries and improves performance in the database.

Use the caching capabilities of iPlanet Application Server to improve performance. Plan on caching early in the design process, because its use affects how you design the client side of the application. All the criteria needed for caching must be present in the input parameters of the request. For example, when the clients are Web browsers, this means the caching criteria must be present as fields on an HTML form, or as arguments in the URL that calls the AppLogic.

When you are finished using a result set, release it by calling Release( ). This method releases the database connection so that it is available for use by other application code. Do not release the result set or close the database connection until you are finished using the result set. Just because the query has run and returned a result set interface, that doesn't mean all the data is there. Typically the result set is buffered, and live database cursors may still be open. Therefore, when you reach the last row in the buffer, the result set object still needs the connection to get the next batch of rows into the buffer.



HTML Tips



To improve the performance of any application with Web browser clients, always use the NSAPI/ISAPI plugin.

Avoid building HTML strings in code, because this is difficult to maintain and update. Keep the HTML in files and templates. HTML designers can then improve and enhance your application's Web browser presentation without having to edit business logic code.



Session Tips



Avoid storing too much data in a session. Every time you save or retrieve the session-related data, the whole IGXValList object is involved. This can impact the performance of the application.



Tips for Calling an Applogic From Another Applogic



By using the NewRequest( ) method, an AppLogic can call another AppLogic. Usually, this involves a distributed process-to-process communication. This is slower than an in-process local procedure call, but has the added benefits of allowing AppLogic location transparency, load balancing, more support for partitioning, and result caching. For truly fine-grained, often-called operations, however, the remote communication costs are not worth the benefits.



Streaming Tips



The SaveSession( ) method in the GXAppLogic class performs some processing of HTTP headers, which must be sent before the HTTP body. Therefore, if your application uses sessions, and also streams HTML results to a Web browser, be sure to call SaveSession( ) before calling any streaming methods, including EvalTemplate( ) or EvalOutput( ).

The method called SaveSession( ) exists in both the IGXSession2 interface and the GXAppLogic class. The method in the GXAppLogic class is a wrapper that calls the method in the interface, and performs some tasks that ensure that the session is accessible to future AppLogics. The SaveSession( ) method in the interface saves session data only. Therefore, be sure to call SaveSession( ) in the GXAppLogic class at least once after a session is created.


Streaming Results from EvalTemplate( ) or EvalOutput( ) Using IGXTemplateData

If you are using an IGXTemplateData object rather than a database query as the source of data for a call to EvalTemplate( ) or EvalOutput( ), you can increase the perceived performance of the call by using the following technique. Instead of populating the IGXTemplateData object by calling RowAppend( ) repeatedly, implement the IGXTemplateData interface yourself and call EvalTemplate( ) or EvalOutput( ) much earlier in the AppLogic code. In this way, the Template Engine can call the IGXTemplateData object as it needs data and return results as they are available, keeping the user waiting much less time for a response.

The Template Engine calls the MoveNext( ) method in the IGXTemplateData interface each time it needs a new row of data; for example, when it has completed one pass in a tile tag and is ready to start the next iteration of that tile. If you have implemented your own MoveNext( ) method, you can use that code to retrieve data as needed. This takes the place of calling RowAppend( ) repeatedly to populate the IGXTemplateData object all at once. After MoveNext( ) is called, Get( ) is called to retrieve the values in that row.

For example, the following code shows how the AppLogic code looks when you use RowAppend( ):

// Populate the in-memory template data. The number

// of calls to RowAppend() is unlimited. Meanwhile,

// the user is waiting for an unknown length of time

// until the full template data set is populated.

//

GXTemplateDataBasic *td;

td = new GXTemplateDataBasic("offices");

td->RowAppend("office=New York;revenue=150");

td->RowAppend("office=Hong Kong;revenue=130");

// ... add more records here.

// Pass the finished data set to EvalTemplate().

HRESULT hr;

hr = EvalTemplate("salesReportByOffice.html",

   (IGXTemplateData *) td, NULL, NULL, NULL);

td->Release();

return hr;

Now suppose you create your own implementation of the IGXTemplateData interface or subclass from the GXTemplateDataBasic class. The following code is in the header file:

class MyTemplateDataBasic : public GXTemplateDataBasic

{

public:

   MyTemplateDataBasic(LPSTR group) :

      GXTemplateDataBasic(group)

   {

      // Prepare the retrieval of the offices records here.

      // We don't have to get all the data yet, just

      // the first record data.

   }

   STDMETHOD(IsEmpty) (

      LPSTR group,

      BOOL *empty

   );

   STDMETHOD(MoveNext) (

      LPSTR group

   );

   STDMETHOD(GetValue) (

      LPSTR szExpr,

      IGXBuffer **ppBuff

   );

};

The following code is in the source file:

STDMETHODIMP

MyTemplateDataBasic::GetValue(LPSTR field,

   IGXBuffer **ppBuff)

{

   if (strcmp(field, "offices.office") == 0)

   {

      IGXBuffer *office;

      // ... retrieve current office field value here.

      *ppBuff = office;

      return NOERROR;

   }

   if (strcmp(field, "offices.revenue") == 0)

   {

      IGXBuffer *revenue;

      // ... retrieve current revenue field value here.

      *ppBuff = revenue;

      return NOERROR;

   }

   return GXTemplateDataBasic::GetValue(field, ppBuff);

}

STDMETHODIMP

MyTemplateDataBasic::IsEmpty(LPSTR group, BOOL *empty)

{

   if (strcmp(group, "offices") == 0)

   {

      boolean isOfficeRecordSetEmpty;

      // ... determine if the data set is empty.

      *empty = isOfficeRecordSetEmpty;

      return NOERROR;

   }

   return GXTemplateDataBasic::IsEmpty(group, empty);

}

STDMETHODIMP

MyTemplateDataBasic::MoveNext(LPSTR group)

{

   if (strcmp(group, "offices") == 0)

   {

      HRESULT noMoreRecords;

      // Move to next record in offices data set here.

      // This is where we can dynamically compute

      // the next record.

      //

      // Return NOERROR (0) if next record is available.

      // Return non-zero if no more records.

      return noMoreRecords;

   }

   return GXTemplateDataBasic::MoveNext(group);

}

The following code shows how the AppLogic code looks when you let the Template Engine retrieve the data through MoveNext( ):

// Use our own GXTemplateDataBasic subclass, which is

// smart enough to dynamically retrieve office records

// when called back by the template engine. This allows

// data to be streamed back to the user as it becomes

// available, instead of waiting for the entire

// data set to be created first in memory.

//

MyTemplateDataBasic *td;

td = new MyTemplateDataBasic("offices");

// MyTemplateDataBasic retrieves office records

// as necessary, so we do not prepopulate it here.

// Pass the MyTemplateDataBasic object to EvalTemplate().

HRESULT hr;

hr = EvalTemplate("salesReportByOffice.html",

   (IGXTemplateData *) td, NULL, NULL, NULL);

td->Release();

return hr;


Previous     Contents     Index     DocHome     Next     
Copyright © 2000 Sun Microsystems, Inc. Some preexisting portions Copyright © 2000 Netscape Communications Corp. All rights reserved.

Last Updated April 26, 2000