Configuring JMS Resources

This section looks at the major administered objects that constitute the configuration of WebLogic's JMS provider. To configure WebLogic JMS and related resources, you need to start up the Administration Console and then navigate to the Services/JMS node in the left pane. Here you can access and set up all the crucial JMS resources, such as the JMS servers, connection factories, physical destinations, JMS stores, and more.

8.1.1 Server

A JMS server implements the actual messaging facilities. Each JMS server lives on a single WebLogic instance and hosts a number of messaging destinations. Thus, the WebLogic instance that actually hosts the destination is determined only once you target its JMS server to a WebLogic instance. Distributed destinations, however, deviate from this rule. Because a distributed destination provides a transparent layer above an existing set of server-specific physical destinations, they aren't bound to any of the JMS servers. Note that multiple JMS servers may be hosted by the same WebLogic instance.

In order to configure a new JMS server, select the Servers node from the left pane of the Administration Console, and then click the "Configure a new JMS Server" link from the right pane. All the settings for the JMS server can be found under the Configuration tab. Here you must use the General tab to specify a logical name for the JMS server, to configure a JMS store for the persistent messages, to set the paging store for swapping in-memory messages to disk, and to configure a template that will be used to create any temporary destinations on the JMS server.

Then use the Target and Deploy tab to deploy the JMS server to a particular WebLogic instance. To enable support for high availability, you also can set up a Migratable Target list for the JMS server i.e., a set of WebLogic instances within a cluster to which the JMS server and all its destinations can be migrated.

Whenever you do create a server such as this, it is often a good idea to also set the quotas and thresholds, and configure paging if it's needed.

8.1.2 Destinations

A JMS destination represents a delivery target for any message. It is a virtual channel that acts as an intermediary between the message producer and its consumers. The JMS server ensures that any messages delivered to a JMS destination are received by all who listen to (or poll) the destination. WebLogic supports both queue destinations, in which the delivered message is intended for a single consumer, and topic destinations, in which the message is intended for multiple subscribers. You can use the Administration Console to configure a predetermined set of JMS destinations that will be hosted by the JMS server. In order to set up a new JMS destination, choose a JMS server from under the Servers' node in the left pane, select its Destinations node, and then select either the "Configure a new JMS Queue" or "Configure a new JMS Topic" links.

Now use the General tab to specify a logical name for the destination, and the JNDI name to which the destination will be bound. In addition, you can assign a template to inherit (and possibly override) a set of configuration settings that are defined in the JMS template. You also can assign one or more destination keys to determine the sort order on any incoming messages.

The JMS destination will be available in the server's JNDI tree once the JMS server that owns the destination has been targeted to a WebLogic instance. In fact, if the JMS server is part of a cluster, the JMS destination can be made available to the cluster-wide JNDI tree, and therefore be accessible to all members of the cluster. Later, we examine how to programmatically create permanent destinations on a JMS server.

In addition, you should set the store characteristics of the destination on its Configuration/General tab. Here you can specify one of the following values for the Enable Store setting:

false

By setting the value to false, you indicate to WebLogic that the destination will not support persistent messaging. Any persistent messages delivered to the destination will be downgraded to nonpersistent.

default

The destination supports persistent messages, provided the JMS server has been configured with a JMS store. If a store has not been configured for the server, the behavior of the JMS destination is akin to "false."

true

By setting this value to true, you indicate that the destination does support persistent messaging. Moreover, if the JMS server does not define a JMS store, the JMS server will not start.

8.1.3 Connection Factory

As noted earlier, a connection factory encapsulates a set of configuration properties for connections to the JMS server. A JMS client must use the connection factory to obtain a connection to a JMS server before it can perform any work. A JMS client can then use the connection to create JMS sessions. Each session defines a serial order for how messages are produced or consumed. Thus, a connection factory is an administered object that encapsulates a set of predefined characteristics that are shared by all connections it creates. A connection factory is independent of the JMS servers in the domain, and may be targeted to any number of WebLogic instances. A JMS server can be deployed to a single WebLogic instance only, while a JMS connection factory may be targeted to a WebLogic cluster. In this way, you can provide cluster-wide, transparent access to the destinations on a JMS server.

You can create multiple JMS connection factories that encapsulate predefined connection attributes. When a WebLogic instance starts up, any JMS connection factory targeted to the server is bound into the server's JNDI tree. A JMS client then can obtain a JMS connection factory from JNDI. In fact, WebLogic provides two JMS connection factories by default, which can be looked up using the following JNDI names:

weblogic.jms.ConnectionFactory

A JMS connection factory that can be used to create connections to JMS servers.

weblogic.jms.XAConnectionFactory

A JMS connection factory that creates JMS connections that can participate in distributed transactions, alongside other XA-aware resources.

These default JMS connection factories are available on all server instances in a domain. You have no control over which servers the default JMS connection factories ought to be deployed to. Moreover, applications that rely on a custom JMS connection factory are easier to port to an alternative J2EE environment. Once the application makes an explicit demand for a JMS resource, the Administrator can fulfill those needs, provided you steer away from WebLogic's default JMS factories. You can explicitly disable the default JMS factories on all JMS servers targeted to a server, using the Administration Console. Choose a server from the left pane, and navigate to the Services/JMS tab on the right. Use the Enable Default JMS Connection Factories option to toggle the default factories.

To configure a new JMS connection factory, select the Connection Factories node from the left pane of the Administration Console, and then click on the "Configure a new JMS Connection Factory" link in the right frame. Here you can configure the different aspects of the behavior of the connections returned by the JMS factory. Note that many of the attributes are dynamically configurable, so any configuration changes that are applied to a connection factory will automatically come into effect for subsequent connections made using the connection factory.

JMS connection factories within a domain should be uniquely named, or else the server will not start up.

Use the General tab to specify a name for the connection factory and the JNDI name to which the JMS factory will be bound. You also can set the default message delivery attributes, the acknowledgment semantics, and the behavior of the close( ) method for any JMS clients of the factory. Then use the Targets and Deploy tab to target the connection factory to the desired servers or clusters. All remaining configuration issues are covered in later sections.

Imagine a JMS server, myJMSServer, that hosts a JMS queue, myQueue, targeted to a WebLogic instance named myServer. Furthermore, a JMS connection factory, myCF, is configured and also targeted to the same server. Given this scenario, the following code snippet shows how to create a JMS session that you can use to send or receive messages from the configured JMS queue:

// set up the initial JNDI context
javax.naming.Context ctx = new InitialContext(env);
// lookup the JMS factory from the server's JNDI tree
javax.jms.QueueConnectionFactory factory =
 (javax.jms.QueueConnectionFactory) ctx.lookup("myCF");

// use the factory to manufacture a QueueConnection
javax.jms.QueueConnection con = factory.createQueueConnection( );

// use the QueueConnection object to initiate a JMS session
javax.jms.QueueSession session = con.createQueueSession(false, DUPS_OK_ACKNOWLEDGE);

// use the createQueue( ) method to locate myQueue hosted on myJMSServer
javax.jms.Queue queue = session.createQueue("myJMSServer/myQueue");

// now send/receive messages using the queue
// ...

All this is standard JMS code. Because we wish to interact with a configured JMS queue, we have cast the connection factory object obtained from the JNDI lookup to java.jms.QueueConnectionFactory. This queue connection factory allows us to create a connection to the JMS server, and subsequently initiate a JMS session. A JMS connection factory is thread-safe, and so can be used concurrently by multiple threads. Notice how we've used the createQueue( ) method on the JMS session to locate the configured queue. Alternatively, we could have performed another JNDI lookup to access the JMS queue. Thus, if the queue were assigned the JNDI name oreilly.myQ at configuration time, you could access the destination as follows:

javax.jms.Queue queue = (javax.jms.Queue) ctx.lookup("oreilly.myQ");

If you need to interact with a configured JMS topic, then use the topic counterparts for the JMS factory, connection, and session.

As you can see, JMS servers, destinations, and connection factories are critical resources for any JMS-enabled application. We now look at other optional JMS resources that help you adjust other aspects of the JMS server configuration.

8.1.4 Templates

A template is a convenience mechanism that allows you to define a set of characteristics that then can be shared by multiple destinations. When you assign a template to a JMS destination, it inherits the values for any configuration settings defined by the template. Of course, a destination may override any values configured for its template. Thus, a template eases the configuration of multiple JMS destinations. Any changes to the values of configuration settings for the template are dynamically propagated to all destinations that use that template, provided the destination itself doesn't override those configuration settings. For instance, if all queues must share a particular threshold and quota policy, you can define a new template with the desired threshold and quota settings, and then assign this template to each queue.

To create a new template, expand the JMS/Templates node from the left pane and then select the "Configure a new JMS Template" link on the right pane. Here you can adjust the values for a subset of the usual configuration settings for any JMS destination. In particular, you can set the default threshold and quotas, expiration policy, and redelivery behavior for any destination that uses the template. You also must supply a logical name for the template. You will use this name to refer to the template when assigning it to a JMS destination.

In order to assign a template to a physical destination, you must set the Template option in the Configuration/General tab to the name of an existing destination. After this, the JMS destination automatically inherits any unchanged settings from the template. As you will see later, a template must also be configured for the JMS server if applications need to create temporary destinations.

8.1.5 Destination Keys

A destination key can be used to set the sort order for messages that have arrived at a physical destination. Messages are sorted in either the ascending or descending order of the values of some message header field or property. For example, if an application sets the message expiration times, you may sort the messages arriving at the destination on the JMSExpiration header field to bias the consumers toward processing messages that will expire the soonest. A destination key determines only a specific sort order. To actually implement the sort, you have to assign the key to a destination. You can assign multiple keys to a physical destination.

Sorting occurs only if there are multiple messages waiting at a destination. If a queue is being used, and consumers are always available and constantly removing messages placed on the queue, the messages will not be sorted. Instead, the messages will be processed in the order in which they enter the queue. In such cases, using a destination key will yield no benefits. In fact, destination keys will always be ineffective unless multiple messages are waiting at a destination.

To create a new destination key, select the Destination Keys node from the left pane, and then the "Configure a new JMS Destination Key" link from the right pane. Here you must set the following values for a destination key:

Name

Use this option to set the logical name for the key. The name of the key is used when assigning it to a physical destination.

Direction

Use this option to sort in either ascending or descending order. If ascending order is chosen, and the property is set to JMSMessageID, the messages arrive in a first-in, first-out (FIFO) fashion, which is the default ordering for messages arriving on a destination.

The JMS [1] Sort key

Use this setting to determine the name of the property or header field on which to sort the incoming messages.

Key type

Use this setting to set the expected type of the key. The key type can take any one of the following values: String, Boolean, Byte, Short, Int, Long, Float, or Double. This setting is used only if the destination key defines a sort order on some message property field. If the destination key specifies a message header field instead, this setting is ignored.

If the key defines a sort order in terms of a message header field, then you may use any of the following names: JMSMessageID, JMSTimestamp, JMSCorrelationID, JMSPriority, JMSExpiration, JMSType, JMSRedelivered, or JMSDeliveryTime.

Sorting on JMS header fields is more efficient than sorting on property names, and is recommended for better performance.

Once you have defined a number of destination keys, you can put them into action by selecting a destination from a JMS server. Then, in the Configuration/General tab, you can choose from the available destination keys and assign them to the particular destination.

Let's see how destination keys impact the way messages arrive on a JMS destination. First, let's create two destination keys:

  • A destination key that defines an ascending sort on the JMSPriority header field.
  • A second destination key that defines a descending sort on a message property field price of type Int.

After creating the two keys, assign both of these destination keys, in the same order, to an existing queue. Now imagine a queue receiver that does nothing much except listen to the queue and print out the values of the priority and price fields of any message delivered to the queue. In addition, we have a producer that sends a number of messages to the same queue in a loop, increasing the value of the price property field on each iteration. To make things interesting, our queue sender also delivers three messages of different priority on each iteration. The following code sample illustrates this:

for (int i=0;i<2;i++) {
 msg.setIntProperty("price", i);
 qsender.send(wlmsg, javax.jms.DeliveryMode.NON_PERSISTENT,
 2, 1800000);
 qsender.send(wlmsg, javax.jms.DeliveryMode.NON_PERSISTENT,
 3, 1800000);
 qsender.send(wlmsg, javax.jms.DeliveryMode.NON_PERSISTENT,
 1, 1800000); 
 msg.clearProperties( );
}

Remember that sorting occurs only if there are multiple messages waiting at the queue. If the queue receiver is listening at the time the messages are sent, no sorting will occur and the messages will arrive in the order in which they were sent. In that case, the queue receiver will generate the following output:

Got Message: Priority = 2 & Price = 0
Got Message: Priority = 3 & Price = 0
Got Message: Priority = 1 & Price = 0
Got Message: Priority = 2 & Price = 1
Got Message: Priority = 3 & Price = 1
Got Message: Priority = 1 & Price = 1

In order to ensure that sorting does occur on messages arriving on the queue, we must create a scenario in which multiple messages are waiting on the queue. One way to achieve this is by running the queue sender first, and then only later starting the queue receiver. With destination sorting enabled, the queue receiver will generate the following output:

Got Message: Priority = 1 & Price = 1
Got Message: Priority = 1 & Price = 0
Got Message: Priority = 2 & Price = 1
Got Message: Priority = 2 & Price = 0
Got Message: Priority = 3 & Price = 1
Got Message: Priority = 3 & Price = 0

As specified, the messages arriving on the destination are sorted first in ascending order of the priority field, and then in descending order of the property field price.

By default, messages received by a destination are sorted in ascending order of the JMSMessageID header field, which results in a FIFO ordering. If you want to impose a last-in, first-out (LIFO) ordering on the destination, simply assign a destination key that sorts on the JMSMessageID field in descending order. Note that sorting may degrade server performance because the JMS server must scan the destination to find the correct place to insert a received message.

Later in this chapter, in Section 8.3, we shall see how WebLogic allows you to delay the delivery of JMS messages. In such cases, you must take care to sort on the JMSMessageID header field, as the delayed delivery times could mean that messages arrive at the JMS destination out of their message ID order, thereby defeating the purpose of the destination key altogether. In such situations, the better approach would be to sort on the JMSDeliveryTime header field.

8.1.6 JMS Stores

WebLogic lets you configure JMS stores for two reasons:

  • For storing and paging persistent messages and durable subscribers to an external store, thereby guaranteeing their existence even if a JMS server fails
  • For paging nonpersistent messages to an external store so that in-memory JMS messages can be swapped out when the JMS server is under heavy load

WebLogic lets you choose from two types of JMS stores: a file-based store and a JDBC-accessible database store. Both of these choices provide the same transaction semantics and guarantees, and so can be used interchangeably. A file store also is recommended for temporarily swapping out nonpersistent messages to disk when the JMS server is under load. Persistent messages don't need a dedicated paging store. If paging is enabled, they will simply use the persistent store. However, nonpersistent messages do need a dedicated paging store. If a JMS server must support both persistent and nonpersistent message paging, you will need to configure a persistent store and a paging store.

In order to configure a new file store, expand the JMS/Stores node from the left pane of the Administration Console, and then click the "Configure a new JMS File Store" link from the right pane. Use the General tab to specify a logical name for the file store and the location of the directory that will host the file store. Also, select the Synchronous Write Policy option to determine how the file store writes data to disk. In order to create a new JDBC store, select the JMS/Stores node from the left pane, and then click the "Configure a new JMS JDBC Store" link from the right pane. Once again, specify a logical name for the JDBC store, choose from one of the existing connection pools that will be used to access the database, and set the prefix name that will help identify the JMS-related tables in the database.

File stores generally are considerably faster than JDBC stores. Moreover, no network traffic is generated when you use a file-based store for the JMS server, whereas JDBC stores will generate network traffic if the database lives on a different machine. JDBC stores offer another significant advantage: they make it somewhat easier to recover from failures. If a machine hosting a JMS server fails, the JDBC store still can be accessed if you migrate the service to another machine. If you need the same functionality for a JMS file store, ensure that it resides on a shared disk. [2] This way, if a machine hosting the JMS server fails, the persistent messages are still accessible once the JMS server is migrated to another machine.

[2] Preferably a SAN, or dual-ported SCSI not NFS or even Windows network drives because these are not transactionally safe.

A JMS store for persistent messages invariably will slow down the application, so ensure that your JMS applications really need the persistent quality of service. Similarly, enabling message paging to a JMS store will be more expensive than disabling paging altogether.

8.1.7 Distributed Destination

A distributed destination represents a collection of physical destinations that may be hosted on multiple JMS servers within a cluster. A distributed destination is accessible through a single JNDI name because it is bound to the cluster-wide JNDI tree. Just like physical destinations, it implements the javax.jms.Destination interface, and can be used just like them as well. Accessing a distributed queue is no different from accessing an ordinary queue:

javax.jms.QueueSession session = con.createQueueSession(false, DUPS_OK_ACKNOWLEDGE);
javax.jms.Queue queue = session.createQueue("myDistributedQueue");

The fact that the distributed queue represents multiple physical queues is transparent to the JMS client. Later, we shall see how you also can access the individual members of a distributed destination.

In order to configure a distributed destination, you must navigate to the Services/JMS/Distributed Destinations node within the left pane of the Administration Console, and then click the "Configure a new Distributed Topic/Queue" link. Here you must specify a logical name for the destination and the JNDI name to which the distributed queue or topic will be bound. Because a distributed destination includes multiple JMS destinations spread across different JMS servers in the cluster, you also must configure the distributed destination's load-balancing policy and its forward delay. After this, you must use the Configuration/Members tab to configure the physical destinations that belong to the distributed destination. As we shall see later, you can choose from preexisting destinations or from the existing set of JMS servers, and let WebLogic create the physical destinations on these servers for you.

8.1.8 JMS Session Pools

The JMS standard requires that JMS sessions be single-threaded. This means that a JMS listener cannot concurrently process multiple messages within a single JMS session. To circumvent this problem, the specification allows for a pool of server-side sessions, a feature that enables an application to process messages concurrently.

The server-side pool of JMS sessions is preloaded with a custom JMS consumer that is dedicated to processing the messages that arrive at the destination. In other words, each member of the JMS session pool loads an instance of a message listener class. When a message arrives on a JMS destination, the JMS server automatically dips into this session pool and invokes the onMessage( ) method supplied by your listener class. Because a separate instance of the message listener is assigned to each session in the pool, the JMS server is able to handle multiple incoming messages concurrently. Under heavy load, the JMS server may even dispense multiple messages to a session, thereby reducing context switching between the sessions (which is permitted by the JMS specification).

There are several key issues to resolve when configuring a server-side JMS session pool:

  • You must specify the JNDI name of the connection factory that will be used to create the JMS sessions within the pool.
  • You should specify the fully qualified name of the listener class that will consume the messages that arrive on the destination.
  • You should set the JNDI name of the destination to which the JMS message listeners will be bound.
  • You should configure the acknowledgment mode for the JMS sessions within the pool, and also indicate whether the sessions are transacted.
  • Finally, of course, you must determine the size of the JMS session pool.

You wouldn't be completely wrong to think that JMS session pools were quite similar to message-driven beans. Both enable you to set up a pool of JMS consumers that concurrently process incoming messages on a configured destination. On the whole, message-driven beans are more powerful than JMS session pools. For instance, message-driven beans can participate in distributed transactions, whereas JMS session pools provide you with (local) transacted sessions only. In addition, message-driven beans are a part of the J2EE standard, whereas session pools are simply an optional feature of the JMS specification. If you want portability, use message-driven beans instead. For this reason, we recommend you use message-driven beans in any new development.

In order to "configure a JMS session pool," you must first select a JMS server from the left pane of the Administration Console and then expand the Session Pools node. Next, you must select the "Configure a new JMS Session Pool" link from the right pane to proceed. You can then attach one or more connection consumers to this session pool, and thereby concurrently handle messages arriving at multiple destinations. The actual setup is deferred until later in this chapter, in Section 8.2.6, where we also look at how to programmatically create a JMS session pool. For now, you must recognize the role of JMS session pools in concurrent message processing.

8.1.9 Foreign JMS Servers

WebLogic 8.1 provides direct support for accessing third-party JMS providers. Using the Administration Console, you can map the connection factories and destinations on the remote JMS server to the local JNDI tree. When a JMS client looks up the foreign JMS object from the server's JNDI tree, WebLogic automatically performs another lookup on the remote JMS connection factory or destination using its remote JNDI name and the supplied JNDI context factory for the remote JMS provider. In this way, WebLogic applications create seamless access to the foreign JMS server and its administered objects. You also can use this approach to reference JMS connection factories and objects on another WebLogic JMS server in another domain.

Note that this type of integration is slightly different from WebLogic's bridging between two JMS servers. Through the configured foreign JMS provider, your applications are direct clients of the remote JMS server. On the other hand, a message bridge creates a pipe between a destination on the local JMS server, and another on the remote JMS server, so that any messages that are sent to the source destination are automatically forwarded to the target destination. In other words, your applications can indirectly interact with the remote JMS server through the configured message bridge.

Foreign JMS providers provide an additional benefit. Because the foreign destinations are directly mapped to WebLogic's JNDI tree, any message-driven bean (MDB) that you deploy to the server can simply reference the remote destination using its local JNDI name. The MDB will continue to function as if it were bound to a destination on the local JMS server, except that now, of course, the MDB will be triggered whenever a message arrives on the foreign JMS destination.

8.1.9.1 Configuring a foreign JMS provider

In order to set up a foreign JMS provider, you must configure the following resources using the Administration Console:

  • A foreign JMS server that provides WebLogic with the initial context factory
  • A foreign JMS connection factory that is used to create connections to the remote JMS server
  • Any number of foreign JMS destinations that live on the remote JMS server

To configure a foreign JMS server, select the JMS/Foreign JMS Servers node from the left pane, and then select the "Configure a new foreign JMS provider" option from the right pane. Here you must supply the following information:

JNDI Initial Context Factory

Enter the fully qualified classname of the initial context factory that must be used to access the JNDI provider.

JNDI Connection URL

Enter the URL that WebLogic should use to contact the JNDI provider. This URL will depend on the context factory that you have configured.

JNDI Properties

Supply any additional JNDI properties that you want to be passed to the JNDI provider. Use the format "name=value" to specify each property.

This provides WebLogic with enough information to access the external JNDI provider. As an example, if you want to provide WebLogic applications with transparent access to JMS destinations on an IBM MQSeries installation, you could use the following settings to configure the foreign JMS server:

JNDI Initial Context Factory = "com.sun.jndi.fscontext.RefFSContextFactory"
JNDI Connection URL = "file:/MQJNDI/"
JNDI Properties = ""

Once you've created the foreign JMS server, you should use the Targets and Deploy tab to target the JMS server to multiple servers and/or clusters. After this, you must select the Foreign JMS Connection Factories node under your newly configured JMS server to configure a new JMS connection factory. Here, you will be asked to supply the following information:

Remote JNDI Name

Use this attribute to specify the JNDI name of the connection factory on the remote JMS server. WebLogic then looks up the remote connection factory, using this JNDI name, and the JNDI initial context factory that you specified for the foreign JMS server.

Local JNDI Name

Use this attribute to specify the JNDI name to which this foreign JMS connection factory will be bound.

Username and Password

Use these attributes to specify a username and password that should be used to obtain a connection from the referenced JMS connection factory. These credentials are used only when the foreign JMS connection factory is referenced from within a resource-ref element in the deployment descriptors of the EJB or a web application, with a Container mode of authentication.

Use the Targets and Deploy tab to assign the connection factory to multiple WebLogic servers and/or clusters. When you assign the foreign JMS connection factory to multiple servers or a cluster, WebLogic binds a nonreplicated instance of the JMS connection factory to the local JNDI tree of each targeted server.

Once you have specified a foreign connection factory, you can configure one or more foreign destinations by choosing the "Foreign JMS destinations" node from under the new foreign JMS server node in the left pane. For each foreign destination, you must supply the remote JNDI name of the destination and the local JNDI name to which the foreign destination should be mapped. In this way, when an application looks up the foreign destination from the local server's JNDI tree, WebLogic transparently looks up the destination on the remote JMS server using its remote JNDI name. The foreign JMS destinations will be deployed to all servers and/or clusters to which the owning foreign JMS server has been targeted.

Client applications must use the foreign connection factory to create producers and consumers that can interact with the configured foreign destinations. So, any MDBs that listen to a foreign destination also must be configured to use the foreign connection factory.


Introduction

Web Applications

Managing the Web Server

Using JNDI and RMI

JDBC

Transactions

J2EE Connectors

JMS

JavaMail

Using EJBs

Using CMP and EJB QL

Packaging and Deployment

Managing Domains

Clustering

Performance, Monitoring, and Tuning

SSL

Security

XML

Web Services

JMX

Logging and Internationalization

SNMP



WebLogic. The Definitive Guide
WebLogic: The Definitive Guide
ISBN: 059600432X
EAN: 2147483647
Year: 2003
Pages: 187

Similar book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net