Oracle® Retail POS Suite Implementation Guide, Volume 2 – Extension Solutions Release 14.1 E54476-02 |
|
Previous |
Next |
This chapter describes the Manager/Technician pair relationship and how it is used to provide business and system services to the application. It also describes how to build a Manager and Technician and provides sample implementation and sample code.
Oracle Retail POS Suite provides the technology for distributing business and system processes across the enterprise through plug-in modules called Managers and Technicians. Manager and Technician classes come in pairs. A Manager is responsible for communicating with its paired Technician on the same or different tiers. The Technician is responsible for performing the work on its tier. By design, Managers know how to communicate with Technicians through a pass-through remote interface called a valet. The valet is the component that handles data transfer. The valet can travel across networks. It receives the instructions from the Manager and delivers them to the Technician. A valet follows the Command design pattern described in the Oracle Retail POS Suite Implementation Guide – Volume 1, Implementation Solutions.
There is a M:N relationship between instances of Managers and Technicians. Multiple Managers may communicate with a Technician, or one Manager may communicate with multiple Technicians. While most Managers have a corresponding Technician, there are cases such as the Utility Manager where no corresponding Technician exists.
There are three Manager/Technician categories. These types have different usages and are started differently. The three types are:
Global—These Managers and Technicians are shared by all tours. They provide global services to applications.
Session—These Managers and Technicians perform services for a single tour. They are started by each tour and exist for the length of the tour.
Embedded—Thread Manager is embedded inside the Oracle Retail POS Suite engine. It is essential to the operation of the engine. This is currently the only embedded Manager.
Table 10-1 lists each of the three Manager/Technician categories, along with examples.
Table 10-1 Manager/Technician Type Examples
Manager/Technician Type | Examples |
---|---|
Global |
|
Session |
|
Embedded |
Thread |
Session Managers are started up by the tour bus when a tour is invoked and can only be accessed by the bus in the tour code. Global Managers, on the other hand, can be used at any time and are not specific to any tour. Each type of Manager has a specific responsibility.
Table 10-2 lists the functions of some of the Managers.
Table 10-2 Manager Names and Descriptions
Manager Name | Description |
---|---|
Data |
The Data Manager is the system-wide resource through which the application can obtain access to persistent resources. The Data Manager tracks all data stores for the system, and is the mechanism by which application threads obtain logical connections to those resources for persistence operations. |
Device |
The Device Manager defines the Java interfaces that are available to an application or class for accessing hardware devices, like printers and scanners. |
Journal |
The Journal Manager is the interface that is used to write audit trail information, such as start transaction, end transaction, and other interesting register events. |
Log |
The Log Manager is the interface that places diagnostic output in a common location on one tier for an application, regardless of where the actual tours run. |
Parameter |
The Parameter Manager is the interface that provides access to parameters used for customization and runtime configuration of applications. |
Thread |
The Thread Manager is a subsystem that provides system threads as a pooled resource to the system. |
Tier |
The Tier Manager interface starts a tour session and mails letters to existing tour sessions. The Tier Manager enables the engine to start a tour on any tier specified in a transfer station, regardless of where that tier runs. In addition, the Tier Manager enables a bus to mail a letter to any other existing Bus in the system on any tier. |
Timer |
The Timer Manager provides timer resources to applications that require them. It does not have a Technician because all timers are local on the tier where they are used. |
User Interface |
The UI Manager is a mechanism for accessing and manipulating user interface components. The user interface subsystem within a state machine application must also maintain a parallel state of screens, so the appropriate screens can be matched with the application state at all times. The user interface subsystem within a distributed environment must enable application logic to be completely isolated from the user interface components. |
XML |
The XML Manager locates a specified XML file, parses the file, and returns an XML parse tree. |
When creating a new Manager and Technician pair, you must create a Manager and Technician class, a Valet class, and interfaces for each class. Managers are the application client to a Technician service, Technicians do the work, and the valet tells the Technicians what work to do. Managers can be considered proxies for the services provided by the Technicians. Technicians can serve as the interfaces to resources. Managers communicate with Technicians indirectly using valets. Valets can be thought of as commands to be executed remotely by the Technician. Samples for the new classes that need to be created are organized together in the next section.
Requesting services from the Managers only requires familiarity with the interface provided by Managers. However, building a new Manager/Technician pair requires implementing the interfaces for both the new Manager and Technician, as well as creating a Valet class.
A Manager merely provides an API to tour code. It behaves like any other method except that the work it performs may be completed remotely. The input to a Manager is usually passed on to the valet that in turn, passes it on to the Technician, which actually performs the work.
The Manager class provides methods for sending valets to the Technician. The sendValet() method makes a single attempt to send a valet to the Manager's Technician. The sendValetWithRetry() method attempts to send the valet to the Manager's Technician, and if there is an error, reset the connection to the Technician and then try again.
Managers must implement the ManagerIfc, which requires the methods in Table 10-3.
Table 10-3 ManagerIfc Methods
Method | Description |
---|---|
MailboxAddress getAddress() |
Gets address of Manager |
Boolean getExport() |
Returns if this Manager is exportable |
String getName() |
Gets name of Manager |
void setExport(Boolean) |
Sets whether the Manager is exportable |
void setName(String) |
Sets name of Manager |
void shutdown() |
Shuts this Manager down |
void startUp() |
Starts this Manager |
Often, a subclass of Manager can use these methods exactly as written. Unlike the Technicians, Managers seldom require special startup and shutdown methods, because most Managers have no special resources associated with them.
You can provide runtime configuration settings for each Manager using a conduit script. The Dispatcher that loads Point-of-Service configures the Managers by reading properties from the conduit script and calling the corresponding set() method using the Java reflection utility. All properties are set by the Dispatcher before the Dispatcher calls startUp() on the Manager.
Every Manager should have the following:
Name—Tour code typically locates a Manager using its name. Often this name is the same as the name of the class and may be defined as a constant within the Manager. This is what the getName() method returns.
Class—This is the name of the class, minus its package.
Package—This is the Java package where the class resides.
Managers may have an additional property file defined that specifies additional information such as the definition of transaction mappings. If a separate configuration script is defined, the startup() method must read and interpret the configuration script. The following sample from <source_directory>
\applications\pos\deploy\client\config\conduit\ClientConduit.xml
shows this.
Technicians implement functions needed by Point-of-Service to communicate with external or internal resources, such as the UI or the store database. Technicians must implement the TechnicianIfc, which requires the following methods in Table 10-4:
Table 10-4 TechnicianIfc Methods
Method | Description |
---|---|
MailboxAddress getAddress() |
Gets address of Technician |
Boolean getExport() |
Checks if this Technician is exportable |
String getName() |
Gets name of Technician |
void shutdown() |
Shuts this Technician down |
void startUp() |
Starts up Technician process |
Often, a subclass of Technician can use these methods exactly as written. The most likely methods to require additional implementation are startUp() and shutdown(), which needs to handle connections with external systems.
The Technician is configured within the conduit script. Each Technician should have the following:
A Manager typically locates its Technician using its name. Often this name is the same as the name of the class and may be defined as a constant within the Technician. This is what Technician.getName() returns.
The name of the class, minus its package.
The Java package where the class resides.
This should be Y if the Technician may be accessed by an external Java process; N otherwise. The value returned by Technician.getExport() is based on this. In Technicians, it indicates whether the Technician can be remotely accessed from another tier.
Some Technicians may require complex configuration. In cases like this, it may be preferable to define an XML configuration script specific to the Technician, rather than to rely on the generic property mechanism. Therefore, Technicians may have an additional property defined that specifies additional information such as log formats or parameter validators. If a separate configuration script is defined, the startup() method must read and interpret the configuration script. The following sample from <source_directory>
\applications\pos\deploy\server\config\conduit\StoreServerConduit.xml
shows an additional script defined in the configuration of the Data Technician.
The valet is the intermediary between the Manager and Technician. Valets act as commands and transport information back and forth between the Manager and Technician. Valets must implement ValetIfc, which contains a single method.
Table 10-5 lists the ValetIfc method.
Table 10-5 ValetIfc Method
Method | Description |
---|---|
Serializable execute(Object) |
Executes the valet-specific processing on the object |
The execute method is called by the Technician after the valet arrives at its destination as a result of the Manager's sendValet() or sendValetWithRetry() methods.
Example 10-3 illustrates the primary changes that need to be made to create a Manager/Technician pair. Note that interfaces also need to be created for the new Manager, Technician, and Valet classes.
The conduit script needs to define the location of the Manager and Technician. This code would be found in a conduit script such as config\conduit\ClientConduit.xml
. These code samples would typically be in different files on separate machines. It would include snippets like the following.
Example 10-3 Sample Manager and Technician Configuration
<MANAGER name="MyNewManager" class="MyNewManager" package="oracle.retail.stores.foundation.manager.mynew"> </MANAGER> <TECHNICIAN name="MyNewTechnician" class="MyNewTechnician" package="oracle.retail.stores.foundation.manager.mynew" export="Y" > <PROPERTY propname="techField" propvalue="importantVal"/> <PROPERTY propname="configScript" propvalue="classpath://com/extendyourstore/pos/config/myconfigscript.xml"/> </TECHNICIAN>
Tour code might include a snippet like the following, which might be located in <source_directory>
\applications\pos\src\oracle\retail\stores\pos\services
.
This is a minimal Manager class to illustrate how to create a new Manager. A new Manager interface also needs to be created for this class. Note that this class references the sample MyNewTechnician class shown in a later code sample.
Example 10-5 Sample Manager Class
package oracle.retail.stores.foundation.manager.mynew; import oracle.retail.stores.foundation.manager.log.LogMessageConstants; import oracle.retail.stores.foundation.tour.manager.Manager; import oracle.retail.stores.foundation.tour.manager.ValetIfc; public class MyNewManager extends Manager implements MyNewManagerIfc { //-------------------------------------------------------------------------- /** Constructs MyNewManager object, establishes the manager's address, and identifies the associated technician. */ //-------------------------------------------------------------------------- public MyNewManager() { getAddressDispatcherOptional(); setTechnicianName("MyNewTechnician"); } //-------------------------------------------------------------------------- /** This method processes the input argument (via its technician). @param input a String to illustrate argument passing. @return a transformed String **/ //-------------------------------------------------------------------------- public String doSomeClientWork(String input) { String result = null; ValetIfc valet = new MyNewValet(input); try { result = (String)sendValetWithRetry(valet); } catch (Exception e) // usually ValetException or CommException { logger.error(LogMessageConstants.SCOPE_SYSTEM, "MyNewManager.doSomeClientWork, " + "could not sendValetWithRetry: Exception = {0}", e); } logger.debug(LogMessageConstants.SCOPE_SYSTEM, "MyNewManager.doSomeClientWork, returns {0}", result); return result; } }
The following code defines a valet to send input to MyNewTechnician.
Example 10-6 Sample Valet Class
package oracle.retail.stores.foundation.manager.mynew; import oracle.retail.stores.foundation.tour.manager.ValetIfc; import java.io.Serializable; public class MyNewValet implements ValetIfc { /** An input used by the Technician. **/ protected String input = null; //-------------------------------------------------------------------------- /** The constructor stores the input for later use by the Technician. @param input the input to be stored. **/ //-------------------------------------------------------------------------- public MyNewValet(String input) { this.input = input; } //-------------------------------------------------------------------------- /** This method causes the MyNewTechnician to "doSomething" with the input and returns the results. @param techIn the technician that will do the work @return the results of "MyNewTechnician.doSomething" **/ //-------------------------------------------------------------------------- public Serializable execute(Object techIn) throws Exception { if (!(techIn instanceof MyNewTechnician)) { throw new Exception("MyNewTechnician must passed into execute."); } MyNewTechnician tech = (MyNewTechnician)techIn; String result = tech.doSomething(input); return result; } }
The following code provides an example of a minimal Technician class. A new Technician interface also needs to be created for this class.
Example 10-7 Sample Technician Class
package oracle.retail.stores.foundation.manager.mynew; import oracle.retail.stores.foundation.manager.log.LogMessageConstants; import oracle.retail.stores.foundation.tour.manager.Technician; import oracle.retail.stores.foundation.tour.manager.ValetIfc; public class MyNewTechnician extends Technician implements MyNewTechnicianIfc { /** A value obtained from the config script. **/ protected String techField = null; public void setTechField(String value) { techField = value; } public void setConfigScript(String value) { // Complicated configuration could go here } //-------------------------------------------------------------------------- /** This method processes the input argument (via its Technician). @param input a String to illustrate argument passing. @return a transformed String **/ //-------------------------------------------------------------------------- public String doSomething(String input) { String result = null; result = "MyNewTechnician processed " + input + " using " + techField; logger.debug(LogMessageConstants.SCOPE_SYSTEM, "MyNewTechnician.doSomething, returns {0}", result); return result; } }
The following sections describe a Manager/Technician pair, important methods on the Manager, and an example of using the Manager in the application code.
The Parameter Manager is the interface that allows parameters to be used for customization and runtime configuration of applications. The following code from <source_directory>
\applications\pos\deploy\client\config\conduit\ClientConduit.xml
specifies the location and properties of the Parameter Manager and Technician. Note that the Parameter Manager is a Session Manager because it is defined with a PROPERTY element inside the APPLICATION tag. This means it can only be accessed using a tour bus.
Example 10-8 ClientConduit.xml: Code to Configure Parameter Manager
<APPLICATION name="APPLICATION" class="TierTechnician" package="oracle.retail.stores.foundation.manager.tier" startservice="classpath://com/extendyourstore/pos/services/main/main.xml"> <PROPERTY propname="managerData" propvalue="name=ParameterManager,managerpropname=className,managerpropvalue=oracle.retail.stores.foundation.manager.parameter.ParameterManager"/> <PROPERTY propname="managerData" propvalue="name=ParameterManager,managerpropname=useDefaults,managerpropvalue=Y"/> ... </APPLICATION>
Example 10-9 ClientConduit.xml: Code to Configure Parameter Technician
<TECHNICIAN name="ParameterTechnician" class = "ParameterTechnician" package = "oracle.retail.stores.foundation.manager.parameter" export = "Y" > <PROPERTY propname="paramScript" propvalue="classpath://config/manager/PosParameterTechnician.xml"/> </TECHNICIAN>
The Parameter Manager classes contain methods to retrieve parameter values. The Customization chapter describes details about where and how parameters are defined. A list of parameters can be found in the Parameter Names and Values Addendum.
The following code sample from <source_directory>
\applications\pos\src\oracle\retail\stores\pos\services\inquiry\iteminquiry\AdvanceSearchSite.java
illustrates the use of the Parameter Manager to retrieve parameter values.
The UI Manager/Technician is used to abstract the UI implementation. User events captured by the screen are sent to the UI Manager. The UI Manager communicates with a UI Technician, which updates the screen for a client running the UI. The UI Technician provides access to the application UI Subsystem. There is one UITechnician per application.
The model is an object that is used to transport information between the screen and the UI Manager using the UI Technician. Models and screens have a one-to-one relationship. The UI Manager allows you to set the model for a screen and retrieve a model for a screen; it knows which screen to show and which model is associated with the screen. The model has data members that map to the entry fields on the given screen. It can also contain data that dictates screen behavior, such as the field that has the starting focus or the state of a specific field.
The following code samples from <source_directory>
\applications\pos\deploy\client\config\conduit\ClientConduit.xml
specify the UI Manager and Technician properties. Like the Parameter Manager, the UI Manager can only be accessed using a tour bus.
Example 10-11 ClientConduit.xml: Code to Configure UI Manager
<APPLICATION name="APPLICATION"
class="TierTechnician"
package="oracle.retail.stores.foundation.manager.tier"
startservice="classpath://com/extendyourstore/pos/services/main/main.xml">
<PROPERTY propname="managerData"
propvalue="name=UIManager,managerpropname=className,managerpropvalue=oracle.retail.stores.pos.ui.POSUIManager"/>
...configuration of other Managers...
</APPLICATION>
Example 10-12 ClientConduit.xml: Code to Configure UI Technician
<TECHNICIAN name="UITechnician" class="UITechnician" package="oracle.retail.stores.foundation.manager.gui" export="Y"> <CLASS name="UISubsystem" package="oracle.retail.stores.pos.ui" class="POSJFCUISubsystem"> <CLASSPROPERTY propname="configFilename" propvalue="classpath://com/extendyourstore/pos/config/defaults/defaultuicfg.xml" proptype="STRING"/> ... </TECHNICIAN>
The UI is configured in XML scripts. Each tour has its own uicfg file in which screen specifications are defined. The screen constants that bind to screen specification names are defined in <source_directory>
\applications\pos\src\oracle\retail\stores\pos\ui\POSUIManagerIfc.java
. The UI Framework chapter discusses screen configuration in more detail.
POSUIManager is the UI Manager for the Point-of-Service application. One is started for each tour that is created.
Table 10-6 lists important POSUIManagerIfc methods, implemented in <source_directory>
\applications\pos\src\oracle\retail\stores\pos\ui\POSUIManager.java
.
Table 10-6 Important POSUIManagerIfc Methods
Method | Description |
---|---|
void showScreen(String screenId, UIModelIfc beanModel) |
Displays the specified screen using the specified model |
UIModelIfc getModel(String screenId) |
Gets the model from the specified screen |
String getInput() |
Gets the contents of the most recent Response area as a string |
void setModel(String screenId, UIModelIfc beanModel) |
Sets the model for the specified screen |
These methods are used in tour code to display a screen, as in the following code from <source_directory>
\applications\pos\src\oracle\retail\stores\pos\services\tender\check\GetCheckIDTypeSite.java
.
Example 10-13 GetCheckInfoSite.java: Tour Code Using POSUIManagerIfc
POSUIManagerIfc ui = (POSUIManagerIfc) bus.getManager(UIManagerIfc.TYPE);
CheckEntryBeanModel model = new CheckEntryBeanModel();
Locale lcl = LocaleMap.getLocale(LocaleConstantsIfc.USER_INTERFACE);
if (personalIDTypes != null)
{
model.setIDTypes(personalIDTypes.getTextEntries(lcl));
}
...set additional attributes...
ui.showScreen(POSUIManagerIfc.ENTER_ID, model);
The Journal Manager provides location abstraction for journal facilities by implementing the JournalManagerIfc interface. By communicating with a JournalTechnicianIfc, the Journal Manager removes your need to know the location of resources. The Journal Technician is responsible for providing journal facilities to other tiers. The Journal Manager must be started on each tier that uses it. There must be a LocalJournalTechnician running on the local tier or an exported JournalTechnician running on a remote tier, or both. Transactions should be written to EJournal only when completed.
The following code samples from <source_directory>
\applications\pos\deploy\client\config\conduit\ClientConduit.xml
specify the Journal Manager and Technician properties. Note that this Manager is a Session Manager; it is defined outside of the APPLICATION element in which the UI Manager and Parameter Manager were defined. This allows the Journal Manager to be accessed outside of the bus, meaning it is more accessible and flexible.
Example 10-14 ClientConduit.xml: Code to Configure Journal Manager
<MANAGER name="JournalManager" class="JournalManager" package="oracle.retail.stores.foundation.manager.journal" export="N"> </MANAGER>
Example 10-15 ClientConduit.xml: Code to Configure Journal Technician
<TECHNICIAN name="LocalJournalTechnician" class="JournalTechnician" package="oracle.retail.stores.foundation.manager.journal" export="Y"> </TECHNICIAN>
The Journal Manager must be started on each tier that uses it. The Journal Manager sends journal entries in the following order: (1) Console if consolePrintable is set, (2) LocalJournalTechnician if defined, (3) JournalTechnician if defined.
To internationalize static texts, the hardcoded messages in Point-of-Service must be externalized to locale-specific resource bundle files and applications to use the resource bundle based on the configured Journal locale for EJournal. These modifications can be made either in-house or with the assistance of third-party system integrators.
If the modification efforts are not done correctly, the deployed product may not operate correctly. This situation causes serious issues for the retailer, Oracle Retail, and any system integrators involved. This section aims to mitigate that risk by providing guidance on how to safely and effectively make modifications to internationalize EJournal.
To internationalize static texts, the hardcoded messages in Point-of-Service will be externalized to locale-specific resource bundle files and applications to use the resource bundle based on the configured Journal locale for EJournal. Extend the existing resource bundle ejournalText_<language code>.properties for EJournal static texts.
Transaction EJ contains following type of information:
Transaction information such as Item Description, Reason code, Role Name, and so on, is retrieved from database for Journaling. This information must be in the Journal locale.
In the classes where Journal strings are hard-coded, use APIs provided by domain objects to get the data in Journal locale and use it for Journaling. Use Journal Locale configured in the Point-of-Service Client as argument to get the journal locale-specific data from domain objects.
Deprecate the existing toJournalString() method of domain classes and create new method toJournalString(locale) for preparing locale specific Journal String. Pass the Journal locale configured in the Point-of-Service Client application.properties file to toJournalString(locale) method of domain objects for Journal message.
The information such as Tender type is defined as constant in Java file. Use resource bundle equivalent data for journaling instead of constant values.
For example, TenderTypeEnum has all the tender type constants defined. Get the journal locale equivalent data from EJournalText ResourceBundle for the value obtained from TenderTypeEnum. For Cash tender, define JournalEntry. Cash as key in EJournalText Resource bundle and get the value associated with it for journaling in formatter/domain classes.
Avoid journaling the messages, which need to be formed by concatenating two or more messages. However, if concatenated strings need to be logged, use argument-based messages in resource bundle and MessageFormat class to replace the arguments with the actual data before journaling.
For example, for the message Buy 2 items and get a 25% discount, we can prepare message such as Buy {0} items and get {1} discount. The arguments 0 and 1 can be replaced with the actual data.
The DateTime and Currency Data to be Journaled must be in the Journal locale format. Use the following:
DateTimeServiceIfc.formatDate() to get the Journal locale format date
DateTimeServiceIfc.formatTime() to get the Journal locale format time
CurrencyServiceIfc.formatCurrency() to get the Journal locale format currency
For Journaling the events such as Parameter value addition/modification, the EJournal message is prepared by concatenating one or more messages with actual data.
For example, the message <<Parameter Name>> is modified is formed by concatenating the Parameter Name and the constant string is modified. The approach to internationalize this data is same as internationalizing transaction data for concatenated Strings.
Do the following to persist the EJournal in the UTF8 format:
Modify the JL_TPE column type of the JL_ENR table from BLOB to CLOB.
Modify the existing EJournal text:
Retrieve the data from the eJournalText bundle for the key in the class where it needs to be saved in EJournal.
Convert the date, time, and currency data to the store locale format.
Prepare the EJournal text using locale-specific journal static text, date, time and currency format.
Write the modified EJournal to the database and JMS using the existing framework.
Do the following to write the EJournal to a file:
Modify the startUp method of the JournalTechnician to generate the EJournal text file name and create a JournalDisk object with the generated EJournal file name. The EJournal text file is generated by concatenating the JournalFileName and SequenceNumber values retrieved from JournalConfig.properties.
Modify the store method of a JournalDisk class to write the EJournal string in UTF format instead of writing the EjournalEntry object in binary format.
Modify the indexEntry method of the JournalTechnician to get the current EJournal file name.
Modify the indexEntry method to append the journal file name to the index entry. The modified index entry looks like the following:
0000000 0008 042411290119 09/02/2008 11:59 pos pos 129 journal_1.txt
If the current journal file size exceeds the configured limit:
Generate the next sequence number and update the SequenceNumber property of the JournalConfig.properties file.
Modify the indexEntry method of the JournalTechnician to generate the journal file name from the JournalFileName (containing the initial journal file name, such as journal.txt) and the SequenceNumber configured in JournalConfig.properties. The generated journal file name is of the format JournalFileName_SequenceNumber (for example, journal_1.txt).
Close the currently open journal file and create a new journal file with the generated journal file name.
Modify the JdbcSaveJournalEntry class to contain updateClob method.
Create new classes DatabaseClobHelper and OracleDatabaseClobHelper and to update data to CLOB field.
Do the following to retrieve EJournal from Point-of-Service:
Modify classes DatabaseClobHelper and OracleDatabaseClobHelper to read EJournal text from CLOB field.
Do the following to read EJournal from a file:
Modify the searchJournal method of JournalTechnician to read the journal file name from index entry.
Modify the getEntry method of the JournalDisk to get the journal file name as an argument.
Modify the getEntry method of the JournalDisk class method to read the EJournal string from the given journal file in UTF format.
Modify the readJournalEntry method of JdbcReadJournalEntry class to read CLOB data instead of BLOB using OracleDatabaseClobHelper.
Do the following to display EJournal from Central Office:
Modify the JL_TPE column type of the JL_ENR table from BLOB to CLOB.
Add a new method readTapeClob to the class EJournalBean to read CLOB data and call this method from ejbLoad for setting tape data.
Add a new method updateTapeClob to the class EJournalBean to update CLOB data to the database and call this method from ejbCreate for setting tape data.