Building Web Applications with SAS/IntrNet:: A Guide to the Application Dispatcher (SAS Press) 1599941899, 9781599941899

Learn how you can save countless hours of research and development time building your Web applications with the SAS/Intr

916 91 6MB

English Pages 376 Year 2007

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Building Web Applications with SAS/IntrNet:: A Guide to the Application Dispatcher (SAS Press)
 1599941899, 9781599941899

Table of contents :
Contents
Overview of SAS/IntrNet and Related Technologies
Is the Application Dispatcher a Good Fit?
Components of SAS/ IntrNet Software
The Application Dispatcher
SAS Design- Time Controls
Xplore Sample Web Application
htmSQL
SAS/ CONNECT Driver for Java
SAS/ SHARE Driver for JDBC
Tunnel Feature
Other SAS Web Components and Technologies
The Output Delivery System
The Web Publishing Tools and Related Macro Tools
SAS AppDev Studio, a SAS Applications Development Environment
SAS
Business Intelligence Platform
Industry Components
Scripting Languages
Dynamic HTML
Web Services
Component- Based Architectures
Terminology
How the SAS/IntrNet Application Dispatcher Works
Reader Notes
Overview of the Application Dispatcher Process Flow
Introduction
Application Broker— Application Server Process Flow
Process Flow Including the Load Manager
Performance Benefits of Using the Load Manager
The Application Broker and the Load Manager
Introduction
Specifying Additional HTML Output
Defining the Load Manager
The Load Manager Command
Defining the SAS Application Servers
Socket Servers
Pool Servers
Launch Servers
Application Broker Process Flow
Load Manager Administrative Functions
The Application Server
Introduction
SAS Application Server Executives
Application Server Sessions
ODS and Sessions
PROC APPSRV
TCP/ IP Port
Assigning Libraries
Initiating and Terminating Requests
Application Server Process Flow
Application Server Functions Available to the Executing Program
Application Server Clean- Up Processing
Communicating with the Application Dispatcher
Introduction
Name/ Value Pairs Defined by the User’s Request
_ program: The SAS Program to Execute
_ service: The Application Server to Process the Request
_ debug: The Output to Display
Program- Specific HTML Name/ Value Pairs
Name/ Value Pairs Defined by the Application Broker
Application Broker Administrative Fields
Environment Variables
Installation Dependent Fields
Name/ Value Pairs Defined by the Application Server
Methods You Can Use to Access and Reference Input Parameters
Introduction
Accessing Parameters as Macro Variables
Using and Referencing the Multiple Values for a Single Parameter ( the Suffix Variables)
Using the APPSRV_ UNSAFE Function
Accessing Parameters in SCL Programs
Various Techniques to Generate HTML
Introduction
The Problem with Generating Valid HTML
Simple PUT and FILE Statements
The generateFormTag and generateInputTag Sample Macros
Extending the Output Delivery System
FORM Tags Via TITLE and FOOTNOTE Statements
Character Variables with HTML Form Text
SCL Submit Blocks
Including Static HTML from External Sources
A Macro Tool to Include External HTML
SAS Server Pages
The SAS Server Page Macro
Sample Server Page: List Libraries
A Macro to Generate Data- Driven SELECT Tags
Sample Server Page: List the Data Sets in the Selected Library
Sample Server Page: List the Variables in the Selected Data Set
A Macro to Generate Checkboxes for Variables in a SAS Data Set
Program to Page Through the Selected Data Set
SAS Design- Time Controls
Creating Pages with Mixed and Alternative Content Types
Introduction
Defining the Content Type to Be Generated
Other Headers: Selected Examples
Generating Pages with Other Content Types
Creating a Comma- Separated Values File
Downloading Reports and Results into Microsoft Excel
Generating Content for Printing
Generating Multiple Output Types at Once
Generating Pages with Mixed Content Types: Text and Graphics
Ensuring That the Graph Is Current
Using REQUEST INIT and REQUEST TERM to Specify Set- up and Shut- down Behavior
Introduction
Specifying REQUEST INIT and REQUEST TERM Programs
Using REQUEST INIT and REQUEST TERM
Defining Libraries and Files
Overriding or Supplying Parameter Values
Standard Header and Trailer Blocks
Terminating a Request
How to Create and Use Sessions
Introduction
Creating a Session
Saving or Restoring All Macro Variables
Reconnecting to a Previous State of a Session
Generating Friendlier Messages for Expired Sessions
Extending Sessions
Session INIT and TERM Programs
Tools and Techniques for Debugging
Introduction
Using the _ debug Parameter
The PROC APPSRV LOG Statement
Conditionally Generating Debugging Output
Running Application Dispatcher Programs Using SAS Display Manager
Special Handling for SCL Programs
Dedicating an Application Server for Debugging
Tips for Safeguarding Security
Introduction
The Application Server Environment
Limiting Which Application Brokers Can Access an Application Server
Hiding Passwords
Best Practices for a Secure Application Server Environment
Controlling Access to Data and Reports
Customizing Menu Choices
Using AUTH= HOST
Integrating Application Dispatcher Applications with Other Applications, Products, and Environments
Introduction
htmSQL
Integrating htmSQL with the Application Dispatcher
Integrating the Application Dispatcher with htmSQL
Single Sign- On Environments
External Session Facilities
Maintaining an Applications Environment
Introduction
Supporting Development, Test, and Production Environments and Applications
Using a Developer’s Workstation
Distinct Services
Distinct Brokers
Customized INIT Program
Metadata Approaches to Minimize Code Changes
The getTextString Macro
Generating Static Versions of Dynamic Reports
Introduction
Sample Program
Ensuring That Generated Links Function Correctly
E- mailing Static Copies of Dynamic Reports
Simulating a Pause and Resume Capability for the Application Server
Introduction
Sample Implementation
The Metadata Control Table
The Toggle Program
Macro to Check Status
The HTML Templates
Doing More with the Sample
Techniques for Handling Long-Running Processes
Introduction
Using the Cascading Style Sheets Display Attribute
Using the JavaScript location. replace Function
Using Scheduled Execution to Handle Long- Running Processes
Introduction
Sample Implementation
Doing More with Scheduled Execution
Handling On-Demand Long-Running Requests
Introduction
E- mailing Results from an On- Demand Long- Running Process
Notifying the User
Updating Process Status by Refreshing the User’s Browser
Notifying the User
The Sample Framework— Spawning a Separate SAS Session
Sample Program to Submit a Long- Running Program— spawnSAS. source
Sample spawnSAS Macro
Sample Long- Running Program
Doing More with Independent Sessions
Using Metadata-Driven Reporting to Construct Reports
Introduction
Sample Implementation
The Logon Template
The Logon Program
Selecting the Case or Individual to Examine
Report Components
The assembleReport Macro
Packaging the Application Dispatcher as a Web Service
Introduction
The Sample . NET Client Application
The . NET Web Service
Using the Sample Client Application
Final Thoughts
Index

Citation preview

Praise from the Experts “While building SAS/IntrNet applications, I would like nothing better than to have someone with the skills of Don Henderson looking over my shoulder. Other than some SAS-L posts and a few personal e-mail messages, this desire went unrealized. That was until I read Don’s book, Building ® Web Applications with SAS/IntrNet : A Guide to the Application Dispatcher. “This book picks up where SAS documentation leaves off. It shows how to increase the functionality of the SAS/IntrNet Application Dispatcher to solve problems commonly encountered when building Web-based SAS applications. This book also supplies a host of ‘best practices’ that can help keep one from creating future difficulties. “An example of the gems that Don has unearthed is the INVSESS option. This option allows developers to implement friendlier responses to failed attempts to reconnect to a session. Along with techniques to refresh long-running requests, this book shows how to partially overcome the problem of user sessions that time-out from user inattention. “One of the most helpful chapters is Chapter 11, “Tools and Techniques for Debugging.” One especially useful tip is to seed programs under development with %testPrint macros to conditionally print intermediate results. This chapter also provides good guidance on how to run Application Dispatcher programs in a stand-alone mode. “In my opinion, this is a must-have book if one is responsible for creating or maintaining Webbased SAS applications.”

Michael Davis

“At long last we have a comprehensive book on the intricacies of SAS/IntrNet programming! Don Henderson has written the definitive, authoritative book on how to develop SAS/IntrNet applications so that you can easily host your SAS programs on the Web. There is so much information packed into this book that you will find something that you did not know in every chapter. Though I have been working with SAS/IntrNet for many years, I was amazed at the volume of information that I was not aware of. “There are several chapters of this book that will allow you to recoup the book’s purchase price, almost immediately. Chapter 7, “Various Techniques to Generate HTML,” is a case in point. It takes you through increasingly advanced material, moving from using simple PUT/FILE statements, to extending the Output Delivery System to the Web, to using SCL submit blocks, to including HTML from external sources, to using SAS Server pages, and finally to using designtime controls. All of these techniques have great merit; and this chapter explains how and when to use them while providing great examples. This chapter will save you hours of research and development time. “Another chapter that would have saved my staff at least a week’s worth of work is Chapter 19, “Handling On-Demand Long-Running Requests.” Many organizations face the unenviable prospect of having to offer Web-based reports or analysis against large, complicated data sources that end up as long-running SAS jobs. In doing so, one cannot expect Web users to sit idly by, wondering when their output will arrive as the minutes tick by. This chapter provides two specific

techniques for spawning asynchronous SAS batch jobs, behind the scenes, to produce result sets. The first technique launches the batch job, and then e-mails the result set to the user when the batch job completes. The second technique provides the user with a status page in their browser that is continuously updated as each batch job step completes, and then overlays the status page with the final output when it becomes available. Both techniques are clearly described and include easy-to-follow examples. “Singling out specific chapters for comment is hard, because all of the chapters in this book have significant tips, insights, and well-explained examples. It is obvious to me that there will not be another book published about SAS/IntrNet software, because Don has covered everything there is to say about the topic in this one great publication. But, then again, one would expect no less from one of the original creators of SAS/IntrNet software!”

Michael A. Raithel Westat

“The Paris Herald Tribune once published a satirical column by Art Buchwald describing a mythical American tourist who visited Paris and ran a ‘four-minute Louvre.’ The tourist touched all the must-see bases—the Venus de Milo, the Mona Lisa, and the Winged Victory of Samothrace—making excellent time Buchwald wrote, ‘under perfect conditions, with a smooth floor, excellent lighting, and no wind.’ “Superficial involvement with substance is something one can poke fun at, but it has no place in serious matters. There is no way one could race through Don Henderson’s new book about how to use the Application Dispatcher in SAS/IntrNet. This is material that demands close, patient, and studious attention. “This book reveals two characteristics of the author himself. First, he is a person who willingly gives away his own know-how, sharing highly valuable insights with anyone who cares to listen. Second, he is someone who is able to master advanced coding techniques easily and apply that mastery to solving complex coding problems. Any programmer who works with SAS/IntrNet will feel a sense of indebtedness to Don for making life easier. “This book, which could have been subtitled Making the Most of SAS/IntrNet, makes it easy to realize that the software has tremendous depth and is up to the job of dealing with practical coding requirements. This shouldn’t astonish us. After all, SAS software in general has continued to evolve and take on enhanced capabilities. Thus, to understand the Application Dispatcher, one must make the same kind of effort that is involved in learning ODS or SAS/GRAPH, for example. Don challenges the reader to do just that. There is no fluff here, only solid meat. Each paragraph, each example, contains important information that a reader must digest slowly and carefully. “This text is so educational that it really ought to become material for a new course offered by SAS. Each chapter contains examples that could be converted easily into exercises. In any form, Don’s masterful work will help SAS programmers to become better and stronger.”

Jim Sattler, President Satmari Software Systems, Inc.

Building Web Applications with SAS/IntrNet

®

A Guide to the Application Dispatcher

Don Henderson

The correct bibliographic citation for this manual is as follows: Henderson, Don. 2007. Building Web Applications with SAS/IntrNet®: A Guide to the Application Dispatcher. Cary, NC: SAS Institute Inc.

Building Web Applications with SAS/IntrNet®: A Guide to the Application Dispatcher Copyright © 2007, SAS Institute Inc., Cary, NC, USA ISBN 978-1-59994-189-9 All rights reserved. Produced in the United States of America. For a hard-copy book: No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, or otherwise, without the prior written permission of the publisher, SAS Institute Inc. For a Web download or e-book: Your use of this publication shall be governed by the terms established by the vendor at the time you acquire this publication. U.S. Government Restricted Rights Notice: Use, duplication, or disclosure of this software and related documentation by the U.S. government is subject to the Agreement with SAS Institute and the restrictions set forth in FAR 52.227-19, Commercial Computer Software-Restricted Rights (June 1987). SAS Institute Inc., SAS Campus Drive, Cary, North Carolina 27513. 1st printing, March 2007 SAS® Publishing provides a complete selection of books and electronic products to help customers use SAS software to its fullest potential. For more information about our e-books, e-learning products, CDs, and hard-copy books, visit the SAS Publishing Web site at support.sas.com/pubs or call 1-800-727-3228. SAS® and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration. Other brand and product names are registered trademarks or trademarks of their respective companies.

Contents Preface

xi

Acknowledgments

Part 1

xiii

An Introduction to SAS/IntrNet Software

Chapter 1 Overview of SAS/IntrNet and Related Technologies 3 1.1

Is the Application Dispatcher a Good Fit? 4

1.2

Components of SAS/IntrNet Software 5 1.2.1 The Application Dispatcher 5 1.2.2 SAS Design-Time Controls 5 1.2.3 Xplore Sample Web Application 6 1.2.4 htmSQL 6 1.2.5 SAS/CONNECT Driver for Java 6 1.2.6 SAS/SHARE Driver for JDBC 6 1.2.7 Tunnel Feature 7

1.3

Other SAS Web Components and Technologies 7 1.3.1 The Output Delivery System 7 1.3.2 The Web Publishing Tools and Related Macro Tools 8 1.3.3 SAS AppDev Studio, a SAS Applications Development Environment 9 ®

1.3.4 SAS 9 Business Intelligence Platform 9 1.4

Industry Components 12 1.4.1 Scripting Languages 12 1.4.2 Dynamic HTML 15 1.4.3 Web Services 16

1.5

Component-Based Architectures 16

1.6

Terminology 18

iv Contents

Part 2

How the SAS/IntrNet Application Dispatcher Works

Chapter 2 Overview of the Application Dispatcher Process Flow 23 2.1

Introduction 23

2.2

Application Broker—Application Server Process Flow 24

2.3

Process Flow Including the Load Manager 25

2.4

Performance Benefits of Using the Load Manager 27

Chapter 3

The Application Broker and the Load Manager 31

3.1

Introduction 31

3.2

Specifying Additional HTML Output 33

3.3

Defining the Load Manager 34 3.3.1 The Load Manager Command 34

3.4

Defining the SAS Application Servers 35 3.4.1 Socket Servers 35 3.4.2 Pool Servers 37 3.4.3 Launch Servers 39

3.5

Application Broker Process Flow 40 3.5.1 Load Manager Administrative Functions 46

Chapter 4

The Application Server

47

4.1

Introduction 47

4.2

SAS Application Server Executives 49

4.3

Application Server Sessions 50 4.3.1 ODS and Sessions 50

4.4

PROC APPSRV 51 4.4.1 TCP/IP Port 51 4.4.2 Assigning Libraries 51 4.4.3 Initiating and Terminating Requests 54

4.5

Application Server Process Flow 55 4.5.1 Application Server Functions Available to the Executing Program 60 4.5.2 Application Server Clean-Up Processing 60

Contents

Chapter 5

v

Communicating with the Application Dispatcher 63

5.1

Introduction 63

5.2

Name/Value Pairs Defined by the User’s Request 64 5.2.1 _program: The SAS Program to Execute 64 5.2.2 _service: The Application Server to Process the Request 65 5.2.3 _debug: The Output to Display 65 5.2.4 Program-Specific HTML Name/Value Pairs 68

5.3

Name/Value Pairs Defined by the Application Broker 72 5.3.1 Application Broker Administrative Fields 72 5.3.2 Environment Variables 73 5.3.3 Installation Dependent Fields 74

5.4

Name/Value Pairs Defined by the Application Server 76

Part 3

Developing Application Dispatcher Programs

Chapter 6

Methods You Can Use to Access and Reference Input Parameters 81

6.1

Introduction 81

6.2

Accessing Parameters as Macro Variables 82 6.2.1 Using and Referencing the Multiple Values for a Single Parameter (the Suffix Variables) 85 6.2.2 Using the APPSRV_UNSAFE Function 87

6.3

Chapter 7 7.1

Accessing Parameters in SCL Programs 91

Various Techniques to Generate HTML

94

Introduction 94 7.1.1 The Problem with Generating Valid HTML 95

7.2

Simple PUT and FILE Statements 96 7.2.1 The generateFormTag and generateInputTag Sample Macros 101

7.3

Extending the Output Delivery System 102 7.3.1 FORM Tags Via TITLE and FOOTNOTE Statements 102 7.3.2 Character Variables with HTML Form Text 105

7.4

SCL Submit Blocks 107

7.5

Including Static HTML from External Sources 110 7.5.1 A Macro Tool to Include External HTML 114

vi Contents

7.6

SAS Server Pages 116 7.6.1 The SAS Server Page Macro 116 7.6.2 Sample Server Page: List Libraries 118 7.6.3 A Macro to Generate Data-Driven SELECT Tags 119 7.6.4 Sample Server Page: List the Data Sets in the Selected Library 121 7.6.5 Sample Server Page: List the Variables in the Selected Data Set 122 7.6.6 A Macro to Generate Checkboxes for Variables in a SAS Data Set 124 7.6.7 A Program to Page Through the Selected Data Set 125

7.7

Chapter 8

SAS Design-Time Controls 127

Creating Pages with Mixed and Alternative Content Types 131

8.1

Introduction 131

8.2

Defining the Content Type to Be Generated 132 8.2.1 Other Headers: Selected Examples 132

8.3

Generating Pages with Other Content Types 135 8.3.1 Creating a Comma-Separated Values File 136 8.3.2 Downloading Reports and Results into Microsoft Excel 137 8.3.3 Generating Content for Printing 138 8.3.4 Generating Multiple Output Types at Once 143

8.4

Generating Pages with Mixed Content Types: Text and Graphics 146 8.4.1 Ensuring That the Graph Is Current 153

Chapter 9

Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 155

9.1

Introduction 155

9.2

Specifying the REQUEST INIT and REQUEST TERM Programs 156

9.3

Using REQUEST INIT and REQUEST TERM 156 9.3.1 Defining Libraries and Files 157 9.3.2 Overriding or Supplying Parameter Values 159 9.3.3 Standard Header and Trailer Blocks 161 9.3.4 Terminating a Request 163

Contents

Chapter 10 How to Create and Use Sessions

165

10.1 Introduction 165 10.2 Creating a Session 166 10.3 Saving or Restoring All Macro Variables 171 10.4 Returning to a Previous State of a Session 175 10.5 Generating Friendlier Messages for Expired Sessions 179 10.6 Extending Sessions 182 10.7 Session INIT and TERM Programs 186

Part 4

Addressing Common Application Requirements

Chapter 11 Tools and Techniques for Debugging

191

11.1 Introduction 191 11.2 Using the _debug Parameter 192 11.3 The PROC APPSRV LOG Statement 196 11.4 Conditionally Generating Debugging Output 197 11.5 Running Application Dispatcher Programs Using SAS Display Manager 200 11.5.1 Special Handling for SCL Programs 201 11.6 Dedicating an Application Server for Debugging 202

Chapter 12 Tips for Safeguarding Security

205

12.1 Introduction 205 12.2 The Application Server Environment 206 12.2.1 Limiting Which Application Brokers Can Access an Application Server 206 12.2.2 Hiding Passwords 208 12.2.3 Best Practices for a Secure Application Server Environment 211 12.3 Controlling Access to Data and Reports 212 12.3.1 Customizing Menu Choices 219 12.3.2 Using AUTH=HOST 223

vii

viii Contents

Chapter 13 Integrating Application Dispatcher Applications with Other Applications, Products, and Environments 225 13.1 Introduction 225 13.2 htmSQL 226 13.2.1 Integrating htmSQL with the Application Dispatcher 228 13.2.2 Integrating the Application Dispatcher with htmSQL 230 13.3 Single Sign-On Environments 235 13.4 External Session Facilities 236

Chapter 14 Maintaining an Applications Environment 239 14.1 Introduction 239 14.2 Supporting Development, Test, and Production Environments and Applications 239 14.2.1 Using a Developer’s Workstation 240 14.2.2 Distinct Services 240 14.2.3 Distinct Brokers 241 14.2.4 Customized INIT Program 242 14.3 Metadata Approaches to Minimize Code Changes 244 14.3.1 The getTextString Macro 249

Part 5

Selected Examples

Chapter 15 Generating Static Versions of Dynamic Reports 253 15.1 Introduction 253 15.2 Sample Program 254 15.3 Ensuring That Generated Links Function Correctly 256 15.4 E-mailing Static Copies of Dynamic Reports 256

Chapter 16 Simulating a Pause and Resume Capability for the Application Server 259 16.1 Introduction 259 16.2 Sample Implementation 260 16.2.1 The Metadata Control Table 262 16.2.2 The Toggle Program 263 16.2.3 Macro to Check Status 264 16.2.4 The HTML Templates 265 16.3 Doing More with the Sample 266

Contents

ix

Chapter 17 Techniques for Handling Long-Running Processes 267 17.1 Introduction 267 17.2 Using the Cascading Style Sheets Display Attribute 268 17.3 Using the JavaScript location.replace Function 271

Chapter 18 Using Scheduled Execution to Handle Long-Running Processes 275 18.1 Introduction 275 18.2 Sample Implementation 276 18.3 Doing More with Scheduled Execution 279

Chapter 19 Handling On-Demand Long-Running Requests 281 19.1 Introduction 281 19.2 E-mailing Results from an On-Demand Long-Running Process 282 19.1.1 Notifying the User 284 19.2 Updating Process Status by Refreshing the User’s Browser 285 19.2.1 Notifying the User 287 19.3 The Sample Framework—Spawning a Separate SAS Session 290 19.3.1 Sample Program to Submit a Long-Running Program— spawnSAS.source 295 19.3.2 Sample spawnSAS Macro 298 19.3.3 Sample Long-Running Program 300 19.4 Doing More with Independent Sessions 305

Chapter 20 Using Metadata-Driven Reporting to Construct Reports 307 20.1 Introduction 307 20.2 Sample Implementation 309 20.2.1 The Logon Template 311 20.2.2 The Logon Program 312 20.2.3 Selecting the Case or Individual to Examine 315 20.2.4 Report Components 319 20.2.5 The assembleReport Macro 323

x Contents

Chapter 21 Packaging the Application Dispatcher as a Web Service 327 21.1 Introduction 327 21.2 Solution Architecture 329 21.3 Sample Application 330 21.3.1 The Sample .NET Client Application 331 21.3.2 The .NET Web Service 332 21.4 Using the Sample Client Application 335 21.5 Final Thoughts 338

Index

339

Preface The rush to take mission-critical applications to the Web has changed the way companies compete and interact. This competition has produced a lower cost-per-desktop and has extended information access to just about anyone, whether through a Web browser, a personalized portal, or a handheld device. Therefore, it is essential that organizations use the tools at their disposal to their best advantage. This book focuses on the Application Dispatcher, which is one of the components of SAS/IntrNet software. SAS/IntrNet, and specifically the Application Dispatcher, is a mature and proven technology for the deployment of Web solutions. However, as with most products, it has many features and capabilities that are not fully known or exploited by users. This book discusses many such features, thus enabling you to take better advantage of the software’s facilities and tools. In the move to the Web, it is also important to note that applications to be deployed there must recognize the underlying distributed nature of Web-based applications. The native Web environment is a two-tier architecture: the Web server/Web browser tier and the data/compute services tier. This separation means that many architectural assumptions made in the development of applications are incompatible with the Web. The resulting issues can include the following: …

Many Web applications are stateless (i.e., each request for compute or data services is completely independent of the other). Thus, applications that assume exclusive access to data are problematic (e.g., in the transaction-based world of the Web, the concept of opening a data set for exclusive update access is not the same as in a desktop world).

…

The event model in a Web application is very different from the event model in a desktop application. For example, if the client is a browser, the back-end compute engine has no access to key clicks until the browser has forwarded a request for processing to the compute server.

…

A corollary to the previous point is that even if some of the logic relating to the event model can be surfaced in the client (e.g., using Java, JavaScript, Dynamic HTML, etc.), the separation of the client and the server can be a complicating factor in moving existing applications to a Web environment.

While we will not explore these issues in great detail, many of the ideas and techniques presented here will enable you to better address these requirements while building Web-based applications using the Application Dispatcher.

Organization of This Book This book contains five parts:

ƒ

Part 1 provides an overview of SAS/IntrNet and the role the Application Dispatcher can play in building Web applications.

ƒ

Part 2 provides details about how the components of the Application Dispatcher work and interact with one another.

ƒ

Part 3 discusses techniques and examples illustrating the features and capabilities of the SAS/IntrNet Application Dispatcher. A variety of tools and best practices are provided to

xii Preface

facilitate developing applications for the Application Dispatcher. Many of these ® techniques apply to the SAS 9 Stored Process Server as well.

ƒ

Part 4 shows how the techniques and features of the Application Dispatcher presented in Parts 1 through 3 can be used to address common application requirements. It contains a number of short chapters that focus on how to use the ideas discussed earlier, with a few new techniques and methods included.

ƒ

Part 5 provides a number of examples that use the techniques, samples, and approaches described in previous chapters and illustrates how they can be integrated to address commonly encountered requirements. Each example describes a scenario or requirement along with a suggested approach to implementing it using the SAS/IntrNet Application Dispatcher.

Sample Environment Almost all of the examples discussed in this book are available in a sample environment that is a companion to the book. You can access it from the following URL: http://hcsbi.com/IntrNetAppDev/

The sample environment allows you to run demonstrations and most examples. Some examples are not available for security reasons. The demonstrations and examples are organized by chapter (i.e., you access the examples for Chapter X by clicking the link for Chapter X). The location of each example appears in the text that describes that example. Example text is easily identified by a Run icon in the left margin, as shown here. The Web site also contains instructions and links to download and install the sample environment on your local PC. By downloading and installing the sample environment locally you will be able to do the following:

ƒ

run selected examples that are disabled on the Web site

ƒ

easily modify and experiment with the examples

ƒ

add your own data to the sample environment

ƒ

start using the samples and tools in your own SAS/IntrNet Application Dispatcher applications

You can start out by accessing the examples from the Web site. Later, download and install the sample environment. Thus, you can begin (and perhaps complete) reviewing the book without having to install anything locally. Be sure to check the sample environment frequently for updates.

Acknowledgments Among the people I’d like to thank are the technical reviewers: Robert Allison, Vincent DelGobbo, Paul Grant, Dana Rafiee, Michael Raithel, Warren Repole, David Shinn, Heather Weinstein, and Bryan Wolfe. I’d also like to thank Alan Churchill of Savian and Scott Wood of Zencos Consulting. Alan wrote the Windows .NET sample applications discussed in Chapter 21 that illustrate packaging the Application Dispatcher as a Web service. Scott provided the sample Java code discussed in Section 13.4 that illustrates integration with external session facilities. Thanks, also, to the SAS Press team who produced the book: Caroline Brickley, Patrice Cherry, Jennifer Dilley, Shelly Goodin, Candy Farrell, Stephenie Joyner, Mary Beth Steinbach, and Liz Villani. I would like to thank those folks who provided feedback (and continue to provide feedback) on the sample environment (http://hcsbi.com/IntrNetAppDev). And finally, I would like to thank my wife, Celia Henderson, for reviewing parts of the book even though she knows very little about SAS as well as for her patience when I would disappear for hours and days at a time using the excuse I need to work on my book.

xiv

1

P a r t

An Introduction to SAS/IntrNet Software Chapter

1

Overview of SAS/IntrNet and Related Technologies 3

SAS/IntrNet software opens SAS to the Internet, extranet, or intranet. Specifically, this software enables users to run ad-hoc reports and dynamic applications via the Web. Broadly speaking, SAS/IntrNet is divided into three areas:

ƒ

data services that enable the user to make SQL-type queries to the SAS server. Users can query, update, and report data.

ƒ

compute services that give the user full access to the analytical capabilities of the SAS server. Users can access and use any non-visual functionality provided by the SAS server.

ƒ

out-of-the-box applications that use the SAS/IntrNet data and compute services to deliver access to OLAP cubes created with PROC MDDB (the MDDB Report Viewer) and to explore and report the contents of SAS catalogs and libraries (the Xplore Sample Application).

2 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher Organizations often use a diverse collection of technologies to store, manage, and report on information. Therefore, organizations should consider factors such as the following when choosing a technology for developing a Web-based information delivery system:

ƒ

user interface requirements

ƒ

data access and analysis requirements

ƒ

application performance

ƒ

costs of development, deployment, and maintenance

The Application Dispatcher component of SAS/IntrNet software has many facilities that can make the applications development task easier. This book explains many of those facilities including some that are not known to the broad population of users. Chapter 1 gives a high-level overview of the Application Dispatcher component of SAS/IntrNet software and places the software in context with related Web technologies.

C h a p t e r

1

Overview of SAS/IntrNet and Related Technologies 1.1 Is the Application Dispatcher a Good Fit? 4 1.2 Components of SAS/IntrNet Software 5 1.2.1 The Application Dispatcher 5 1.2.2 SAS Design-Time Controls 5 1.2.3 Xplore Sample Web Application 6 1.2.4 htmSQL 6 1.2.5 SAS/CONNECT Driver for Java 6 1.2.6 SAS/SHARE Driver for JDBC 6 1.2.7 Tunnel Feature 7 1.3 Other SAS Web Components and Technologies 7 1.3.1 The Output Delivery System 7 1.3.2 The Web Publishing Tools and Related Macro Tools 8 1.3.3 SAS AppDev Studio, a SAS Applications Development Environment 9 ®

1.3.4 SAS 9 Business Intelligence Platform 9 1.4 Industry Components 12 1.4.1 Scripting Languages 12 1.4.2 Dynamic HTML 15 1.4.3 Web Services 16 1.5 Component-Based Architectures 16 1.6 Terminology 18

4 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

1.1 Is the Application Dispatcher a Good Fit? One of the primary design goals for the SAS/IntrNet Application Dispatcher was to mask the complexity of other technologies, such as CGI and HTML, which are needed to implement SAS Web technologies. The Application Dispatcher enables organizations to deploy a Web application without requiring the development staff to have extensive knowledge of CGI or HTML. The advent of ODS makes this even easier. Of course, some knowledge of these technologies is advantageous in implementing a Web solution. However, the application and the data usage factors should be the driving force behind determining which technologies to use. There are many different factors and criteria that should be considered when deciding which components are most appropriate for a given set of development needs. In addition to the requirements and the skills available for the current application, other Web-based applications and skills available within the organization should be considered. Applications that meet the following criteria are likely to be good candidates for the Application Dispatcher:

ƒ

Existing SAS programs produce output and reports that should be made available to some or all users in an organization over the Web.

ƒ

The application is an extension of existing SAS applications.

ƒ

The reports are generated from dynamic data. Note that if data is reasonably static (e.g., only change monthly), then Web publishing the data via ODS might suffice to meet the application needs.

ƒ

Users need the ability to customize the reports (e.g., select specific subsets) to get the output they need.

ƒ

The development staff has a broad range of SAS programming skills. If there is only limited knowledge of or experience with CGI, HTML, Java, etc., then the Application Dispatcher with ODS would be a good choice.

ƒ

SAS programming expertise is available and the timeframe to deploy is a critical factor (i.e., time to train the staff in new technologies is not available).

ƒ

The application will be run on-demand to deliver specific reports.

ƒ

The data is maintained centrally (e.g., in a data mart or data warehouse). Data which is inherently local or specific to a particular user is typically not a good candidate for application or report distribution.

ƒ

The data files are potentially large and the reporting process reduces the size of the data (i.e., downloading a final customized report as HTML, PDF, or RTF is much faster than downloading the data for the user to process locally).

ƒ

The data already exists (or is accessible) as SAS data sets.

ƒ

There is a need to reduce paper reports by allowing users to access customized reports that meet their specific requirements.

Chapter 1: Overview of SAS/IntrNet and Related Technologies 5

1.2 Components of SAS/IntrNet Software SAS/IntrNet contains a variety of components that can be used individually or in combination to address specific needs of a Web-enabled information delivery solution. If multiple components are used, each component provides services that best meet the requirements. The power of SAS/IntrNet lies in the remarkable range of its components. They are discussed in the following subsections. For more information about terminology, refer to Section 1.6.

1.2.1 The Application Dispatcher The Application Dispatcher has two required components: the Application Server and the Application Broker. It provides both compute (i.e., running a SAS program) and data services (i.e., running a SAS program that does a SQL query using PROC SQL). It also includes out-ofthe-box applications (e.g., Xplore). The Application Server and the Application Broker are discussed in detail in Chapters 3 and 4. The Application Dispatcher provides a CGI gateway (namely, the Application Broker) between a Web browser and SAS. This gateway lets programmers build dynamic applications that can access the power of SAS (the Application Server) from a Web browser. The gateway also provides the capability to run any SAS program that can be executed in batch mode. The programmers need to work only with the details of what results (e.g., reports) the SAS program is to generate. All of the details relating to CGI (e.g., communicating the parameter values from the HTML page and returning the results to the user’s Web browser) are handled by the Application Broker and require no knowledge of CGI development or programming. The Application Broker passes all incoming requests to the Application Server, which can execute almost any batch SAS program. The Application Dispatcher allows the deployment of SAS programs to multiple users (whether or not they have SAS installed on their machines). The SAS/IntrNet Application Dispatcher also includes an optional Load Manager that can be used to intelligently route and handle a large volume of incoming requests. Based on its configuration, the Load Manager can start and stop remote Application Servers based on demand, and route requests across multiple back-end application servers, regardless of the operating environment. The Application Broker, Load Manager, and Application Server are described in more detail in Chapters 3 and 4.

1.2.2 SAS Design-Time Controls SAS Design-Time Controls (DTCs) make it easy to create SAS/IntrNet applications without prior knowledge of HTML or SAS programming. DTCs are add-in components for What You See Is What You Get (WYSIWYG) HTML editors that support the SAS Design-Time Controls standard defined by Microsoft, including Microsoft FrontPage, Macromedia Drumbeat 2000, SoftQuad HoTMetaL PRO 5, webAF, and more. DTCs are ActiveX controls that run inside the HTML editor and generate text based on the user’s input. In this way, a favorite visual HTML editor can be used to build SAS/IntrNet reports and applications via a point-and-click interface. With SAS DTCs, static reports can be created and published to a Web server. Dynamic reports can be created using JSP and ASP technologies, retrieving and displaying the latest information when a user selects the page from the Web browser. Both static and dynamic DTC reports can be

6 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

viewed by any browser (e.g., Internet Explorer, Netscape, Firefox, etc.). Using the DTCs does not require any prior experience with developing ASP or JSP pages.

1.2.3 Xplore Sample Web Application SAS/IntrNet includes a sample application called Xplore which exposes the SAS data environment to the Web. Using the Application Dispatcher, Xplore can dynamically access a variety of SAS data and file types for reporting, generating graphics, and performing drill-down analysis on the Web. The Xplore sample application is shipped with the SAS source and can provide a valuable resource of techniques and tools.

1.2.4 htmSQL htmSQL is a CGI gateway to SAS and provides data services that, in turn, provide access to SAS data from a Web browser, enabling the building of dynamic SQL queries. htmSQL consists of a CGI program that resides on the Web server and can be used to access and update a SAS data set (including Read access through views to an external DBMS). The developer provides an input file, e.g., a .hsql file (note that the extension does not have to be hsql), containing SQL statements embedded in HTML, and htmSQL submits the statements to a SAS data server (either SAS/SHARE or the SAS Scalable Performance Data Server). htmSQL retrieves and formats the results according to the HTML embedded in the .hsql file. htmSQL can be used to create sophisticated, dynamic applications that let users manipulate data to address their specific information requirements. htmSQL can also be used to generate any other type of markup language such as Extensible Markup Language (XML), Compact HTML (cHTML), Handheld Device Markup Language (HDML), and Wireless Markup Language (WML).

1.2.5 SAS/CONNECT Driver for Java The SAS/CONNECT driver for Java provides compute services. It is a set of Java classes that can be used to create Java applets, JSP, and Java applications that communicate with SAS software on a server, thus taking advantage of remote SAS computing resources. The driver provides functionality that is similar to what a SAS client can do with SAS/CONNECT, except that the functionality is available to any Java program and does not require SAS to be locally installed. Programs that use the SAS/CONNECT driver for Java can start a SAS session, connect to that session, create data sets, access existing SAS data, run SAS programs to analyze SAS data, and retrieve the results. The Java components in SAS AppDev Studio can be used to create applications that use the SAS/CONNECT driver for Java without requiring extensive knowledge of Java.

1.2.6 SAS/SHARE Driver for JDBC The SAS/SHARE driver for Java Database Connectivity (JDBC) provides data services and is a set of Java classes that can be used to create Java applets, JSP, and Java applications that communicate with a SAS data server (either SAS/SHARE or the SAS Scalable Performance Data Server) via SQL queries. The Java programs can let a user view and update data by submitting SQL queries and statements through a direct connection to the SAS server. The Java components in SAS AppDev Studio can be used to create applications that use the SAS/SHARE driver for JDBC without requiring extensive knowledge of Java.

Chapter 1: Overview of SAS/IntrNet and Related Technologies 7

1.2.7 Tunnel Feature The SAS/IntrNet tunnel feature employs HTTP tunneling to allow Java applets to communicate with remote systems via a CGI program running on the Web server. As a security measure, Java specifications dictate that an applet cannot make network connections to a machine other than the machine from which it was downloaded, unless Java 2 (or later) is being used and explicitly allows this. When deploying Java applets, this security measure might force the use of a server configuration that is less than ideal, because that server would have to be installed on the same machine as the Web server. In addition, many firewalls prohibit applets from communicating beyond the firewall, a restriction that can further reduce server configuration options. The tunnel feature addresses both of these configuration problems. The tunnel feature, with Java applets written using the Java components in SAS/IntrNet or the Java components in SAS AppDev Studio, can be used to eliminate the restriction on where the SAS server runs in relation to a Web server and firewall. JSP applications do not have any of these restrictions because they run as native operating environment applications and have access to all resources.

1.3 Other SAS Web Components and Technologies Other SAS resources provide facilities and tools that provide critical capabilities for any Web application that is based on SAS. Selected resources are briefly described in this section.

1.3.1 The Output Delivery System Starting with Version 7 of SAS, the Output Delivery System (ODS), which is part of Base SAS software, allows the direct creation of Internet content, including HTML files. In general, ODS is a method of delivering output in a variety of formats and of making the formatted output easy to access. Instead of taking the output from a procedure and creating HTML via post-processing, ODS creates the HTML during the initial creation of the procedure output. This method is more efficient and provides many new possibilities for report layout. Through the addition of a few simple lines of code, developers can convert a standard program into one that creates HTML output. It’s not necessary to know HTML or modify existing SAS code. In addition to HTML, ODS supports many other types of output formats, including Adobe Acrobat Portable Document Format (PDF), Rich Text Format (RTF) for use in Microsoft Word and other word processing programs, CSV for use by Microsoft Excel and other spreadsheet tools, XML, and WML for use with WAP-enabled hand-held devices. ODS also produces GIF-, JPEG-, ActiveX- and Java-based graphics in conjunction with SAS/GRAPH software. This capability lets developers create a wide variety of content that can be delivered over the Web with little or no extra effort. Consider the following example that uses ODS to produce the desired output and automatically passes the results back to the user’s browser: ODS HTML BODY=_webout; proc print data=sashelp.class; run; ODS HTML CLOSE;

ODS, which is the subject of a number of papers and SAS Press books, should be the default mechanism for producing the Internet content output required of most Web applications.

8 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

1.3.2 The Web Publishing Tools and Related Macro Tools For users running SAS Version 6 and later, the Web publishing tools are a collection of tools that generate Internet content output. While these tools continue to exist in SAS software, the tasks they perform are usually easier to accomplish with ODS. However, there are occasions where their use might be a better fit. Also note that these tools are not part of SAS/IntrNet and may be used in any SAS program. The Web publishing tools include the following:

ƒ

ƒ

The HTML formatting tools, included with Base SAS software, are a collection of macros that format SAS data sets and procedure output into HTML pages that can be shared with Web users: …

The Output Formatter (%out2htm), which reformats output from any SAS procedure as an HTML file.

…

The Data Set Formatter (%ds2htm), which displays SAS data sets as HTML 3.x tables. The Data Set Formatter supports WHERE clauses, BY-group processing, and other data presentation capabilities.

…

The Tabulate Formatter (%tab2htm), which reformats the output from the TABULATE procedure into HTML 3.x tables.

Thin-client graphics are part of SAS/GRAPH software and are specialized, lightweight, visual Java applets or ActiveX controls which do not establish a persistent connection to a server. Instead, they receive all the information they need from applet parameters. The following tools generate the applet and ActiveX HTML calls along with the custom data values that are used as input to the visual component: …

The GraphApplet HTML Generator (%ds2graf) produces graphs and charts from SAS data. It generates HTML that uses the GraphApplet or SAS/GRAPH Control for ActiveX to display and manipulate the graphics.

…

The MetaView HTML Generator (%meta2htm) produces an HTML file that includes metadata through which users can view SAS/GRAPH output in a Java applet. To view the output as graphics, you must use the MetaViewApplet.

…

The RangeView HTML Generator (%ds2csf) produces an HTML file that displays a critical success factor (CSF) from a SAS data set using the RangeViewApplet.

…

The TreeView HTML Generator (%ds2tree) produces an HTML file that displays a hierarchical tree from a SAS data set using the TreeViewApplet.

…

The Constellation Chart HTML Generator (ds2const) produces an HTML file that displays data from a SAS data set as a constellation chart using the ConstellationChartApplet.

Chapter 1: Overview of SAS/IntrNet and Related Technologies 9

The following are two other macro tools from SAS:

ƒ

The DS2CSV macro takes as its input a SAS data set and creates a comma-separated value file. Optionally, the hexadecimal code for the separator character can be specified to create other types of output file (e.g., a tab-separated values file). Prior to SAS 9.1, the DS2CSV macro was shipped only with SAS/IntrNet for use with the Application Dispatcher. Beginning with SAS 9.1, the macro is available for use in any SAS program.

ƒ

The FILESRV macro is used to serve a wide variety of external files and catalog entries, including those that are not defined to a Web server. The FILESRV program enables the files that are to be served to be controlled (including what HTTP and MIME headers are served with the file). This macro uses an authorization data set to determine which files can be served. The mechanism for making this decision is RX function pattern matching. This data set is used to set patterns (or masks) for the files that can be served. If a requested file does not match one of these patterns, then it is not served and an error message is issued.

1.3.3 SAS AppDev Studio, a SAS Applications Development Environment SAS AppDev Studio is a development suite that supports various ways to develop, deploy, and maintain information delivery applications. SAS AppDev Studio offers users the choice of building Java-based applications on the client or on the server, flexible CGI and HTML applications, ASP applications, or traditional full-client applications, with ease and efficiency, all from within one stand-alone development environment. In addition, this software provides access to the proven, extensive server capabilities of SAS for enterprise applications development. SAS AppDev Studio includes the two Java components webAF and webEIS. webAF is a complete Java Integrated Development Environment (IDE) tailored for the rapid creation of Java applications, applets, servlets, and JSPs that use the power of SAS. SAS AppDev Studio also includes for development purposes multiple SAS products, including SAS/IntrNet, that are required to develop Web-based applications.

1.3.4 SAS®9 Business Intelligence Platform ®

The SAS 9 Business Intelligence Platform is a framework for providing business intelligence services that use SAS Stored Processes. Like SAS/IntrNet Application Dispatcher programs, SAS Stored Processes are traditional SAS language programs that use SAS DATA steps, procedures, macros, and the SAS Component Language (SCL) to deliver powerful SAS capabilities to client applications across an enterprise. SAS Stored Processes are stored and managed from a central server and must be described by metadata, which includes information about where each process is stored, how it is executed, and what output it generates. However, SAS Stored Processes are slightly different from traditional SAS programs in that they are executed by a SAS Stored Process Server. SAS Stored Processes are conceptually similar to SAS/IntrNet Application Dispatcher programs. In fact, many Application Dispatcher programs can be executed as is, or with only slight modifications, by the SAS Stored Process Server. This is true because both use macro variables to communicate the user’s parameters to the program or process being executed, and both use the same reserved fileref, _WEBOUT, to stream the results back to the user’s browser. Many of the examples and techniques presented in this book for the Application Dispatcher can also be used with the SAS Stored Process Server.

10 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

Because almost any valid batch SAS program can be converted to a stored process, the stored process capability delivers access to the full range of SAS analytics. Stored processes can be used to accomplish tasks such as these:

ƒ

generate report content or analytic results for Web browsers, Microsoft Excel, Web services, and other clients.

ƒ

generate report content or analytic results for SAS solutions and custom applications.

ƒ

implement Data Warehouse Extract-Transform-Load (ETL) job flows and other data transformation procedures.

ƒ

implement Web applications or functional components of Web applications.

ƒ

create or update data on the SAS server.

ƒ

run any batch SAS process.

Note, however, that there are currently differences between the two that will affect any decision to switch from the Application Dispatcher to the SAS Stored Process Server. Consider, for example, the following facts:

ƒ

SAS Stored Processes can be invoked from a broader range of clients (e.g., the Web, Microsoft Office, and SAS Enterprise Guide).

ƒ

Application Dispatcher programs can be external .sas files, catalog source entries, compiled SCL entries, and compiled macros. Stored processes may invoke any of these, but the process itself must be a .sas file.

Stored process results can be made available to users in a number of ways, including, but not limited to the following:

ƒ

SAS Stored Process Web Application is a Java Web application that executes stored processes and returns results to a Web browser. For Web-based applications it performs services comparable to what the Application Broker component of the Application Dispatcher provides.

ƒ

SAS Information Delivery Portal provides integrated Web access to SAS reports, stored processes, SAS Information Maps, and publish-and-subscribe channels. If the SAS Information Delivery Portal is installed, stored processes can be made available for execution from the portal without the need for additional programming.

ƒ

SAS Add-In for Microsoft Office is a component object model (COM) add-in that extends Microsoft Office by enabling dynamic execution of stored processes with the output embedded in Microsoft Word documents and Microsoft Excel spreadsheets. Additionally, within Excel the SAS Add-In for Microsoft Office can be used to access and view SAS data sources or any data source that is available from a SAS server.

ƒ

SAS Enterprise Guide is a client application for the Microsoft Windows environment that provides a guided interface to SAS for business analysts, statisticians, and programmers. It can be used to create as well as access SAS Stored Processes.

ƒ

SAS Web Report Studio is a Web-based interface that provides access to query and reporting capabilities.

1.3.4.1 The SAS Stored Process Web Application For Web-based applications similar to what can be done with the Application Dispatcher, stored processes can be accessed from a browser using the SAS Stored Process Web Application (part of

Chapter 1: Overview of SAS/IntrNet and Related Technologies 11

the SAS Web Infrastructure Kit). The user interacts with a Web interface and clicks a link to retrieve results, or the user can interact with an HTML form to specify any required parameters. Other similarities to the Application Dispatcher include these:

ƒ

The user interacts with a Web browser interface. In most cases the user enters parameters into an HTML form and clicks Submit. The request is then sent to the SAS Stored Process Web Application, a Java Web application located on the Web application server that can execute stored processes that are on a SAS Stored Process Server and return results to a Web browser.

ƒ

To write a Web application that uses SAS Stored Processes, only SAS and HTML programming skills are needed; no other programming (e.g., Java, CGI) is required. Thus, applications can be Web-enabled very quickly.

1.3.4.2 SAS Information Delivery Portal The SAS Information Delivery Portal disseminates information in a targeted, secure, and personalized way. It allows companies to distribute SAS products and solutions to users within an enterprise and to external customers, vendors, and partners through a secure, Web-based client interface. It also supports various languages. The SAS Information Delivery Portal is a J2EE, N-tier Web application that is deployed on Java application servers across various operating environments. Built using the SAS Intelligence Platform, it shares infrastructures and metadata with other SAS solutions and technologies. ®

Any stored process that is registered in the SAS Management Console (a component of the SAS 9 BI tool set) can be readily searched and run using the SAS Information Delivery Portal. After selecting the search link, the user can search specifically for SAS Stored Processes.

1.3.4.3 SAS Add-In for Microsoft Office SAS Add-In for Microsoft Office enables users to connect to a SAS Server in order to do several things:

ƒ

provide users access to SAS analytics from within Microsoft Office. This is accomplished by providing a self-sufficient, automated way to populate and maintain Microsoft Office reports with information from corporate data stores as well as from the results of analytic analysis.

ƒ

make enterprise data from multiple platforms easily available to business users. Users can be given broad access to all relevant data sources, even data repositories larger than those allowed by Excel. Users can access and switch between any enterprise data source and control the amount of data that is loaded into their Excel applications.

1.3.4.4 SAS Enterprise Guide SAS Enterprise Guide is a Microsoft Windows interface that allows a user to access a SAS server (either a locally installed or remote SAS server). Here are some features of SAS Enterprise Guide:

ƒ

The software has a graphical user interface that allows access to SAS. A process flow diagram facility lets users organize, view, and maintain their projects visually. It includes reporting, graphical, and analytical tasks, as well as more than 60 wizard-based tasks. Advanced users can create programs that produce tables and charts that can be easily embedded in other SAS applications and easily and securely shared.

12 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

ƒ

Users can perform data management functions allowing them to visually access any data types supported by SAS and native Windows data types. Users can create, update, subset, and join tables themselves using a graphical query builder.

ƒ

Data stored as OLAP (i.e., multidimensional data stores) can be presented allowing users to navigate through multidimensional data, add topic-specific business calculations, and extract information from multidimensional sources for further analysis.

1.3.4.5 SAS Web Report Studio SAS Web Report Studio has a Web-based interface that provides access to query and reporting capabilities on the Web. It is targeted to non-technical business users, providing them with selfservice access to corporate data. It is completely Web-based and does not require the installation of any local software on the end-user’s PC. A Report wizard enables novice or casual users to quickly create basic queries and reports. Users can search and choose the information they need. As users’ needs evolve, more sophisticated layout and query capabilities are available from a Report Builder interface. Business users can create folders for organizing reports. Existing reports can be edited, moved, copied, deleted, or renamed. Any user with the appropriate access can search for relevant reports in public or private folders. Users can print or save a PDF version of their report. Graphs and tables can be exported to Microsoft Excel as images. Alternatively, the data presented in a graph or table can be exported as tab-delimited text.

1.4 Industry Components By definition, the Web is an open environment. The use of other technologies is an integral part of Web-based solutions. Of course, Web servers and Web browsers are a standard component. However, there are other technologies that should be considered in using SAS/IntrNet and other SAS Web technologies.

1.4.1 Scripting Languages Scripting languages, such as JavaScript and VBScript, are lightweight interpreted programming languages with simple object-oriented capabilities. Scripting language programs operate at the client side (i.e., the Web browser), and they allow executable content to be included in Web pages. Executable content within the Web page means that Web pages can include dynamic programs that interact with the user, control the Web browser, and create the HTML content dynamically. If development staff is well versed in VBScript and if the user community is using Microsoft Internet Explorer as the Web browser, then VBScript is a viable choice for a scripting language. Some will say that JavaScript is more widely used and supported, and should be the scripting language of choice. One of the more important capabilities of JavaScript is its ability to define code fragments that are to be executed when a specific event occurs. JavaScript code can be used to perform functions such as the following:

Chapter 1: Overview of SAS/IntrNet and Related Technologies 13

ƒ

Validating input (e.g., check for required fields, numeric values, etc.) before submitting a CGI request.

ƒ

Submitting a CGI request automatically when an item is selected from a list box (an HTML select tag).

ƒ

Updating a user’s choices based on previous selections without requiring a round-trip to the server.

ƒ

Reading and writing properties of, and invoking methods of, Java applets and plug-ins.

For example, general purpose JavaScript functions are used in the Xplore sample application. As one example, consider the JavaScript functions defined in the page titled Select Variables to Process. This is the page users receive when they click on a non-summary SAS data set. Those functions are used to update both a text display and a cumulative list of variables in drill-down order. Figures 1.1 through 1.3 demonstrate this. Figure 1.1 shows the initial display. Figure 1.2 shows the results after the user selects the Region check box; note how the text display was also updated. Then Figure 1.3 shows the display after the user selects another check box (Subsidiary).

Figure 1.1 JavaScript Used by Xplore to Provide Dynamic Content—Initial Display

14 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

Figure 1.2 JavaScript Used by Xplore to Provide Dynamic Content—User Selects Region

Figure 1.3 JavaScript Used by Xplore to Provide Dynamic Content—User Selects Subsidiary

Chapter 1: Overview of SAS/IntrNet and Related Technologies 15

1.4.2 Dynamic HTML As discussed in Section 1.6, Dynamic HTML (DHTML) provides facilities to make HTML-based reports dynamic in terms of interactivity and visual effects in response to user actions without requiring a round-trip to a server to regenerate HTML. It was created to overcome the interaction limitations of standard HTML. Unfortunately, different browsers support different versions of DHTML. However, in an environment (e.g., an intranet) where browser vendor and versions can be assumed, DHTML can provide an easy and powerful mechanism to extend the functionality of a Web application. For example, with Microsoft DHTML, a simple (and generic) JavaScript function can be used to expand and collapse simple lists that were built using the HTML

  • tag. Xplore has an Internet Explorer implementation of the SAS library viewer that is automatically used whenever the Web browser is Internet Explorer 4 or later. While the browser is loading, the content of all libraries and catalogs is defined in the initial HTML. However, the DHTML capability in Internet Explorer is used to expand and collapse libraries and catalogs without requiring any additional server processing. Figure 1.4 shows an initial folder list produced by Xplore. If the user clicks a folder (e.g., SASUSER), Figure 1.5 shows the resulting display.

    Figure 1.4 Library List Produced by Xplore

    16 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 1.5 SASUSER Library Expanded

    1.4.3 Web Services Web services is a framework in which applications that provide specific content in response to a request are packaged as services that can be made available over the Web. All communication between services (i.e., the request for a service, and the results of running a service) is handled via XML files. Web services have been embraced by a number of vendors (e.g., the Microsoft .NET framework provides extensive support and use of the Web services model). Web services are discussed in more detail in Chapter 21, which discusses how the Application Dispatcher can be packaged to provide Web services.

    1.5 Component-Based Architectures The future of Web applications development consists of a component-based architecture in which developers can mix and match a variety of programming languages and concepts, such as the SAS and industry components mentioned previously, using whichever is most appropriate for each application. Consider an example where Pareto charts are generated on historical shop-floor data. Using ODS, the code is relatively straightforward to generate one or more HTML pages with the appropriate graphics. Management decides that such charts need to be produced so that the staff monitoring the shop floor can see the most recent data being collected. But there is a wrinkle: the requirement is that the graphs need to be refreshed every ten minutes (and, of course, it is discovered later that the users may want to change that refresh frequency) with no user intervention. At first glance it is not clear how to do this using SAS or SAS/IntrNet. However, the solution is quite simple: HTML constructs exist to force a page to be refreshed on a scheduled basis. The solution is to include an

    Chapter 1: Overview of SAS/IntrNet and Related Technologies 17

    appropriate META tag in the HEAD section of the HTML. The following code (where the LINE data to select and the refresh rate are passed in as parameters) addresses the requirement: goptions device=jpeg cback='white'; ods html body=_webout (title="&Line Data as of &sysdate:&systime") metatext="http-equiv=""refresh"" content=""&refresh""" path=&_tmpcat (url=&_replay) rs=none nogtitle nogfootnote style = sasweb ; proc pareto data=qcdata.failure ; title .h=1 "&line Data as of &sysdate:&systime"; where line = "&line"; vbar DEFECT / freq = COUNT weight = WEIGHT chigh(2)=red cframe=gray cbars=blue nohlleg; run; ods html close; ®

    The results of running this program using SAS 9 are shown in Figure 1.6. The only additions to the code to cause the refresh and to update the display with the information about the time are highlighted in bold. By combining SAS technology components (e.g., the Application Dispatcher, ODS, the PARETO procedure from SAS/QC) with an industry component (e.g., the META tag), the complete requirement has been addressed. For demonstrations and examples described in this section, go to the sample environment for this book and select Chapter 1.

    Figure 1.6 Pareto Chart—Automatically Refreshed with No Additional User Input Needed

    18 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    1.6 Terminology Web technology has had a dramatic impact on information delivery and the applications development process. Today’s technologies provide many benefits over the client/server model because it is no longer necessary to distribute and install applications on each desktop; information is accessible using a Web browser. The Web, as the main delivery mechanism, provides the infrastructure to ease the deployment of applications so that a client can request data or compute services. Web-enabled information delivery systems can be described in terms of the function they serve, how they work, and the types of technology they use. Here are the broad categories of how Web technologies can be used:

    ƒ

    Web publishing is the creation of static reports, which are made available via a Web server. Any user with a Web browser can access the information. The generated reports can be produced in a variety of ways. Scheduled batch jobs are an example. The only constraint is that the files containing the reports must be written in valid Internet content form. HTML for text-based files and GIF or JPEG for graphics files are some examples of valid Internet content form.

    ƒ

    Report distribution means that reports can be customized for specific user needs. Simple queries constructed on the clients (usually a Web browser) are passed through the Web server to a data repository, and then formatted as a report to be viewed on the client.

    ƒ

    Application distribution means that requests for decision support services can be initiated on the client (e.g. via a Web browser), and then executed by an application server with the results available to be viewed by the client.

    ƒ

    Data services enable the creation of Web-based applications that let the user view and query data from a Web browser. These applications are implemented using Web-enabled SQL. At the user’s request, dynamic queries can be sent to a server.

    ƒ

    Compute services provide the ability to not only query or view data, but to actually process the data on the back-end server, thus making it possible to analyze the data, create custom reports with interactive graphics, carry out OLAP analysis, and interact with data in a data warehouse all via a user’s browser.

    SAS/IntrNet data and compute services can be accessed via the Common Gateway Interface or Java technology. Here are definitions of common technology terms.

    ƒ

    CGI (Common Gateway Interface) is a programming interface that allows a Web server to communicate with an external program. Typically, CGI programs are small, written in a script or a high-level language, and reside on the Web server to act as the interface between a Web browser and a content server (providing data and compute services). When a Web browser accesses a Uniform Resource Locator (URL) for a CGI program, the Web server executes the CGI program. Usually, the CGI program invokes a session with a database or an application server, which processes the request from the client and returns the result to the Web browser via the Web server. Strictly CGI-based applications need repeated interaction with a server or servers in order to provide an interactive interface for the user.

    Chapter 1: Overview of SAS/IntrNet and Related Technologies 19

    ƒ

    Dynamic HTML (DHTML) provides facilities to make HTML-based reports dynamic in terms of interactivity and visual effects in response to user actions, without requiring a round-trip to a server to regenerate HTML.

    ƒ

    JavaScript is a lightweight, interpreted programming language with simple objectoriented capabilities. JavaScript operates on the client side (i.e., the Web browser) and enables executable content in Web pages.

    ƒ

    Java is an object-oriented programming language originally developed and promoted by Sun Microsystems. It now has broad support across a variety of platforms. This language is expressly designed for use in the distributed environment of the Internet. Java enables the creation of two types of applications relevant to the Web: …

    Perhaps best known are Java applets. When deployed, an applet contains two parts: an HTML page, which contains a reference to the applet, and a set of Java classes, which contains the program logic. The HTML page specifies which Java applet is to be invoked. (In fact, a single HTML page can include multiple applet references.) When a Web server returns an HTML page to a Web browser that contains a reference to an applet, the Web browser reads the applet information and requests the applet classes from the Web server. After these classes are downloaded, the applet is executed, based on input from a user. For example, the applet can establish a connection to a database or application server, submit requests for processing, and receive and display the results. Because the Java applet is executing locally on the client side, it can provide a much richer interactive environment than a CGI program.

    …

    Java Server Pages (JSP) allow the execution of Java code on the Web server. A JSP page contains HTML interspersed with Java code that is executed when the page is requested to produce the desired dynamic content. This process reduces the need to download Java classes to the local machine, reduces the need to ensure that the clients all support the necessary version of Java (a major problem in the Java applet space), and speeds up execution.

    ƒ

    JavaBeans is a component architecture framework for Java. This component defines a software Application Program Interface (API) that lets developers write reusable components once and run them anywhere a Java virtual machine is available. These components can be used in either Java applets or Java Server Pages.

    ƒ

    ActiveX controls are an implementation for Windows only that takes advantage of the Microsoft Windows implementation of COM technologies to provide interactivity and interoperability with other types of COM components and services.

    ƒ

    Active Server Pages (ASP) can be compared to JSPs in that ASPs are programs that execute on the Web server and return Internet content to the client. ASP is a Microsoft standard and is available on Microsoft Web servers.

    ƒ

    .NET is the Microsoft Web-enablement architecture. .NET facilities are available or planned for virtually every Microsoft application.

    ƒ

    Web services provides a mechanism that cross vendor, application, hardware and operating system boundaries. Web services uses XML as the transport mechanism for defining both the request as well as the delivery of results (e.g., the data).

    20 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    2

    P a r t

    How the SAS/IntrNet Application Dispatcher Works Chapter

    2

    Overview of the Application Dispatcher Process Flow 23

    Chapter

    3

    The Application Broker and the Load Manager 31

    Chapter

    4

    The Application Server 47

    Chapter

    5

    Communicating with the Application Dispatcher 63

    Part 2 provides details about how the components of the Application Dispatcher work and interact with one another. Here are some of the topics covered:

    ƒ

    the foundation for understanding the techniques and examples presented in Parts 3 through 5

    ƒ

    Best Practices for the Application Dispatcher environment as well as Application Dispatcher programs

    ƒ

    tools (available as a download as a part of the sample environment) that can be used in developing and deploying SAS Web-based applications built using the foundation provided by the Application Dispatcher

    ƒ

    the environment to evaluate and test the examples presented in the book

    Chapter 2 provides a high-level description of the components of the Application Dispatcher (the Application Broker, Application Server, and Load Manager) and how they interact with each other.

    22 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Chapter 3 provides more detail regarding how the Application Broker and Load Manager components work, including a detailed discussion of the logical processing flow of the Application Broker. Chapter 4 further describes how the Application Server works, including a detailed discussion of its logical processing flow as it executes the user-requested SAS programs. Chapter 5 discusses in detail how parameter values specified via links or HTML forms are communicated to the Application Dispatcher, and how those values can be used and referenced in the SAS programs run in response to user requests. The material contained here is not intended to be a substitute for the SAS/IntrNet documentation, which is available on the SAS Web site at support.sas.com/rnd/web/intrnet. Only selected material/topics are included here. After gaining an understanding of the material in this book, you might want to read the full details about the particular component, statement, or directive. The techniques and examples presented in Parts 3, 4, and 5 rely on the concepts presented in Part 2 and so it may be useful to refer back to Part 2 while reading Parts 3, 4, and 5.

    Reader Notes The topics presented in Part 2 are inter-dependent and require the background that is presented elsewhere in this part. Unfortunately, there is no optimal reading order that works for everyone. (Users can approach the material from a variety of directions, depending on what their primary goals are.) The following suggested paths should help you get the maximum benefit from Part 2. Chapters 9 and 10 include material that discusses how the Application Dispatcher works. These two chapters are included in Part 3 instead of Part 2 so the discussion can leverage some of the techniques presented in Part 3. You may want to briefly review these chapters after reading Part 2.

    If You Are New to the SAS/IntrNet Application Dispatcher Readers that are new to the SAS/IntrNet Application Dispatcher may want to first scan Chapter 5, not worrying about the details and nuances. Then, go back to Chapter 2 and read the chapters in order, finishing with a detailed reading of Chapter 5. Alternatively, the reader may want to consider scanning Chapters 5, 2, 3, then 4, and then reading the chapters in order for more detail.

    For Experienced SAS/IntrNet Application Dispatcher Develops and Users Experienced developers and users may want to scan Chapters 2 through 5 and then go back and re-read selected chapters in order.

    C h a p t e r

    2

    Overview of the Application Dispatcher Process Flow 2.1 Introduction 23 2.2 Application Broker—Application Server Process Flow 24 2.3 Process Flow Including the Load Manager 25 2.4 Performance Benefits of Using the Load Manager 27

    2.1 Introduction The Application Dispatcher comprises three distinct components:

    ƒ ƒ ƒ

    the Application Broker the Application Server optionally, the Load Manager

    These three work in concert to respond to a user’s request. This chapter provides a high-level overview of the components of the Application Dispatcher and provides the framework for the detailed processing flow discussed in the next two chapters.

    24 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    2.2 Application Broker—Application Server Process Flow In Web applications, the user’s browser provides interface functions, querying the user about what he wants to do. The browser displays and formats the results as well. In most cases, the request for processing starts with the user filling out a form or clicking a link. This action causes the components of the Application Dispatcher to begin its processing (see Figure 2.1):

    Figure 2.1 Application Dispatcher Process Flow

    1. The user performs one or more of the following actions: a. b. c. d.

    fills out a form and selects Submit clicks a hyperlink selects a favorite uses another HTTP client to initiate a request (e.g., the SAS URL access method)

    Note: In Step 1, any client that can submit an HTTP request can invoke the Application Dispatcher. If such a client makes a request, that client will be the recipient of the streamed output in Step 5. 2. The Application Broker begins execution on the Web server: a. The Application Broker configuration file is checked to verify that the requested service (i.e., Application Server) is defined. b. If it is defined, the Application Broker forwards the request to an Application Server via TCP/IP. If there are multiple servers defined for the service, the Application Broker picks one at random, regardless of whether that server is currently running another request. Section 2.3 discusses how the use of the Load Manager enables a more intelligent selection. c. Otherwise (i.e., the service is not defined), the Application Broker streams HTML back to the user’s browser with a message to that effect. Note: The Application Server is associated with a service, and a service may include one or more Application Servers.

    Chapter 2: Overview of the Application Dispatcher Process Flow 25

    3. The Application Server receives the request: a. It checks to make sure the requested program is available. b. If the program is available, it then executes the program that writes Internet content to the reserved fileref _webout. c. If the requested program is not available, it streams HTML back with a message to that effect. 4. The output written by the program to _webout is streamed back to the Application Broker via TCP/IP. 5. The Application Broker streams the output back, as it is received, to the user’s browser. 6. When the Application Server has finished executing the requested program it tells the Application Broker that it has completed execution of the requested program. 7. The Application Broker finishes its processing and ends execution. All of the above steps are expanded upon in Chapters 3 and 4, which discuss the processing details of the Application Broker, Load Manager, and Application Server.

    2.3 Process Flow Including the Load Manager The addition of the Load Manager enables intelligent routing and handling of incoming requests. Working in concert with the Application Broker, the Load Manager enables the Application Broker to perform the following actions:

    ƒ

    route new incoming requests to an available server, regardless of the operating environment

    ƒ

    start and stop remote servers based on demand

    The processing logic is shown in Figure 2.2.

    Figure 2.2 Application Dispatcher Process Flow Including the Use of Load Manager

    Note: The Load Manager adds value only when there are multiple Application Servers defined for a service. However, the Load Manager will run correctly for services with only one server. It will always tell the Application Broker to use the single server defined for that service.

    26 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    1. The user performs the following actions:

    a. fills out a form and clicks on a submit button b. clicks a hyperlink c. selects a favorite d. uses another HTTP client to initiate a request (e.g., the SAS URL access method) 2. The Application Broker begins execution on the Web server: a. The Application Broker configuration file is checked to verify that the requested service (i.e., Application Server) is defined. b. If it is defined, the Application Broker sends a request to the Load Manager requesting the server and port for an available server. c. Otherwise (i.e., the service is not defined), the Application Broker streams HTML back to the user’s browser with a message to that effect. d. If the Load Manager determines that no Application Servers are currently available to process the request, the Load Manager performs one of the following actions: i. For Pool services (see Section 3.4.2), the Load Manager requests information from the Application Broker so that it can determine if starting a new Application Server (see Chapter 3) is possible. ii. If starting a new Application Server is possible, the Load Manager requests the details for starting a new Application Server from the Application Broker. It starts a new Application Server and then selects that Application Server for the current request. iii. Otherwise, if starting a new Application Server is not possible, the Load Manager waits until it is notified that an Application Server is available. e. Otherwise, the Load Manager selects an available Application Server. f. The Load Manager flags the selected Application Server’s status as Pending. g. The Load Manager tells the Application Broker the server and port to forward the request to. 3. The Application Broker forwards the request to the selected Application Server via TCP/IP. 4. Upon receiving the request, the Application Server performs the following actions: a. It tells the Load Manager to change its status from Pending to Busy. b. It checks to make sure the requested program is available. c. If the program is available, it then executes the program that writes internet content to the reserved fileref _webout. d. Otherwise, if the requested program is not available, it streams HTML back with a message to that effect. Note: Only one Load Manager should be associated with a specific Application Server. If multiple Load Managers are associated with a specific Application Server (e.g., by defining the same Application Server in two Application Broker configuration files), the different Load Managers will not be able to correctly track the state of the Application Server. 5. The output written by the program to _webout is streamed back to the Application Broker via TCP/IP. 6. The Application Broker streams the output, as it is received, back to the user’s browser. 7. When the Application Server has finished executing the requested program, it does two things:

    Chapter 2: Overview of the Application Dispatcher Process Flow 27

    a. It tells the Load Manager that its status is now Idle. b. It tells the Application Broker that it has completed execution of the requested program. 8. The Application Broker finishes its processing and ends execution.

    2.4 Performance Benefits of Using the Load Manager When multiple Application Servers are used, it is clear that using the Load Manager will improve performance. Recall that the Load Manager causes the Application Broker to route requests to an available Application Server (if one is available). Without the Load Manager, the action is to randomly select an Application Server (which may result in an Application Server that is currently busy being selected). To illustrate that the Load Manager is instructing the Application Broker to route requests to specific Application Servers, a second request must be submitted while a first request is still processing. The same technique used for the Pareto chart example in Chapter 1 (where a META tag caused the browser to automatically refresh the requested URL) can be used here to ensure that there is a second request while the first one is still running. The secret is to pick a refresh value that is shorter than the expected duration of the SAS job. To run the examples in this section, go to the sample environment and select Chapter 2. Then select Investigate Load Manager Routing, and Open a second instance in a new browser window. The example prints the SASHELP.CLASS data set, and displays (in a title) which Application Server is running the request by displaying the server and port (see Chapter 5 for a discussion of how these values are available to the program) for the executing Application Server, along with the date and time the request was run. By default, the request is refreshed every second. Note how the requests in the two browser windows intermittently alternate between the two Application Servers. Note that there is also an option that allows a refresh rate other than 1 second to be used. It is possible to model the performance of an Application Server environment, including the potential performance gain realized by using the Load Manager, using queuing theory. The model described here is conceptually similar to the common textbook bank teller example where there is a line of customers and a fixed number of available tellers with an average time for each customer request. The pool of tellers is analogous to the pool of Application Servers; the line of customers is analogous to requests waiting for execution. Such a model can also be used to provide an estimate of the number of required Application Servers, as well as to demonstrate the benefit provided by the Load Manager. The model, as implemented, makes these assumptions:

    ƒ

    that requests arrive randomly following a distribution quite common in queuing theory, a Poisson distribution. The defining parameter is the total number of requests per hour.

    ƒ

    that the length of time to satisfy a request also follows a distribution quite common in queuing theory, an exponential distribution. The defining parameter is the average job length. Note that this is not a symmetric distribution, i.e., most jobs will be short, but there is a possibility of a few long running jobs. This is a very reasonable assumption.

    Note: The model described here is not intended to be a robust model for Application Dispatcher environments. It is intended to provide only an approximation of the expected performance.

    28 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    In order to use this model, the following parameters must be specified:

    ƒ ƒ ƒ

    the number of requests per hour the average time for a typical request the number of available Application Servers

    Tip: Estimating the average job time for a typical request is relatively straightforward. The Application Server is simply a SAS session. Thus, a simple estimate is to simply run typical jobs on the Application Server and note the average time. For demonstrations and examples described in this section, go to the sample environment for this book and select Chapter 2. This page provides a simple interface to select various values for these parameters. When the user selects Run, the queuing model is executed and the results are returned to the user’s browser. The output includes the following information:

    ƒ

    an HTML table (see Figure 2.3) with performance statistics for both the case of the Load Manager not being used, as well as the Load Manager being used.

    Figure 2.3 Load Manager Queuing Model—Tabular Output

    Here are explanations of the statistics. …

    Expected # of Total Waiting Requests This means the average number of requests that are waiting to begin execution because the Application Servers are busy. For the No Load Manager case, this includes the jobs that were routed to a busy Application Server. For the With Load Manager case, it represents the jobs that were submitted while all the Application Servers were busy.

    …

    Expected # of Requests in the System This means the average number of requests in the system at any point. It includes both requests that are currently executing, as well as requests that are waiting.

    …

    Expected Waiting Time in Queue This means the average time that a request waits before beginning execution. Ideally, this number should be small. A value of zero results if there are never more requests than Application Servers.

    …

    Expected Time Spent in System This means the average time a user waits before seeing the results. It is the sum of the Average Request Time (one of the input parameters) and the Expected Wait Time in Queue. Note, however, that it does not include any network communication time.

    Chapter 2: Overview of the Application Dispatcher Process Flow 29

    ƒ

    a graph (see Figure 2.4) that shows the cumulative distribution of how many jobs are in the system (including running jobs) at any point. A reference line is drawn at the number of Application Servers. The intersection of this line with the graph shows what percentage of requests (on average) begin execution with no wait time.

    ƒ

    a graph that shows what percentage of jobs are completed (i.e., results returned) versus time. A reference line is drawn at the Expected Time Spent in System (i.e., the average total waiting time). The intersection of this line with the graph shows what percentage of the jobs are completed in less than the average time.

    Figure 2.4 Load Manager Queuing Model—Graphical Output

    After the model has run with the default parameters (as specified in the HTML file), the output demonstrates that, for this set of parameters, the addition of the Load Manager decreases the average time a user waits for requests by approximately 20% = (3.79–3.01)/3.79. Note that the wait time for the Load Manager case shows that nearly all the requests start almost immediately. The graph confirms this observation by demonstrating that 99.8% of the time a new request starts immediately because there is an available Application Server.

    30 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    3

    The Application Broker and the Load Manager 3.1 Introduction 31 3.2 Specifying Additional HTML Output 33 3.3 Defining the Load Manager 34 3.3.1 The Load Manager Command 34 3.4 Defining the SAS Application Servers 35 3.4.1 Socket Servers 35 3.4.2 Pool Servers 37 3.4.3 Launch Servers 39 3.5 Application Broker Process Flow 40 3.5.1 Load Manager Administrative Functions 46

    3.1 Introduction The Application Broker and Load Manager’s processing is controlled through directives that are contained in the Application Broker configuration file. These directives define the following:

    ƒ ƒ

    the processing, including server identification, that is involved in processing each request values that are to be made available to Application Dispatcher applications

    Providing values to Application Dispatcher applications is discussed in more detail in Chapter 5. Figure 3.1 shows a sample of the directives discussed in this chapter.

    32 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Note: The Application Broker is a Common Gateway Interface (CGI) program. CGI applications are quite often broadly categorized as less than optimal because the CGI process must execute on the Web server. For CGI applications that are “doing all the work” this is a valid criticism. However, the Application Broker is a very lightweight application; it is doing very little work other than communicating a request for services to another computer. Thus, the typical criticisms of the Application Broker as a CGI program are often misplaced. An exception is the rarely used launch services extension where all the work is done on the Web server since, for launch services, the SAS Application Server is also running on the Web server. For heavily loaded systems where performance is critical, the reader may want to consider one of the two additional versions (an ISAPI and a GWAPI version) of the Application Broker. These versions may also be less vulnerable to attacks by hostile users.

    Figure 3.1 Sample of Application Broker Directives SASPoweredLogo "/sasweb/IntrNet9/images/saslanim.gif" SASPoweredAlt "SAS Institute Inc." SASPoweredHref "http://www.sas.com" SASPoweredTarget "wwwsascom" PrependFile "/inetpub/wwwroot/css/default.css" AppendFile "/inetpub/wwwroot/defaultContact.html" LoadManager localhost:5555 SocketService default "Default Socket Service" ServiceDescription "Default service used for SAS/IntrNet samples." ServiceAdmin "Don Henderson" ServiceAdminMail "[email protected]" Server localhost Port 5001 FullDuplex True PoolService appdisp ServiceDescription "Demo Environment for SAS Press book Application + Development Using the SAS/IntrNet Application Dispatcher" ServiceAdmin "Don Henderson" ServiceAdminMail "[email protected]" Server localhost Port 4 MinRun 1 IdleTimeout 30 FullDuplex True ServiceLoadManager localhost:5555 SasCommand "\"DRIVE:\\Program Files\\SAS\\SAS 9.1\\sas.exe\" + \"DRIVE:\\root\\SAS\\IntrNet\\appdisp\\appstart.sas\" + -rsasuser -noterminal -nolog -SYSPARM " ServicePrependFile "/inetpub/wwwroot/css/serviceSpecific.css" ServiceAppendFile "/inetpub/wwwroot/serviceSpecificContact.html"

    Chapter 3: The Application Broker and the Load Manager 33

    3.2 Specifying Additional HTML Output The PrependFile, ServicePrependFile, AppendFile, and ServiceAppendFile directives can be used to define a set of static HTML (tags and text) to insert at the beginning or end of every HTML page generated by an Application Dispatcher application. The PrependFile and AppendFile directives provide files to use for all the services in the Application Broker configuration file. They may be overridden on a per-service basis with ServicePrependFile and ServiceAppendFile directives. Here is the syntax: directive host-path-name

    Note that the value is a host path name, not a URL, for a file that is available to the Application Broker (which executes on the Web server). See Figure 3.1 for an example of those directives. Tip: Use the forward slash (/) instead of the backward slash (\) when specifying the path name because the \ character is the Application Broker escape character. / works on both UNIX and Windows. Alternatively, use a double backslash (\\) for a single \. Some example uses of these directives could be to insert the following:

    ƒ

    standard style sheets or JavaScript function definitions (using one of the prepend directives)

    ƒ

    copyright text at the bottom of the generated HTML (using one of the append directives)

    Best Practice: Use the prepend and append directives with care. If they are used, any ODS statement should include the no_top_matter or no_bottom_matter option or both in order to prevent malformed HTML. For example if you put an HTML and BODY tag in the PrependFile, and the application also outputs them, the generated HTML is malformed. Major browsers, however, are forgiving about this.

    The SASPoweredLogo directive can be used to instruct the Application Broker to include a graphical logo which links, by default, to the SAS home page at the end of the output (and after any Append directives). It may be overridden on a per-service basis with the ServiceSASPoweredLogo. Here is the syntax: SASPoweredLogo url-path-name-for-an-image

    The SASPoweredAlt, SASPoweredHref and SASPoweredTarget (and the corresponding service level directives) can be used to specify the following:

    ƒ

    The hover text to be displayed when the user’s cursor hovers over the image (this is also the text that is displayed if the image cannot be displayed).

    ƒ ƒ

    The URL to go to when the user clicks the image. The value to use for the TARGET= option of the image tag. Specifying a value here can force the browser to open the URL specified in SASPoweredHref in a new or different browser window.

    Tip: The SASPowered directives can be used to generate an image that links to any address (e.g., a corporate home page) at the end of the generated output. See Figure 3.1 for examples of these directives.

    34 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    3.3 Defining the Load Manager Whether or not the Load Manager is to be used is defined by the LoadManager and the ServiceLoadManager directives. The LoadManager directive defines a default Load Manager which is used for all the services defined in the Application Broker configuration file. This value may be overridden on a per-service basis with the ServiceLoadManager directive. Note: The use of the Load Manager, and thus a LoadManager directive, is required for pool services. Both the LoadManager and ServiceLoadManager directives define the host and port for the Load Manager, using the following syntax: LoadManager server:port ServiceLoadManager server:port

    The value for server can be the DNS name (e.g., appsrv.yourcomp.com) or the IP address (e.g., 127.0.0.1) of the machine where the Load Manager is to run. The Load Manager can run on any machine that the Application Broker and Application Server can communicate with via TCP/IP, including the servers that the Web server and SAS Application Server are running on. The value for port can either be a numeric port value or a symbolic name that is defined in the system services file on the Load Manager server. Note: The Load Manager creates a log file with an audit trail of how requests are processed. Such information can provide useful diagnostics. The Load Manager is available under OS/390, UNIX, and Windows and is included in the CGI Tools download package. It is also included in the SAS installation under UNIX and Windows. See Figure 3.1 for examples of these directives.

    3.3.1 The Load Manager Command The Load Manager is invoked by issuing the Load Manager command. Under Windows, the SAS/IntrNet Service Configuration Utility (inetcfg) creates the command and adds it to the Start menu (it also creates the command to define it as a Windows service). Here is the syntax: loadmgr

    The most commonly used option is PORT, which specifies the port number or service name for the socket on which the Load Manager listens. If this parameter is not specified, the services file in the /etc directory is checked for an entry for LOADMGR. ®

    The Load Manager shipped with SAS 9 includes options that provide additional capabilities for heavily used environments and production level applications that use pool services.

    Best Practice: Always use the most current Broker and Load Manager. They are backwards compatible with previous releases of the Application Server.

    Chapter 3: The Application Broker and the Load Manager 35

    ƒ

    maxreq=minutes the maximum time it should take for the Application Server to send a BUSY state after the Application Server is selected for a request. The default is 1 minute.

    ƒ

    maxrun=minutes the expected maximum job run time in minutes before an Application Server is declared to be hung. The default is 60 minutes. Note: The default value for maxrun is likely too long. Set maxrun to a value that is longer than the longest running request (see Chapters 17, 18, and 19 for techniques to handle long-running requests). Likewise, a shorter value for maxstart may also be appropriate (e.g., 1 or 2 minutes).

    ƒ

    maxstart=minutes the maximum time that it should take an Application Server to start. The default is 5 minutes.

    ƒ

    nokill the instruction to Load Manager to not kill a pool server that is marked as hung.

    3.4 Defining the SAS Application Servers The SAS/IntrNet Application Dispatcher includes three kinds of Application Servers:

    ƒ ƒ ƒ

    socket servers pool servers launch servers

    The type of server to use for a user request is referred to as a service and is defined in a request using the name _service (see Chapter 5 for a discussion of _service). The value must correspond to a service defined in the Application Broker configuration file for a launch, socket or pool Application Server.

    3.4.1 Socket Servers Socket services consist of one or more Application Servers that run continuously servicing client requests. Socket services can be started at administrator control, typically whenever a machine is restarted (either manually or by an operating system mechanism for starting processes at boot or log-on time). The service usually runs until the machine is shut down. Socket services are relatively simple to configure and manage and are typically adequate for many applications. Here are the primary advantages of Socket servers:

    ƒ ƒ

    Socket services are supported on all SAS/IntrNet platforms.

    ƒ

    The Application Server is already running by the time a client request appears, so clients do not have to wait for an Application Server to start.

    ƒ

    The administrator has explicit control of resources allocated to the service: the administrator can control how many Application Servers are run on each system and the resources to allocate to each Application Server.

    An Application Dispatcher service can include socket services running on multiple different host platforms (e.g., Windows, UNIX, MVS, etc.).

    36 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Increasing load can be handled by an administrator adding more Application Servers to the service. The potential impact of adding Application Servers was seen in the Load Manager Queuing Model results in Section 2.4.

    ƒ

    The number of Application Servers is static since they must be started and stopped manually or by the operating environment.

    ƒ

    A fixed number of Application Servers are available to handle all client requests. Thus, one (or more) long-running requests can slow the entire service for all clients.

    ƒ

    Additionally, since the servers are static, they are all always running, even if there are no requests.

    3.4.1.1 Directives to Define Socket Servers Defining a socket server is also done with directives. Three directives are required to define a socket server:

    ƒ

    ƒ

    ƒ

    SocketService The SocketService directive begins the definition of the socket service and provides a name for the service as well as an optional short description. The name is the value for the _service field that is passed to the Application Broker from the user request. Best Practice: If the Application Servers are running on the Web Server server, use localhost for the server The Server directive follows the name. This allows the SocketService directive. It provides the names configuration file to remain or IP numbers of the physical machines where unchanged if it is necessary to the SAS/IntrNet Application Servers are move to a different server. running. The value can be the DNS name (e.g., machine-name.your-domain.com) or the IP address (e.g., 127.0.0.1) of the machine. Port The Port directive follows the server directive. It specifies the TCP/IP port number or numbers or network service name or names used by the Application Servers for this service. You can define multiple ports by separating their values with spaces or by issuing the port directive multiple times. Numeric port ranges and symbolic names defined in the system services file are supported.

    See Figure 3.1 for examples of these directives to define a socket server. The server directive can specify multiple names or IP numbers for the servers on which Application Servers are running. The port directive can specify multiple TCP/IP port numbers that represent multiple Application Server processes on a single machine. The following example shows two servers, each with three Application Servers (i.e., three port numbers): Server server_a server_b Port 5001-5003

    This example defines six Applications Servers:

    ƒ ƒ

    three Application Servers (ports 5001, 5002, and 5003) on server_a three Application Servers (ports 5001, 5001, and 5003) on server_b

    Chapter 3: The Application Broker and the Load Manager 37

    Note: If the Application Server selected by the Broker is not running, the Broker tries to access another one (until it finds one that is running). If the Load Manager is not being used, each of the previous Application Servers has a 1 in 6 chance of being selected for a specific request. The Application Broker randomly selects the specific Application Server (i.e., the server or port). If the Load Manager is being used, it selects which Application Server to use for a specific request. See Section 3.5. Multiple server and port lines can be used to accommodate asymmetrical configurations. Here is an example: Server server_a server_b Port 5001 5002 Server server_b Port 5003

    This example defines five Application Servers:

    ƒ ƒ

    two Application Servers (ports 5001 and 5002) on server_a three Application Servers (ports 5001, 5002, and 5003) on server_b

    Since some servers are more powerful than others, you can assign weights to servers. For example, consider the following: Server server_a server_b*5

    Here, server_a has a 1 in 6 chance of being assigned a request, while server_b has a 5 in 6 chance. Note that the weights apply to servers and not to individual ports on those servers. The Application Broker cannot be forced to favor one port over another on the same server. Note: Weights can be used to specify a back-up Application Server that receives requests only if the primary Application Server or servers are not operating. Set the weight to zero (0), e.g., server server_a

    server_b*0

    The result is that server_b (that is, the back-up server) is selected only if server_a is unavailable. Consult the SAS/IntrNet Application Dispatcher documentation for a list of SocketService directives and their options.

    3.4.2 Pool Servers Pool services consist of a pool of Application Servers shared by clients. They combine the advantages of socket and launch services. (See Section 3.4.3.) Based on the system load (e.g., the number of requests), Application Servers can be started and stopped by the Load Manager. Here are the primary advantages of pool servers:

    ƒ

    Servers are started as needed. If all the Application Servers in the service are busy, the Load Manager can start an additional Application Server.

    ƒ

    However, once started, the Application Servers can be used for additional requests. Once an Application Server is started, it remains in the pool until an idle timeout is reached (which causes the Load Manager to stop the Application Server).

    ƒ

    Like socket services and unlike launch services, pool services can be on a different system than the Web server.

    38 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Here are the primary disadvantages of Pool Servers:

    ƒ ƒ

    Installation and configuration is more complex for pool services than for socket servers.

    ƒ

    The Load Manager is required for pool servers.

    The Application Dispatcher service can include only pool services running under a single operating environment (e.g., all Windows, all UNIX, all MVS, etc.).

    3.4.2.1 Directives to Define Pool Servers Defining a pool server is also done with directives. Four directives are required to define a pool server. Since a pool server combines the features of both launch and socket servers, it has the same set of required directives (which require similar information):

    ƒ

    PoolService The PoolService directive begins the definition of the pool service and provides a name for the service as well as an optional short description. Just as for socket services, the name is the value for the _service field that is passed to the Application Broker from the User request.

    ƒ

    Server The Server directive follows the PoolService directive. It provides the names or IP numbers of the physical machines where the SAS/IntrNet Application Servers are running. Note that these Application Servers are started and stopped by the Load Manager. The value can be the DNS name (e.g., machine-name.your-domain.com) or the IP address (e.g., 127.0.0.1) of the machine. Note: Weights can also be used with pool servers. However, the weights serve only to order the servers. When the Load Manager selects a server, it picks a server with the largest weight that is available at the time.

    ƒ

    Port The Port directive follows the server directive. It specifies the TCP/IP port number(s) or network service name or names used by the Application Servers for this service. You can define multiple ports by separating their values with spaces or by issuing the port directive multiple times. Numeric port ranges and symbolic names defined in the system services file are supported. If the value of port is less than or equal to 256, the value is the number of Application Servers. In this case the Application Server picks an available port once it is started and relays the value to the Load Manager.

    ƒ

    SasCommand The SasCommand directive provides the command and arguments that are necessary to invoke a new SAS session and is usually the fully qualified path to the SAS executable or shell script as well as the fully qualified path to the SAS program that starts the Application Server. The template provided at installation of SAS/IntrNet includes a sample SAS command.

    Note: The SAS/IntrNet Service Configuration Utility (inetcfg) provides the default value to use for the SasCommand directive. Use the value provided during the installation. The value specified in the SasCommand directive must end with the text sysparm. When starting a server, the Load Manager inserts a port value at the end of the provided value. That value is used in the PROC APPSRV statement (see Chapter 4). Also note that under Windows, the .exe extension for the SAS executable file is required. See Figure 3.1 for an example of these directives to define a pool server (a subset of the directives used to define the pool server for the sample environment).

    Chapter 3: The Application Broker and the Load Manager 39

    The following directives are optional, but are typically included in the server definition of the configuration file:

    ƒ

    SpawnerPort specifies the port on which the SAS Spawner is listening. The SAS Spawner is used to start new Application Servers, using the value specified in the SasCommand directive, when the Application Servers are running on a server other than the Web server. This directive is required in such cases.

    ƒ

    IdleTimeout specifies an Application Server timeout in minutes. If no requests are sent to an Application Server, it is shut down after the IdleTimeout value is reached. The default is 60 minutes. A value of 0 indicates immediate shutdown after processing a request, including all of its sessions.

    ƒ

    MinRun specifies the minimum number of Application Servers to keep running.

    Note: When the STOP command is used, it brings the service down (i.e., no Application Servers running). Note that the service is not reset to its minimal state. MinRun isn’t enforced at Load Manager start-up. That is, the Application Server or Servers won't be started until the first request for that service. Consult the SAS/IntrNet Application Dispatcher documentation for a list of PoolService directives and their options.

    3.4.3 Launch Servers A launch service starts a new Application Server for each request and then shuts down. If no sessions are involved, the Application Server shuts down as soon as the requested program finishes execution. See Chapter 4 for the details.

    Best Practice: Except in rare cases, launch servers should be avoided in favor of pool servers.

    The primary advantages of launch servers stem from the fact that each request is run by a separate Application Server. Therefore, the following are true:

    ƒ ƒ

    Long-running requests do not block access for other requests. Requests with serious errors are isolated from other requests.

    The major disadvantages of launch servers are these:

    ƒ ƒ

    Launch servers must be run on the Web server.

    ƒ ƒ

    Launch servers are not available on all hosts (e.g., MVS and VMS).

    Each new request incurs the resource overhead and delay of starting a new Application Server. Launch servers are not suitable for high user loads since the Application Broker must start a new Application Server for each request.

    40 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    3.4.3.1 Directives to Define Launch Servers Defining a launch server is straightforward since it involves starting a new SAS session on the Web server. There are two directives that are used in the configuration file to do this:

    ƒ

    LaunchService The LaunchService directive begins the definition of the launch service and provides a name for the service as well as an optional short description. Just as for socket and pool services, the name is the value for the _SERVICE field that is passed to the Application Broker from the user request.

    ƒ

    SasCommand The SAS command and arguments that are necessary to invoke a new SAS session on the Web server and is usually the fully qualified path to the SAS executable or shell script as well as the fully qualified path to the SAS program that starts the Application Server. The SYSPARM parameter (without a value) must be included at the end of the command. Note: Just as for pool services, the SAS/IntrNet Service Configuration Utility (inetcfg) provides the default value to use for the SasCommand.

    Consult the SAS/IntrNet Application Dispatcher documentation for a list of LaunchService directives and their options.

    3.5 Application Broker Process Flow The process flow for the Application Broker begins with the user making a request as described in Chapter 2. The Application Broker is started on the Web server, and the processing flow is illustrated in Figures 3.2 through 3.7 Note: The outline of steps described here is very detailed. You are encouraged to review it to get a general sense of the process flow. As you work through developing applications, consult the steps described here as needed.

    Chapter 3: The Application Broker and the Load Manager 41

    Figure 3.2 shows the processing flow for the initial set up processing done by the Application Broker. The value of _debug (discussed in detail in Section 5.2.3) is a critical component of the process flow.

    Figure 3.2 Application Broker Set-Up

    42 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 3.3 shows the process flow for the primary administrative functions provided by the Application Broker. The process flow makes reference to _debug containing certain values. _debug is a binary flag whose values are additive, e.g., the value 9 contains 1 and 8. Each component of _debug corresponds to a distinct power of 2. Values are added together to specify multiple options. As shown in Figure 3.3, a request may not be sent to an Application Server.

    Figure 3.3 Application Broker Administrative Functions

    Chapter 3: The Application Broker and the Load Manager 43

    Upon successful completion of its set-up and administrative processing, the Application Broker selects the Application Server to which the request should be forwarded. This processing is shown in Figure 3.4. Note: _service is required, even if _server and _port are specified. If _server and _port are specified, their values must be consistent with the service definition. Note: During this selection process, the Application Broker generates HTML content and returns it to the user’s browser with details of the Application Server selection process if _debug contains 256, 512, or 2048. These are advanced options and are rarely used.

    Figure 3.4 Select Application Server

    44 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The logic shown in Figure 3.4 includes a determination of whether the Load Manager is to be used to select the Application Server. That logic is shown in Figure 3.5

    Figure 3.5 Application Server Selection Delegated to Load Manager

    Chapter 3: The Application Broker and the Load Manager 45

    Figure 3.6 shows the process flow for forwarding the request to the Application Server. Note: The Application Server will continue to execute the user’s request (assuming it has not failed) even if the Application Broker determines that the timeout has been reached. If this happens, the content generated by the Application Server and passed back to the Application Broker is lost.

    Figure 3.6 Send Request to Application Server

    46 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 3.7 shows the process flow for streaming the results back to the user’s browser. See Section 4.6 for details of the Application Server process flow.

    Figure 3.7 Stream Content Back to User’s Browser

    3.5.1 Load Manager Administrative Functions The Load Manager performs several administrative tasks in parallel to its function of selecting servers:

    ƒ

    It polls all the Application Servers to ensure that its table of their status is up-to-date. …

    It also responds to requests for its status (i.e., the LOADSTAT program) administrative request.

    ƒ

    If an Application Server has been in a BUSY state for a period that exceeds the time period specified in the maxrun option, it attempts to kill the Application Server process. For pool servers the nokill option must also be specified in order for the Load Manager to attempt to kill the server.

    ƒ

    If Application Servers have been idle for a period longer than the server timeout period, and the number of Application Servers is greater than the minimum number of Application Servers, the Load Manager sends a STOP command to those extra Application Servers.

    C h a p t e r

    4

    The Application Server 4.1 Introduction 47 4.2 SAS Application Server Executives 49 4.3 Application Server Sessions 50 4.3.1 ODS and Sessions 50 4.4 PROC APPSRV 51 4.4.1 TCP/IP Port 51 4.4.2 Assigning Libraries 51 4.4.3 Initiating and Terminating Requests 54 4.5 Application Server Process Flow 55 4.5.1 Application Server Functions Available to the Executing Program 60 4.5.2 Application Server Clean-Up Processing 60

    4.1 Introduction A component of the Application Dispatcher, the Application Server executes SAS programs that use SAS for data access and reporting. The Application Server not only executes the programs; it also provides a robust execution environment, which enables SAS programs to generate their results as Internet content. This content is then sent back to the user’s browser. The execution details for the Application Server are the same regardless of the type of service (e.g., launch, socket, or pool service) and regardless of where the Application Server is running. From the perspective of the Application Server, there are only two differences between these types of services:

    ƒ ƒ

    how and when the Application Server process begins execution when the Application Server stops processing requests

    48 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The programs that are executed by the Application Server are SAS programs. All of them have these characteristics:

    ƒ ƒ

    they can be executed in batch

    ƒ

    they generate output as Internet content

    they use macro variables (see Chapter 5 for a more detailed discussion of this) to specify parameters used to customize what the program does

    The Application Server itself is simply a constantly running SAS job that was invoked by the APPSRV procedure. Once PROC APPSRV begins execution, it performs the following tasks:

    ƒ ƒ ƒ ƒ

    listens on a TCP/IP socket for requests to run a program from the Application Broker runs the requested program sends the results back to the Application Broker waits for another request, and if one is received, cycles through this process again

    See Figure 4.1 for a sample SAS job that invokes PROC APPSRV. If the SAS/IntrNet Service Configuration Utility was used, it creates a default SAS job. The bold text was added to the job for the Application Server that supports the sample environment.

    Figure 4.1 Sample SAS Program to Invoke PROC APPSRV %let rc=%sysfunc(ntlog(INFORMATION,SAS/IntrNet Application Server started for the appdisp service.)); proc appsrv port=0 adminpw='********' unsafe='&";%''' &sysparm ; allocate file sample '!SASROOT\intrnet\sample'; allocate library samplib '!SASROOT\intrnet\sample' access=readonly; allocate library sampdat '!SASROOT\intrnet\sample' access=readonly; allocate library tmplib '\root\SAS\IntrNet\appdisp\temp'; allocate file logfile '\root\SAS\IntrNet\appdisp\logs\%a_%p.log'; proglibs sample samplib %ifcexist(sashelp.webeis) sashelp.webprog; proglibs sashelp.websdk1; adminlibs sashelp.webadmn; datalibs sampdat tmplib; log file=logfile display=all symbols=all; allocate library saspress '\root\IntrNetAppDev\sasserver\Programs'; adminlibs saspress.appserver_admin saspress.requires_security; proglibs saspress.chapter1 saspress.chapter2 saspress.chapter5 saspress.chapter6 saspress.chapter7 saspress.chapter8 saspress.chapter9 saspress.chapter10 saspress.chapter11 saspress.chapter12 saspress.chapter13 saspress.chapter14 saspress.chapter16 saspress.chapter17 saspress.chapter18 saspress.chapter19 saspress.chapter20 saspress.webservice saspress.chap12; allocate library sampdata '\root\IntrNetAppDev\sasserver\Data'; datalibs sampdata; request init=saspress.appserver_admin.init_program.source term=saspress.appserver_admin.term_program.source fromadr=("127.0.0.1"); session init=saspress.appserver_admin.session_init_program.source invsess=saspress.appserver_admin.expired_session.source; run; %let rc=%sysfunc(ntlog(INFORMATION,SAS/IntrNet Application Server stopped for the appdisp service.));

    Chapter 4: The Application Server 49

    An understanding of how the Application Server executes SAS programs will help you use the Application Server effectively. This chapter focuses on four key areas:

    ƒ ƒ ƒ ƒ

    the concept of a SAS Application Server Executive Application Server sessions the APPSRV procedure the process flow for user requests

    4.2 SAS Application Server Executives A SAS Executive is an independent runtime environment that can be started within a SAS process. SAS can create multiple Executives in a single SAS session (conceptually similar to having SAS spawn a new SAS session using an operating environment command or through SAS/CONNECT software). When SAS is started, one Executive is created. Most user programs run in this single Executive and then terminate. In the case of the Application Server, PROC APPSRV runs in this main Executive and the Application Server starts and stops other Executives to run the user programs. These are referred to as Request Executives. Since each Executive is completely independent, statements that define or set global macro variables, librefs, filenames, and options apply only to the Executive in which they are defined. This means, for example, that any such statements defined in the SAS program that executes the PROC APPSRV statement do not have any impact on user programs that are executed by PROC APPSRV. The reason is that they each run in their own Request Executive. Note: The example presented at the end of Chapter 1 used &sysdate and &systime as the current date time. In a typical SAS job those macro variables represent the date and time the SAS job started. Given that these are created in a Request Executive, the values correspond to the date and time the Request Executive was created. Thus they reflect the date and time that the current request was started and not the date and time the Application Server itself was started. Tip: Since user programs are executed in separate Executives, an ENDSAS statement in a program tells the Application Server to end the Executive for that user program; it does not end the Application Server. Thus, an ENDSAS statement can be used to terminate a user request. If an ENDSAS statement is used to terminate a request, the REQUEST TERM program, if specified, will not be run. Check with your server administrator to see if this is a problem. Examples that use this technique are discussed in Parts 4 and 5. Note: In Version 6 an ENDSAS statement in a user-requested program ended the execution of the Application Server.

    50 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    4.3 Application Server Sessions The Web is a stateless environment. That is, each request knows nothing about any prior request. This fact creates a simple environment in that each request is completely self-contained. However, quite often it is desirable and necessary to maintain certain information from one request to the next. This is known as maintaining state. The Application Dispatcher supports the creation and use of sessions as a way to share data and macro variables across separate requests without requiring each application program to provide a mechanism to store and retrieve such information. Sessions provide a way to maintain this state across multiple requests. A session is simply the saving of data and parameters from one program execution to the next. Sessions provide the ability to save library members (data sets and catalogs) and macro variables. The program explicitly specifies what to save and restore. The Application Server can support multiple independent sessions (for separate sets of user request streams). It is the responsibility of each program to perform the following tasks:

    ƒ ƒ ƒ ƒ

    create a session only when necessary and appropriate specify what information (e.g., data sets, catalogs, macro variables) is to be saved ensure that future requests reconnect to the session retrieve any needed information from a prior session

    A program can specify that future requests reconnect to a session by ensuring that any generated links or forms include the _SERVICE, _SERVER, _PORT, and _SESSIONID values for that session. These fields are discussed further in Chapter 5. The mechanics of using sessions are straightforward. Once the session has been created, a library named SAVE is created. By creating or copying data sets and catalogs to this library, the user program can trust that they will be there the next time a request is made that uses this session. All the global macro variables whose names begin with SAVE_ are saved by the Application Server at the completion of any request that uses sessions. At the beginning of any future request, they are restored. Chapter 10 discusses creating and reconnecting to sessions in more detail.

    4.3.1 ODS and Sessions Sessions are used in Application Dispatcher programs that use ODS where ODS creates multiple separate output files. Here are just two examples:

    ƒ ƒ

    an HTML page with both a tabular report and a graph a frame with a table of contents with links to each part of the output

    If the program has already created a session of its own, ODS will use it; otherwise ODS will create a lightweight session. Since a single Web request can return only one file, ODS will create the additional files and save them in SAS catalog entries. For an HTML page with an embedded graph, the graph (or graphs) will be created and saved as a graphic (e.g., GIF or JPEG) entry in a catalog. For a frame, the table of contents file and the body file are saved in HTML entries in such a catalog. These entries are returned to the user’s browser by a link that reconnects to the session to replay the entry. The replay tool is discussed in Parts 3, 4, and 5, and specifically in Chapter 8. Sessions created by ODS are lightweight sessions in that they do not have their own distinct SAVE library, nor are macro variables saved. They share a single APSWORK library instead of having separate SAVE libraries for each session.

    Chapter 4: The Application Server 51

    4.4 PROC APPSRV A SAS/IntrNet Application Server is started by a SAS program that invokes PROC APPSRV. The SAS/IntrNet Service Configuration Utility (inetcfg) creates a SAS program (appstart.sas) that includes a PROC APPSRV statement with reasonable defaults. The most commonly used options and statements of PROC APPSRV are discussed here as they are important components of the process flow. Selected options and statements are covered later. For complete details on PROC APPSRV statements and options, see the SAS/IntrNet documentation. The statements and options addressed here are those that relate to the following:

    ƒ ƒ ƒ

    communicating with the Application Broker via TCP/IP port assigning libraries initiating and terminating requests

    Figure 4.1 includes an example PROC APPSRV statement.

    4.4.1 TCP/IP Port The PORT= option of the PROC APPSRV statement specifies the TCP/IP socket that the Application Server uses to communicate with the Application Broker. The Application Server listens on this port, waiting for requests. Once a request is received and processed, the results are sent back to the Application Broker on the same port. The value for port can be specified in one of three ways:

    ƒ

    a numeric value other than zero is assumed to be the TCP/IP port number on which the server listens for requests

    ƒ

    an alphanumeric value is assumed to be a network service name. The name is searched in the system services file (for example, /etc/services) and translated to a port number

    ƒ

    a value of zero is used for launch and pool services, causing PROC APPSRV to choose an available port

    4.4.2 Assigning Libraries The following are four PROC APPSRV statements are used to specify the libraries and directories that are available to programs executed by the Application Server in response to user requests:

    ƒ ƒ ƒ ƒ

    ALLOCATE LIBRARY and ALLOCATE FILE DATALIBS PROGLIBS ADMINLIBS

    Note: SAS LIBNAME and FILENAME statements should not be used in the SAS program used to run PROC APPSRV if the intent is to make those files and libraries available to programs executed by the Application Server in response to user requests. As mentioned previously, each request is run by a separate Request Executive and the program that runs PROC APPSRV is its own Executive. Since each Executive is independent, library and file definitions are not shared between Executives. See Figure 4.1 for examples of each of these statements.

    52 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    4.4.2.1 ALLOCATE The ALLOCATE statement takes two forms:

    ƒ

    ALLOCATE LIBRARY libref 'SAS-data-library' ; This form associates a SAS libref with a SAS data library.

    ƒ

    ALLOCATE FILE fileref 'directory-or-PDS-path' ; This form associates a SAS fileref with an external file or directory (including PDS’ on MVS)

    The syntax and options for both of these statements are identical to the syntax for the SAS LIBNAME and FILENAME statements. The key point is that the ALLOCATE statement provides the information needed by PROC APPSRV to issue LIBNAME and FILENAME statements that are associated with the Request Executives. However, the SAS librefs and filerefs defined in an ALLOCATE statement are not automatically made available to the Request Executives, so they must also be listed in a DATALIBS, PROGLIBS, or ADMINLIBS statement.

    4.4.2.2 DATALIBS SAS librefs and filerefs that are listed in a DATALIBS statement are made available to all programs that are run by the Application Server. Here is the syntax: DATALIBS libref-1 | ; The librefs and filerefs must correspond to librefs and filerefs defined in an ALLOCATE statement or that are defined externally to SAS (e.g., in the JCL stream that starts the Application Server on MVS). Best Practice: Since including a libref or fileref in a DATALIBS statement specifies that the libref/fileref is assigned and accessible to all programs that run on the server, use DATALIBS statements only for globally accessible data repositories that contain non-sensitive data. Keep private or application-specific data in its own library and assign it by using a LIBNAME statement. Techniques to do this are discussed in Part 3 and used in the examples in Part 5.

    Librefs and filerefs listed in a DATALIBS statement cannot be cleared by programs that are executed by the Application Server (that is, by PROC APPSRV). In other words, if the libref MYDATA is defined using a DATALIBS statement, that libref is available to all programs run by the Application Server and cannot be cleared by a LIBNAME statement like this: libname MYDATA clear;

    4.4.2.3 PROGLIBS The PROGLIBS statement defines which libraries, catalogs, and filerefs contain the SAS programs that can be run by a request to an Application Server. Here is the syntax: PROGLIBS libref-1 | libref-1.catalog-1 | fileref-1 ;

    Chapter 4: The Application Server 53

    Just as for the DATALIBS statement, the librefs and filerefs defined in a PROGLIBS statement must correspond to librefs and filerefs defined in an ALLOCATE statement or that are defined externally to SAS (e.g., in the JCL stream that starts the Application Server on MVS). They cannot be cleared by programs that are executed by the Application Server. SAS librefs listed in PROGLIBS statements should contain catalogs. In turn, these catalogs should contain SAS programs that can be executed by the Application Server. The SAS programs can be SOURCE, MACRO, or SCL catalog entries. The same librefs and filerefs can be listed in both DATALIBS and PROGLIBS statements. However, it is typically not a good idea to put application-specific code in a library that is available to all requests run by an Application Server. SAS filerefs listed in a PROGLIBS statement should point to a directory or partitioned data set (PDS) that contains SAS code in individual .sas files (a PDS is assumed to contain SAS source code in individual members). Note that if a libref is specified in the PROGLIBS statement, then any SCL, SOURCE, or MACRO entry in any catalog in that library can be run by the Application Server in response to a user request. Instead of specifying the libref, you can specify the catalogs in that library as libref.catalog. Using this form causes the libref to be defined, but restricts how they are run: only entries in the listed catalogs can be run by the Application Server in response to a user request.

    Best Practice: For code contained in SAS catalogs, if just the libref is specified in the ADMINLIBS or PROGLIBS statements, then any entry in any catalog in the library can be specified as the program to run. Using the libref.catalog syntax instead of just specifying the libref is a suggested Best Practice and enables a developer to include other catalogs in the library that contain utility code (used by an application) that should not be directly executable by the Application Server in response to a user request. Utility code that is used by multiple applications should be stored in a separate library. That code can be made available to all programs run by the Application Server by including the libref or fileref in a DATALIBS statement.

    When a request is received by the Application Server, it checks the PROGLIBS list for a match on the first one or two levels in the program name that is supplied in the special request variable _PROGRAM. If a match is found, then that program can be executed.

    4.4.2.4 ADMINLIBS The ADMINLIBS statement is similar (in syntax Best Practice: Since the Application Server has and function) to the PROGLIBS statement except several built-in programs (such as STOP) that that it specifies one or more librefs, catalog, and should not be available to all users, using the filerefs that contain password-protected ADMINPW variable is recommended. Otherwise any user can stop an Application Server from any administrator programs. They are used in Web browser. conjunction with the ADMINPW option in the PROC APPSRV statement. If ADMINPW is specified in the PROC APPSRV statement (see Figure 4.1 for an example), the user must supply the password in the request (using the _ADMINPW variable) in order to run a program contained in one of the librefs, libref.catalogs, or filerefs listed in an ADMINLIB statement.

    54 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    4.4.3 Initiating and Terminating Requests The REQUEST and SESSION statements define a number of parameters and actions for PROC APPSRV to use as it executes a request.

    Best Practice: Specifying a program to run at the beginning of any program (i.e., REQUEST INIT=) is often a good idea. This initial program can be used to provide default options and parameters that apply to all programs; for example, graphics options, sasautos, and others. Numerous examples are presented in Parts 3, 4, and 5 that make use of this Best Practice.

    The INIT option specifies the name of a program whose purpose is similar to what an autoexec.sas file does. Since user The location for the REQUEST (and SESSION) INIT and requests are executed in separate Request TERM programs should be listed in an ADMINLIBS Executives, statements in the standard statement, not a PROGLIBS statement. autoexec.sas file are not applied to the Request Executive. Instead, this option is used to provide the name of a program that is run at the following times:

    ƒ

    at the beginning of any request to the Application Server to run a program (i.e., REQUEST INIT=)

    ƒ

    when a session is created by a program that is being executed by the Application Server in response to a user request (i.e., SESSION INIT=) Note: Lightweight sessions created by ODS do not cause the SESSION INIT= program to be run.

    By default, no program is run before beginning a request or when a session is created. The program names referenced in the INIT option must be in the same format as the _PROGRAM variable (see Chapter 5 for a discussion of the _PROGRAM variable). The libraries, catalogs, or directories that contain these programs must be defined in a PROGLIBS or ADMINLIBS statement. The TERM option specifies the name of a program that PROC APPSRV runs:

    ƒ

    at the completion of any request to the Application Server to run a program (i.e., REQUEST TERM=)

    ƒ

    when a session is destroyed by a program that is being executed by the Application Server in response to a user request (i.e., SESSION TERM=)

    ƒ

    when a session expires Note: Sessions created by ODS do run the SESSION TERM= program when the session is terminated.

    Chapter 4: The Application Server 55

    The TERM option is comparable to the INIT option in several ways:

    ƒ ƒ ƒ

    By default, no program is run. The program name must be in the same format as the _PROGRAM variable. The libraries, catalogs, or directory must be defined in a PROGLIBS or ADMINLIBS statement.

    The INVSESS option enables more control over what the Application Server does if a request is to reconnect to a previously created session that no longer exists. It provides the name of a program that is to be run in the place of the requested program. The name of the program that was requested by the user is made available to this program as the value of the _USERPROGRAM macro variable.

    Best Practice: Specifying a program to run when an attempt to reconnect to a session fails (i.e., SESSION INVSESS=) is often a good idea. This program can display an informative response when a reconnect fails, generate HTML to redirect the user to an application login screen, explain how to restart the application, or provide a friendlier error message. Examples are presented in Chapter 10 and later chapters using sessions that make use of the INVSESS option.

    The INVSESS option is comparable to the INIT option in several ways:

    ƒ ƒ ƒ

    By default, no program is run. The program name must be in the same format as the _PROGRAM variable. The libraries, catalogs, or directory must be defined in a PROGLIBS statement.

    4.5 Application Server Process Flow The process flow for the Application Server begins with the execution of a SAS program that invokes PROC APPSRV. Typically this program is named appstart.sas and has been customized from a default appstart.sas file built by the SAS/IntrNet Service Configuration Utility (inetcfg) (see Figure 4.1 for a sample appstart.sas file). Running this appstart.sas file starts a SAS Executive that runs PROC APPSRV. The appstart.sas file can be run in a number of ways, e.g.:

    ƒ

    For pool services, it is launched by either the Load Manager or the SAS spawner (in response to a request from the Load Manager).

    ƒ

    For socket services it can be started in several ways:

    ƒ

    …

    by the operating environment when the machine boots (e.g., as a Windows service)

    …

    by an administrator-defined batch process

    …

    interactively (i.e., a user starts it as a batch process)

    …

    in an interactive SAS session (this is typically be limited to situations where advanced debugging is required. See Chapter 11)

    For launch services, it is launched by the Application Broker, which starts the Application Server on the Web server.

    56 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The syntax of the statements in the appstart.sas file is checked as is the case for any submitted SAS program (e.g., mismatched quotes, invalid statements, etc.). There are several additional conditions that can cause the Application Server to not start. Conditions such as the following are all noted in the log:

    ƒ

    One or more of the programs referenced in the INIT, TERM, or INVSESS options of the REQUEST or SESSION statements do not exist (or the libref libref.catalog were not listed in a PROGLIBS or ADMINLIBS statement).

    ƒ

    The specified value for PORT is missing, invalid, or unavailable.

    Note: If a directory, library, or catalog listed in an ALLOCATE, DATALIBS, or PROGLIBS statement statement does not exist, the Application Server will still start execution. However, a note is displayed in the log that the file was unavailable. If the library, directory, or catalog exists when a Request Executive begins, they are available to the executing program. A functional overview of the processing logic once the Application Server begins execution is shown in Figures 4.2, 4.3, and 4.4. Note: The outline of steps described here is very detailed. You are encouraged to briefly review the outline to get a general sense of the processing flow. As you work through developing applications, consult the steps described here as needed. Figure 4.2 shows the set-up processing done by the Application Server to prepare to run the requested program. Upon successful completion of this set-up processing, the Application Server will run the program using a Request Executive as illustrated in Figure 4.3. After the programming has completed execution, the Application Server performs termination processing as illustrated in Figure 4.4.

    Chapter 4: The Application Server 57

    Figure 4.2 Set-Up to Run Program

    Footnote: The status of an Application Server that is associated with more than one service (which have different Load Managers) can cause the updates sent from the Application Server to Load Manager to get out of synch.

    58 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 4.3 Process in Request Executive

    Footnote: _GRPHOUT was needed in an earlier version of the Application Server where it was not possible to write both text (e.g., HTML) and binary (e.g., graphics) content to the same fileref. That restriction was removed in Version 7 and _WEBOUT can now be used for both text and binary output.

    Chapter 4: The Application Server 59

    Figure 4.4 Termination Processing

    60 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    4.5.1 Application Server Functions Available to the Executing Program While the requested program is executing, there are a number of Application Server functions or services that the program can invoke by using one of the Application Server functions. Consult the SAS/IntrNet documentation for a list of functions and the details of how they work. The most commonly used functions and their actions are the following:

    ƒ

    APPSRV_SESSION can be used to create a new session or flag the existing session for deletion. When this function is used to create a session the following actions occur: …

    The macro variable _SESSIONID is assigned and given a value to uniquely identify the session.

    …

    The macro variables _REPLAY and _TMPCAT (see Section 5.4) to reflect the creation of a user session are created or updated.

    …

    A temporary SAVE library is defined. This library exists for the length of the session.

    …

    The SESSION INIT program, if specified, is run.

    ƒ

    APPSRV_HEADER can be used to explicitly specify the content-type header that should be sent at the beginning of the generated Internet content.

    ƒ

    APPSRV_UNSAFE is used to get the values of parameters before any special characters were stripped out. The values are from the internal table created as shown in Figure 4.2.

    ƒ

    APPSRVSET can be used to set various parameters that control the characteristics of the request or the session. For example, the maximum time to allow a program to run can be set or updated. Similarly, the time that a session is to remain active between reconnect requests can be set or updated.

    ƒ

    APPSRVGETN (or C) can be used to get various numeric (N) or character (C) parameters (e.g., this function can be used to get the name of the REQUEST TERM program).

    4.5.2 Application Server Clean-Up Processing The Application Server has a number of clean-up or bookkeeping tasks that it does in parallel with what is described above for the processing of requests. Here is a functional overview of bookkeeping or clean-up logic (not necessary in this order):

    ƒ

    If a program has been running longer than Best Practice: If the majority of requests to be the maximum allowed time, the Application processed by an Application Server are long and Server terminates it (as described above). consistent in their length, use the REQUEST The default time a program is allowed to run TIMEOUT option. Otherwise, use the APPSRVSET function in long-running jobs to is 5 minutes. This value can be changed on update the maximum time. This limits the the REQUEST statement using the longer times to just those requests that need it TIMEOUT option. Values set this way and allows for the time to be customized to the apply to all programs run by the Application specific request. Server. An individual program can update the maximum allowed time using the APPSRVSET function.

    Chapter 4: The Application Server 61

    ƒ

    Sessions that were last accessed earlier than Best Practice: If the majority of sessions to be the current time minus the session timeout processed by an Application Server are are terminated. In addition, sessions that consistent in their length, use the SESSION were flagged for deletion by a request TIMEOUT option. Otherwise, use the (which has completed its processing) are APPSRVSET function to update the maximum also terminated. In both cases the SESSION time. This limits the longer times to just those sessions that need it and allows for the time to TERM program, if specified, is run. The be customized to the specific session. SAVE library, if created for the session, is deleted and is no longer available on disk. The default time a session is allowed to remain active is 15 minutes. This value can be changed in the SESSION statement using the TIMEOUT option. Values set this way apply to all programs run by the Application Server. An individual program can update the maximum allowed time using the APPSRVSET function. This TIMEOUT value can be updated by the program that created the session as well as by any program that is run in response to a request that uses the session.

    ƒ

    If a request with _program=STOP was made (see Figure 4.2), the Application Server (i.e., PROC APPSRV) stops accepting new requests. Once any active sessions have timed out, control is returned to the next step (if any) in the appstart.sas program. Typically, there is no next step after the PROC APPSRV and the SAS session ends.

    ƒ

    Responses to the Load Manager queries about its status: The Application Server informs the Load Manager of its current status (either Busy or Idle).

    62 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    5

    Communicating with the Application Dispatcher 5.1 Introduction 63 5.2 Name/Value Pairs Defined by the User’s Request 64 5.2.1 _program: The SAS Program to Execute 64 5.2.2 _service: The Application Server to Process the Request 65 5.2.3 _debug: The Output to Display 65 5.2.4 Program-Specific HTML Name/Value Pairs 68 5.3 Name/Value Pairs Defined by the Application Broker 72 5.3.1 Application Broker Administrative Fields 72 5.3.2 Environment Variables 73 5.3.3 Installation Dependent Fields 74 5.4 Name/Value Pairs Defined by the Application Server 76

    5.1 Introduction When the Application Dispatcher executes a SAS program and generates Internet content to be returned to the user’s browser, it needs to know the values for a number of parameters. Likewise, the program typically has a number of parameters whose values need to be passed to it. When values are defined in an HTML page they are referred to as HTML name/value pairs. As mentioned in Chapters 3 and 4, this construct is used by the Application Dispatcher for the information that needs to be passed from the user’s browser to the Application Broker and then on to the Application Server and eventually to the requested program.

    64 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    There are three points in the process flow where these name/value pairs are defined and made available to the program as macro variables:

    ƒ

    by the user’s request (e.g., an HTML page or form or a URL such as a saved favorite or link on an HTML page)

    ƒ ƒ

    by the Application Broker by the Application Server

    5.2 Name/Value Pairs Defined by the User’s Request The request to the Application Dispatcher must define the name/value pairs necessary for the program to be executed. Note that the Application Dispatcher has three required name/value pairs:

    ƒ ƒ

    _program which identifies the specific SAS program to be executed

    ƒ

    _debug which is used to define what output is displayed

    _service which identifies the SAS Application Server or Servers to be used to process the user’s request

    Note: Default values can be provided for _service and _debug in the Application Broker configuration file. These three are discussed first, followed by a discussion of defining program-specific HTML name/value pairs.

    5.2.1 _program: The SAS Program to Execute The _program name/value pair specifies the name of the program to be executed. The program can be any of the following:

    ƒ ƒ ƒ ƒ

    an external file containing SAS source code (i.e., a .sas file) a SAS source entry containing SAS source code (i.e., a .source catalog entry) the name of a compiled SAS macro (i.e., a .macro catalog entry) a SAS SCL entry containing a compiled SCL program (i.e., a .scl catalog entry)

    Note: The SAS Process Server can only directly execute an external file containing SAS code. The other three entries can be executed if they are invoked from such a file of SAS code. For the three types that are SAS catalog entries, a four-level name must be provided. The first node is the libref (as defined in the ALLOCATE LIBRARY statement). The second node is the catalog; the third node is the entry name; and the fourth node is the entry type (e.g., SOURCE, MACRO, or SCL). For example, a value of saspress.chapter1.pareto.source for _program runs the program that generated the Pareto chart shown in Chapter 1. For an external file of SAS code, a three-level name must be provided. The first node in the name is the directory (as defined in the ALLOCATE FILE statement). The second node is the actual filename as it is defined to the operating environment (including the case if the operating environment is case sensitive); and the third node is sas. For example, a value of

    Chapter 5: Communicating with the Application Dispatcher 65

    sample.webhello.sas for _program will run one of the sample programs provided as part of the installation of the SAS/IntrNet Application Dispatcher.

    Tip: The source code for a macro entry can be stored in the same catalog as the compiled macro. If it is stored in the same catalog, the source entry can be executed by a user when the user specifies the four-level name as the value of _program. If such an entry contained only the macro definition, it would not generate any output when executed by the Application Dispatcher. Instead, the program would simply compile the macro. Depending on the environment, it is a Best Practice either to always or never store the source in the same catalog. Note that for the Xplore sample application, which is distributed with SAS/IntrNet, the macro source is included in the catalog.

    5.2.2 _service: The Application Server to Process the Request As discussed in Chapter 3, the value of _service specifies which Application Server (or pool of Application Servers) to use for the request. The value must correspond to a service defined in the Application Best Practice: If a program generates a link or form, the value of _service should Broker configuration file. This value defines both what be defined using the macro variable kind of service to use for the request (i.e., socket, launch reference &_service. It should not be or pool) and which particular SAS Application Server hardcoded in the program. should be used to process the request (i.e., the server and port that the Application Server is listening on). A default value for _service can be defined in the Application Broker configuration file. That value is used by the Application Broker whenever the user request does not explicitly define which service to use.

    Best Practice: Don’t define a default value for _service except in limited situations. Specifying a default value, while convenient, enables requests to be sent to those servers without requiring the requestor to know the name of the services at your site.

    When a program has implemented sessions, the Application Server to process the request is more precisely defined. If the service contains multiple Application Servers, then it is necessary to define exactly which Application Server and exactly which port (as well as an identifier of the created SAS session). Those values are defined using the special fields _server, _port, and _sessionid. When sessions are being used, these fields should be used in addition to _service. Note: Programs that use sessions that generate a link or form to reconnect to the session must use the macro variables _server, _port, and _sessionid. When generating a hyperlink as opposed to form fields, the special variable _thissession can also be used to define the name/value pairs for _server, _port and _sessionid as well as _url. Other special fields created by the Application Broker and Application Server are discussed in Sections 5.3 and 5.4.

    5.2.3 _debug: The Output to Display The value of _debug defines what output is returned to the user’s browser as well as how the output is displayed. Values are powers of two define distinct values each of which turns on a specific option. In order to turn on multiple options, you need to add values together to get the value that turns on all the desired options. The values can be categorized. Selected details are provided in Table 5.1 for the more commonly used values. See the SAS documentation for details on the list of all the values along with the options they control.

    66 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Table 5.1 Selected Values of the _debug Parameter To control What output to display and how to display it

    Enter the value 0

    1

    Echoes the field values passed from the Application Broker to the Application Server.

    8

    Tells the Application Dispatcher not to execute the requested program. This can be useful when it is necessary to see all the fields being passed to the Application Server by the Application Broker (i.e., a value of 1), but when you do not want to run the program. In order to do this, use a value of 9 (equal to 8+1).

    16

    Displays the output in hexadecimal. Most typically used for debugging problems with the HTTP header or non-text Internet content such as graphics, PDF, comma-separated values, etc.

    128 The display of the SAS Powered Logo and Execution Details

    Advanced features

    Information about the environment defined for the Application Broker

    The result Displays only the output generated and written to the fileref _webout (or _grphout) by the requested program.

    Returns the SAS log for the requested program as HTML.

    2

    Displays the Application Broker version number and elapsed time after each run. The Powered by SAS logo is also displayed if the location of the logo is defined in the Application Broker configuration file.

    32

    Displays the Powered by SAS logo without Application Broker version or elapsed time information if the location of the logo is defined in the Application Broker configuration file.

    256, 512, or 2048

    Traces socket connection attempts. These are advanced options and are rarely used (they are typically used at the suggestion of SAS Technical Support).

    1024, 4096, or 8192

    Performs debugging for earlier versions. Used either prior to Version 8 or for debugging. Values 4096 and 8192 are specific to Launch Services. They are most commonly used at the advice of SAS Technical Support.

    4

    Lists definitions of all services defined in the Application Broker configuration file. The specified program is not run.

    16384

    Displays Web server environment variables that have nonblank values and that can be made available for export.

    Chapter 5: Communicating with the Application Dispatcher 67

    Here are some typical scenarios and the appropriate values of _debug to use:

    ƒ

    Use a value of 0 when no extra content is to be generated: the only content to be displayed is the output generated by the program.

    ƒ

    If HTML is being generated to be followed by only the SAS Powered logo, use a value of 32. If the time and broker version are also desired, use a value of 2.

    ƒ

    To see what values are passed to the program, followed by the output, the SAS log, the SAS Powered logo, the time, and the Application Broker version, use a value of 1+128+2 = 131. Note that this value can be used even with non-HTML output (e.g., PDF or graphics). However, the non-HTML output is not displayed in a readable format (since the inclusion of 1 in the value causes a text or HTML content header to be sent to the user’s browser).

    ƒ

    To see what values are being passed to the program and then have the output displayed as hexadecimal, use a value of 1+16 = 17. ®

    Note: The Application Broker that shipped with SAS 9 allows for mnemonic values for selected _debug values: 1: 2: 4: 16: 128: 1024: 2048: 16384:

    FIELDS TIME SERVICES DUMP LOG ECHO TRACE ENV

    To turn multiple options on, each mnemonic is included, separated by commas. The value sent to the Application Server is the corresponding numeric value. The _debug parameter gives a great deal of control to Best Practice: For each service, determine the requestor to get additional information. Often the exactly what values are to be allowed and developer or administrators for such applications want specify the sum of those values as the to control users’ access to get such output (e.g., ServiceDebugMask. protecting the SAS log from being seen by others). The DebugMask parameter (defined in the Application Broker configuration file using the DebugMask or ServiceDebugMask directive) allows the developer to control which values of _debug are allowed. The DebugMask parameter applies to all the services defined in the Application Broker configuration file. There is a comparable parameter (ServiceDebugMask) that can be used to define a service-specific value. The DebugMask value is additive, just like the value of _debug is. For example, if the only values to be allowed for _debug are 0, 2, or 32, then setting DebugMask (or ServiceDebugMask) to 34 (=0+2+32) accomplishes that.

    68 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    5.2.4 Program-Specific HTML Name/Value Pairs When the requested program begins execution, macro variables are used to make name/value pairs available to the SAS program that is to be run. The macro variable names correspond to the HTML names and the values of macro Best Practice: For SCL entries, the name/value pairs are variables are the HTML values. The also available in an SCL list passed into the program. Application Dispatcher automatically While it can be easier to use the SCL list functions, the creates the macro variables corresponding to same name/value pairs are available as macro variables. the HTML name/value pairs. As a result, the If the SCL program accesses the macro variables instead of the SCL list, the program can be more easily used in SAS programmer need not be concerned other environments (e.g., the SAS Stored Process with obtaining the values specified by the Server). In addition, macro variable values can be overuser on the HTML page. However, HTML ridden and augmented in the REQUEST INIT program. names must correspond to valid SAS names since non-valid SAS names are rejected by the Application Broker. The Application Server provides a facility to prevent certain special characters (e.g., single and double quotes, semicolons, and the percent sign and ampersand, which are used as macro triggers) from being included in a value. When specified special characters are found in a value, the Application Server strips those characters out of the value. The UNSAFE option of PROC APPSRV statement is used to specify what those characters are. There are several cases where it is not possible to directly map the values from an HTML page to a single macro variable. Here are two:

    ƒ

    the same HTML name being used multiple times in the HTML form or URL

    ƒ

    long text values

    Best Practice: Do not remove any of the default special characters that the Application Server strips out. Doing so could make programs less stable and could enable a sophisticated hacker to do damage. Instead, for those parameters where such characters are possible, use the APPSRV_UNSAFE function to get the original value and use caution when referencing or using the values. See Chapter 6 for more information.

    5.2.4.1 Multiple Values for a Single HTML Name HTML allows multiple values to be passed using a single HTML name; however, SAS macro variables do not allow multiple values. Macro variables can contain a list of values which a SAS program can parse to recreate the distinct values. However, the Application Dispatcher uses a different method to handle multiple values. The Application Broker appends a suffix to the original name creating multiple macro variables when it receives multiple values for a single name. Consider an example where the HTML has five check box fields, all called var: Weight

    Chapter 5: Communicating with the Application Dispatcher 69

    If all five values are checked, the Application Dispatcher defines the following macro variables and values (note that you cannot assume that the values are passed in the indicated order, e.g., the third check box could be the last value passed):

    ƒ ƒ ƒ ƒ ƒ ƒ ƒ

    VAR0: 5 VAR: name VAR1: name VAR2: sex VAR3: age VAR4: height

    Best Practice: Programs that have check box fields as input name/value pairs should define those using a %GLOBAL statement in order to ensure that the variable exists with a null value if the check box is not selected. Note that the macro variable is global only to the Request Executive for this particular execution of the program.

    VAR5: weight

    The macro variable VAR0, referred to as the Suffix 0 variable, is created to contain the total number of values received. Note that both VAR and VAR1 (referred to as the Suffix 1 variable) have the same value. In a case where a check box is not selected, the browser does not send a blank value for the name and the SAS program must be prepared for such a scenario. In a similar situation, if only the third check box is selected, the Application Dispatcher recognizes only a single value:

    ƒ

    VAR: age.

    The Application Dispatcher does not know that there could have been multiple values, so it does not create the Suffix 0 (the number of name/value pairs) variable nor does it create the Suffix 1 variable. Thus the program needs to check if the Suffix 0 variable exists to determine if multiple values were passed in. A simple way to check this is to include the Suffix 0 variable in a %GLOBAL statement and then check for a null value. For most situations, it is recommended that the HTML page not use a name more than once. However, there are at least two situations where this is not possible. Note: There are cases where using a name more than once is desired, as in the example given where the check boxes correspond to a variable list. The macro presented in Section 5.2.4.2 provides an example where this is done. First are SELECT tags (i.e., list boxes) that allow multiple selections. The selected values are passed as multiple values for a single name. Thus, if a list box (e.g., lbox) allows for multiple selections and only one selection is made, the value is passed with a name of lbox. If three selections are made, the number of selections is passed as the value of lbox0 and the three values (in no predictable order) are passed as lbox1/lbox, lbox2 and lbox3. Second, are fields where the length of the value can be long, e.g., TEXTAREA tags. Only one value is passed from the browser. The Application Dispatcher treats these as multiple name/value pairs if the value is longer than a specified width. In such cases, multiple macro variables are th created by splitting the value at the first blank before the n character, or at character n if there are no preceding blanks. Note: Application Dispatcher defined fields have names that begin with an underscore (_). Such fields are not split and the entire value is passed as a single name/value pair. By prefixing a program-specific name with an underscore, you ensure that the name/value pair is always passed as a single name/value pair. Care must be taken, however, to choose names that are not already used by the Application Dispatcher (or that may be used in future releases).

    70 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Best Practice: Consider using two underscores in program-specific names that should not be split. This avoids conflicts with both current and future Application Dispatcher special names.

    The value for n can be specified in a number of ways (the criteria are used in the order listed below and the first one defined is used):

    ƒ ƒ ƒ ƒ

    a name/value pair is included using a name of _fldwdth in the request the ServiceFieldWidth parameter defined in the Application Broker configuration file the FieldWidth parameter defined in the Application Broker configuration file th

    the string is split at the 32K character (the Version 6 Application Broker splits the string th at the 200 character because character variables are limited to 200 characters in Version 6 of SAS)

    The number of values passed is stored in the Suffix 0 variable, a described above. The original HTML names must be chosen with care when new macro variables are created as a result of multiple values. Here are some tips for choosing names.

    ƒ

    ensure that the names, once suffixes have been added, are not longer than 32 (in Version 6 this limit is 8).

    ƒ

    avoid names that end in numbers. For example, the names LBOX and LBOX1 should not be used in the same HTML form or link because of unavoidable confusion if new numbered macro variables were created as a result of multiple values.

    5.2.4.2 Macro Tool to Handle Multiple Names The need to handle name/value pairs where there may be multiple values is a common occurrence. A good way to handle such values is to use a macro tool, instead of including the code in each program. Such a macro, multipleNames, is provided as part of the sample environment. This macro can serve as the basis for such a tool. It handles multiple names by doing several things:

    ƒ

    Best Practice: Define and use an autocall macro that is available to the Application Server that handles multiple name/value pairs. Define the autocall location using the sasautos option in the program specified in the REQUEST INIT= parameter.

    ensuring that the Suffix 0 variable always exists with the following values: …

    a value of 0 if the field is not defined (e.g., multiple check boxes with the same name and none selected)

    …

    a value of 1 if only one value is defined

    ƒ

    ensuring that the Suffix 1 variable always exists and has the same value as the field without the suffix (allowing the program to always refer to the Suffix 1 variable)

    ƒ

    allowing for an option to collapse all the values into a single space-delimited field (e.g., if multiple name/value pairs are used for a variable list)

    It can be called repeatedly in the same program if, for example, some names are to be collapsed to a single name and some are not. The options for the macro include the following:

    ƒ ƒ

    the list of parameter names that can potentially have multiple values. a parameter (singleList) that specifies whether the multiple values are to be collapsed into a single space-delimited field. A non-blank value turns this option on.

    Chapter 5: Communicating with the Application Dispatcher 71

    To see an example of the features of the multipleNames macro, go to the sample environment and select Chapter 5. Then select Demo multipleNames Macro. Try the following combinations (plus others) to determine how to use the maro.

    ƒ

    Select no values and note how the macro creates macro variables for both the original name and the Suffix 1 name. That allows the program code to refer to either the original name or the Suffix 1 name. Note how the Suffix 0 variable has a value of 0. When used in the upper bound of a DATA step or macro DO loop, the loop is never be entered.

    ƒ

    Select only one value for one or more of the fields and note how the values are set. Again the original name and the Suffix 1 name both exist and have the same value, which means that the Suffix 1 value exists.

    ƒ ƒ

    Select more than one value. When selecting more than one value, also check the check box to Combine into a single field and note that the variables with the suffixes have each individual value, while the original name, as a result of the macro execution, includes them all as a spacedelimited list. Also use this option with the radio box option to print the SASHELP.CLASS data set.

    Figure 5.1 shows the output produced when three check boxes (used to specify the variables to include) are selected from the above link.

    Figure 5.1 Output Demonstrating the Use of the multipleNames Macro

    72 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Consider the example above where a check box was used for each variable in a data set. If the macro is applied to that variable, your program can do the following:

    ƒ

    ƒ

    If it is necessary to loop over the names, then either a macro or DATA step DO loop could be used with the Suffix 0 variable as the upper bound. The looping logic correctly handles the following cases: …

    no values (the loop is never be entered)

    …

    one value since the Suffix 1 variable has the same value as the original variable name

    …

    multiple values

    Alternatively, if a variable list is needed, then the singleList parameter can be used. In this case, the list is the values of the original name. If looping over the list of variables is still required, those values are also available in the suffix variables. The sample environment provides an option to see the use of the macro to create the variable list in a single macro variable.

    5.3 Name/Value Pairs Defined by the Application Broker The Application Broker can define fields that should be passed to the Application Server as if they were provided as HTML name/value fields. There are three different classes of such fields:

    ƒ ƒ ƒ

    Application Broker administrative fields environment variables installation-dependent fields

    Note: _server, the server name as defined in the Application Broker configuration file, and _port, the port the current Application Server is listening on, are also passed as name/value pairs to the Application Server.

    5.3.1 Application Broker Administrative Fields The following fields are created by the Application Broker (defined in the Application Broker configuration file), and the values can be customized by editing the Application Broker configuration file:

    ƒ

    _ADMAIL contains the e-mail address of an administrator. A global value (defined in the AdministratorMail directive) can be provided as well as service-specific values (defined in the ServiceAdminMail directive). The global value is used only when no servicespecific value is defined. One example use of this value is when the requested program needs to generate a MAILTO tag.

    ƒ

    _ADMIN corresponds with _ADMAIL and contains the name of the administrator (set in the Administrator and ServiceAdmin statements). Note: If the statements for both the global and service-specific values are commented out, the name/value pairs are not defined.

    Chapter 5: Communicating with the Application Dispatcher 73

    ƒ

    _URL Best Practice: Use the macro variable reference the URL for the Application Broker &_url in programs that need to generate links or component of the Application Dispatcher. forms. The value should never be hardcoded. The Application Broker determines this Using the macro variable reference minimizes value dynamically. The SelfURL directive the changes needed if the location changes. in the configuration file provides a facility to define an alternative value. Typically the value determined by the Application Broker is correct and the SelfURL directive is not needed.

    ƒ

    _VERSION an internal version number for the Application Broker component of the Application Dispatcher. It cannot be updated locally and is typically not used.

    5.3.2 Environment Variables Environment variables can also be made available to Application Dispatcher programs. The values of these variables (also known as Web server environment variables) can be made available using an Export directive in the Application Broker configuration file. Here is the syntax: Export environment-variable-name sas-macro-variable-name The Application Broker configuration file contains a number of Export statements. However many of them are commented out. To see this example, go to the sample environment and select Chapter 5. Then select Show Available Environment Variables, which shows all the environment variables accessible by the Application Broker (i.e., _debug=16384), and select Show Application Broker Fields, which shows the list of values being exported by the Application Broker (i.e., _debug=1). These fields can be used to address common requirements. Two fields that are often exported are REMOTE_USER and SERVER_NAME. The default macro variable names for these, as defined in the Application Broker configuration file, are _RMTUSER and _SRVNAME. A Best Practice was suggested in Section 5.3.1: always use the macro variable reference &_url when a program needs to build a link or a form that executes another Application Dispatcher program. Quite often, it is necessary when building such links to have the complete path, starting with the protocol (e.g., http). The information needed to do that is available from the following server variables:

    ƒ

    SERVER_PROTOCOL (e.g., http or https)

    ƒ ƒ

    SERVER_PORT (typically 80) SERVER_NAME

    Best Practice: Define and use an autocall macro that is available to the Application Server that builds the complete name for the Broker, including the protocol, port, and server name.

    The macro code provided with the sample environment, completeBrokerName, builds such a string using the exported values for the above fields. The macro can be demonstrated by a program that contains the following: %put %completeBrokerName;

    To see the results of the %PUT statement, go to the sample environment and select Chapter 5. Then select Demo completeBrokerName Macro, which includes _debug=128 to cause the results (e.g., the SAS log) to be returned to the browser. Figure 5.2 shows the output produced by this macro.

    74 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 5.2 SAS Log Output of Sample completeBrokerName Macro Call

    5.3.3 Installation Dependent Fields In addition to the values of Web server environment fields, there are quite often other values that programs need to reference. Some examples might include the following:

    ƒ ƒ ƒ

    location of a style sheet that should be used by all generated output directory path for utility graphics (e.g., arrows, folders, buttons) contact person

    Instead of hardcoding these in programs, specify them in the Application Broker configuration file using either the Set or ServiceSet directive. Here is the syntax: Set name value ServiceSet name value Tip: Such fields can also be defined as macro variables using %LET statements in the Application Server REQUEST INIT program. This might be appropriate for values that are specific to the Application Server (as opposed to the Web server). If a Set directive is used, the name/value pair is generated by the Application Broker and passed to all services. If a ServiceSet directive is used, the name/value pair is generated by the Application Broker and passed to only the specific service. The default installation of SAS/IntrNet includes several such parameters that are used by SAS/IntrNet samples and applications: Best Practice: Use SET statements for global type ƒ _mrvimg: the directory that parameters (e.g., the corporate style sheet) and contains the images used by the ServiceSet statements for parameters that are specific to a service (e.g., a contact person for the MDDB Report Viewer service or application). ƒ _grfaplt: the complete path for the graph applet

    ƒ

    _grafloc: the directory that contains the SAS/GRAPH applets

    These parameters have default values, but can be changed by a site as desired by editing the Application Broker configuration file. Note that if multiple values are defined (e.g., two SET statements for the same name, or both a SET and ServiceSet statement for the same name), multiple values are created by the broker using the rules as discussed in Section 5.2.4.1 with the values defined in SET statements defined as the Suffix 1 values.

    Chapter 5: Communicating with the Application Dispatcher 75

    As a security precaution, fields that are defined in the Application Broker using any of the following take priority over any value defined in the URL:

    ƒ ƒ ƒ

    Export Set ServiceSet

    This prevents a user from, for example, spoofing who they are by providing a value for _rmtuser. To see this example, go to the sample environment and select Chapter 5. Then select Spoofing _RMTUSER, which uses a URL like this: http://server-name/scripts/broker.exe?_debug=9&_service=appdisp&_rmtuser=Iam-the-Boss

    Even if authentication is not turned on, the first value passed to the Application Server for _RMTUSER is a null value, as the Web server environment variable is null. The relevant part of the output is shown in Figure 5.3. Note how the spoofed value is shown in the Suffix 2 variable, not in _RMTUSER.

    Figure 5.3 Attempt (Failed) to Spoof the Value of _RMTUSER

    Note: The SAS Process Server provides a facility to provide default values that are used if no value is defined in the URL or form. The Application Dispatcher does not include such a facility. Each program must add logic to assign default values. Best Practice: Use the macro setDefaultValue, which is provided with the sample environment, to facilitate assigning default values. Call the macro and specify the name and the default value. Select Demo setDefaultValue Macro (see Figure 5.4), which provides an example that shows its use, in combination with the multipleNames macro to create a single list containing all the variables whose check boxes were selected, with a default value of _ALL_ if no check boxes were selected.

    Figure 5.4 Sample Use of the setDefaultValue Macro

    76 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    5.4 Name/Value Pairs Defined by the Application Server The Application Server also creates name/value pairs that can be referenced by a program. To see this example, go to the sample environment and select Chapter 5. Then select Show all the Name/Value Pairs Available in the Request Executive - No Sessions, which shows all the available name/value pairs with no sessions. Select Show all the Name/Value Pairs Available in the Request Executive - With Sessions, which shows the available name/value pairs when a session has been created. In order to identify the fields that are created by the Application Server it is necessary to disregard fields that were created by the user request or the Application Broker. A value of 1 is included in _debug so the name/value pairs passed from the Application Broker can be compared with the ODS output of all the available macro variables. A portion of the output is shown in Figures 5.5 and 5.6.

    Figure 5.5 Partial Listing of Name/Value Pairs—No Sessions

    Chapter 5: Communicating with the Application Dispatcher 77

    Figure 5.6 Partial Listing of Name/Value Pairs—With Sessions

    The fields can be grouped into several categories. The following fields are more generally used:

    ƒ

    The value of _program (the currently executing program) parsed into its components:

    Best Practice: Use the macro, programName, provided with the sample environment to build a program name that can point to another program in the same library (or to the same program in another library).

    …

    _pgmlib: the library

    …

    _pgmcat: the name of the SAS catalog. This field has a null value if an external file (e.g., a .sas program) is being executed

    …

    _pgm: the name of the actual program in the directory or catalog

    …

    _pgmtype: the type of program

    To see an example of the Best Practice, go to the sample environment and select Chapter 5. Then select Demo programName Macros. The results are shown in Figure 5.7.

    78 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 5.7 Sample Use of the programName Macro

    ƒ ƒ ƒ

    ƒ

    Fields that define which Application Server is processing the request: …

    _thissrv: a string that can be used instead of _url when building a link. It contains the values for _url, _service, _server and _port. When this value is used such links are always submitted to the same Application Server, even if the service has multiple Application Servers defined for it. Therefore, it should be used only when that is appropriate. The inclusion of _server and _port prevents the Application Broker and Load Manager from selecting which Application Server to use. Note: The field _thissrv should be used with care. It is not available in the SAS Process Server and even in the Application Server its use is less reliable than using the individual fields.

    ƒ

    …

    _replay: a complete link which is used to invoke the utility REPLAY program. The REPLAY program is used to replay a catalog entry such as a graph. It is used by ODS.

    …

    _tmpcat: the name of a temporary catalog in the APSWORK library that can be used during the current request. The catalog is not created until the executing program references it to create an entry.

    Fields that define the session being used or that are updated to reflect the current session. These fields point back to a particular session for an Application Server: …

    _replay: a complete link which is used to invoke the utility REPLAY program. This macro variable is updated when a user session is created. The REPLAY program is typically used to replay an entry that is stored in a catalog (e.g., in the catalog referenced by the macro variable _tmpcat).

    …

    _thissession: a string that can be used instead of _url when building a link. It is the value of _thissrv with the session (i.e., _sessionid) included.

    …

    _sessionid: the unique identifier for the particular session. This field is created only by the Application Server when the session is first created. In later requests, it is passed in from the user request.

    …

    _tmpcat: the name of a temporary catalog in the SAVE library that can be accessed by any request in the session. The catalog is not created until the executing program references it to create an entry. This catalog also exists when no sessions are involved. Note that it is located in the APSWORK library when there is no session or when the session is a lightweight session created by ODS.

    A field called _apslist is also created and is available as a macro variable. It is a comma-separated list of all the other macro variables created by the Application Dispatcher and is available in the Request Executive.

    3

    P a r t

    Developing Application Dispatcher Programs Chapter

    6

    Methods You Can Use to Access and Reference Input Parameters 81

    Chapter

    7

    Various Techniques to Generate HTML 93

    Chapter

    8

    Creating Pages with Mixed and Alternative Content Types 131

    Chapter

    9

    Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 155

    Chapter 10

    How to Create and Use Sessions 165

    Part 3 discusses techniques and examples illustrating the features and capabilities of the SAS/IntrNet Application Dispatcher. A variety of tools and best practices are provided. Many of ® these techniques apply to the SAS 9 SAS Stored Process server as well. Chapter 6 provides a basic overview of how a program can access or reference the parameters (i.e., the name/value pairs) passed to the Application Server and extends some of the concepts presented in Chapter 5. Chapter 7 discusses a variety of techniques that can be used to generate HTML with a particular emphasis on the quoting issues that surround HTML generation. A single example application is used throughout the chapter: paging through a SAS data set displaying only a set number of observations at a time. A number of tools and techniques are presented to facilitate generating HTML. Many of these are used throughout the remainder of the book. The chapter concludes with

    80 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    a SAS Server Page application that can be used to page through any data set, allowing the user to select the library, the member in that library, and the variables to include. Chapter 8 provides a discussion of generating output other than HTML (e.g., PDF, RTF, Excel) as well as output with mixed types (e.g., HTML text and images). Tools to facilitate generating other content types are provided with the examples. Chapter 9 provides an overview of the facility to define programs that are run before and after each request submitted to an Application Server. Using this facility (running specified programs before and after any request) to address common application needs is shown through examples. Chapter 10 discusses sessions from an abstract perspective. It describes how they work as well as some common problems encountered with trying to use sessions. A variety of tools and techniques that make using sessions easier are presented. Chapters 9 and 10 include material that discusses how the Application Dispatcher works. These two chapters are included in Part 3 instead of Part 2 so the discussion can leverage some of the techniques presented in Part 3. You may wish to briefly review these chapters after reading Part 2.

    C h a p t e r

    6

    Methods You Can Use to Access and Reference Input Parameters 6.1 Introduction 81 6.2 Accessing Parameters as Macro Variables 82 6.2.1 Using and Referencing the Multiple Values for a Single Parameter (the Suffix Variables) 85 6.2.2 Using the APPSRV_UNSAFE Function 87 6.3 Accessing Parameters in SCL Programs 91

    6.1 Introduction Recall from Chapter 5 that name/value pairs from a request are made available to the SAS program to be run as macro variables. The Application Dispatcher creates these variables, assigns their values, runs the program, and clears the values after the program has run. Name/value pairs are also available in programs that are SCL entries, through an SCL list which is an input parameter to the SCL program.

    82 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    These parameter values can be accessed in any of the following ways: as macro variable references the DATA step SYMGET function the SUPERQ macro function the APPSRV_UNSAFE function In SCL entries, the SCL SYMGETN, SYMGETC, GETNITEMC, and GETITEMC functions can also be used to access the values.

    6.2 Accessing Parameters as Macro Variables For parameters whose values are used to fill in values in the program (e.g., data set names, values to subset in a WHERE clause) access the value of a macro variable by simply referencing it. For example, consider an HTML page (see Figure 6.1) that prompts the user for both the name of a data set as well as a value to subset the data on (state).

    Figure 6.1 Sample HTML Input Form

    In this example, the values could be referenced with the following code: proc print data=&data; title “Print of &data for &state”; where state = “&state”; run;

    Note: Macro variable references within single quotes do not resolve. If you must quote macro variable references, use double quotes. You can also use the SUPERQ macro function to substitute the value of a macro variable into code: proc print data=%superq(data); title “Print of %superq(data) for %super(state)”; where state = “%superq(state)”; run;

    Chapter 6: Methods You Can Use to Access and Reference Input Parameters 83

    The argument that you use with the SUPERQ function is the macro variable name, not a macro variable reference. Use the SUPERQ function whenever its value could contain values that might need macro quoting. For example, if the value is to be used in macro logic, use this form of the argument: %if &state = NC %then

    . . .

    In this example, if the value for the macro variable state is NE or OR, the reference causes a macro syntax error since the macro processor interprets the values NE and OR as operators and not as the values (Nebraska and Oregon, respectively) to be compared. To handle a situation like this, use the SUPERQ function (or other macro quoting functions): %if %superq(state) = NC %then

    . . .

    Note: Macro variable references, including quoting such references, are beyond the scope of this book. For more information on macro variable references, see support.sas.com/pubs for a list of current titles such as SAS Macro Language Reference. When the parameter values appear in a DATA step, you can often use the SYMGET function if you don’t need the value until the DATA step execution time. Consider the following data entry example, shown in Figure 6.2, where a comment field is captured.

    Figure 6.2 Sample HTML Input Form Including a Free-Form Text Entry

    Each of the following assignment statements results in a DATA step character userComment variable having the value entered for Comment on the HTML form: userComment = “&Comment”; userComment = “%superq(Comment)”; userComment = symget(‘Comment); Note: The DATA step compiler reads the first two references as an assignment of character literals. If this is the first reference to the variable name, the length of the variable is defined to be the length of the value. For the SYMGET and APPSRV_UNSAFE functions, the length will default to character 200.

    84 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    All three statements assign the value of the specified parameter to the DATA step variable called userComment. These three methods assume that the value does not contain certain specified special characters (something that is not likely to be true for a comment field). As a security measure, the Application Server strips characters defined by the UNSAFE option on PROC APPSRV from the value. Here is a list of the default characters that were removed, along with the rationale for removing them: ;

    a semicolon, since it can cause a statement to end prematurely;

    % a percent sign, since it can be interpreted as triggering a macro call; & an ampersand, since it can be interpreted as triggering a macro variable reference; “

    a double quote, since it can end a character literal;



    a single quote, since it can also end a character literal

    If the unaltered value is required, the APPSRV_UNSAFE function (discussed in Section 6.2.2) can be used as follows: userComment = appsrv_unsafe(‘Comment’);

    Best Practice: Do not remove any of the characters from the default unsafe list. Consider what would happen with the following simple program: proc print data=&data; title “&title”; run;

    if a hostile user entered this value for title: gotcha”;proc delete data=&data;*

    the following code would be executed: proc print data=&data; title “gotcha”; proc delete data=&data; *”; run;

    The data set would be printed with a title of “gotcha” and would then be deleted. With the unsafe characters removed, the following code would be run: proc print data=&data; title “gotchaproc delete data=data*”; run;

    which prints the data using a TITLE statement that does no damage.

    Chapter 6: Methods You Can Use to Access and Reference Input Parameters 85

    Because these characters are removed from user input values, it is usually safe to use macro variable references, e.g., &userComment, that allow the SAS word scanner to process them as code in Application Dispatcher programs (for Version 8 and later). ®

    Note: The SAS 9 Stored Process Server does not remove these special characters. Instead, it uses macro quoting to hide the value. The unquoted values are returned by the APPSRV_UNSAFE function. If the APPSRV_UNSAFE function is used to get the value of userComment seen in Figure 6.2, the apostrophe (single quote) is included in the value (i.e., the value is It’s up to the author ☺). If either SYMGET or a macro variable reference is used, the value retrieved will not include the apostrophe (single quote) (i.e., the value is Its up to the author ☺) If you need to assign values to a numeric DATA step variable, it’s simplest use a macro variable reference. Suppose that in addition to prompting for the Comment field, the HTML form also prompts for Rank: Rank = &Rank;

    Using a macro variable reference is reasonably safe if it can be assumed that only numeric values can be provided for Rank (e.g., the value comes from an HTML radio box or select tag, or the value has been validated using JavaScript). Alternatively, the SYMGET function, combined with the INPUT function, can be used: Rank = input(symget('Rank'),8.);

    Using the SYMGET and INPUT functions enables you to perform additional editing to the value and also protects the program from hostile users as described in Best Practice above (since the value is not inserted into the program as text).

    6.2.1 Using and Referencing the Multiple Values for a Single Parameter (the Suffix Variables) Section 5.2.4.2 discussed how HTML name/value pairs may result in the creation of multiple values for a single parameter name. The multipleNames macro was provided as a tool to help deal with the nuances of such situations. Note: If the multipleNames macro is not used, the Suffix 0 and the Suffix 1 variable will exist only if two or more values are passed from the user request. By forcing them to exist, you can use the looping constructs described below regardless of how many values are created by the user request. Here are some features of the multipleNames macro: Ensures that the Suffix 0 variable always exists with the following conditions: …

    A value of 0 if the field is not defined.

    …

    A value of 1 if only one value is defined.

    Ensures that the Suffix 1 variable always exists and has the same value as the field without the suffix (allowing the program to always refer to the Suffix 1 variable). Provides an option to collapse all the values into a single space-delimited field.

    86 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Note: The macro can be easily extended to create a space-delimited list of quoted values. Such a list could be used to construct a WHERE clause from a list of selected values. Consider the example HTML form shown in Figure 6.3 that allows the user to select variables using check boxes.

    Figure 6.3 HTML Form Showing Multiple Selections Using Check Boxes

    For such cases where the name/value pairs that can have either no or multiple values, you can use any of the following methods to reference the values: A DATA step DO loop with the SYMGET (or APPSRV_UNSAFE) function do i = 1 to &var0; variable (or array) = symget(‘VAR’||left(put(i,5.))); ….. other statements end;

    If the multipleNames macro has been used, &Var0 always resolves to the number of values passed in (including 0 if none are). Both &Var1 and &Var are also defined, allowing either to be used. The above loop accesses all the values. A macro %DO loop using either macro variable references or the SUPERQ function %do i = 1 %to &var0; &&var&i other macro (or data step) logic %end; or %do i = 1 %to &var0; %superq(var&i) other macro (or data step) logic %end;

    Note: The reference &&var&i within a loop is a commonly used macro construct (delayed resolution) when you want to reference &Var1, then &Var2, and so on. Processing from left to right, the rule is that && becomes an &, any text (e.g., “var”) is carried along, then &i resolves to 1 (or 2, or 3, . . . ). The result is that &Var1 (or &Var2) gives the desired behavior.

    Chapter 6: Methods You Can Use to Access and Reference Input Parameters 87

    Note: Since the argument to the SUPERQ function is the macro name, it is only necessary to construct a reference to the macro name, var&i (i.e., delayed resolution is not required). Since it’s simpler to construct such references, using the SUPERQ function may be desirable. If the values are to be used as a space-delimited list, the multipleNames macro can be used to provide a macro variable which can be referenced in order to return the whole list. To see this example, go to the sample environment and select Chapter 6. Then select Collapsing a list of names into a single reference. This link uses an _debug value of 129 (= 1 + 128) to show the name/value pairs created by the Application Broker for the multiple values chosen for Var. You can also see the SAS log that shows the output from %PUT statements used to write the values for all the Var suffix variables. Figure 6.4 shows the output of the %PUT statements when all five variables are selected. Note that the multipleNames macro was called with the option to update the original variable (e.g., Var) to contain all the values as a space-delimited list.

    Figure 6.4 %PUT Statement Output Showing Check Box Name/Value Pairs

    6.2.2 Using the APPSRV_UNSAFE Function The APPSRV_UNSAFE function can be used whenever the original input value is needed and unsafe characters are expected or allowed to be part of the value, e.g., when the value is a last name such as O’Conner. The function should be used with great care however: the program should make sure to access or reference the value in a way that does not permit the problem highlighted in the Best Practice mentioned in Section 6.2. The APPSRV_UNSAFE function can be used in both the DATA step and in SCL. In such cases if the returned value is data as opposed to SAS code the adverse effects of using the value are minimized. The APPSRV_UNSAFE function can also be used with the %sysfunc (or %qsysfunc) macro functions to reference the value as a snippet of SAS code. Consider two examples where the user is prompted for a title for their output as well as the name of a person which should be used in a WHERE clause. title “%sysfunc(appsrv_unsafe(title))”; or title “%qsysfunc(appsrv_unsafe(title))”;

    Generate a TITLE statement whose value is the value of the name/value pair title. As mentioned in the Best Practice above, this usage can permit security issues to occur. where name = “%sysfunc(appsrv_unsafe(name))”; or where name = “%qsysfunc(appsrv_unsafe(name))”;

    The value of the name/value pair name is used to generate a WHERE clause.

    88 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    In both of these cases, the values with the unsafe characters included are required. However, there are ways to meet the requirements (e.g., a user-specified TITLE or WHERE clause) without requiring the values to be included in the job Best Practice: Whenever possible use SAS stream as code. Consider the two examples functions in the DATA step or SCL instead of above. For the WHERE clause example, the referencing the original value (before the unsafe statement can be coded so that the value for the characters are removed) in SAS code. name/value pair is not seen by the SAS word scanner at all: where name = appsrv_unsafe(‘NAME’);

    This usage (in a DATA step) prevents the SAS word scanner from seeing or processing the value of the macro variable name because the macro variable reference is processed once during the compilation of the DATA step. The APPSRV_ UNSAFE function is used as an execution time function and may be executed respectedly (e.g., for each observation). For the example where the title is passed in, SAS functions can be used to directly use the value instead of letting the SAS word scanner see the value. The SETTITLE SCL function can do this. Unfortunately this function cannot be used in the DATA step. But a very simple SCL program can be used: init: call settitle(1,appsrv_unsafe('title')); return;

    This program can then be executed in any SAS program via the DISPLAY procedure: proc display c=library.catalog.entry.scl; run;

    See the member setttitle.scl contained in the Chapter 6 catalog of the sample environment. Note: This example can also be implemented using the %qsysfunc macro function, e.g., Title “%qsysfunc(appsrv_unsafe(title))”;

    For cases where the %qsysfunc macro function is not an option, the use of simple SCL programs and tools should be considered. This example illustrated a technique to prevent the SAS word scanner from seeing the values. To see this example, go to the sample environment and select Chapter 6. Then select Special Characters in Titles. Figure 6.5 shows the output produced when the value for Title includes the unsafe characters.

    Figure 6.5 PROC PRINT Output with Unsafe Characters in a Title

    Chapter 6: Methods You Can Use to Access and Reference Input Parameters 89

    6.2.2.1 Using APPSRV_UNSAFE to Run User-Specified Code Recall that the semicolon is one of the special characters that is removed. That is why a user cannot pass any arbitrary SAS statements to the Application Server for execution. However, if you use the APPSRV_UNSAFE function you can create a program which executes any specified user code. To see this example, go to the sample environment and select Chapter 6. Then select Run User Defined Code. A text area HTML field (called line) is defined to allow the user to enter any arbitrary SAS statements. The following SAS program is executed and uses the APPSRV_UNSAFE function as the call execute argument to retrieve the original values and execute them as SAS code, with the unsafe characters (e.g., the semicolon (;), the percent sign (%), the ampersand (&), etc.) included: %global ods; %multipleNames(names=line) data _null_; /* if ODS parameter set, generate an ODS statement */ if "&ods" ne " " then call execute('ods html file=_webout style=sasweb;'); call execute(appsrv_unsafe('line')); /* loop thru the others submitted lines */ do i = 2 to &line0; call execute(appsrv_unsafe(trim('line'||left(put(i,4.))))); end; /* close ODS if the parameter was set */ if "&ods" ne " " then call execute('ods html close;'); stop; run;

    The first value cannot be referenced as line1 since line1 might have been created by the multipleNames macro. If so, APPSRV_UNSAFE returns a value without the unsafe characters, as they were not included when line1 was created from line. See Figure 6.6 for an example HTML page that allows the user to enter code to be run. Figure 6.7 shows the output produced by the code shown in Figure 6.6.

    90 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 6.6 Sample HTML Interface Allowing User to Enter SAS Code

    Figure 6.7 Output Produced from User-Entered SAS Code

    Chapter 6: Methods You Can Use to Access and Reference Input Parameters 91

    Providing such functionality can compromise security because it allows the user to run any arbitrary code. The example provided with this book is for illustrative or academic purposes only and is stored in a separate catalog from the other examples in order to isolate this functionality. The catalog containing this program should be defined only in a PROGLIBS or ADMINLIBS statement with great care. In the sample environment, this catalog is defined in an ADMINLIBS statement. The HTML form requires that the Application Server password, as discussed in Sections 4.5.2.4 and 4.6, be entered in order to execute the code.

    6.3 Accessing Parameters in SCL Programs If your program is written in SCL, another method is available for referencing or accessing the variable values. An SCL list is passed to an Application Dispatcher program written in SCL. Such SCL programs contain the following statement: entry inputlist 8;

    This list contains named character items that correspond to the name/value pairs. SCL programs can use either this input list or the macro variables. For example, you could use this statement to access value for the name of the input data set: data=getnitemc(inputlist,'DATA',1,1,' ');

    Note: The GETNITEMN function (for numeric list items) produces an error message, because all the items are inserted as character values. The Application Server cannot infer the type from the current value. It is the responsibility of the program to know when or if to convert list items to numeric values. The arguments specify that the value to be returned is the first value provided for DATA, with a default of blank (the fifth argument) if no value is provided. As with the macro variables, this SCL list is cleaned up by the Application Server when the Application Dispatcher program has run. Once the value is available as an SCL variable, it can be used in SCL functions for additional validation. Here is an example: data_exists = exist(data); if not data_exists then . . . . . . .

    Note: The SCL EXIST function can also be used in the DATA step, or you can use a %sysfunc macro call to perform such validation. The value that is retrieved from the list has the unsafe characters removed. If the program needs to get the values, including those characters, you can use the APPSRV_UNSAFE function. data = appsrv_unsafe(‘DATA’);

    Note: If the SCL entry uses the macro variables to retrieve the values instead of accessing them from the SCL list, it is possible to access (and change) the macro variable values in the entry defined by the REQUEST INIT statement.

    92 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The values can also be retrieved from the macro variables using the SYMGETC, SYMGETN or SYMGET functions, e.g.: data = symget(‘DATA’); data_exists = exist(data); if not data_exists then . . . . . .

    When an SCL program includes a reference to a variable prefixed with an ampersand, the context of the reference is important in determining how the value is resolved. An SCL program can submit statements to SAS for execution using a submit block. Consider the following example: submit [options]; proc print data=&data; title “Print of &data” for &state”; where state = “&state”; run; endsubmit;

    If SCL variables data and state exist, the values of the SCL variables are substituted for the & references in the above submit block. If there are no SCL variables that correspond to the & references in the submit block, the code is submitted to SAS and the SAS word scanner will handle them as macro variable references. Note that if SCL programmers want to make sure that any & references are always passed to the SAS word scanner for resolution as macro variables, they should reference them using two ampersands (e.g., &&data) in the submit block. For more information on SCL, including examples, see support.sas.com/pubs for a list of titles. References to macro or SCL variables in a submit block should not be confused with such references in SCL code itself where the value is resolved when the SCL program is compiled. For example, in the following SCL statement, the value of the macro variable password is resolved at compile time: If password = “&password” then

    . . .

    The value of an SCL variable password is compared to a literal value corresponding to how &password was resolved when the SCL program was compiled; it is not compared to the value passed to the SCL program from the user’s browser. See Section 12.2.2 for an example that illustrates how macro variables can be resolved when compiling SCL programs.

    C h a p t e r

    7

    Various Techniques to Generate HTML 7.1 Introduction 94 7.1.1 The Problem with Generating Valid HTML 95 7.2 Simple PUT and FILE Statements 96 7.2.1 The generateFormTag and generateInputTag Sample Macros 101 7.3 Extending the Output Delivery System 102 7.3.1 FORM Tags Via TITLE and FOOTNOTE Statements 102 7.3.2 Character Variables with HTML Form Text 105 7.4 SCL Submit Blocks 107 7.5 Including Static HTML from External Sources 110 7.5.1 A Macro Tool to Include External HTML 114 7.6 SAS Server Pages 116 7.6.1 The SAS Server Page Macro 116 7.6.2 Sample Server Page: List Libraries 118 7.6.3 A Macro to Generate Data-Driven SELECT Tags 119 7.6.4 Sample Server Page: List the Data Sets in the Selected Library 121 7.6.5 Sample Server Page: List the Variables in the Selected Data Set 122 7.6.6 A Macro to Generate Checkboxes for Variables in a SAS Data Set 124 7.6.7 A Program to Page Through the Selected Data Set 125 7.7 SAS Design-Time Controls 127

    94 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    7.1 Introduction You can use numerous techniques to generate HTML. The range of techniques includes using simple PUT and FILE statements, using the ODS facility, using and customizing HTML templates, and using macro tools. These techniques are illustrated by a common example: paging through a SAS data set. The basic program uses the PRINT procedure and the Output Delivery System (ODS), directing the output to the requestor’s browser by using the reserved fileref _webout. To run the example in this section, go to the sample environment and select Chapter 7. Then select Base Program. %setDefaultValue(firstobs,1) %setDefaultValue(atATime,5) %setDefaultValue(data,sashelp.class) %let obs = %eval(&firstobs + &atATime - 1); ods listing close; /* no listing output*/ ods html file = _webout (title="Base Program") style=sasweb; proc print data = &data(firstobs=&firstobs obs=&obs); X title “Observations &firstobs-&obs of &data”; run; ods html close;

    X In order to add a paging facility, the firstobs and obs SAS options are used to specify what observations to display. The setDefaultValues macro presented in Section 5.3 is used to provide values if they are not provided by the requesting HTML page (or link). Figure 7.1 shows the output produced by selecting the above link.

    Figure 7.1 Sample PROC PRINT Output

    Chapter 7: Various Techniques to Generate HTML 95

    This program will be enhanced and extended by generating additional HTML that includes an HTML form to allow paging through the data set. Here are the form fields:

    ƒ ƒ ƒ ƒ

    the required fields (e.g., _service, _program, etc.) for any Application Server request data – the name of the data set to page through (a hidden field) atATime – the number of observations to display (a hidden field) firstobs – the first observation to display (a radio button field for the previous or next

    page)

    ƒ

    a Submit button

    7.1.1 The Problem with Generating Valid HTML Generating valid HTML can be complicated by the fact that both SAS and HTML require strings to be quoted. For HTML, the double quote (“) character is used. For SAS, either a single (‘) or double quote (“) can be used. In addition, SAS uses the ampersand (&) as a trigger to indicate that the next word or token should be interpreted as a macro variable reference. However, in HTML, the & is interpreted as the separator character for name/value pairs in links and as the indicator for HTML character entities (e.g.,   - a non-breakable space). For example, the following HTML string invokes the Application Dispatcher to execute a program and pass the values A and B as the values for parameters one and two: http://server-name/scripts/broker.exe?_program=a.b.c.source&one=A&two=B

    When SAS sees this string, depending on how the values are quoted, SAS might attempt to resolve &one and &two as macro variable references instead of allowing the &s to remain as the HTML separator characters. In these cases, SAS does not produce the desired results. The solution is to use single quotes. However, there are typically cases where the desired values for name/value pairs are macro variable references, e.g., if the generated URL is to include the value of _debug. The value of _debug to be used is the value of the macro variable reference &_debug. In the following URL string, the second reference to &_debug is to a macro variable reference, while the first one should be treated as a character literal: &_url?_program=a.b.c.source&_debug=&_debug&one=A&two=B

    In this case, &_url should be resolved, and the first &_debug reference is to be taken literally: the generated text should be the text &_debug. However, the second reference should be resolved by the macro processor. The first reference must be single quoted in SAS to prevent macro resolution, while the second reference must be double quoted to allow resolution. These two are in conflict. Now consider another type of HMTL string for a FORM tag field in the following example.

    Here we want some value to resolve from the macro variable reference &_debug. This would mandate that we use double quotes to quote this string; yet this is at conflict with the requirement that the quoted string include embedded double quotes. The following sections introduce techniques to address these issues. Although the examples have been coded to illustrate the various techniques, you should adopt a consistent approach when implementing an application; i.e., use a single technique consistently.

    96 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    7.2 Simple PUT and FILE Statements The following DATA step illustrates the use of PUT and FILE statements to generate HTML content (specifically the form controls to enable paging through the data set). This DATA step is included after the PROC PRINT step described in Section 7.1, and it augments the HTML generated by ODS. Figure 7.2 shows the resulting output. To run the example in this section, go to the sample environment and select Chapter 7. Then select Simple PUT/FILE Example.

    Figure 7.2 PROC PRINT Output with Paging Controls Generated with PUT and FILE Statements

    data _null_; length string $256; file _webout; /* get the number of observations */ if 0 then set &data nobs=nobs; put ''; put “”; X put ''; string = ''; Z put string; string = ''; [ put string;

    Chapter 7: Various Techniques to Generate HTML 97

    put ''; put ''; if &firstobs gt 1 then do; /* not on first page-generate Previous option */ previous = max(&firstobs - &atATime,1); put 'Previous'; \ end; /* not on first page-generate Previous option */ if &firstObs + &atATime lt nobs then do; /* not on last page - generate Next option */ next = &firstobs + &atATime; put 'Next'; ] end; /* not on last page - generate Next option */ put ''; put ''; put ''; /* close the HTML since NO_BOTTOM_MATTER used */ run;

    X The PUT string for the FORM tag uses double quotes to delimit the string, which allows macro variable resolution. The embedded “ must be coded as double double quotes (e.g., “”). Y The PUT string for _PROGRAM separates the string into pieces based on whether & references should be resolved as macro variable references. The parts with embedded “ use ‘ and those that require resolution use “. Z Assign the string to a DATA step variable. The parts are quoted using either single or double quotes based on whether macro variable resolution is to occur. [ This is similar to the immediately preceding example, except that the SYMGET function is used to insert the value of the macro variable. This occurs at execution time, but has the advantage that the value is never seen by the SAS word scanner. Thus, there are no unsafe characters. \ This is similar to the technique used for _PROGRAM, except a DATA step variable value is used in the generated string. The +(-1) eliminates the trailing blank by moving the SAS pointer back one space in the PUT statement. ] Using the CHECKED option on both radio buttons ensures that only the last one is checked if multiple values are displayed.

    98 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 7.3 shows the HTML generated by the DATA step.

    Figure 7.3 HTML Generated by the PUT and FILE Statements

    The complexity of the above DATA step relates to the quoting requirements of the PUT statements combined with the requirement to resolve macro variable references (or to insert the values of DATA step variables). The quoting requirements can be seen by examining just a couple of statements:

    ƒ

    put “”;

    Assuming the value of _url is /scripts/broker.exe, this results in the following HTML FORM tag:

    The PUT statement contains a double-quoted string that generates this text result. Since the string is enclosed in double quotes, the macro variable reference &_url resolves. The embedded double double quotes (“”) become a single double quote (“), thus generating the desired string.

    ƒ

    put ''

    Assuming the value of _program is saspress.chapter7.putnfile1.source, here is the resulting string:

    This PUT statement contains three quoted strings that generate the desired text result: …

    ''

    The INPUT tag is closed by another single-quoted string which includes, as text, the double quote needed to close the value for the VALUE parameter

    ƒ

    put 'Previous';

    This PUT statement is similar to the one on the previous page in that it has three segments, the first and last being single quoted strings which contain static HTML text. The middle segment is a reference to a DATA step variable, Previous, whose value must be inserted into the HTML. Assuming the value of Next is 6, here is the resulting HTML: Previous

    The DATA step variable Previous is not quoted because the PUT statement needs to write the value of the DATA step variable and not the text string “previous.” The variable is written using List output. The +(-1) is used to move the SAS pointer back one space over the trailing blank generated by the list output style. All of the complexity of the statements in the above DATA step is due to the quoting requirements:

    ƒ ƒ ƒ

    The parameter values in the generated HTML must be quoted by double quotes.

    ƒ

    Macro variable references are often required to resolve in order to generate the needed HTML.

    Constant text in PUT statements must be quoted by either double or single quotes. Macro variable references (which correspond to the name/value pairs from the request) do not resolve inside single quotes.

    The complexity is the same regardless of whether the text to be generated is first assigned to DATA step character variables or is coded directly in the PUT statements. Note: The examples discussed here can also be used for links that take the following form after resolution: /scripts/broker.exe?_debug=0&_program= . . .

    As mentioned above, we need &_debug to both not resolve and resolve, depending on the context. Breaking the string up into segments that are single or double quoted as appropriate accomplishes this: put “&_url” ‘?_debug=’ “&_debug” ‘&_program=’ . . . . ;

    Alternatively, the %nrstr macro function can be used to prevent resolution when the & is to be included in the resulting text: put “&_url?%nrstr(&_program)=&_program%nrstr(&_debug=) &_debug” . . . . ;

    100 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Fortunately, this complexity can be masked through the use of macro tools to deal with these quoting issues. Two template macro tools are provided with the sample environment and can be enhanced and extended as needed:

    ƒ

    generateFormTag Generates the form tag along with the three key fields (i.e., _service, _debug, and _program that are needed when generating a form to invoke an Application Dispatcher program), all properly quoted. The current values (i.e., the macro variable values) for these three fields are used. They can each be overridden by providing their values using a macro parameter whose name is the same except that the leading underscore (_) is not included in the macro parameter.

    ƒ

    generateInputTag Generates an input tag using the value of either a DATA step or macro variable. The DATA step or macro variable name becomes the value of the NAME= option of the INPUT tag.

    The source for these macros is available in the sample environment. The logic of the DATA step above is replicated in the following DATA step. This DATA step uses these macros to mask the quoting complexity of the PUT statements and allows you to focus on the logic of the problem rather than on the details of the quoting needed to generate the proper HTML. The following DATA step produces the same output and HTML (except for a different value for _program) as shown above in Figures 7.2 and 7.3. To see this example, go to the sample environment and select Chapter 7. Then select PUT/FILE Example Using Sample Form Macros. data _null_; file _webout; if 0 then set &data nobs=nobs; /* get the number of observations */ put ''; %generateFormTag() /* take all the defaults */ %generateInputTag(type=HIDDEN,mvar=atATime) %generateInputTag(type=HIDDEN,mvar=data) if &firstobs gt 1 then do; /* not on first page - generate previous option */ firstobs = max(&firstobs - &atATime,1); %generateInputTag(type=RADIO,dsvar=FIRSTOBS, options='CHECKED',text='Previous'); end; /* not on first page - generate previous option */ if &firstObs + &atATime lt nobs then do; /* not on last page - generate Next option */ firstobs = &firstobs + &atATime; %generateInputTag(type=RADIO,dsvar=FIRSTOBS, options='CHECKED',text='Next'); end; /* not on last page - generate Next option */ submit = 'Page'; %generateInputTag(type=SUBMIT,dsvar=SUBMIT)

    Chapter 7: Various Techniques to Generate HTML 101

    put ''; put ''; /* close the HTML since NO_BOTTOM_MATTER used */ run;

    7.2.1 The generateFormTag and generateInputTag Sample Macros The generateFormTag and generateInputTag macros illustrate methods you can use to avoid the complexity of dealing explicitly with the quoting requirements in every DATA step program used to generate HMTL. The macros can serve as templates that can be enhanced to meet your requirements. However, they are not intended to be fully functional, debugged, or supported macro tools.

    7.2.1.1 Usage Instructions: generateFormTag This macro has seven parameters that can be used to customize the generated FORM tag. Note that the macro does not generate the tag to close the form because it is expected that other HTML must be included before the tag.

    ƒ

    ACTION This is the action to invoke upon submitting the form and should invoke the Application Broker. It defaults to the value of the macro variable _url. The completeBrokerName macro can be used to create a value that can be used as the parameter value if necessary (e.g., Action = %completeBrokerName).

    ƒ

    NAME An optional parameter which provides a name for the form to be generated. If not provided, no name is associated with the form.

    ƒ

    TARGET An optional parameter which provides that target window for the place where the results of executing the form should be displayed. The default is the current browser window.

    ƒ

    METHOD An optional parameter which allows for the specification of whether the GET or POST methods should be used.

    ƒ

    SERVICE The value to use for _service. If not specified, the current value of the macro variable _service is used.

    ƒ

    DEBUG The value to use for _debug. If not specified, the current value of the macro variable _debug is used.

    ƒ

    PROGRAM The value to use for _program. If not specified, the current value of the macro variable _program is used. The programName macro can be used to provide the value, e.g., Program=%programName(pgm=nextPGM) if the entry nextPGM in the same catalog is the desired program to run.

    7.2.1.2 Usage Instructions: generateInputTag This macro has five parameters that can be used to customize the generated tag.

    102 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    TYPE The type of tag (e.g., text, radio button, checkbox, hidden) to generate. The default is text.

    ƒ

    MVAR | DSVAR Only one of these two parameters should be provided (if both are provided, only MVAR is used). These two parameters provide the name of either a macro variable (MVAR) or DATA step variable (DSVAR) whose name and value correspond to the name/value pair for the tag.

    ƒ

    OPTIONS Optional text to include as part of the tag itself (e.g., the CHECKED option for a radio tag). This value should be provided as a string that can be used as is in a PUT statement (e.g., a quoted text string or the name of a DATA step variable).

    ƒ

    TEXT Optional text to include after the tag (e.g., the text value to display for the a checkbox or radio button). This value should be provided as a string that can be used as is in a PUT statement (e.g., a quoted text string or the name of a DATA step variable.

    7.3 Extending the Output Delivery System The Output Delivery System (ODS) provides the most straightforward and direct technique to generate HTML (or any other type of Internet content). The program merely needs to direct the output to the special fileref _webout. In the previous section, ODS code was combined with PUT and FILE statements in a DATA step to generate the form controls. ODS can be used to generate forms directly (i.e., without the need for extra DATA steps) by using one of these techniques:

    ƒ ƒ

    including FORM tags as TITLE and FOOTNOTE statements creating character variables whose values are HTML tags themselves

    7.3.1 FORM Tags Via TITLE and FOOTNOTE Statements You can use TITLE and FOOTNOTE statements to generate the paging controls shown in Section 7.2. The following text needs to be included in the generated HTML output with the ampersand (&) references replaced by the values of the respective macro variables:





    Including this text in FOOTNOTE statements raises the same quoting problems as mentioned previously. Another simple macro tool uses the SAS QUOTE function to simplify the quoting problem. The QUOTE function takes any character string and places it in double quotes,

    Chapter 7: Various Techniques to Generate HTML 103

    replacing embedded double quotes with double double quotes. When the QUOTE function is applied to just the first line (the FORM tag) the resulting text is this: “”

    This is exactly the text needed for a FOOTNOTE statement (with the addition of DIV tags to ensure that the < and > symbols are not escaped). You can use the macro tool quoteHTML: %macro quoteHTML(value); %sysfunc(quote(&value)) %mend quoteHTML;

    so such text can be directly included in FOOTNOTE statements, e.g.: footnote1 %quoteHTML();

    with comparable statements used for the other footnotes. There is an additional wrinkle that was handled in the previous examples using the programming capabilities (i.e., IF statements) of the DATA step: the Previous choice should only be included if it is not on the first page; the Next choice if it is not on the last page. This functionality could be easily accommodated if the entire program were packaged as a macro. Since there are often cases where the requirement is to conditionally include some text without having to write a macro for each such instance, a simple, general-purpose macro (iif) with three parameters can provide the needed functionality:

    ƒ ƒ ƒ

    a logical expression the text to generate when the expression is true the text to generate when the expression is false

    The sample iif macro: %macro iif (cond, positive, negative ); %if &cond %then %do; &positive %end; %else %do; &negative %end; %mend iif; ®

    Note in SAS 9, you can also use the %sysfunc function with the IFN or IFC DATA step function. Both the iif macro as well as the quoteHTML macro tool are included in the sample environment. The following example uses the iif and quoteHTML macros. The program uses the iif macro to generate the FOOTNOTE statements for the Previous and Next options only when they are appropriate. Figure 7.4 shows the resulting output. The radio button and Submit button have a different appearance (i.e., on separate lines) due to the HTML style used by ODS for the FOOTNOTE statements.

    104 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    To run this example, go to the sample environment and select Chapter 7. Then select Paging via ODS. %setDefaultValue(firstobs,1) X %setDefaultValue(atATime,5) %setDefaultValue(data,sashelp.class) %let obs = %sysfunc(max(&firstobs + &atATime - 1,1)); %let previous = %sysfunc(max(%eval(&firstobs - &atATime),1)); %let next = %eval(&firstobs + &atATime); data _null_; /* this step is used to just make sure the maximum obs is not exceeded */ if 0 then set &data nobs=nobs; call symput('datasetObs',compress(put(nobs,8.))); obs = min(&obs,nobs); call symput('Obs',compress(put(obs,8.))); stop; run; ods listing close; /* no listing output*/ ods html file = _webout (title="Paging via ODS") style=sasweb; proc print data = &data(firstobs=&firstobs obs=&obs); title "Observations &firstobs-&obs of &data"; footnote1 %quoteHTML(); Y footnote2 %quoteHTML(); footnote3 %quoteHTML(); footnote4 %quoteHTML(); footnote5 %quoteHTML(); footnote6 %quoteHTML(); %iif Z (&firstobs ne 1, footnote7 %quoteHTML(Previous); ) %iif (&next lt &datasetObs, footnote8 %quoteHTML(Next); ) footnote9 %quoteHTML(); run; ods html close;

    X This part of the program is the same as in the previous examples. Y The quoteHTML macro is used to generate the appropriate FOOTNOTE statements.

    Z The iif macro is used to conditionally generate the Previous and Next options only when they are appropriate.

    Chapter 7: Various Techniques to Generate HTML 105

    Figure 7.4 PROC PRINT Output with Paging Controls Generated Using FOOTNOTE Statements

    7.3.2 Character Variables with HTML Form Text This technique involves creating character variables whose values contain HTML text. Consider an example where the goal is to display the observations in a data set and allowing the values on individual observations to be updated. The example above is modified to define a form for each observation that is displayed. The basic idea is to create character variables which have the data values embedded in the desired HTML text. Note: This example will not include the update functionality. Instead the generated form will run the sample program from Section 5.4 that shows all the name/value pairs. By examining the output, the user can verify that the values passed to the program are for the selected observation. It is possible to extend this capability to implement any desired functionality. The following example:

    ƒ ƒ ƒ

    displays the SASHELP.CLASS data set

    ƒ

    creates a character variable for Name that displays the value and also generates a FORM tag that includes the value as a hidden field

    creates extra variables that can be used to wrap an HTML form around each observation creates character variables for the Height and Weight variables that display them as the value of HTML fields

    106 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The net result is an HTML page that shows how an update facility could be written (just replace the value for &_program in the generated FORM tag with one that does the update). The following DATA step, when added to the program shown in Section 7.3.1 demonstrates this. To run this example, go to the sample environment and select Chapter 7. Then select Forms via ODS. %setDefaultValue(firstobs,1) %setDefaultValue(atATime,5) data updateForm; set sashelp.class; label formTag = '/' closeTag = '/' nameForm = 'Name' weightForm = 'Weight' heightForm = 'Height'; formTag = "" || X "" || "" || ""; nameForm =

    '' || Name || ''; Y weightForm = ''; heightForm = ''; closeTag = '';

    Z

    run;

    X The variable formTag will be included first by the PROC PRINT step, and contains all the HTML text needed to generate a form for each observation. Note the use of the programName macro to call the program from Chapter 5 that displays the name/value pairs. Y The nameForm variable includes the HTML text needed to display Name as well as include the value as a hidden field. The weightForm and heightForm variables contain the respective values embedded in HTML text fields so they are both displayed and editable. Z The variable closeTag is listed last by the PROC PRINT step and contains the HTML text needed to close a form for each observation.

    Chapter 7: Various Techniques to Generate HTML 107

    The preceding DATA step, when added to the example from Section 7.3.1, along with the following TITLE and VAR statements (the FOOTNOTE statements are the same) generates a FORM tag for each observation. This can be demonstrated by changing the values, clicking Update, and noting the name/value pairs that result. var formTag nameForm weightForm heightForm closeTag; title "Observations &firstobs-&obs of sashelp.class"; title2 "Change values for a student and then select Update"

    Figure 7.5 HTML Form to Update a Data Set Produced using ODS and PROC PRINT

    7.4 SCL Submit Blocks SAS Component Language (SCL) submit blocks provide additional capabilities to generate custom HTML. Submit blocks are typically used to submit SAS code for execution, including SAS code that uses ODS to generate HTML. However, the submit capability in SCL provides a rich-text substitution capability that can be used to directly generate HTML. When SCL encounters a submit block, the contents of the submit block are scanned to identify all references to SCL variables that begin with an &. Any such references are replaced by the corresponding value of the SCL variable.

    108 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Caution: References to variables in SCL submit blocks that begin with an ampersand (&) are not necessarily resolved as macro variables. If the name prefixed with an & is defined as an SCL variable in the SCL program, it is resolved as the value of the SCL variable. Only once the code is submitted to SAS will any remaining & references be seen and resolved as macro variable references. See support.sas.com/pubs for a list of current titles such as SAS Component Language: Reference. To see an example where the sample program in this chapter has been packaged as an SCL program, go to the sample environment and select Chapter 7. Then select Paging SCL Example. Note: The earlier examples did no error handling. The conditional capabilities of SCL facilitate error checking, entry inputList 8; rc = rc; /* suppress compile time message */ init: /* do not do flow control on submit block contents */ control ASIS; data = getnitemc(inputList,'DATA',1,1,'sashelp.class'); X firstobs = input(getnitemc(inputList,'FIRSTOBS',1,1,'1'),best8.); atATime = input(getnitemc(inputList,'ATATIME',1,1,'5'),best8.); dsid = open(data); if dsid le 0 then do; /*error handling if data set does not exist*/

    Y submit;

    Data Set &data does not exist

    Data Set &data does not exist

    endsubmit; rc = preview('FILE','_WEBOUT'); Z rc = preview('CLEAR'); return; end; /* error handling if data set does not exist */ obs = firstobs + atATime - 1; /* can check for maximum observation to display */ datasetObs = attrn(dsid,'NOBS'); if obs gt datasetObs then obs = datasetObs; dsid = close(dsid); submit continue; [

    Chapter 7: Various Techniques to Generate HTML 109

    ods listing close; /* no listing output*/ ods html file = _webout (title="Paging SCL Example" NO_BOTTOM_MATTER) style=sasweb; proc print data = &data(firstobs=&firstobs obs=&obs); title "Observations &firstobs-&obs of &data"; run; ods html close; endsubmit; /* first get the needed values as SCL variables */ _url = getnitemc(inputList,'_URL',1,1,' '); _program = getnitemc(inputList,'_PROGRAM',1,1,' '); _service = getnitemc(inputList,'_SERVICE',1,1,' '); _debug = getnitemc(inputList,'_DEBUG',1,1,' '); submit;

    \





    endsubmit; if firstobs gt 1 then ] do; /* if not at first page, generate Previous choice */ Previous = max(firstobs - atATime,1); submit; Previous endsubmit; end; /* if not at first page, generate Previous choice */ Next = firstobs + atATime; if next lt datasetObs then ] do; /* if not at last page, generate Next choice */ submit; Next endsubmit; end;

    /* if not at last page, generate Next choice */

    submit; ^

    endsubmit;

    110 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher rc = preview('FILE','_WEBOUT'); _ rc = preview('CLEAR'); return;

    X The GET functions allow for the specification of default values. All the list items passed in from the Application Server are character. Thus the program must convert numeric ones. Y A submit block is used to generate HTML. The & references (e.g., &data) resolve to their SCL variable values. There is no quoting requirement beyond what is required for the generated HTML. Z The contents of the submit block are sent directly to _webout and are then cleared to avoid being submitted to SAS for execution. [ This submit block, unlike the one above, immediately (due to the CONTINUE option) submits code to SAS for execution. This code uses ODS to direct the results to _webout. \ Another submit block is used to generate HTML. Since the contents of this submit block are not submitted to SAS, the contents are not subject to macro variable resolution. Thus, the & references must be resolved based on their existing as SCL variables. As in the above submit block used to generate HTML, there is no quoting requirement beyond what is equired for the generated HTML. ] The Previous and Next choices are conditionally generated (based on which page of data is being examined). ^ The final snippet of HTML is generated in the submit block. _ The (cumulative) contents of this submit block are then appended to the end of the ODSgenerated HTML and are sent directly to _webout. The submit block is then cleared so it is not submitted to SAS for execution.

    7.5 Including Static HTML from External Sources Quite often the HTML that is to be included in the output from an Application Dispatcher program already exists as an HTML file (or can be created with an HMTL editor). Instead of coding PUT statements to generate such HTML, it is possible to write a simple DATA step program that reads the HTML text and writes it back to _webout. The output is shown in Figure 7.6. To see this example, go to the sample environment and select Chapter 7. Then select Including Externally Defined HTML.

    Chapter 7: Various Techniques to Generate HTML 111

    Figure 7.6 Including Externally Defined Static HTML with ODS Output

    This sample appends the externally defined HTML (stored in a SAS catalog source entry) to the end of the ODS PROC Best Practice: Store the templates in a separate catalog that is PRINT output by adding the not defined as a PROGLIB in the appstart.sas file. following simple DATA step to the Base Program example (Section 7.1): filename external catalog 'saspress.template7.externalHTML.source'; data _null_; infile external; file _webout; input; put _infile_; X run;

    X The special variable _infile_ is used to capture the entire contents of the input buffer and write it back out. Note that no complicated PUT statements were needed to include this HTML. An additional advantage is that such a DATA step can handle any amount of HTML text. This technique can be expanded to also allow the external HTML to include macro variable references to be resolved. The only addition to the above program is to add a call to the RESOLVE function after the INPUT statement, i.e.: _infile_ = resolve(_infile_);

    112 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The RESOLVE function takes a character string, and if it contains any macro variable references, the returned result is the original string with any macro variable references replaced by the macro variable values. This technique can be used to generate the paging form controls that required the resolution of macro variables. The sample program shown in Section 7.3.1 can be modified to remove the FOOTNOTE statements, replacing them with the following DATA step that is run after the PROC PRINT step: filename external catalog 'saspress.template7.ODSPagingExternal.source'; data _null_; infile external; file _webout; input; _infile_ = resolve(_infile_); X put _infile_; run;

    X The use of the RESOLVE function may cause the length of the special variable _infile_ to be longer than the defined length for the FILE or PUT statement output. This will be addressed in the next sections when this functionality is packaged into two macro tools. The DATA step copies the following HTML to _webout and resolves the embedded macro variable references:



    Previous Next

    Note: Externally stored HTML can be generated from any number of sources, such as HTML editors (e.g., FrontPage), existing Web sites (subject to any copyright restrictions), and corporate or organizational libraries. Including externally stored HTML in Application Dispatcher programs is a powerful technique. This will be expanded upon in the next section and used throughout the book. To see this example, go to the sample environment and select Chapter 7. Then select Including Externally Defined HTML with Macro Variable Resolution. See Figure 7.7.

    Chapter 7: Various Techniques to Generate HTML 113

    Figure 7.7 HTML Form Controls Generated by Including Externally Defined HTML

    The initial page includes the Previous choice and the last page includes the Next choice. Ideally, it is best to suppress the Previous and Next choices when they are not appropriate. The example from Section 7.3.1 did this by using the %iif macro to conditionally generate those choices. The same technique can be used here since the RESOLVE function not only resolves macro variable references, it also executes macros as long as the entire macro call is included in the input character string (e.g., the entire macro call is on a single input line). For example, consider replacing the lines above that specify the Previous and Next choices with the following lines: %iif (&firstobs ne 1,Previous) %iif (&next lt &datasetObs,Next)

    The result is that the HTML controls for the Previous and Next choices are included only when they are appropriate. To see this example, go to the sample environment and select Chapter 7. Then select Including Externally Defined HTML with Macro Variable Resolution and Macro Execution. See Figure 7.8.

    114 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 7.8 HTML Form Controls with Conditional Logic Embedded in Externally Defined HTML

    7.5.1 A Macro Tool to Include External HTML Including external HTML is another sample macro tool. Using a macro, instead of copying the DATA step shown above into every program that uses this technique, is not only simpler, it is also more easily maintained. A sample macro tool, externalHTML, is included in the sample environment for this book. It has two parameters:

    ƒ

    SOURCE Gives the name or location of the external HTML or both.

    ƒ

    TYPE Defines how to interpret the value of the SOURCE parameter: …

    Fileref: the value of SOURCE is an existing SAS filename.

    …

    Catalog: the value of SOURCE is a four-level SAS catalog source entry name.

    …

    Any other value, including blank, implies that the value of SOURCE is the path to or the name of an operating system file.

    Chapter 7: Various Techniques to Generate HTML 115

    The code for the externalHTML macro is included in the sample environment and is substantially the same as the code that appears above. The primary difference is that the macro does not use the special variable _infile _. Instead, it uses a DATA step variable, which can include up to 32,755 characters. Using a DATA step variable allows any single line in the template file to expand (after macro variable resolution and macro execution) to this length. To run an example that uses this macro, go to the sample environment and select Chapter 7. Then select Using a Macro Tool to include Externally Defined HTML. Other than the text note at the bottom of the page (which is caused by the use of a different external HTML template), the macro produces the same output as shown in Figure 7.8. Here is the complete program for this example: Note: This is a generic tool that can be used to page through any SAS data set. It will be expanded upon in the next section. %setDefaultValue(firstobs,1) %setDefaultValue(atATime,5) %setDefaultValue(data,sashelp.class) %let obs = %sysfunc(max(&firstobs + &atATime - 1,1)); %let previous = %sysfunc(max(%eval(&firstobs - &atATime),1)); %let next = %eval(&firstobs + &atATime); data _null_; /* this step is used to just make sure the maximum obs is not exceeded */ if 0 then set &data nobs=nobs; call symput('datasetObs',compress(put(nobs,8.))); obs = min(&obs,nobs); call symput('Obs',compress(put(obs,8.))); stop; run; ods listing close; /* no listing output*/ ods html file = _webout ((title="Using a Macro Tool to include Externally Defined HTML" no_ bottom _matter) style = sasweb; proc print data=&data(firstobs=&firstobs obs=&obs); title "Observations &firstobs-&obs of &data"; run; ods html close; %externalHTML X (type=catalog, source=saspress.template7.external_HTML_Macro.source ) %externalHTML X (type=catalog, source=saspress.template7.trailer_Content.source )

    X The externalHTML macro is called twice since the HTML that represents the form controls is applicable only to this program, while the other entry might be used more broadly. Such externally defined HTML should be partitioned to maximize its re-use.

    116 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    7.6 SAS Server Pages The use of the externalHTML macro discussed in Section 7.5 suggests that Application Dispatcher programs can be driven by HTML templates. These templates include macro variable references and macro calls that customize the HTML and generate data-driven content. Conceptually, such templates are server pages, i.e., SAS Server Pages. Note: The HTML template driven application examples presented here work for both the ® Application Dispatcher as well as the SAS Process Server in SAS 9. SAS Server Pages contain the HTML text along with commands interpreted by SAS to generate additional data-driven HTML content. The fact that macro variables are resolved and macros are executed by the RESOLVE function provides the functionality needed for SAS Server Pages. The key point is that any macros invoked are used as a text generation facility; but instead of generating SAS code, they generate HTML content. SAS macros can access data set information—both attributes and data—by using the %sysfunc macro function in conjunction with the SCL functions that access data sets. This fact allows macros to generate data-driven HTML content without requiring the generation of any SAS code. To demonstrate how SAS Server Pages work, a sample application is provided that allows the user to page through a SAS data set. This example implementation of SAS Server Pages enables the user to select the following:

    ƒ ƒ ƒ

    the SAS data library the SAS data set (from the selected library) variables to include

    To see an example of SAS Server Pages, go to the sample environment and select Chapter 7. Then select SAS Server Pages. The results of running this example can be seen in Figures 7.9 through 7.12, later in this chapter.

    7.6.1 The SAS Server Page Macro The first tool needed is a macro that can be invoked directly by the Application Server and that processes a specified SAS Server Page (i.e., an HTML template as discussed above). A macro similar to the externalHMTL macro (discussed earlier), sasServerPage, is included in the sample environment. The sasServerPage macro and the externalHTML macro are different in the following ways:

    ƒ

    sasServerPage is invoked directly by the Application Server by specifying sasServerPage as the value of the _program name/value pair. For the example presented here, the value of _program is saspress.chapter7.sasServerPage.macro.

    ƒ

    sasServerPage has no parameters in the macro definition. The type of template and the template location or name are passed in as the name/value pairs Type and Template.

    ƒ

    sasServerPage cannot specify a fileref (there is no calling program available to define the fileref).

    Chapter 7: Various Techniques to Generate HTML 117

    The following two name/value pairs must be specified when invoking the sasServerPage macro:

    ƒ ƒ

    a value for Type a value for Template

    These two parameters have the same definition as the corresponding parameters for the externalHTML macro. For the SAS Server Pages link, the values provided are as follows: . . . . &type=catalog&template=saspress.template7.liblist.source

    The source code for the macro (saspress.chapter7.sasServerPage.macro) is shown below: %macro sasServerPage; %if %upcase(&type) = CATALOG %then %str(filename _srvrpge catalog "&template";); X %else %str(filename _srvrpge "&template";); data _null_; infile _srvrpge truncover; Y file _webout; input infile $32755.; infile = resolve(infile); put infile; run; %mend sasServerPage; proc catalog c = work.sasmacr; Z copy out = saspress.chapter7; select sasServerPage / et=macro; run; quit;

    X The templates can be stored either externally or as SAS catalog entries. Y The template contents are read in a line at a time; any included macro variable references and macro calls are resolved or executed. The resolved content is written directly to _webout. Note that macro calls must be contained on a single input line. Z Store the macro source and the compiled macro in the same catalog. When this code is submitted to SAS for execution (the macro definition and the PROC CATALOG step), the sasServerPage macro will be compiled, stored in WORK.SASMACR, and then copied to a SAS catalog entry. The compiled macro can be invoked directly by the Application Server. Defining macros in catalog entries this way is a Best Practice.

    118 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    7.6.2 Sample Server Page: List Libraries The results of processing the following SAS Server Page provides the user with a list of libraries to select from (see Figure 7.9):

    Sample SAS Server Pages Application

    Demonstrate Use of SAS Server Pages Demonstrates how macros can be used inside of HTML text to customize it to include data values.

    This template allows the user to select which library contains the data to be paged through. After clicking Get List of Data Sets, the user is given a choice of data sets in the selected library.



    X

    Select the Library: Y %generateOptionTag(data=sashelp.vslib,var=libname,where=upcase(libname) not in ("APSWORK" "MAPS" "SASHELP" "WORK"))) Z

    The option tag above was generated by embedding a macro call:

        %generateOptionTag(data=sashelp.vslib, var=libname,where=upcase(libname) not in ("APSWORK" "MAPS" "SASHELP" "WORK"))) [

    in the template. This macro generates no SAS code. Instead the macro is used as simple text generation tool to generate an option tag containing the values from the input data set.

    in the template. This macro generates no SAS code. Instead the macro is used as simple text generation tool to generate an option tag containing the values from the input data set.

    The WHERE parameter is used to exclude certain libraries that will typically not be of interest.



    X Macro variable references will resolve the values defined by the Application Server. Type specifies a catalog entry for the server. Y A macro (discussed below) that generates the HTML for a select list will be executed. This produces a list of available SAS data libraries from SASHELP.VSLIB. Z The WHERE parameter is used to exclude certain libraries that will typically not be of interest.

    Chapter 7: Various Techniques to Generate HTML 119

    [ As explained in the template text, another format for HTML entities (  for a nonbreakable space and % for a % sign) is used to prevent SAS from attempting to resolve the HTML entity   (a non-breakable space) as well as forcing %generateOptionTag to be seen as text rather than a macro call. Note: Virtually all of the text in this page is HTML (and could have been created with an HTML editor). It can be edited to include any necessary macro variable references and macro calls and then stored as a SAS Server Page.

    Figure 7.9 List of Available Libraries Generated Using SAS Server Pages

    7.6.3 A Macro to Generate Data-Driven SELECT Tags The generateOptionTag macro was used in the above SAS Server Page in order to generate a list of available libraries. This simple macro can be used to generate an HMTL select list based on the values in any SAS data set. The parameters for the macro are as follows:

    ƒ

    DATA the name of the data set which contains the values to be displayed.

    ƒ

    VAR the name of the variable containing the values.

    ƒ

    LABEL the name of the variable containing the labels for the values. If not provided, the label defaults to the value specified for Var.

    ƒ

    NAME the name for the SELECT tag. If no value is provided, the name defaults to the value of Var.

    120 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    WHERE an optional WHERE clause to subset the values included in the SELECT tag.

    ƒ

    SELECTED an optional value that is compared to the values from the data set to set the selected option.

    ƒ

    otherOptions a parameter whose value is used as-is in the SELECT tag. This parameter allows the SELECT tag to be customized as needed (e.g., an onClick or size attribute, etc.)

    The macro source is described below: %macro generateOptionTag (data=, var=, label=, name=, where=, selected=, otherOptions= ); %local dsid varnum varlabel value display; %if %length(&where) ne 0 %then %let where = (where = (&where)); %if %length(&name) = 0 %then %let name = &var; %let dsid = %sysfunc(open(&data&where)); X %let varnum = %sysfunc(varnum(&dsid,&var)); %if %length(&label) gt 0 %then %let varlabel = %sysfunc(varnum(&dsid,&label)); %else %let varlabel = &varnum; %let vartype1 = %sysfunc(vartype(&dsid,&varnum)); %let vartype2 = %sysfunc(vartype(&dsid,&varlabel));

    %do %while(%sysfunc(fetch(&dsid)) = 0); %let value = %qsysfunc(getvar&vartype1(&dsid,&varnum)); %let display = %qsysfunc(getvar&vartype2(&dsid,&varlabel)); &display %end; Y %let dsid = %sysfunc(close(&dsid)); %mend generateOptionTag;

    Note: This macro is provided as an example only. It is not intended to be used as a robust tool (e.g., it does no error checking to verify that the data set or variable exists). However, it can be used as the foundation for a macro tool that meets your application needs. X The %SYSFUNC function is used to enable access to data via the SCL data access functions. Y The only text generated by the macro is the and tags that surround the loop that reads all the observations in the data set as well as an tag for each value in the data set.

    Chapter 7: Various Techniques to Generate HTML 121

    7.6.4 Sample Server Page: List the Data Sets in the Selected Library The next SAS Server Page lists the data sets in the selected library and is displayed as a result of the user clicking the Submit button after selecting a data library. The selected data library is available as a macro variable (the value of the SELECT tag from the previous page). See Figure 7.10. X

    Sample SAS Server Page Application

    Demonstrate Use of SAS Server Pages Demonstrates how macro can be used inside of HTML text to customize it to include data values.

    This template allows the user to select the data set from a previously selected library. When the user clicks Submit Variables, he is allowed to select the variables to display, along with the number of observations to display at a time.

    Y



    Select the Data Set from &libname: %generateOptionTag(data=sashelp.vstable,var=memname,name=data, where=libname="&libname") Z





    X As in the prior SAS Server Page, virtually all of the text in this page is HTML. Y As in the template above, macro variable references will resolve the values defined by the Application Server. Type specifies a catalog entry for the SAS Server Page and template is name of the next SAS Server Page (to select the variables). Z The generateOptionTag macro generates a select list of the available data sets in the selected library. The WHERE clause subsets the data to just the observations in sashelp.vstable for the previously selected library.

    122 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 7.10 List of Available Data Sets in the Selected Library Generated Using SAS Server Pages

    7.6.5 Sample Server Page: List the Variables in the Selected Data Set The final SAS Server Page in the example application displays a list of variables and allows users to select which variables they wish to see. When the Submit button is clicked, a modified version of the data set paging program is executed. See Figure 7.11.

    Chapter 7: Various Techniques to Generate HTML 123

    Sample SAS Server Page Application

    Demonstrate Use of SAS Server Pages Demonstrates how macro can be used inside of HTML text to customize it to include data values.

    This template allows the user to define which variables to include. After clicking Page through..., the user is allowed to page through the selected data set.

    X

    Select Variables from Data Set &libname..&data %getVarsAsCheckbox(data=&libname..&data) Y

    All variables included if none are selected. Number of Observations Per Page 5 Z 10 15 20 25





    X The %programName macro points to the program which will run PROC PRINT, allowing the user to page through the data. Y Another macro (discussed below) generates checkboxes for the variables in the selected SAS data set. Z HTML for a radio box is included to specify the number of observations to display at a time.

    124 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 7.11 List of Variables in the Selected Data Set Generated Using SAS Server Pages

    7.6.6 A Macro to Generate Checkboxes for Variables in a SAS Data Set The getVarsAsCheckbox macro generates HMTL checkbox tags for all the variables in a specified SAS data set. The parameters for the macro are as follows:

    ƒ

    DATA the name of the data set.

    ƒ

    NAME the name for the generated checkboxes. If no value is provided, the name defaults to the value of Var.

    ƒ

    CHECKED a non-blank value indicates that the checkboxes are to be selected by default. The default value for this parameter is blank.

    The macro source is described below: %macro getVarsAsCheckbox (data=, name=var, checked=) );

    Chapter 7: Various Techniques to Generate HTML 125

    %local dsid nvars varname varlabel i br; %if %length(&checked) ne 0 %then %let checked = checked; %let dsid = %sysfunc(open(&data)); n %let nvars = %sysfunc(attrn(&dsid,NVARS)); %do i = 1 %to &nvars; %let varname = %sysfunc(varname(&dsid,&i)); %let varlabel = %sysfunc(varlabel(&dsid,&i)); %if %length(&varlabel) = 0 %then %let varlabel = &varname; &br&varlabel %let br =
    ; o %end; %let dsid = %sysfunc(close(&dsid)); %mend getVarsAsCheckbox;

    Note: This macro is provided as an example only. It is not intended to be used as a robust tool (e.g., it does no error checking to verify that the data set or variable exists). However, it can be used as the foundation for a macro tool that meets your application needs. n The %SYSFUNC function is used to enable access to data via the SCL data access functions. The variable names and labels are determined. If there is no label, the variable name is used. o The only text generated by the macro is the tags for each variable in the data set. The variable name is used as the value and label is the display value. The checkbox tags each start on a new line (because of the
    tag).

    7.6.7 Program to Page Through the Selected Data Set The final component of this sample is the program that pages through the selected data set. It is substantially the same as what was discussed in Section 7.5. The complete program is included below with the changes annotated. The results of running this program can be seen in Figure 7.12. %setDefaultValue(firstobs,1) %setDefaultValue(atATime,5) %setDefaultValue(data,sashelp.class) %multipleNames(names=var,singleList=y) X %setDefaultValue(Var,_All_) %let obs = %sysfunc(max(&firstobs + &atATime - 1,1)); %let previous = %sysfunc(max(%eval(&firstobs - &atATime),1)); %let next = %eval(&firstobs + &atATime); data _null_; /* this step is used to just make sure the maximum obs is not exceeded */ if 0 then set &data nobs=nobs; call symput('datasetObs',compress(put(nobs,8.))); obs = min(&obs,nobs); call symput('Obs',compress(put(obs,8.))); stop; run;

    126 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ods listing close; /* no listing output*/ ods html file = _webout (title="Observations &firstobs-&obs of &datasetObs from &data") style = sasweb; proc print data = &data(firstobs=&firstobs obs=&obs) label noobs; var &var; Y title "Observations &firstobs-&obs of &datasetObs from &data"; run; ods html close; %externalHTML(type=catalog, Z source=saspress.template7.paging_form_controls.source) )

    X The multipleNames macro collapses all the selected values for VAR into a single name. The setDefaultValue macro assigns a default, _All_ if none were selected. Y The only change to the PROC PRINT is the addition of the VAR statement. Z The template to generate the form controls, including one for VAR, is used.

    Figure 7.12 Paging Through PROC PRINT Output

    This example, like the one described in Section 7.5.1, can serve as the foundation for a general purpose tool that allows users to page through any SAS data set.

    Chapter 7: Various Techniques to Generate HTML 127

    7.7 SAS Design-Time Controls The SAS Design-Time Controls (DTCs) are ActiveX controls that are designed to be run inside selected HTML editors. They provide a point-and-click interface that enables you to add SAS content to HTML pages. Note: SAS DTCs were originally designed for the Version 6 Application Dispatcher and were updated for Version 8. They are considered stable and will not be updated in future releases of SAS software. If the current controls meet the needs of an application, their use should be considered. The user interface of the HTML editor is used to insert a control into an HTML page. The controls present dialog boxes that offer choices and settings. After the choices are made and the dialog box is closed, the DTC writes the appropriate content or instructions. The controls can be used to insert the content at build time (i.e., when the developer is building the page in the editor) or at browse time (i.e., when a user requests the page via a link). The controls are needed only when the page is built; users who browse the pages using their browser do not need to have the controls locally installed. For more information about SAS DTCs, see support.sas.com/pubs. Note: SAS DTCs provide functionality similar to the SAS Server Pages discussed in Section 7.6. The key difference is that the SAS DTCs can invoke any SAS/IntrNet Application Dispatcher SAS program to generate content, and the HTML programs that use the controls reside on the Web server. They are not limited to macros that generate only HTML text. However, they do require either ASP or JSP functionality on the Web server in order to generate the content at browse time. Here is a list of available SAS DTCs:

    ƒ ƒ ƒ ƒ ƒ ƒ ƒ

    SAS Critical Success Factor SAS MDDB Report SAS Stored Program SAS Table SAS Tabular Report (i.e., PROC TABULATE) SAS Thin-Client Graphics (versions that generate HTML for either Java applets or ActiveX objects) SAS TreeView

    To run this example, go to the sample environment and select Chapter 7. Then select SAS Design Time Controls, which shows a sample page built with the SAS DTCs. (If you are running the sample environment locally, this assumes that your Web server supports ASP processing and the SAS DTCs have been installed.) Figure 7.13 shows this page in the Microsoft FrontPage HTML editor. Figure 7.14 shows the output in a browser.

    128 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 7.13 HTML Editor (FrontPage) View SAS Design-Time Controls

    Figure 7.14 Output of the SAS Design-Time Controls

    Chapter 7: Various Techniques to Generate HTML 129

    The Stored Program Control is of particular note. It can be used to run (virtually) any Application Dispatcher program. The user interface for this DTC allows you to specify the program to run as well as a query string that contains all the name/value pairs to be passed to it. Since any program can be run by this control, it can be used, in conjunction with ASP or JSP pages, to deliver SAS content as part of the page. In other words, it can be used to assemble output from multiple programs into a single HTML page. Note: Consult SAS OnlineDoc at support.sas.com/onlinedoc for any restrictions on the use of SAS Design-Time Controls. The Stored Program Design-Time Control generates ASP code that defines and then invokes a function that connects to an Application Server, causing the function to generate HTML, which is then inserted into the HTML in place of the function call.

    Comparably, in a JSP page: { appserver.AppServer IappServer = new appserver.AppServer(); IappServer.setURL(null); IappServer.setURL("http://localhost/scripts/broker.exe"); String queryString = "_service=appdisp&_debug=0&_program=bbu.chapter7.initial.source&data=sashelp.c lass&atATime=7"; java.io.OutputStream os = IappServer.getOutputStream(); os.write(queryString.getBytes()); os.close(); String HTML = IappServer.getHTML(); int responseCode = IappServer.getResponseCode(); if (responseCode >= 200 && responseCode < 300) out.println(HTML); else if (responseCode == 401) {

    130 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    HTML = "

    ERROR: Authenticated sites are not supported.


    " + HTML; IappServer.printDTCError(new PrintWriter(out, true), responseCode, HTML); } else IappServer.printDTCError(new PrintWriter(out, true), responseCode, HTML); }

    The preceding ASP or JSP code could be packaged as a function that can be included and then invoked as needed without using the DTC user interface. For example, the ASP version could be packaged as an include file:

    An include file can be included in any ASP page:

    Such an include file can be called as many times as needed with the appropriate parameters, e.g.,

    C h a p t e r

    8

    Creating Pages with Mixed and Alternative Content Types 8.1 Introduction 131 8.2 Defining the Content Type to Be Generated 132 8.2.1 Other Headers: Selected Examples 132 8.3 Generating Pages with Other Content Types 135 8.3.1 Creating a Comma-Separated Values File 136 8.3.2 Downloading Reports and Results into Microsoft Excel 137 8.3.3 Generating Content for Printing 138 8.3.4 Generating Multiple Output Types at Once 143 8.4 Generating Pages with Mixed Content Types: Text and Graphics 146 8.4.1 Ensuring That the Graph Is Current 153

    8.1 Introduction The most common form of content type for the Internet is HTML. Other forms of content such as PDFs, graphic images (e.g., GIF and JPEG), and comma-separated values (CSV) files can be generated and returned to a user’s browser. Pages with mixed content types (e.g., an HMTL page with embedded images) are also common. Such pages require special handling since browsers can handle only one content type at a time (i.e., per request to the server). Thus, pages with multiple content types must accommodate that constraint. This chapter begins with the task of defining content type and continues with details about generating pages containing multiple content types.

    132 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    8.2 Defining the Content Type to Be Generated All output generated by Application Server programs must include a content type in the HTTP header. This header is prepended to the output and includes all lines until the first null line (e.g., a line with no content). Starting with SAS 8.1, the Application Server generates an HTTP header to define the content type. Starting with SAS 8.2, if ODS is used, ODS generates a content type based on the type of output being produced. HTML, GIF, JPEG, PDF, and POSTSCRIPT are supported by this automatic generation. For all other types, or for other headers, an appropriate content type must be set. The APPSRV_HEADER function can be used to do this. Note that if no content type is specified with APPSRV_HEADER and no HTTP header is written to _webout, a default of text/html is generated. The APPSRV_HEADER function must be invoked before any content is written to the reserved fileref _webout. Here is an example that can be used in a DATA step: rc = appsrv_header(‘Content-type’,’text/html’);

    Content can also be generated using a macro %LET statement via the %SYSFUNC macro function. This can be used when there is no need for a DATA step in the program (e.g., all the content is being generated by ODS). %let rc = %sysfunc(appsrv_header(Content-type,text/html));

    8.2.1 Other Headers: Selected Examples In addition to the type of content being generated, headers can include other information, such as the address of a redirect to another URL, or whether and how long a request can be cached by the user’s browser. Note that such uses can be dependent on the user’s browser settings. Selected examples are included in the sample environment and can be run by selecting the Chapter 8 link.

    8.2.1.1 Location Headers Location headers can be used to redirect the user’s browser to a different URL. data _null_; file _webout; rc = appsrv_header( 'Location', 'http://support.sas.com/publishing/bbu/companion_site/home.html' ); put; n run;

    n The PUT statement is needed to write something to _webout. If no output is written, no content headers are generated. To run this example, go to the sample environment and select Chapter 8. Then select Using the Location Header. Note how the user’s browser is automatically directed to the specified URL (in this example, the SAS Press Web site). Note: Other uses of the location header are included in the examples presented in later chapters.

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 133

    8.2.1.2 The Content-Disposition Header The Content-disposition header can used with the ATTACHMENT argument to specify that users be prompted when the generated content is sent to the user’s browser as to whether they want to save it or view it using the appropriate viewer. The default filename to use can also be provided. Note, however, that this header’s behavior depends on the user’s local environment. Here is an example program that optionally generates this header: %setDefaultValue(attachment,0); X %setDefaultValue(filename,shoes.gif); goptions reset=all gsfname=_webout gsfmode=replace dev=gif; data _null_; if &attachment then rc = appsrv_header('Content-disposition', Y “attachment; filename=&filename”); run; proc gchart data = sashelp.shoes; title; hbar3D region/sumvar=sales subgroup=product nostats discrete; run; quit;

    X The default is to not download the graph as an attachment. A default name is provided for the name of the file. Y Conditionally generate the disposition header. The content-type header was automatically generated by the Application Server. The following three behaviors of this program can be seen in the sample environment. To run this example, go to the sample environment and select Chapter 8. Then select Simple Graph, Simple Graph as a File, and Simple Graph as a File called MyGraph.Gif.

    ƒ ƒ

    no header, the graph is displayed in the browser (Simple Graph)

    ƒ

    download as a file, giving the user the choice of save or open, with the filename passed in as a parameter (Simple Graph as a File called MyGraph.gif)

    download as a file, giving the user the choice of save or open, with the default name (Simple Graph as a File)

    The generated File Download dialog box can be seen in Figure 8.1 and is the dialog box for the Simple Graph as a File example. The only difference between the two “download” links is the name of the graph (i.e., MyGraph.gif instead of shoes.gif).

    134 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 8.1 File Download Dialog Box

    The Content-disposition header can also be used with HTML output. The following program demonstrates this. To run this example, go to the sample environment and select Chapter 8. Then select Download HTML as a File. Figure 8.2 shows the File Download for the HTML report.

    Figure 8.2 File Download Dialog Box to Save the HTML Content

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 135

    %setDefaultValue(obs,5) %setDefaultValue(startAt,1) %setDefaultValue(data,sashelp.shoes) %setDefaultValue(filename,MyReport.html) %let rc = %sysfunc(appsrv_header(Content-disposition,attachment; filename=&filename)); X ods listing close; ods html file = _webout style = sasweb; proc print data = &data(firstobs=&startAt obs=&obs); title "Simple HTML Output as a File"; run; ods html close;

    X The %SYSFUNC macro function is used to unconditionally execute the APPSRV_HEADER in order to generate the content-disposition header.

    8.2.1.3 Other Useful Content Headers Two other headers of particular note are the expires header and the cache-control headers which define how long the user’s browser should hold the generated content in the local cache. These are useful to force the browser to request updated content from the server even if the URL is the same as one previously loaded. Note, however, that these may not always work, depending on the user’s browser settings. Note: For more information on available http headers see www.w3c.org. See support.sas.com/rnd/web/intrnet for details on the APPSRV_HEADER function and related functions.

    8.3 Generating Pages with Other Content Types SAS Application Server programs can generate virtually any desired content type using methods such as these:

    ƒ ƒ ƒ

    ODS with the standard file types (e.g., HTML, PDF, RTF, etc.)

    ƒ

    any SAS program which can use the APPSRV_HEADER function to generate the desired content-type header

    ODS with special mark-up tagsets (e.g., ExcelXP) SAS formatting tools such as the DS2CSV macro which generates comma-separated values files

    The short examples in the following sections demonstrate how to generate and use other HTTP headers.

    136 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    8.3.1 Creating a Comma-Separated Values File This first example creates a comma-separated values file. To run this example, go to the sample environment and select Chapter 8. Then select Creating a CSV File. The example uses the SAS DS2CSV macro (which can also create a tab-separated file): %setDefaultValue(data,sashelp.shoes) X %ds2csv(data=&data, runmode=S, csvfref=_webout)

    X A default value for the data set to be used to create the CSV file is set and then the ds2csv macro is called directing the output to the reserved fileref _webout. The user is allowed to save or open the file typically into a Microsoft Excel spreadsheet. Figure 8.3 shows the result of the user’s choice to open the file.

    Figure 8.3 Comma-Separated Values (CSV) Output (Microsoft Excel)

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 137

    8.3.2 Downloading Reports and Results into Microsoft Excel A common requirement is to download results into Microsoft Excel. There are a number of ways to do this (e.g., using the ds2csv macro as in the previous example or using the ExcelXP tagset in ® SAS 9). Since Microsoft Excel can read an HTML file, an Application Server program can generate HTML and define the content type for Microsoft Excel (instead of letting ODS generate the default text/html header) which will cause the output to be displayed in Microsoft Excel. The following program demonstrates this for an HTML report. To run this example, go to the sample environment and select Chapter 8. Then select Download HTML into Excel. See Figure 8.4.

    Figure 8.4 HTML Report Downloaded into Microsoft Excel

    %let rc = %sysfunc(appsrv_header(Content-type,application/vnd.ms-excel)); ods listing close; ods html file = _webout style = sasweb; proc report data = sashelp.shoes nowd style(summary) = [foreground=green]; columns region subsidiary product, sales; define region / group; define subsidiary / group; define product / group across ' '; define sales / sum ' '; break after region / summarize page; rbreak before / summarize page; run; ods html close;

    138 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The preceding program uses the REPORT procedure to create an HTML report. PROC REPORT features are used to summarize and color-code the summary lines. The report is displayed using Excel because the custom content-type header specifies that Microsoft Excel should be used to view the returned content. Although such techniques are quite useful, they should be used with care since ODS is not delivering a native file format. However, keep in mind that if users want to make changes to the document or save a personal copy that they can change later, so the file needs to be in the native format. To do this, users should select File > Save as from the Microsoft Excel menu, and then select Microsoft Excel Workbook (*.xls) in order to save a copy in the native Microsoft Excel format.

    8.3.3 Generating Content for Printing HTML is the standard for browsing or reviewing output online. A common user requirement is content that can be printed. However, printing HTML output leaves a great deal to be desired. One solution is to generate a PDF or RTF (which can be viewed in Microsoft Word). Consider the example above without the Microsoft Excel content header. To generate the output as a PDF, it is only necessary to change both occurrences of ods html to ods pdf. The following example shows how the type of output can be passed in as a parameter to the Application Server program. To run this example, go to the sample environment and select Chapter 8. Then select Pass in the Output Type as a Parameter: Default - HTML or As PDF. See Figures 8.5 and 8.6.

    Figure 8.5 HTML Report

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 139

    Figure 8.6 PDF Report

    %setDefaultValue(obs,5) %setDefaultValue(startAt,1) %setDefaultValue(data,sashelp.shoes) %setDefaultValue(outputType,HTML) n ods listing close; ods &outputType file = _webout style = sasweb; proc print data=&data(firstobs=&startAt obs=&obs); title "&outputType Report"; run; ods &outputType close;

    n The type of output is passed in as a parameter. This program simply sets HTML as the default and then uses the value in the ODS statements (both file= and close). Such techniques enable a single program to produce multiple types of output by invoking the program with a different value for outputType. In Figure 8.6 you can see that the PDF output includes a table of contents in the left-hand panel. This is probably not desired in the case of this short report and suggests that, based on the type of output to be generated, there are other options that may be desirable, e.g., NOTOC for PDF (i.e., don’t display the table of contents by default). Unfortunately, many options are specific to the particular ODS statement (e.g., NOTOC produces a syntax error if used in an ODS HTML statement). Conditional logic is needed to generate the appropriate ODS statement. One way to accomplish this is to use a simple macro, generateOdsStatement, to generate the ODS statement. To run this example, go to the sample environment and select Chapter 8. Then select HTML Using the generateOdsStmt Macro, PDF, RTF, or Excel (as HTML).

    140 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    As with all the macros provided in the sample environment, this macro is not a general-purpose robust macro. However, it can be enhanced as needed. The following parameters enable the generateOdsStmt macro to customize the ODS statement:

    ƒ

    Type specifies the type of output to be generated (e.g., HTML, PDF, RTF, Excel, or a tagset). If Excel is specified, HTML output is generated, but with a content type for Microsoft Excel (as in Section 8.3.2). HTML is the default.

    ƒ

    File the fileref to write the output to. The default is _webout.

    ƒ

    Options any string that is to be included in the ODS statement. The value for Options is included in the ODS statement regardless of the type of output being requested. Thus the value provided here should be limited to values that are valid for any output type (e.g., a style parameter).

    ƒ

    pdfOptions a string containing options to be included only if the requested output type is PDF. The default is NOTOC,

    ƒ

    htmlOptions a string containing options to be included only if the requested output type is HTML. The default is NULL.

    ƒ

    rtfOptions a string containing options to be included only if the requested output type is RTF. The default is NULL.

    ƒ

    tagsetOptions a string containing options to be included only if the requested output type is a tagset. The default is NULL.

    The macro source follows: %macro generateODSStmt (type = html, file = _webout, Options = , pdfOptions = notoc, htmlOptions = , rtfOptions = , tagsetOptions = ); %local outputType; %if %upcase(&Type) = EXCEL %then %do; X /* generate an Excel Content-type header, and then generate HTML */ %let rc = %sysfunc(appsrv_header(Content-type,application/vnd.ms-excel)); %let type = HTML; %end; /* generate an Excel Content-type header, and then generate HTML */ %let outputType = %scan(&type,1,=); /*strips off tagset name if provided*/Y ods &type file=&file &&&outputType.Options &Options; %mend generateODSStmt;

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 141

    X If Microsoft Excel output is requested, generate a content-type header for Microsoft Excel output and then have ODS generate HTML output. Y Create outputType so its value can be suffixed with the text Options to generate the appropriate text, based on the type of output. The following sample program demonstrates the use of this macro: %setDefaultValue(obs,5) %setDefaultValue(startAt,1) %setDefaultValue(data,sashelp.shoes) %setDefaultValue(outputType,html) X %setDefaultValue(asExcel,%str()) %generateOdsStmt (type=&outputType, htmlOptions = (Title="Using the generateOdsStmt Macro"), Y options = style=sasweb, pdfOptions=notoc ); proc print data = &data(firstobs=&startAt obs=&obs); title "Simple Output - as HTML, PDF, RTF or Excel"; run; ods _all_ close; Z

    X Default values specify the type of output and whether HTML results are to be viewed in HTML. This is necessary in case no values are passed in from the URL. Y Different options can be set for each type of output. They are used only when the appropriate output type has been selected. Z _all_ is typically used when there are multiple destinations. However, it can also be used when there is only one type and the value is not known. &outputType should not be used since it may provide invalid syntax. To run this program, select any or all of the following:

    ƒ ƒ ƒ ƒ

    HTML, using the generateOdsStmt macro PDF RTF Excel (as HTML)

    Each of these links runs the same program on the server with the only difference being the type of output generated by ODS. The output for HTML is the same (except for the title) as seen in Figure 8.5. The PDF, RTF, and Microsoft Excel output can be seen in Figures 8.7, 8.8, and 8.9.

    142 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 8.7 The %generateOdsStmt Macro to Generate PDF Output

    Figure 8.8 The %generateOdsStmt Macro to Generate RTF Output

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 143

    Figure 8.9 The %generateOdsStmt Macro to Generate HTML Output Displayed in Microsoft Excel

    8.3.4 Generating Multiple Output Types at Once The previous example showed how easy it can be to use ODS to generate other types of Internet content. However, the example required that the same program be run repeatedly. An alternative approach is to run such programs once and use multiple ODS statements in that single execution to generate all the different types or formats that might be desired, e.g., HTML, PDF, and RTF. Since the user’s browser might not be able to receive and format more than one of these types of formats at a time, alternative forms (e.g., PDF and RTF) can be generated and saved in a temporary file with links for the other forms of output. The following sample program demonstrates using the _tmpcat macro variable and the Replay program to address this common requirement for many Web-based reports: HTML-based versions for viewing on the screen with an option to generate PDF and RTF versions for printing or editing. See Figure 8.10. To run this example, go to the sample environment and select Chapter 8. Then select HTML, with options for PDF and/or RTF.

    144 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 8.10 HTML Output with Options for PDF or RTF Output

    %setDefaultValue(obs,5) %setDefaultValue(startAt,1) %setDefaultValue(data,sashelp.shoes) filename pd catalog "&_tmpcat..shoes.pdf"; X filename rt catalog "&_tmpcat..shoes.rtf"; %generateOdsStmt(type=html, Y options=style=sasweb, htmlOptions=(no_bottom_matter title="Generate Multiple Output Types")) %generateOdsStmt(type=pdf, options=style=sasweb, pdfOptions=notoc, file=pd ) %generateOdsStmt(type=rtf, options=style=sasweb, file=rt )

    Y

    Y

    proc print data = &data(firstobs=&startAt obs=&obs); title "Simple Output"; title2 "PDF and/or RTF Output Available using the Replay Program"; run; ods _all_ close; Z data _null_; file _webout;

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 145

    put ''; put '

    Printable (PDF)'; [ put '   '; put 'Editable (PDF)'; put ''; run;

    X Use the catalog access method to define filenames in the _tmpcat catalog for the PDF and RTF output. Y Generate multiple ODS statements: one for each content type desired. ODS will format the output as HTML, and direct it to _webout. ODS will also format the output as PDF and direct the output to a catalog entry. Finally, ODS will format the output as RTF and direct the output to a catalog entry. The program needs to be run only once. The PDF and RTF output is saved in the _tmpcat catalog (which causes the creation of a lightweight session). Z Run the report once and then close all the ODS output destinations. [ Generate links for the Replay program to return the PDF or RTF versions of the report. The NO_BOTTOM_MATTER option was used in the ODS HTML statement. The _replay macro variable is used to generate the link, as it includes all the name/value pairs needed to generate a link for the Replay program, including all the fields to direct the request back to the same server. This is necessary since the _tmpcat catalog is available only to the specific server running the current request. The value of _replay includes the value for the _tmpcat catalog; it is necessary only to append the entry name and type. _replay includes the & sign to separate the name/value pairs for the hyperlink and it is already quoted with a single quote (‘) so it can be used directly in a PUT statement.

    Upon examining the links (e.g., by selecting view source) that are generated to view the PDF and RTF output, you see that they contain session information (i.e., the URL contains values for _server, _port, and _sessionid). See Figure 8.11. Note that line feeds were added to the HTML source for readability. Writing content to the catalog referenced by the macro variable _tmpcat (referred to as the _tmpcat catalog) creates a lightweight session just as using ODS does. ODS also writes content to the _tmpcat catalog and then uses the replay link to surface that content when generating a page with mixed text and graphics. Selecting either of these links will produce the same output (except for the TITLE statement) shown in Figures 8.7 and 8.8. The difference here is that the program to create the report was only run once.

    146 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 8.11 HTML Source Generated Using _replay Showing Session Information

    The use of the _tmpcat catalog to create other components of the desired output (e.g., frame-based content, graphics, etc.) is a useful technique when the full overhead of sessions is not needed. Since using the _tmpcat catalog triggers a lightweight session, the Application Server deletes the catalog once the lightweight session expires. There is no need for the application to do this cleanup; unlike the situation if another storage location, e.g. a file system file, is used. Note: The PDF is returned as an attachment since the Replay program generates the attachment header, by default, for PDF output. It is not possible to change or customize this behavior of the Replay program; it was implemented this way by SAS to deal with browser issues.

    8.4 Generating Pages with Mixed Content Types: Text and Graphics The most common type of page with mixed content is a page that contains a text report (in HTML) with embedded graphics. This is created in a relatively straightforward way for static pages by using an IMG tag:

    When the graph is being generated dynamically, there is no filename to use as the value of the SRC parameter of the IMG tag. Luckily, the SRC parameter can accept a URL if the results returned by the URL are an image with the appropriate content-type header (e.g., image/GIF or image/JPEG). Thus a URL that invokes the Application Dispatcher to generate a graph can be used. For example, here is a link for the Simple Graph example in Section 8.2.1:

    The Application Server supports a number of techniques to do this, with the easiest being ODS. To run this example, go to the sample environment and select Chapter 8. Then select Mixed Content Types - Table and Graph via ODS, which runs the program and produces the output shown in Figure 8.12:

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 147

    Figure 8.12 ODS Output with Mixed Content Types (Text and Graphics)

    goptions dev=gif xpixels=800 ypixels=400 transparency; ods html body=_webout (title="Mixed Content Types - Table and Graph via ODS") path=&_tmpcat (url=&_replay) style=sasweb; n proc gchart data = sashelp.shoes; title; hbar3D product/sumvar=sales subgroup=region shape=cylinder nostats discrete; run; quit; proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; columns product region, sales; define product / group ' '; define region / across ' '; define sales / sum ' '; rbreak after / summarize; run; ods html close;

    148 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    n When you specify the _tmpcat catalog as the value of the path variable, the two-level name generated for the graph causes it to be written as a .GIF entry in the catalog. The macro variable _replay includes a name/value pair for the entry to be replayed, but includes only the catalog name. When ODS automatically appends the two-level name of the graph entry to &_replay when constructing the URL for the SRC parameter of the IMG tag, the result is the four-level catalog name for the entry to be replayed. The resulting URL will look like this: /scripts/broker.exe?_service=appdisp&_server=localhost &_port=1251&_sessionid=D8QnokXRJ52&_program=replay &_entry=apswork.x0000003,gchart.GIF

    As discussed in Section 8.3.4, writing to _tmpcat causes a lightweight session to be created. Although ODS is certainly the easiest way to generate pages with mixed text and graphics, any Application Server program can do this and allow more control over the placement of the images. Consider the following example that displays tables and graphs side by side. To run this example, go to the sample environment and select Chapter 8. Then select Mixed Content Types - Table and Graph - Side by Side, which produces the output seen in Figure 8.13:

    Figure 8.13 Custom Generation of Links for Graphic Output: Side by Side Tables and Graphs

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 149

    goptions gsfname=myGraph gsfmode=replace dev=gif xpixels=500 ypixels=400 transparency; proc gchart data = sashelp.shoes; title; filename myGraph catalog "&_tmpcat..US.gif"; X where region = 'United States'; hbar3D product/sumvar=sales subgroup=subsidiary shape=cylinder nostats discrete nolegend; run; filename myGraph catalog "&_tmpcat..WE.gif"; X where region = 'Western Europe'; hbar3D product/sumvar=sales subgroup=subsidiary shape=cylinder nostats discrete nolegend; run; quit; data _null_; Y file _webout; put '' / 'Sample Report with Custom Graphics'/ '' / '

    ' / '
    '; run; ods html file = _webout (no_top_matter no_bottom_matter) style=sasweb; proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; where region = 'United States'; columns product region, sales; define product / group ' '; define region / across ' '; define sales / sum ' '; rbreak after / summarize; run; ods html close; data _null_; Z file _webout; put '
    '; run; ods html file = _webout (no_top_matter no_bottom_matter); proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; where region = 'Western Europe'; columns product region, sales; define product / group ' '; define region / across ' ';

    150 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    define sales / sum ' '; rbreak after / summarize; run; ods html close; data _null_; file _webout; [ put '
    '; run;

    X Generate the graphs and store them in the _tmpcat catalog. Note that this example could be extended to dynamically generate graphs for each region, or for just the top N regions. A simple macro could be developed to do this dynamically. Y Use HTML tables to lay out the tables and graphs side by side. Generate a link to replay the graph in the first table cell and then create a second table cell as a wrapper for the HTML generated by PROC REPORT. Z Generate another table row for the next region with a link for the graph and a table cell for the next segment of PROC REPORT output. [ Close the HTML table and the HTML. In the above examples (both the ODS and the custom one), lightweight sessions were created by writing the graph to the catalog referenced by _tmpcat. The IMG tags were generated so the value for the SRC parameter is a URL that calls the replay program to return the image. Before ODS and lightweight sessions were available in the SAS Application Server, the typical approach to a page with mixed text and graphics was to have one program generate the text and have it generate an IMG tag that calls another program to generate the graph. The following is a sample of such a stand-alone program. To see the default graph produced by the program below, go to the sample environment and select Chapter 8. Then select Stand-alone Graph Program. The output produced is shown in Figure 8.14. Note: There are numerous graphics examples on the SAS Web site, including examples with drilldown links, etc. For more information on how to generate detailed graphics, consult support.sas.com/samples.

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 151

    Figure 8.14 Graphical Output Using a Stand-Alone Program

    %setDefaultValue(data,sashelp.shoes) X %setDefaultValue(var,region) %setDefaultValue(subgroup,region) %setDefaultValue(sumvar,sales) %setDefaultValue(type,hbar3d) %setDefaultValue(where,1) %setDefaultValue(hsize,600) %setDefaultValue(vsize,600) goptions gsfname=_webout gsfmode=replace hsize=&hsize vsize=&vsize dev=gif; proc gchart data = &data; Y where %sysfunc(appsrv_unsafe(where)); title; &type &var/sumvar=&sumvar; run; quit;

    X Set default values for all the parameters. Y Generate the graph. Note how APPSRV_UNSAFE is used for the WHERE clause (e.g., it may include ‘ signs for a character value).

    152 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The advantage of using a stand-alone graph program is that it can be reused; the disadvantage is that the calling program needs to generate the link and pass all the parameters; and often the parameters require special handling. For example, parameters for use in WHERE clauses typically need to be referenced using the APPSRV_UNSAFE function. When the value for the SRC parameter of the IMG tag is generated, the value has to be URL-encoded so special characters do not cause problems. The following program illustrates how to generate such a URL. To run this example, go to the sample environment and select Chapter 8. Then select Generating an IMG tag to invoke a stand-alone Graph Program. See Figure 8.15.

    Figure 8.15 ODS Tabular Report with Graphical Output Via Generating a Link to a Stand-alone Graph Program

    ods html file=_webout (title="Generating an IMG tag to invoke a standalone Graph Program" no_bottom_matter) style=sasweb; %let where=product in ("Boot" "Men's Dress" "Men's Casual"); X proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; where &where; columns product region, sales; define product / group ' '; define region / across ' ';

    Chapter 8: Creating Pages with Mixed and Alternative Content Types 153

    define sales / sum ' '; rbreak after / summarize; run; ods html close; data _null_; file _webout; put '' ''; put ''; run;

    X Hard-code the WHERE clause. It will typically be dynamically defined (it is hard-coded here to simplify this example). Y Generate the IMG tag. Z The programName macro is used to point to another entry in the same catalog. [ 0 is used for _debug since no additional output should be created: it could corrupt the graph. \ The value for WHERE is URL-encoded. The %SYSFUNC macro is used to generate the encoded value as text. Given the complexities of the above program, the best approach is to generate the graph in the initial program and use the Replay facility to generate the image. The URL is simpler to generate and there is no need to worry about passing or URL-encoding all the parameters (i.e., the name/value pairs) in the generated URL. While there may be cases where a separate program is desired, it is typically simpler to generate the image in the original program and use the Replay facility to generate the URL for the image.

    8.4.1 Ensuring That the Graph Is Current Some browsers may display an image from their cache/history instead of making a dynamic request to the server. This can happen even if the program that generated the image sent a content header that specifies that the graph should not be cached. This can happen if the URL in the IMG tag is the same as a previous URL. If the underlying data has changed, but the URL is the same, the browser may display the cached image instead of the new one. Note: This is not a problem if the Replay facility is used, as the URL will be unique since the value of the entry parameter as well as the session ID will change. It is a problem only if a stand-alone graphics program is used (another reason to use the Replay facility instead). One solution to this problem is to ensure that the generated URL for the SRC parameter of the IMG tag is a unique URL. It is possible to do this by adding a name/value pair to the URL that

    154 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    has an arbitrary value (such as a random number or a datetime value). For example, the PUT statement in the previous example can be modified by adding a parameter called UNIQUE: put '' '';

    n Generate the IMG tag as in the prior example; the only addition is the name/value pair Unique. The %SYSFUNC macro function is used to call the DATETIME function to create a unique value in the generated URL. The value is generated at compile time and results in a character string that can be used in the PUT statement; it is not necessary to create a DATA step variable. Each time the code is run, a different datetime value is returned and causes a unique URL to be generated. The UNIQUE parameter is not used by the graphics program; its only purpose is to trick the browser into not using the image from its cache.

    C h a p t e r

    9

    Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 9.1 Introduction 155 9.2 Specifying the REQUEST INIT and REQUEST TERM Programs 156 9.3 Using REQUEST INIT and REQUEST TERM 156 9.3.1 Defining Libraries and Files 157 9.3.2 Overriding or Supplying Parameter Values 159 9.3.3 Standard Header and Trailer Blocks 161 9.3.4 Terminating a Request 163

    9.1 Introduction The REQUEST INIT and REQUEST TERM arguments in the APPSRV procedure enable you to specify the name of a program to run before (INIT) as well as after (TERM) each request to the Application Server. In Section 4.4.3, the use of the INIT option was discussed as a mechanism to provide functionality that is similar to what an autoexec program offers to batch or interactive SAS programs. This functionality is important given that each request runs in its own Request Executive and does not inherit any attributes from either the program that invoked PROC APPSRV or any settings from its autoexec.sas program. Recall from Chapter 4 (Section 4.4.3) that the use of REQUEST INIT and REQUEST TERM to specify programs was suggested as a best practice to provide default options and parameters (such as the location of a SAS macro autocall library) that apply to all programs. Use an INIT and possibly a TERM program when you design an application as the mechanism to specify the set-up and shut-down functions that need to be run for each application.

    156 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    When you develop an application with SAS/IntrNet Application Dispatcher, use the following structure: 1. Set up the environment (i.e., run the REQUEST INIT program). 2. Run the program (i.e., run the requested program). 3. Close the environment (i.e., run the REQUEST TERM program). This chapter provides an example that covers assigning data libraries in the INIT program instead of defining the libraries in PROC APPSRV ALLOCATE statements. Still another example covers enabling multiple applications to share a single pool of Application Servers, while keeping their data independent. In addition, there are examples that cover checking and overriding parameters that are passed to a program and that cover terminating requests based on application-specific criteria.

    9.2 Specifying REQUEST INIT and REQUEST TERM Programs The names of the programs to use are specified in the REQUEST statement in PROC APPSRV. The following REQUEST statement specifies the location of the INIT and TERM programs included with the sample environment for this book: request init=saspress.appserver_admin.init_program.source term=saspress.appserver_admin.term_program.source;

    Note: As mentioned in Section 5.2.4.2, the REQUEST INIT program for the sample environment uses the SASAUTOS option to point to the macro library for the sample environment. The INIT and TERM programs can be any of four program types (e.g., external SAS file, catalog source, SCL (Screen Control Language), or compiled macro entries) that can be run by a request to the Application Server. The location must also be listed in a PROGLIBS or ADMINLIBS statement. As a best practice, use an ADMINLIBS statement like the following: adminlibs sasopress.appserver_admin;

    The INIT and TERM programs apply to all requests submitted to the Application Server and cannot be made specific to an application or a program. See Section 14.2.4 for techniques to address this requirement.

    9.3 Using REQUEST INIT and REQUEST TERM The function of the REQUEST INIT and REQUEST TERM programs varies, depending on the environment (e.g., does the Application Server support many applications, just a few, or one) as well as the specifics of the function of those applications. Setting the SASAUTOS option is a good example of what should be done in the REQUEST INIT program. Writing default header and trailer HTML is another example of how to use the REQUEST INIT and REQUEST TERM programs. However, be careful to not overload the processing in these programs as they are run for each request.

    Chapter 9: Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 157

    The examples provided in this chapter demonstrate the INIT and TERM programs. The sample environment contains sample INIT and TERM programs that can be changed and customized, based on your specific requirements. When you are deciding whether to use the INIT and TERM programs in the sample environment as models for your applications, note that they were designed to be used with the examples in this book (or extensions to these examples based on your experimentation). Some decisions about program logic, such as placing the examples for each chapter in a separate catalog, are specific to the sample environment for this book. The logic may not apply to your situation, or you may choose to implement it differently.

    9.3.1 Defining Libraries and Files You may often want to restrict access to certain data sets by specific users, applications, or programs. Since the DATALIBS statement of PROC APPSRV applies to all programs, the librefs for such data sets (and files) cannot be defined that way. One way to do this is to define the libraries and files needed in the INIT program. Since the INIT program has access to all the macro variables (or SCL list entries, if an SCL entry is used) for the request, the INIT program could use any or all of those values to make a determination of what libraries should be defined. Such information could be stored in a SAS data set which can then be subset based on criteria such as these:

    ƒ ƒ

    who the user is (using the value of _rmtuser) what the application is (using the value of either the libref or catalog name of the requested program)

    The next example demonstrates how to define libraries using the data set SAMPDATA.WHATDATALIBS. To see the data for this example, go to the sample environment and select Chapter 9. Then select Libnames to Define. Clicking this link runs the SAS Server Page example described in Section 7.6 to display the data set SAMPDATA.WHATDATALIBS. The output can be seen in Figure 9.1.

    Figure 9.1 Metadata Data Set Specifying Libraries to Define

    Note: If you have downloaded and installed the sample environment locally, you might want to edit the SAS data set SAMPDATA.WHATDATALIBS before running this example to provide a path for the libref (in observation 1). This is not required for the example to work. The following code is included in the INIT entry for the sample environment and defines the librefs for the rows where the WHERE clause is true.

    158 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    data _null_; set sampdata.whatdatalibs; where upcase(application) = "%upcase(&_pgmcat)"; rc = libref(libname,path); run;

    Including this code in the INIT program will cause an additional libref, CHAP9, to be defined when you are running any of the examples from the chapter9 catalog. To run this example, go to the sample environment and select Chapter 9. Then select Display Librefs Available to Chapter 9. The program in the sample environment uses the PRINT procedure to print the data set SASHELP.VSLIB, which is a list of all the defined libraries. Figure 9.2 shows the output.

    Figure 9.2 List of Available Libraries

    As an exercise, you may want to do one of the following:

    ƒ

    Edit SAMPDATA.WHATDATALIBS and define additional libraries (making sure to specify chapter7 as the value for Application).

    ƒ ƒ

    Run the Chapter 7 example SAS Server Pages. Then note how additional libraries are now available for that example.

    Chapter 9: Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 159

    Note: A DATA step similar to the preceding one, combined with a metadata data set to define what data libraries should be made available for specific applications, can be used instead of a DATALIBS statements in PROC APPSRV. A specific application could be using the libref or catalog name as the criterion to specify what data sets are appropriate for a given program or application. Using this technique allows multiple applications to share the same pool of Application Servers while keeping their data libraries separate. With different logic to specify the librefs to define, e.g., based on who the user is (using _rmtuser), this technique, when combined with the SAS Server Pages example from Chapter 7, can be used to provide a simple data browsing tool. The technique can also be applied to the use of the Xplore sample application (that ships with source with SAS/IntrNet). Xplore provides a number of facilities to browse the contents of SAS data libraries, including catalogs and data sets, and is the basis for the example in the next section.

    9.3.2 Overriding or Supplying Parameter Values Since the INIT program runs before the requested program, it can be used to create a new parameter (or parameters) or to reset the value of an existing parameter (or parameters) before the program is executed by the Application Server. In Section 5.3.3, the use of the Set and ServiceSet directives in the Application Broker was introduced to provide values for parameters. Such values can also be provided in the INIT program using %LET statements. Typically values that are specific to the Web server (e.g., style sheet locations) are set with the Set or ServiceSet directives. Values that are specific to a particular SAS server or that are application-specific can be set using a %LET statement in the INIT program. Caution: Overriding the value of a parameter by resetting the value of a macro variable does not work in some cases. For example, overriding does not work for SCL programs that retrieve the values from the SCL list that is passed to the program by the Application Server. As an example of resetting parameters, consider the Xplore sample application (Figure 9.3). This SAS sample is frame-based, and the left-hand frame is the list of libraries and members. There are a number of parameters that control this display:

    ƒ

    BG the value for the background – either a color or an image, dependent on the value of BGTYPE

    ƒ

    BGTYPE the type of background – either COLOR or IMAGE

    ƒ

    GIFBASE the location of the images for the folders and data sets

    ƒ

    EXC_LIBS and INC_LIBS the list of libraries to either include or exclude

    ƒ

    MEMTYPES the list of types of members (DATA, VIEW, CATALOG)

    160 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 9.3 Xplore Invoked with Parameter Values Reset

    For this example, assume that the requirement is to customize this display based on who the user is. Assuming that information is stored in a customizations data set, a DATA step like the following customizes the appearance and potentially subsets the output generated by Xplore. data _null_; set [customizations data set]; where upcase(USER) = “%upcase(&_rmtuser)”; if BG ne ' ' then call symput('BG',trim(BG)); if BGTYPE ne ' ' then call symput('BGTYPE',trim(BGTYPE)); if GIFBASE ne ' ' then call symput('GIFBASE',trim(GIFBASE)); if MEMTYPES ne ' ' then call symput('MEMTYPES',trim(MEMTYPES)); if SEL_LIBS ne ' ' then call symput('SEL_LIBS',trim(SEL_LIBS)); if EXC_LIBS ne ' ' then call symput('EXC_LIBS',trim(EXC_LIBS)); run;

    Note: The parameter _rmtuser has a value whenever HTTP authentication is turned on by the Web server. It is a good idea to use a value of blank in the example so you can test this without having to turn on HTTP authentication. This code can be run in the INIT program, thus customizing Xplore without having to touch its code. For example, if the customizations data set specified a value of DATA VIEW for the variable MEMTYPES for the case where USER is blank, then whenever there is no value provided for _rmtuser, only data sets and views (and not catalogs) are shown.

    Chapter 9: Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 161

    Another example of providing parameters is the requirement to provide a password to access a database. Assume that users of the application have already been authenticated and further assume that users have no way of seeing the generated code. You can include a %LET statement such as the following to enable the code to reference that value: %let password = some value;

    Note: There are a number of more secure techniques (e.g., reading the password from a data set or using an SCL program to set the value) to set default passwords. See Section 12.2.2. The techniques above can also be used in combination. For example, suppose that you want to use the macro GenerateOdsStatement for an existing application. Add the following statement to the INIT program: %setDefaultValue(outputType,HTML)

    This addition has this result: the programmer has to provide only a value in the forms and links when output other than HTML is desired. No other changes need be made to the application. If there is an additional requirement to allow user-specific customizations (e.g., using the approach described above for customizing Xplore), all that is necessary is to add another variable to the data set and then use it to set the default value for output type.

    9.3.3 Standard Header and Trailer Blocks Many organizations have a standard set of HTML elements that must be included at the top, bottom or both top and bottom of any generated output. Such standard HTML might be used to define the location of the corporate style sheet, or a standard header, or a copyright, or a security statement to be included at the end of the output. The INIT and TERM programs can be used to generate such output. In Section 3.2, the use of the PrependFile and AppendFile (and the ServicePrependFile and ServiceAppendFile) directives to do this was mentioned. However, if these commands are used, the contents of those files are always included. Moving that functionality to the INIT and TERM programs allows this to be done under program control. This example (see Figure 9.4) illustrates how this might be done. The header and trailer blocks were added to this example by adding the following code to the INIT and TERM programs, respectively. This logic is present for all the examples in the Sample Environment. Since the logic is specific to the program for this example, the header and trailer content is generated for this example only. To run this example, go to the sample environment and select Chapter 9. Then select Standard Header and Trailer Blocks. Best Practice: A requirement for standard header or trailer blocks or both can be implemented by using a metadata approach like the customization example described in Section 9.3.2, where a SAS data set is used to specify what, if any, header or trailer templates apply.

    162 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 9.4 Standard Header and Trailer Blocks of HTML

    This functionality is implemented using a macro tool, externalHTML, that is discussed in Section 7.5. This tool reads HTML from an external source and includes it in the output sent back to the user’s browser. This tool is used in a number of the examples presented throughout the remainder of this book. In the INIT program: data _null_; if "%upcase(&_program)" = 'SASPRESS.CHAPTER9.HEADERANDTRAILER.SOURCE' then call execute('%externalHTML(type=catalog,source=SASPRESS.TEMPLATE9.SAMPLEHEADER.SOURCE)'); run;

    In the TERM Program: data _null_; if "%upcase(&_program)" = 'SASPRESS.CHAPTER9.HEADERANDTRAILER.SOURCE' then call execute ( '%externalHTML(type=catalog,source=SASPRESS.TEMPLATE9.SAMPLETRAILER.SOURCE)' ); run;

    This example generates header and trailer HTML that is specific (and hard-coded) to this one example in the sample environment. To apply this technique in a more general way, base the logic for when to include the templates and what templates to include on a number of applicationspecific and environment-specific factors or criteria. One of the criteria would be that such output should be included only for HTML-based reports. This criterion can be complex to determine; however doing this may be facilitated if a standard of always defining the output type as a parameter is used (discussed in Section 8.3).

    Chapter 9: Using REQUEST INIT and REQUEST TERM to Specify Set-up and Shut-down Behavior 163

    Note: It may be more appropriate to include macro calls (e.g., using the externalHTML macro) in each program to generate such header and trailer blocks as opposed to doing this in the INIT and TERM programs. That needs to be assessed on a case-by-case basis.

    9.3.4 Terminating a Request The final example uses the INIT program to terminate a request based on some criteria. In a real example, such criteria might include:

    ƒ

    the user not being authorized for the requested program. Such requirements are revisited in Chapter 12.

    ƒ

    redirecting a request (using the Location header as discussed in Section 8.2.1.1) to a specific HTML page based on some criteria.

    ƒ

    terminating or redirecting a request because an expiration date might have passed (e.g., online registration is not available after a specified cut-off date).

    ƒ

    disabling an application because of maintenance or update requirements.

    The simple example below demonstrates how a request can be rejected by including logic in the INIT program. The example disables the Hello World example that ships with SAS/IntrNet software. data _null_; if "%upcase(&_program)" = 'SAMPLE.WEBHELLO.SAS' then do; /* display a message and then terminate the request */ call execute('%externalHTML(type=catalog,source=SASPRESS.TEMPLATE9.NOTAVAIL.SOURCE)');n call execute('ENDSAS;'); end; /* display a message and then terminate the request */ run;

    n The Call Execute facility is invoked conditionally to execute macro calls and SAS statements when the condition is met. Call Execute is a convenient technique to execute small pieces of SAS code. The logic is straightforward. If the program to be executed is the Hello World example, then the following actions occur:

    ƒ

    The externalHTML macro (discussed in Section 7.5) is invoked to generate the HTML that indicates that the requested program is not available.

    ƒ

    An ENDSAS statement is generated. The ENDSAS statement ends the Request Executive and not the Application Server itself (as discussed in Section 4.2). Thus, the ENDSAS statement generated in the INIT program prevents the requested program from running.

    The conditional logic is specific to the Hello World example (i.e., the value for program is sample.webhello.sas). To demonstrate this capability, go to the sample environment and select Chapter 9. Then select Terminating a Request (see Figure 9.5) to demonstrate this. In an actual implementation, the logic to determine whether a program is allowed can be metadata driven. Note: Additional examples, including metadata-driven ones, that use the INIT and TERM programs, are included throughout the remainder of this book.

    164 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 9.5 Terminating a Request

    C h a p t e r

    10

    How to Create and Use Sessions 10.1 Introduction 165 10.2 Creating a Session 166 10.3 Saving or Restoring All Macro Variables 171 10.4 Returning to a Previous State of a Session 175 10.5 Generating Friendlier Messages for Expired Sessions 179 10.6 Extending Sessions 182 10.7 Session INIT and TERM Programs 186

    10.1 Introduction Sessions are a convenient method of preserving state information and are an alternative to the use of hidden fields to pass information from one request to another. They also provide a facility to preserve SAS data sets, catalogs, and macro variables from one request to another. Chapter 7 illustrated the use of lightweight sessions by ODS and by programs that write to the catalog specified in the macro variable _tmpcat. Several other requirements can be addressed through the use of sessions, including these:

    ƒ

    Validating a user’s access to an application by prompting for an ID or password. Once the ID or password has been successfully entered, all remaining requests can use the same session.

    ƒ

    Creating a data subset, summary file, or OLAP cube from a larger set of data for further analysis.

    ƒ

    Presenting questionnaires and surveys if the results are not to be posted to a permanent data set until the questionnaire or survey is completed.

    166 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Applications that require the use of sessions are typically complex. In order to focus attention on the mechanics of creating and using sessions, instead of complex application details, most of the examples in this chapter simply print data sets that show the status of the session. The exception is the example that used a stand-alone graphics program shown in Section 8.4. When it is revisited in this chapter, it uses sessions. While reviewing this chapter you may want to review the details of when and how sessions are created and cleaned up, as described in Section 4.5.

    10.2 Creating a Session You can create a session by calling the APPSRV_SESSION function. The following example program demonstrates how to create a session and save macro variables and data so they are available when reconnecting. The example creates a link that reconnects to the session. To run this example, go to the sample environment and select Chapter 10. Then select Create a Session. See Figure 10.1.

    Figure 10.1 Session Information

    This program creates a short session (that expires after two minutes) and then runs a PROC PRINT step that shows the path for the SAVE library. If you have downloaded and installed the sample environment, you can see how the folder or directory is automatically deleted by the Application Server once the session has expired. Simply run the example locally and navigate to the directory for the SAVE library (shown in the output) to verify that the directory is deleted once the session has expired.

    Chapter 10: How to Create and Use Sessions 167 data _null_; n rc = appsrv_session(‘create’,120); run; data save.sessionInfo; o Session = "&_sessionid"; Session_Created_At = datetime(); Session_Ends_At = datetime() + appsrvgetn('session timeout'); format Session_Ends_At Session_Created_At datetime.; Application_Server_Work_lib = pathname('APSWORK'); Save_Lib_Path = pathname('SAVE'); run; %let save_macroVar = Saved Macro Var; p %let another_macroVar = This one not saved/available; ods html file=_webout (title='Creating a Session' no_bottom_matter) style = sasweb; proc sql flow; title 'Macro Variables from the Current Session'; select name, value from dictionary.macros where scope = 'GLOBAL' and name not like ('SQL%') and /* exclude the names created by this PROC SQL step */ substr(name,1,1) ne '_' /* exclude the appserver and broker created fields */ order name, offset; quit; proc print data=save.sessionInfo split = '_' noobs; title "Created Session Info, Current Date/Time: %sysfunc(datetime(),datetime.)"; run; ods html close; data _null_; file _webout; put '

    Connect to Created Session'; run;

    n The session is created and set to expire in 120 seconds. Whenever the session is reconnected to, the time is extended. In other words this session will expire 120 seconds after the last connection to it. o Create a data set in the SAVE library with session information to demonstrate that the library is still available when reconnecting to the session. p Create macro variables. Only those with a prefix of SAVE in the name are automatically available when reconnecting to the session. q Generate a link to reconnect to the session. The macro variable _thissession contains all the information needed to reconnect to the session. The only information that needs to be added to the link is the name of the program, _debug, and any other needed name/value pairs. %superq

    168 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    is used so &_thissession resolves, but with the embedded &s that delimit the name/value pairs in the URL left alone (i.e., they are not interpreted as macro triggers). %programName is used to specify another source entry in the same catalog. Figure 10.2 shows the HTML produced by the DATA _NULL_ step. As discussed in Section 4.5, the presence of the variables _sessionid (and _server and _port) in the request URL causes the request to reconnect to the same Application Server that makes the SAVE library available. The macro variables whose names were prefixed with SAVE_ are also available.

    Figure 10.2 HTML Link to the Created Session

    The USESESSION program (run when the user selects Connect to Created Session) shown below uses the SQL and PRINT procedures to display the information (the data set and the macro variable whose name was prefixed with SAVE_) that was saved in the previous execution of a program in the session. It also shows when the new session timeout will occur by adding the session timeout value to the current datetime. ods html file=_webout (title='Reconnecting to a Session') style = sasweb; proc sql flow; title 'Available Macro Variables When Reconnecting to a Session'; select name, value from dictionary.macros where scope = 'GLOBAL' and name not like ('SQL%') and /* exclude the names created by this PROC SQL step */ substr(name,1,1) ne '_' /* exclude the appserver and broker created fields */ order name, offset; quit; data _null_; timeout = appsrvgetn('session timeout') + datetime(); call symput("Timeout",put(timeout,datetime.)); run;

    n

    proc print data=save.sessionInfo split = '_' noobs; title "Data Set from Previous Request, Current Date/Time: %sysfunc(datetime(),datetime.)"; title2 "Due to Reconnect, Session will Now Timeout at &timeout"; run; ods html close;

    n Calculate when the session will time out by adding the timeout value to the current datetime. This is not precise since the value returned by the datetime function may be different from the actual reconnect time by a few milliseconds. This difference is noise.

    Chapter 10: How to Create and Use Sessions 169

    In Sections 8.3.4 and 8.4, lightweight sessions were created simply by writing to the catalog specified by the macro variable _tmpcat. Such sessions, however, had access only to information stored in the _tmpcat catalog. By explicitly creating a session, you can use two other mechanisms to save information for later (i.e., without passing all the information in name/value pairs in the link):

    ƒ

    Storing the information in the SAVE data library. Data sets as well as catalogs can be saved.

    ƒ

    Saving macro variables by prefixing their names with SAVE. If the names are not prefixed with SAVE_, they can be saved by including %LET statements in the program, e.g.: %let SAVE_myMacroVar = &myMacroVar;

    If the original name is needed, it can be restored at the beginning of later requests with %let myMacroVar = &SAVE_myMacroVar;

    Note: The next section presents an alternative technique for saving and restoring macro variables. The application could also be implemented so that any variable to be saved is named with SAVE_ as a prefix. The following example revisits the example from Section 8.4 that generated an image tag to a standalone graph program. Here we will use sessions. The first program saves the subset data in the SAVE library and uses a prefix of SAVE_ for the macro variables. This eliminates the need to pass the parameters as name/value pairs in the URL (and thus also avoids the encoding issues for special characters in the where clause). The code is shown below. To run this example, go to the sample environment and select Chapter 10. Then select Generating an IMG tag to invoke a standalone Graph Program. The results are identical to what was shown in Figure 8.14. The first program, which generates the REPORT procedure output as well as the image tag, is shown here: data _null_; n rc = appsrv_session('create',120); run; ods html file=_webout (title="Generating an IMG tag to invoke a stand-alone Graph Program" no_bottom_matter) style=sasweb; %let save_where = product in ("Boot" "Men's Dress" "Men's Casual"); %let save_data = save.subset; %let save_subgroup = product; data &save_data; p set sashelp.shoes; where &save_where; run; proc report data=&save_data nowd style(summary)=[foreground=green]; columns product region, sales; define product / group ' ';

    o

    170 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    define region / across ' '; define sales / sum ' '; rbreak after / summarize; run; ods html close; data _null_; q file _webout; put '' ''; put ''; run;

    n Create the session. o Create the macro variables with the SAVE_ prefix so they are automatically saved. p Create the appropriate subset of the data in the SAVE library. Note that if you subset the data only once, the programs will run more efficiently (the ability to subset the data once is another advantage of using sessions). q Generate the image tag as in the example above. Note that no other name/value pairs need be included in the URL as they will be available to the next program since the macro variable names had the prefix of SAVE_. The modified program that generates the graph is shown next: %setDefaultValue(save_data,sashelp.shoes); %setDefaultValue(save_var,region); %setDefaultValue(save_subgroup,region); %setDefaultValue(save_sumvar,sales); %setDefaultValue(save_type,hbar3d); %setDefaultValue(save_where,1); %setDefaultValue(save_hsize,600); %setDefaultValue(save_vsize,600);

    n

    goptions gsfname=_webout gsfmode=replace transparency hsize=&save_hsize vsize=&save_vsize dev=gif; proc gchart data = &save_data; title; &save_type &save_var/sumvar=&save_sumvar subgroup=&save_subgroup; run; quit;

    n All the macro variables use a prefix of SAVE_ so the values from requests in the same session are available. The only other change to the program is that there is no need to use a WHERE clause as the data was subset and saved in the SAVE library.

    Chapter 10: How to Create and Use Sessions 171

    10.3 Saving or Restoring All Macro Variables Saving macro variables from one execution of a program to a later execution of a program (whether it is another instance of the same program or another program) using sessions provides many benefits. This technique is superior to including all the macro variables as name/value pairs in forms or links. You may prefer to use a technique other than prefixing the name with SAVE_. One possible approach is to explicitly save and restore all macro variables except those variables that are created by the Application Server or Application Broker (e.g., all except those whose names begin with an underscore (_)). The following macros, saveMvars and restoreMvars, provide a template for this technique. Here is the saveMvars macro: %macro saveMvars (lib=save, data=savedMacroVars, saveFlag=, includeAuto= ); %if %sysfunc(libref(&lib)) = 0 and n %length(&saveFlag) ne 0 %then %do; /* save is available only for sessions that are not lightweight sessions */ data &lib..&data; set sashelp.vmacro(keep=scope name value offset); o where scope = 'GLOBAL' and offset=0 %if %length(&includeAuto) = 0 %then and substr(name,1,1) ne '_'; and name ne: ‘SYS’; drop scope offset; run; %end; /* save is available only for sessions that are not lightweight sessions */ %mend saveMvars;

    n The libref SAVE (the default value for the LIB parameter) exists only when a full (as opposed to a lightweight) session exists. The macro variable values are saved only when the specified library exists. o This sample macro does not handle macro variables whose values length exceeds what can be accessed in the sashelp.macro (e.g., offset is not 0). Here are the parameters for the saveMvars macro:

    ƒ

    Lib The SAS data library where the macro variables are to be saved. The default value is SAVE.

    172 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    Data The name of the data set to save the macro variable values in. The default value is savedMacroVars.

    ƒ

    saveFlag A non-blank value specifies that the values are to be saved. This parameter facilitates calling the macro in, for example, the TERM program. A macro variable can be set to blank or non-blank in the called program, and then that value can be used as the value of saveFlag to indicate whether the values should be saved. The default value is blank.

    ƒ

    includeAuto A non-blank value specifies that macro variables whose names are prefixed with an _ (e.g., the macro variables defined or created by the Application Broker and Application Server) are also to be saved. The default value is blank.

    Here is the restoreMvars macro: %macro restoreMvars (lib=save, data=savedMacroVars ); %if %sysfunc(exist(&lib..&data)) gt 0 %then %do; /* restore only if data set exists */

    n

    data _null_; set &lib..&data; /* must be added to the global environment for the request executive */ call execute('%global '||trim(name)||';'); o /* only provide value from saved session if not already defined */ if symget(name) = ' ' then call symput(name,trim(value)); p run; %end; /* restore only if data set exists */ %mend restoreMvars;

    n The macro checks to make sure the data set exists before restoring it. This allows programs to call the macro unconditionally. o The CALL EXECUTE statement ensures that the macro variable names exist in the global environment (i.e., the same environment as all the name/value pairs passed in to the ® Application Server program) for the request executive. In SAS 9 this can be done more easily by using the SYMPUTX function which can assign the scope of the macro variable to be global. p The macro variable is assigned the saved value only if the variable does not already have a value. Here are the parameters for the restoreMvars macro:

    ƒ

    Lib The SAS data library where the data set with the saved macro variables is saved. The default name is SAVE, but other libraries can be used.

    ƒ

    Data The name of the data set containing the saved macro variable values. The default name is savedMacroVars.

    Chapter 10: How to Create and Use Sessions 173

    The following sample program demonstrates the use of these two macros to save and restore macro variable values without having to include them in any generated links or forms. Note that this program is called recursively by selecting Reconnect to Session. (This is not a requirement to use the macros.) %restoreMvars()n data _null_; if libref('SAVE') then o do; /* create session since it does not exist */ rc = appsrv_session('create'); call symput('sessionCounter','0'); p call symput('mVarNotRestored', q 'saved-but-not-restored'); end; /* create session since it does not exist */ run; %let sessionCounter = %eval(&sessionCounter + 1); r %let MacroVars&sessionCounter = s Session Connection Counter: #&sessionCounter at %sysfunc(datetime(),datetime.);; ods html file=_webout (title='Creating a Session' no_bottom_matter) style = sasweb; proc sql flow; title "Macro Variables from the Current Session &_sessionid"; select name, value t from dictionary.macros where scope = 'GLOBAL' and name not like ('SQL%') and /* exclude the names created by this PROC SQL step */ substr(name,1,1) ne '_' /* exclude the appserver and broker created fields */ order name, offset; quit; ods html close; data _null_; u file _webout; put '

    Reconnect to Session'; v run; %saveMvars(saveFlag = Non blank says to save values)

    n Invoke the restore macro. Note that the macro includes logic so that it runs only if the data set containing the saved values exists. o If the session does not exist, create it. The existence of the SAVE libref can be used to check if a session exists. p Initialize a session counter macro variable. q Create a macro variable whose value will not be propagated. r Increment the session counter. For now this is an informational field only. The use of this field will be expanded upon in Section 10.4.

    174 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    s Create one macro variable per connection to a session (an artifact to create additional macro variables as the session is reconnected to) to simulate later programs creating additional, application specific macro variables. t Print the current values of all the available macro variables. u Generate a link to run the same program again. Another execution of this program that connects to the session will not create a new session (reconnects do extend the session expiration time). mVarNotRestored is included in the link to demonstrate that even though the macro variable mVarNotRestored value was saved, it is not restored. Here is the reason: since the value is defined in the link, the value is passed in and won’t be reset by restoreMvars. v Since there are cases where you may not want to save the macro variable values, use a flag to indicate that they are to be saved. To run an example that uses these macros, go to the sample environment and select Chapter 10. Then select Save/Restore Macro Variables (see Figure 10.3). Once the initial page is displayed, Reconnect to Session (see Figure 10.4) reruns the program. Note how each execution of the program creates a new macro variable and that each execution has all the previous macro variables available (thanks to the save or restore macros) without their having been included in the generated link.

    Figure 10.3 Session Created

    Chapter 10: How to Create and Use Sessions 175

    Figure 10.4 First Reconnect to a Session

    You can call the saveMvars and restoreMvars macros by specific programs (as in this example), or you can include them in the INIT and TERM programs so the functionality is available automatically to any program that uses sessions. The trade-off is the (minimal) overhead of invoking these macros when sessions do not apply or when the save facility is not needed. In general it is better to call the macros in each program because that gives more control over how and when to save the macro variables. For example, an application could have a metadata approach that specifies which specific variables are to be saved (e.g., by using a data set that lists the specific macro variable names as opposed to the use of sashelp.vmacro). Note: The SESSION INIT and TERM programs cannot be used since those are invoked only when a session is created or ends. They are not invoked in the process of reconnecting to a session.

    10.4 Reconnecting to a Previous State of a Session A common problem with saving the state of a session on the server is handling scenarios where users navigate away from the current page and then return to another page that corresponds to a previous (now potentially invalid) state of the session. This problem exists for any Web-based application that maintains state on the server; it is not specific to the SAS/IntrNet Application Server. The problem can be seen by running the sample from the previous section and selecting Reconnect to Session four times (see Figure 10.5). Figure 10.6 shows what the user will see after pressing the Back button twice, that is, reconnecting to a previous state of a session. Figure 10.7 shows what the user will see upon selecting Reconnect to Session.

    176 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 10.5 Multiple Reconnects to a Session

    Figure 10.6 Browser Back Button Pressed Twice by the User

    Chapter 10: How to Create and Use Sessions 177

    Figure 10.7 Reconnecting after Using the Back Button

    The user would expect a display that shows Step 4 (i.e., a sessionCounter value of 4). Figure 10.7 shows that the user is at Step 6 (the value of mvarNotRestored shows that the user clicked the link created when sessionCounter had a value of 3). The reason the display shows Step 6 is that the saved session has Step 5 as the last step. The saved session is not aware that the user pressed the Back button twice. In a session application, such as a survey or questionnaire with skip patterns, this presents problems. If all the parameters were included in the link, there would be no problem, but this is not a viable option since links and forms cannot handle large volumes of data (e.g., compared with what can be stored in a SAS data set). In addition, if all the data is embedded in a link, sessions would add little or no value. Note: This navigation problem applies even if the SAVE convention is used to save macro variable values. This example presents the issue from the context of the saved macro variables. However, the same conflict exists for any saved SAS data sets (as demonstrated by the fact that the macro variables were saved in a SAS data set). Fortunately there is a framework for a solution to this problem: include in any link that reconnects to the session an indicator or flag as to the current state of the application. For example, include the value of the sessionCounter variable in any link and use that value to determine what data to restore. To see an example of this, select Reconnecting to a Previous State of a Session. If the steps above are repeated using this link instead of the one for the previous section, the results show that whenever a link is selected the state is recovered to its status at the time that particular

    178 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    page was generated. Figure 10.8 shows the output produced when the user clicks Reconnect to Session four times, presses the Back button twice, and then clicks Reconnect to Session again.

    Figure 10.8 Reconnecting after Clicking the Back Button Using Session Tracking Framework

    The following program demonstrates the framework for this approach. The program is substantially the same as the one in the previous section, and so only the differences are included below: %global previousSession; %restoreMvars(data=savemvars&previousSession) . . . data _null_; file _webout; put '

    Reconnect to Session'; run;

    Y

    n

    Chapter 10: How to Create and Use Sessions 179

    %saveMvars (data=savemvars&sessionCounter, saveFlag = Non blank says to save values - note this is saved as well )

    n previousSession is passed in via the link that reconnects to this session. Since it won’t exist when the session is created, it is defined in a %GLOBAL statement and its value is used in the name of the data set that contains the values stored corresponding to the previous execution. If there is no previous execution the macro will not restore any macro variables since the data set does not exist. Y The unique indicator variable, sessionCounter, that identifies the current connection to the session is included in the link as previousSession (see above). sessionCounter is then used in the name of the data set that stores the macro variables. A separate data set for each connection to the session is maintained. When the user clicks the Back button and then clicks a link or a form to rerun the program, the appropriate data sets are overwritten and used. By creating a sessionCounter Best Practice: For applications or programs that create or use macro variable and using its sessions, create a sessionCounter variable and use its value in all data value in the name of the data set set names to be saved. You can also optionally use options to save the macro variable values work=save; to instruct SAS to save all WORK data sets to the SAVE library, making them transparently available throughout the as well as in any other data set session. saved to the SAVE library, you can simplify the logic to handle such navigation problems (e.g., handling skip patterns in a survey or questionnaire).

    10.5 Generating Friendlier Messages for Expired Sessions The default message that is shown below (Figure 10.9) is generated when a user clicks a link to connect to a session that does not exist (e.g., it has expired). The message is cryptic and does not provide any instructions about what the user should do.

    Figure 10.9 Default Session Expired Output

    180 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    There are a number of reasons why a user might get such a message:

    ƒ ƒ

    The session simply expired because there was no activity for the specified timeout period.

    ƒ

    The user saved a URL for a session as a Favorite and attempted to use the Favorite at a later point in time.

    ƒ

    The Application Server was stopped and restarted while a session was active.

    The user completed the process that used sessions, the application deleted the session and then the user pressed the Back button (or the History list), which caused the browser to attempt to reconnect to the expired session.

    The INVSESS option of the APPSRV procedure’s SESSION statement provides a facility to generate a friendlier message than the one above. For example, the sample environment uses the following statement to specify an entry for a more descriptive message. session invsess=saspress.appserver_admin.expired_session.source;

    Note: If the INVSESS option is specified, the Application Server will not generate any message about session unavailability. The specified INVSESS program must generate the message. If this option is specified, the Application Server runs this program (since the session has expired this program won’t be able to recover any information in the now-deleted SAVE library) instead of the originally requested program in order to generate a custom message stating that the session has expired. The value for the name of the program that the request specified is available as _userprogram, and the value of _program is the name specified in the INVNESS option above. Ideally, the INVSESS option produces a meaningful message for the user regarding the expiration of a session. The program can also provide a link that the user can click if he or she wants to restart the application that used the session. Since the session is gone, there is no way to recover the data from the session. Since the INVSESS option gives access to all the parameters (i.e., the name/value pairs) for the request, including the requested program, the program should be able to provide some sort of meaningful message. Note: Since the name/value pairs from the URL are available, the links could include some sort of acronym or message that could be used by the INVSESS option to provide a better message as well as a link to restart the session. The INVSESS option could also be used to automatically invoke the program that created the session. For example, just the name of the program might be sufficient to produce such a message. Using the program name in conjunction with a metadata approach is one way to produce a more meaningful message. The INVSESS program provided with the sample environment illustrates such an approach. It creates a SAS data set that has two variables:

    ƒ ƒ

    a value to be compared to the name of the requested program the name of an HTML template that contains the message

    This data set is subset by comparing the first variable with the program name. The sample uses simple matching logic (for example, it begins with using the =: operator). More complex patternmatching functions can be used as dictated by your application requirements. As soon as a match is found, CALL EXECUTE is used to invoke the externalHMTL macro on the specified entry. The sample INVSESS program is listed below. The data set is created in the WORK library for the purposes of this example. In a real application, the data set would likely be a saved SAS data set.

    Chapter 10: How to Create and Use Sessions 181

    data thisShouldBeStoredMetadata; length program message_template $83; input program $ / message_template $; datalines; SASPRESS.CHAPTER10.USESESSION.SOURCE n SASPRESS.TEMPLATE10.RESTARTCREATESESSION.SOURCE SASPRESS.CHAPTER10.REFRESHSESSION.SOURCE SASPRESS.TEMPLATE10.REFRESHFAILED.SOURCE SASPRESS.CHAPTER10 SASPRESS.TEMPLATE10.CHAPTER10EXPIREDSESSION.SOURCE SASPRESS.CHAPTER20 SASPRESS.TEMPLATE20.LOGON.SOURCE DEFAULT SASPRESS.TEMPLATE10.EXPIREDSESSION.SOURCE; data _null_; set thisShouldBeStoredMetadata; where program = 'DEFAULT' or o trim(program) =: "%upcase(&_userprogram)"; call execute('%externalHTML(type=CATALOG,source='||trim(message_template)||')'); stop; run;

    n Based on the length of the values for program and the order of the data, the most specific match is always found first. o DEFAULT is included as the last row in the data set to ensure that a match is always found. The five templates provide a different message based on the program being run:

    ƒ

    For the example in Section 10.2, Create a Session, the generated message states what the session was and provides a link to restart that example (Figure 10.10).

    ƒ

    The second template is for the example in the next section and states that the request to refresh the session failed.

    ƒ

    For any other example in the Chapter10 catalog, the template used states that this was a session example and suggests that the user restart (see Figure 10.11).

    ƒ

    The fourth listed template is discussed in Section 15.7.3.4. In that example, the user can be automatically redirected to the point where the session was initially created.

    ƒ

    For all other programs, the template that is specified replicates the default message produced by the Application Server when the INVSESS option is not specified (the same output as shown in Figure 10.9).

    182 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 10.10 Customized Session Expired Output Including a Link to Restart the Session

    Figure 10.11 Customized Session Expired Output Including a Link to Restart the Session

    This metadata approach, combined with the use of the externalHTML macro tool, offers great flexibility in customizing session expired messages. The messages generated for invalid sessions can be expanded and customized by adding a new HTML template along with the appropriate row in the metadata table.

    10.6 Extending Sessions A session is automatically extended whenever it is reconnected to. It is not uncommon for a period of time to pass before the user clicks a link that exceeds the session time (e.g., the phone rings and the user is examining the output in detail). A desirable feature is the ability to display a pop-up window when the session is about to expire (just as many commercial banking Web sites do). Go to the sample environment and select Chapter 10. Then select Refreshing a Session to see an example where a pop-up window is presented to the user after a specified timeout value is reached. In this example, the user is asked after 60 seconds if he or she would like to refresh the session. See Figure 10.12. If the user clicks OK, a simple request is made using the session, which then causes it to be refreshed.

    Chapter 10: How to Create and Use Sessions 183

    Figure 10.12 Pop-Up Asking If User Wants to Refresh the Session

    The example uses two common JavaScript functions and the externalHTML macro to provide this functionality. The first step in the example is to create a session and include in the generated output the JavaScript function calls that generate the pop-up window. Note: Understanding this example does require significant knowledge of JavaScript. However, the example can be used as-is. The following program demonstrates how to generate such a pop-up. It is the same program described in Section 10.2 with these exceptions:

    ƒ ƒ

    No macro variables are created or printed. The externalHTML macro is called to include the JavaScript code.

    data _null_; rc = appsrv_session('create',120); run; data save.sessionInfo; Session = "&_sessionid"; Session_Created_At = datetime(); Session_Ends_At = datetime() + appsrvgetn('session timeout'); format Session_Ends_At Session_Created_At datetime.; Application_Server_Work_lib = pathname('APSWORK'); Save_Lib_Path = pathname('SAVE'); run; ods html file=_webout (title='Creating a Session' no_bottom_matter); proc print data=save.sessionInfo split = '_' noobs; title "Created Session Info, Current Date/Time: %sysfunc(datetime(),datetime.)"; run; ods html close; %externalHTML n (type=catalog, source=saspress.template10.refreshSession.source)

    n The externalHTML macro adds additional HTML to the end of the page and includes the JavaScript code that produces the pop-up that asks the user if he or she wants to refresh the session. This technique can be used with any HTML-based report.

    184 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The refreshSession template below creates macro variables and then uses those values to customize the arguments in the JavaScript function calls. The setTimeout built-in JavaScript function is used to specify a period of time to wait before calling the defined refreshSession function. The refreshSession function uses the JavaScript built-in confirm window to ask users if they want to refresh the session, and if they respond yes, the built-in Open function is used to:

    ƒ ƒ

    produce a pop-up that connects to the session in order to refresh it specify that the refreshSession function should be called again after the specified timeout period

    %let msg = Refresh Session &_sessionid?; n %let sessionTime = %sysfunc(appsrvgetn(session timeout)); %let refreshTime = %sysfunc(max(60000,1000*&sessionTime-60000));

    n Create macro variables for the values to use for the message text and the timeout period. The timeout period is set to be the longer of one minute or one minute less than the length of the session. The SAS session time is specified in seconds, while the value for the JavaScript setTimeout must be specified in milliseconds. o _thissession is used to create the URL that connects to the session. %superq is used so _thissession resolves, but without the ampersands (&) it contains causing an attempt to resolve macro variable references. The refreshSession program uses the externalHTML macro to run a program that displays a message that the refresh was successful. Note that this program is run only if the session is still active. Since the program contains only macro statements and then calls the externalHTML macro, it could have been packaged as a SAS Server Page, but then there would not have been a specific program to trigger the correct template in the INVSESS program (without augmenting the selection logic used there to use either a program name or a template name).

    Chapter 10: How to Create and Use Sessions 185

    Here is the program: %let %let %let %let

    msg = Refresh Session &_sessionid?; n sessionTime = %sysfunc(appsrvgetn(session timeout)); now = %sysfunc(datetime()); refreshUntil = %sysfunc(sum(&now,&sessionTime),datetime.);

    %externalHTML(type=catalog,source=saspress.template10.refreshSuccess.source)

    n Create macro variables that are referenced in the template. One variable (msg) is the message that the session was successfully refreshed, and the other (refreshUntil) is a formatted datetime value corresponding to the new session expiration time. The refreshSuccess template is straightforward. See Figure 10.13 for the output produced when the user clicks the OK button (Figure 10.12) to refresh the session. Here is the refreshSuccess template:

    Session Refreshed

    Session, &_sessionID, successfully refreshed.

    Will expire at &refreshUntil..



    Figure 10.13 Session Refresh Successful

    If the session has expired, the Application Server runs the program specified in the INVSESS parameter (as discussed in Section 10.5) instead of the requested program. The second observation in the metadata table shown in the example in Section 10.5 causes the template refreshFailed to be sent to the user’s browser whenever the refreshSession program is not run because the session has expired. See Figure 10.14.

    186 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Here is the refreshFailed template:

    Session Refresh Failed

    Session, &_sessionID, could not be refreshed.

    Please restart the application.



    Figure 10.14 Session Refresh Failed

    By combining the use of the following resources, it is possible to create a simple, straightforward and customizable framework for informing users before their sessions expire:

    ƒ ƒ ƒ ƒ ƒ

    scripting languages, as discussed in Chapter 1 the INVSESS option, as discussed in Section 10.5 the refreshSession program, as discussed in this section the externalHTML macro, as discussed in Section 7.5 HTML templates such as refreshSession, refreshSuccess, and refreshFailed

    10.7 Session INIT and TERM Programs The INIT and TERM arguments also exist for sessions, just as they do for requests. The INIT program runs when a session is first created and the TERM program runs when the session is destroyed or expired. These arguments can provide key functionality in situations such as these:

    ƒ ƒ

    logging session start and end times to a data set committing incomplete data to a suspense file when a session expires

    Such uses are typically application specific. In general, a program that explicitly creates a session can typically invoke any necessary INIT logic at the same time. Likewise, if a session is actively destroyed, using the appsrv_session(‘delete’) function, that program can invoke the TERM logic at the same time. And since the TERM logic is run as part of the Application Server clean-up process, the program specified in the TERM section must run without any user input or intervention, thus limiting its utility.

    Chapter 10: How to Create and Use Sessions 187

    As an example of what can be done with a session INIT program, consider that all the examples in this chapter that have provided information about the session have used the value of the sessionid variable in the message. The value for the sessionid variable is not meaningful to the user. Ideally, a more meaningful description should be provided. The SESSION INIT program can provide such a mechanism. To run an example that provides a session description, go to the sample environment and select Chapter 10. Then select Provide a Session Description (see Figure 10.15).

    Figure 10.15 Using a Session Description

    Providing this more meaningful message was accomplished by:

    ƒ ƒ

    ƒ

    creating a data set to store information about the session. assigning a value to a macro variable, sessionDescription, that contains a text description of the session. Note: You must do this before creating the session (e.g., before executing the appsrv_session(‘create’) function). adding the following code to the SESSION INIT program: %global session Description; proc sql noprint; insert into sampdata.internalSessions(cntllev=record) n set server = "&_server", port = "&_port", sessionID = "&_sessionid", Description = symget('sessionDescription'), start = datetime() ; delete from sampdata.internalSessions(cntllev=record) o where datetime() gt start + 86400 or Description = ' '; quit;

    n Insert a record into the data set for the session. The SYMGET function is used instead of a macro variable reference for sessionDescription because using SYMGET will cause a blank value to be inserted if the macro variable has not been defined.

    188 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    o Since the description will typically be needed even after the session has expired (e.g., to produce the message seen in Figure 10.15), the record cannot be deleted by the session TERM program. Instead, the INIT program deletes records for sessions that are older than 24 hours (86400 seconds). It also deletes records that have no value for the description.

    ƒ

    adding the following code to the INVSESS program to enable the description to be used in place of the session ID: proc sql noprint; select Description into: _sessionID p from sampdata.internalSessions(cntllev=record) where server="&_server" and port="&_port" and sessionid = "&_sessionID"; quit; %let _sessionID= &_sessionID; /*trim trailing blanks*/

    p If there is a row in the data set for this session, overwrite the value of session ID with the description. If there is no row, the original value of session ID is left unchanged. The output shown in Figure 10.15 was produced using a program that is a copy of the program shown in Section 10.2 that created a session. The only change to that program is that the macro variable sessionDescription is created and assigned a value before the session is created. The approach illustrated here describes a framework for how to provide users with more meaningful messages about sessions.

    P a r t

    4

    Addressing Common Application Requirements Chapter 11

    Tools and Techniques for Debugging 191

    Chapter 12

    Tips for Safeguarding Security 205

    Chapter 13

    Integrating Application Dispatcher Applications with Other Applications, Products, and Environments 225

    Chapter 14

    Maintaining an Applications Environment

    239

    Part 4 provides selected examples that show how the techniques and features of the Application Dispatcher presented in Parts 1 through 3 can be used to address approaches to common application requirements. It contains a number of short chapters that focus on how to use the ideas discussed earlier. Chapter 11 discusses the practice of testing Application Server programs. The facilities that are built-in to the Application Server are described from the context of how they should be used. The use and customization of standard testing techniques is presented. Finally, an approach to enable testing Application Server programs in a stand-alone SAS environment is also described. Chapter 12 discusses security, specifically facilities and techniques that can be used to ensure that only authorized users have access to programs and data.

    190 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Chapter 13 describes how systems built with the Application Dispatcher can be integrated with external applications and products. Chapter 14 formalizes many of the techniques presented earlier as a set of Best Practices that can facilitate supporting multiple environments (e.g., Dev/Test/Prod) as well as minimizing maintenance requirements.

    C h a p t e r

    11

    Tools and Techniques for Debugging 11.1 Introduction 191 11.2 Using the _debug Parameter 192 11.3 The PROC APPSRV LOG Statement 196 11.4 Conditionally Generating Debugging Output 197 11.5 Running Application Dispatcher Programs Using SAS Display Manager 200 11.5.1 Special Handling for SCL Programs 201 11.6 Dedicating an Application Server for Debugging 202

    11.1 Introduction Debugging is often a complex and frustrating activity. Debugging SAS/IntrNet Application Dispatcher programs can be challenging for a SAS programmer who is accustomed to using a dedicated SAS session (either interactive or batch) for development. The challenge is due to the fact that the SAS programs are being executed by a SAS server that is by-and-large invisible to the programmer. Fortunately the Application Dispatcher provides a variety of the tools and techniques to facilitate debugging. These techniques allow the programmer to address both kinds of errors typically encountered in developing programs: the wrong output is produced or no output is produced.

    192 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    11.2 Using the _debug Parameter The _debug parameter is one of the best and easiest tools to debug programs being run by the SAS/IntrNet Application Dispatcher. As discussed in Section 5.2.3, there are three values of particular note:

    ƒ

    If you specify a value of 1, then name/value pairs passed from the Application Broker to the Application Server are displayed in the user’s browser. The user is typically the programmer during debugging.

    ƒ

    If you specify a value of 16, the returned content is displayed in the user’s browser in hexadecimal format.

    ƒ

    If you specify a value of 128, the SAS log for the request is returned to the user’s browser at the end of the output.

    Best Practice: The use of &_debug in generating links was discussed earlier as a Best Practice. The use of this practice is of significant value while debugging in that the value can be set once and then propagated to all later requests that result from links. This can make debugging much easier. If the value is not propagated, the only alternative is to edit the URL for each request.

    The complete list of values for _debug is included in the SAS/IntrNet documentation. When you invoke the Application Broker with no name/value pairs as shown in Figure 11.1, the result is a link to the SAS/IntrNet documentation on the Web.

    Figure 11.1 Application Broker Link to SAS/IntrNet Documentation

    Figure 11.2 shows what the user would see when a program has an error. Using a value of 129 for _debug will allow you (the programmer) to see and fix the values passed to the program as well as to the SAS log.

    Chapter 11: Tools and Techniques for Debugging 193

    Figure 11.2 Output Produced When a Program Has an Error

    The results of using a value of 129 for _debug can be seen in Figures 11.3 and 11.4.

    Figure 11.3 SAS Log Output Produced by _debug=128 (Actual Value Used = 129)

    194 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 11.4 List of Name/Value Pairs Produced by _debug=1 (Actual Value Used = 129)

    Figure 11.3 shows two errors in the SAS log:

    ƒ ƒ

    The NOTOC option is generating an error for HTML output. The data set to be printed does not exist.

    Default values for the type of output and the data set name are set using the setDefaultValue macro (discussed in Section 5.3.3). The fact that no values were provided for outputType and data can be seen in Figure 11.4. This debugging example used the most common value, 129, which includes both the name/value pairs and the SAS log. Quite often, this provides sufficient diagnostic output to determine the problem as it includes in a single place: the input to the program, the output from the program, and the SAS log that resulted from running the program. Even when the program itself produces no output (e.g., due to a syntax error, no data being selected due to an invalid WHERE clause, etc.), a value of 128 (or 129) for _debug produces the detail needed to diagnose the problem. Note: As discussed in Section 5.2.3, a value of 129 for _debug can also be specified as _debug=FIELDS,LOG. A value of 16 for _debug is useful in diagnosing problems encountered when the program generates alternative content types and specifically for issues related to HTTP headers. Consider the example from Section 8.3.4 that uses the Replay facility to return PDF or RTF (or both) content to the user’s browser. The example presented here was slightly modified so the links for the Replay facility include _debug=16. When the Printable (PDF) link was selected in Section 8.3.4, it was returned as an attachment, due to the attachment header.

    Chapter 11: Tools and Techniques for Debugging 195

    To run the PDF example with _debug=16, go to the sample environment and select Chapter 11, select RTF and PDF, and then select Printable PDF.

    Figure 11.5 Hex Dump (_debug=16) of PDF Output

    The human readable version is listed in the right-hand column where it is clear that the correct content-type header for a PDF file is generated, followed by the content-disposition header with an attachment to be named temp.pdf. That is then followed by the blank line and then by %PDF, which is the beginning of the PDF file. When you select the Editable (RTF) link, the output shown in Figure 11.6 is returned.

    Figure 11.6 Hex Dump (_debug=16) of RTF Output

    Microsoft Word should be used to view the generated content. Running the example to generate a GIF image (using the stand-alone graph program from Section 8.4) with a value of 16 for _debug produces the output shown in Figure 11.7. The figure shows that a content-type header for GIF files was generated. To run this example, go to the sample environment and select Chapter 11. Then select GIF Image.

    196 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 11.7 Hex Dump (_debug=16) of GIF Output

    In general, using a value of 16 for _debug can be a significant tool in diagnosing problems related to HTTP headers.

    11.3 The PROC APPSRV LOG Statement The LOG statement of the APPSRV procedure specifies where the Application Server writes its log file. It also includes options that provide comparable output to what _debug=1 and 128 return to the user’s browser (and this output is included in the log regardless of the value of _debug). The DISPLAY option controls whether the SAS log for each request is written to the log file and contains output similar to _debug=128. The SYMBOLS option controls whether the name/value pairs (similar to _debug=1) are written to the log. Both have the following options:

    ƒ ƒ ƒ

    NONE – don’t write the output for any request. ERRORS – write them only for requests that completed with a SAS error message. ALL – always write them.

    You can find the location of the log files by examining the appstart.sas file for the Application Server. The location changed after SAS 8 and is also dependent on the operating system. The location is specified in the ALLOCATE FILE statement that defines the fileref LOGFILE. The default value for these parameters is ERRORS. Thus for any request that contained a SAS error, the log includes the same output as could be obtained using a value of _debug= 129. The SAS log corresponding to the error shown in Figure 11.3 can also be seen in the log file as shown in Figure 11.8. Note that the log filename is specific to the day of the week and the port number. The parameters that define the name used for the log file can be specified in the ALLOCATE FILE statement that defines the LOGFILE fileref.

    Chapter 11: Tools and Techniques for Debugging 197

    Figure 11.8 Application Server Log File with DISPLAY=ERROR

    Assuming that you have access to the log file (which on most operating systems can be viewed even while the Application Server is still running), the LOG statement can be an invaluable debugging tool as it contains the output typically needed to diagnose the problem for programs where a SAS error was generated. This is especially true for programs that produce non-HTML content where the use of a value of either 1 or 128 in _debug can corrupt the output or make it non-readable in the browser (as mentioned earlier in Section 5.2.3). The APPSRV LOG statement is also useful in cases where you need to look at an error that has been reported to you after the fact. If the program runs without errors, but the results are wrong, the DISPLAY and SYMBOLS options produce their output only if ALL was selected on the PROC APPSRV statement. This behavior cannot be controlled on a request-by-request basis. Since the ALL output can be voluminous the ALL option should be used with great care. Note: In Section 11.6, the use of a dedicated Application Server for testing will be discussed. This can facilitate the use of the ALL option.

    11.4 Conditionally Generating Debugging Output A common technique during development and testing of a program is conditionally producing extra output (e.g., running the PRINT procedure on intermediate data sets) throughout the program. Such techniques can be implemented in Application Server programs by writing and using a simple macro, testPrint, which runs the PRINT procedure on a specified data set, but only if a flag (the macro variable debugFlag is used here) has been set. The following very simple macro illustrates this: %macro testPrint(data=_last_); %global debugFlag; n %if %length(&debugFlag) ne 0 %then %do; /* flag set - produce debugging output */ proc print data=&data; title "Debug Output: &data"; o run;

    198 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    %end; /* flag set - produce debugging output */ %mend testPrint;

    n The GLOBAL macro statement ensures that the parameter exists, in case it has not been set. o The output is produced only if the _debug parameter values have been set and will be returned to the user’s browser if it is within the ODS block for _WEBOUT. Macro calls such as %testPrint(data=work.intermediateResults) can be included throughout the program. The output is only produced if the macro variable used to specify debugging is set to a non-blank value. The macro calls are always in the program and the output is turned on only when needed. To run this example, go to the sample environment and select Chapter 11. Then select No Debugging Output (Figure 11.9) and Debugging Output--Explicit debugFlag Value (Figure 11.10). The only difference between the two links is that the latter one includes a non-blank value for debugFlag in the URL.

    Figure 11.9 No Debugging Output

    Chapter 11: Tools and Techniques for Debugging 199

    Figure 11.10 Debugging Output—Explicit debugFlag Value

    If you have downloaded the sample environment, you will find the program for this example in the Chapter11 catalog, in the testprint.source entry. The program includes the following macro call: %testPrint(data=sashelp.shoes(obs=2))

    The testPrint macro conditionally generates the PROC PRINT output only when debugFlag is set to a non-blank value. The testPrint macro does not use, by default, _debug. This is true for a variety of reasons. For example, there may be occasions where such debugging output is appropriate and neither _debug=1 or 128 is desired. You may also want to set the value via some other technique (e.g., as a SET parameter in the Application Broker configuration file, or explicitly including it in a URL). If you want to ensure that debugFlag is set whenever _debug contains a value of 128, that can be easily accomplished by adding code like the following to the program specified for REQUEST INIT: data _null_; if &_debug = '1.......'b then call symput('debugFlag','Yes'); run;

    Note: Bit constants are used to facilitate checking that the value contains 128 (i.e., bit 8, counting from the left, is set to ON). When this simple DATA step is run in the INIT program, it updates the value of debugFlag based on the value of _debug. The example below, which produces output similar to that shown in Figure 11.10, demonstrates the technique. Other logic using any of the information available to the INIT program (e.g., all the parameters passed to the Application Server by the Application Broker) can also be used to set or reset its value. Note that the program does not have an ELSE condition because it presumes that the existing value should be unchanged. Since the test Print macro has a %GLOBAL statement, if debugFlag has not been defined or set, it is created by the first execution of the testPrint macro and set to null.

    200 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    To run this example, go to the sample environment and select Chapter 11. Then select Debugging Output--debugFlag Set using _DEBUG.

    11.5 Running Application Dispatcher Programs Using SAS Display Manager Most SAS programmers are used to using an interactive SAS session for development and testing. An interactive SAS session provides a much richer environment for testing and debugging than the Application Server. For example:

    ƒ

    A program can be run in pieces with any created data sets examined by the Viewtable window.

    ƒ ƒ

    The data can be adjusted part-way through to test for other conditions. The debugging options of the DATA step (or SCL for SCL programs) can be used to step through the logic.

    Note: If multiple steps write to _WEBOUT, the MOD option will be needed on the FILENAME statement for _WEBOUT during debugging so the output is appended instead of overwritten by each step. The MOD option is not allowed when the program is run by the Application Server because _WEBOUT uses the socket access method. The behavior of the MOD option is built in to the socket access method. Including the MOD option will produce a syntax error. With a few set up steps, it is possible to run programs designed for an Application Server environment in a stand-alone SAS session (either interactive or batch). Here are the steps:

    ƒ

    Ensure that a fileref for _WEBOUT is defined, e.g.: filename _webout ‘\debuggingOutput.html’;

    ƒ

    Assign values to all the macro variables for the request. This can be done using a series of %LET statements. Alternatively, a call to the %saveMvars macro (Section 10.3) can be added to the top of the program to be debugged, e.g.: %saveMvars(lib=sampdata,data=chapter11DebugData,saveFlag=Y,includeAutos=Y)

    The macro variables can then be made available in an independent SAS session by invoking the %restoreMvars macro (Section 10.3), e.g.: %restoreMvars(lib=sampdata,data=chapter11DebugData)

    ƒ

    If sessions are involved, define a libref for SAVE. If a program for sessions is to be debugged, it can be run with a long timeout in order to allow you to start an independent SAS session that can define the SAVE libref and then use PROC COPY to save the contents of the SAVE library elsewhere for your testing.

    Chapter 11: Tools and Techniques for Debugging 201

    ƒ

    Ensure that all the set-up options from the INIT program are in place (e.g. the SASAUTOS option, etc.). The easiest way to do this is to run that program using the SAS Display Manager.

    ƒ

    Then for any program except SCL entries, run the program from the program editor window.

    Note: For compiled macros, include the macro source followed by a call to the macro. SCL programs can be run with the DISPLAY procedure. The data set sampdata.chapter11DebugData is available in the sample environment and was created by (temporarily) including the above %saveMvars macro call in the SAS Server Pages example of Section 7.6. If you have downloaded the sample environment, you are encouraged to try these actions:

    ƒ ƒ ƒ ƒ ƒ

    Start an interactive SAS session. Define librefs for SAMPDATA and SASPRESS. Define _webout. Run the %restoreMvars macro as listed above to load the macro variables. Copy saspress.chapter7.runTemplate_Proc_Print to the program editor and run it in pieces. The MLOGIC option can be used to see the setDefaultValue macro calls at the top of the program. The _debug parameter can also be added to the data _null_ step if desired.

    Note: Since the %externalHTML macro writes to _webout without the MOD option, the ODS PROC PRINT output will be overwritten.

    11.5.1 Special Handling for SCL Programs Running SCL programs in a SAS Display Manager session requires special handling if the programs use the SCL list to get the name/value pairs. If the SCL program is executed as a standalone program with PROC DISPLAY, there is no input SCL list and the program fails if it attempts to query the list for values. One way to handle this is to run the program with the SCL debugger turned on and step through the statements one at a time, skipping those that query the list. The values would need to be explicitly assigned in the debugger. Alternatively, a driver program can be written that creates an SCL list from a saved SAS data set and then calls the program. A sample program to do this is included in the sample environment (saspress.chapter11.runSCLStandAlone.scl): Note: The SCL code assumes that the name of the data set with the name/value pairs has been assigned to the macro variable listData. rc = rc; /* suppress compile time message */ init: list = makelist(); dsid = open("&listData"); nameVnum = varnum(dsid,'NAME'); valueVnum = varnum(dsid,'VALUE'); do while(fetch(dsid) = 0); n

    202 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    name = getvarc(dsid,nameVnum); value = getvarc(dsid,valueVnum); if upcase(name) = '_PROGRAM' then program = value; rc = setnitemc(list,value,name); end; call display(program,list); o dsid = close(dsid); list = dellist(list); return;

    n Loop through the data set and add the items to an SCL list. Assign the name of the program to an SCL variable so it can be used in the call display. o Run the program using the SCL variable created above. The program may need to be recompiled to turn the debug option on (or off). The data set sampdata.chapter11SCLDebugData is also included in the sample environment. It contains the name/value pairs that are input to the SCL Paging SCL Example (Section 7.4). You are encouraged to run this SCL program as a standalone program by including the following statements in the SAS program editor and submitting them (along with the other set-up steps described above). The program must be compiled with the DEBUG parameter. You can either recompile this entry after turning the SCL debugger on, or you can copy the SCL entry to another entry and compile it, with the SCL debugger turned on. %let listData = sampdata.chapter11SCLDebugData; proc display c = saspress.chapter11.runsclstandalone.scl; run;

    Assuming that the same set-up steps (e.g., defining _WEBOUT, etc.) as described earlier have been done, this program can be run (in debug mode) to facilitate the debugging process. When SCL programs are run in debug mode, no SAS code is submitted. For this reason debug mode should be used only to diagnose problems in the SCL logic.

    11.6 Dedicating an Application Server for Debugging Dedicating an Application Server for debugging can greatly facilitate testing and debugging Application Server programs. The dedicated server allows the environment to be customized in order to facilitate the testing process. The steps to do this are straightforward:

    ƒ

    Define an Application Server using the SAS/IntrNet Service Configuration Utility. For development purposes it may be desirable always to select a Socket service since Socket services offer some additional flexibility in testing.

    ƒ

    Define the testing Application Server to the Application Broker configuration file. If the same Application Broker directory is used for both the production and debugging Application Server, it may be desirable to adopt a convention in the service names to distinguish them (e.g., a suffix of _test). An alternative is to create another copy of the

    Chapter 11: Tools and Techniques for Debugging 203

    broker directory for a test version of the Application Broker. In this case, the same service name can be used. The Application Server defined in the configuration file can be running on the programmer’s local PC, even if the Application Broker is running on a centralized Web server.

    ƒ

    Set the DISPLAY and SYMBOLS options to ALL in the PROC APPSRV LOG statement.

    ƒ

    Assign debugging options in the INIT program. Such options might include these: … mprint (or mlogic or both) … a value that is assigned to debugFlag as discussed in Section 11.4

    ƒ

    Edit the Application Broker configuration file to set a default value for _debug of 128 (or some other value that includes 128, such as 129). This can be overridden by supplying a value in the URL via a link or a form.

    ƒ

    Use the Application Broker Set or ServiceSet directives to specify any other desired values for testing (e.g., debugFlag discussed above can be set in the Application Broker configuration file or in the Application Server INIT program).

    ƒ

    Optionally the STATISTICS option of PROC APPSRV can be used to populate a data set with key values from each request. If your programs have a common set of programspecific name/value pairs, those names can be included in the template statistics data set. That data set can then be used to test programs in a stand-alone SAS session as discussed in the previous section.

    Note: These techniques assume that the Best Practice of using _URL, _SERVICE, _debug has been consistently followed in programs that generate links. If the values are hard-coded, programs run by clicking links or submitting forms are not submitted to the correct Application Server. If the debugging Application Server is running on a server where the programmer can run SAS interactively, a socket service can be run in an interactive SAS session by including in the program editor the appstart.sas file used to start the server before submitting the program. This requires that the default &sysparm reference in the PROC APPSRV statement be replaced by port=[Socket-Service-Port-Number]. If the Application Server is run this way, then for any program which uses the _debug option in a DATA step, the debugger window is activated in the Application Server SAS session. Unfortunately this does not work for SCL programs compiled with the debug option. Since using the debugger can be time consuming, quite often the Application Broker generates a timeout message, thus preventing the output from being visible in the browser. When you are doing this level of debugging, however, this action might not be a problem. Note that a longer timeout value can be defined in the Application Broker configuration file.

    204 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    12

    Tips for Safeguarding Security 12.1 Introduction 205 12.2 The Application Server Environment 206 12.2.1 Limiting Which Application Brokers Can Access an Application Server 206 12.2.2 Hiding Passwords 208 12.2.3 Best Practices for a Secure Application Server Environment 211 12.3 Controlling Access to Data and Reports 212 12.3.1 Customizing Menu Choices 219 12.3.2 Using AUTH=HOST 223

    12.1 Introduction A number of facilities and techniques are available that can be used by the Application Server and the programs that are run by the Application Server to ensure that only authorized users have access to programs and data. This chapter covers two basic approaches to safeguarding security.

    206 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    12.2 The Application Server Environment To ensure a secure environment for the Application Server, at a minimum you must take these two steps:

    ƒ

    enabling http authentication by the Web server (as discussed in Section 5.3.3) to ensure that users can be uniquely and unambiguously identified

    ƒ

    limiting access to debugging facilities

    All the techniques presented in this chapter depend upon these two facilities. Enabling http authentication at the Web server validates the user by prompting for a user ID and a password. This facility ensures the identity of the user who is using operating system facilities on the Web server. Many organizations use http authentication for this reason. With http authentication, the entered user ID can be made available to any Application Server program (as discussed in Section 5.3.2) by having the Application Broker export the Web server environment variable REMOTE_USER. The default name is _RMTUSER. A great deal of information regarding the Best Practice: For production application servers, a DebugMask value of 34 provides the most protection. Application Dispatcher environment is This value allows values for _debug of 0 only (no available via the debugging facilities debugging output), 2 (Broker Version, Time, and the provided by _debug. Limiting access to this SAS Powered Logo), or 32 (the SAS Powered Logo information is critical to ensure that the only). All other values could enable users to access Application Dispatcher environment is information they should not have. In particular, values of secure. As mentioned in Section 5.2.3, the 1 and 4 should be disabled. Application Broker DebugMask or ServiceDebugMask directives should be used to limit what types of debugging output are available.

    12.2.1 Limiting Which Application Brokers Can Access an Application Server One of the strengths of the Application Dispatcher is that any Application Broker can access any SAS Application Server as long as the TCP/IP host (name or number) and port number are known. This also means that it is possible to submit requests to an Application Server from any Web server, including a user’s local Web server. If a hostile or curious user were to do this he would be able to circumvent any security put in place through the use of _rmtuser or DebugMask. Such an Application Broker could then be used to connect to the Application Server with http authentication disabled. This action would allow the user to set any value he wanted for _rmtuser either in the URL or via a Set or ServiceSet directive in the local Application Broker configuration file. Likewise, DebugMask could be set to allow all debug values. In order to prevent such a security exposure, the Application Dispatcher environment should ensure that only the expected Application Broker can send requests to an Application Server. This exposure can be limited by restricting the Application Brokers that can make requests to an Application Server using the FROMADR argument of the REQUEST statement in PROC APPSRV. For example, if the IP address for the Web server where the Application Broker is running is 1.2.3.4, the following REQUEST statement ensures that only Application Broker requests from that Web server are allowed: proc appsrv . . . . . .; request fromadr=("1.2.3.4"); run;

    Chapter 12: Tips for Safeguarding Security 207

    A request from any other IP fails and sends a message back to the user as shown in Figure 12.1. Warning: Unfortunately, it is relatively easy to get the server IP address and port number by examining the HMTL from any request that uses sessions, including lightweight sessions where ODS generated a page with embedded text and graphics. While there are techniques that can often prevent a user from viewing the HTML source, they are typically not robust and are not covered here.

    Figure 12.1 IP for the Application Broker Not Authorized

    The FROMADR argument does allow multiple Application Brokers (from a single Web server) to submit requests to an Application Server. If additional security is needed to limit this access, that can be done by using the Set or ServiceSet directives in the Application Broker configuration file to provide a variable name and value that can be checked in the INIT program of the Application Server. For example, suppose you include the following in the Application Broker configuration file: ServiceSet myBroker "a secret value"

    You could then add the following logic in the INIT program: options nosource; data _null_; if “&myBroker” ne "a secret value" then do; /* Generate HTML to say this is not an authorized broker using, for example, the externalHMTL macro */ call execute('ENDSAS;’); end; run;

    This combination of actions ensures that only requests from the Application Broker with this special name/value pair defined in the configuration file are allowed.

    Best Practice: The NOSOURCE option is included so that the password value is not visible. This is primarily a concern if DebugMask has not been set to limit access to the SAS log (e.g., _debug values that include 128). Section 12.2.2 discusses additional techniques to secure password values.

    Note: See the SAS/IntrNet documentation for additional tips and techniques that protect the broker configuration file.

    208 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    In order for this technique to provide the desired level of protection, take the following steps:

    ƒ

    DebugMask must be set to disable a value of 1 (which causes the Application Broker to echo all the name/value pairs). Note that myBroker is not visible in the URL or in the Web server log files.

    ƒ

    All the Application Server programs need to be reviewed to ensure that none of them include output that displays the name/value pairs passed to the Application Server (e.g., they don't enable SASHELP.VMACRO to be printed by, for example, the data set paging example; that if a debug value of 128 is allowed, that the programs do not include a %put _all_, etc.).

    ƒ

    The Application Broker configuration file must be secured so that unauthorized users are not allowed to read its contents.

    12.2.2 Hiding Passwords Securing the values of passwords is always a concern. In the example provided in the previous section the program contained the password as open text. Additional protection can be realized by packaging that functionality as an SCL program. An SCL program (e.g., checkpw.scl) can be compiled and then a nosource copy of it can be created for use by the Application Server: init: if symget('MYBROKER') ne 'a secret value' then n do; /* not the correct broker */ submit terminate; endsas; endsubmit; end; /* not the correct broker */ return;

    n The value for myBroker is not retrieved from the SCL list since this program is not executed directly by the Application Server. Thus it has no access to the list. The SYMGET function is used to get the value of the macro variable. This program can be run in the INIT program as follows: proc display c = libref.catalog-name.checkpw.scl; run;

    This program terminates any request that does not have the value for MYBROKER set in the Application Broker configuration file. However, it does not provide any message or information to that effect. In addition, the program still contains the password as a hard-coded string. Both of these deficiencies can be addressed using techniques presented in Section 6.3. References to macro variables as SCL code resolve at compile time. Thus, replacing the IF statement with the following code eliminates the password value from the SCL source code: if symget('MYBROKER') ne “&myBrokerPW” then

    The compiled code still contains the correct value to be compared to. The second deficiency can be addressed by including a call to the externalHTML macro (Section 7.5.1) to display a customizable message to the user. Note how the Application Broker fields _ADMIN and _ADMAIL (see Section 5.3.1) are used in this example to provide contact information.

    Chapter 12: Tips for Safeguarding Security 209

    Request Submitted from an Invalid Broker

    This request submitted from an invalid Application Broker Contact &_ADMIN if you believe this error to be incorrect.

    Note: Instead of using an HTML template, the SCL program could redirect the request using the location http header (see Section 8.2.1.1) to redirect the user to define a static HTML page. Here is the complete SCL program that checks the password value and terminates the request if the password check fails: init: if symget('MYBROKER') ne "&myBrokerPW" then n do; /* not the correct broker */ submit terminate; %externalHTML(type=catalog, source=saspress.template12.invalidBroker.source ) endsas; endsubmit; end; /* not the correct broker */ return;

    n The APPSRV_UNSAFE function can be used instead of the SYMGET function if protected special characters are included in the password value. To run this example, go to the sample environment and select Chapter 12. Then select Password not set, e.g. an Invalid Application Broker (Figure 12.2) or Valid Password (Figure 12.3). The first link simulates the results obtained when an invalid Application Broker is used (i.e., the check fails); the second link simulates the results when a valid Application Broker is used. The following sample program demonstrates this functionality. When a valid password is provided, the output shown in Figure 12.3 is produced. proc display c = &_pgmlib..&_pgmcat..checkpw.scl; run; %externalHTML(type=catalog, n source=saspress.template12.validBroker.source )

    n The EXTERNALHTML macro will display only the validBroker.source template if the password is valid. If the password is not valid, the SCL program will display the invalidBroker.source template.

    210 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Note: The sample environment does not include the password check in the INIT program as that would have caused all the samples to fail because of an invalid password. The value for MYBROKER is passed in via the URL instead of via specification in the Application Broker configuration file. If you decide to use this technique, the SCL program should be called in the INIT program and MYBROKER should be set in the Application Broker config file.

    Figure 12.2 Application Broker Password Not Valid

    Figure 12.3 Valid Application Broker Password

    This example demonstrates that the functionality to protect passwords can be used in multiple ways such as these:

    ƒ ƒ

    in the INIT program to check that the request was submitted from the expected broker in any application (by including the appropriate PROC DISPLAY call) that needs to check passwords

    Best Practice: Choose a password value that is hard to guess, including mixed case and special characters. The name of the password field can also be any valid SAS name.

    Chapter 12: Tips for Safeguarding Security 211

    12.2.3 Best Practices for a Secure Application Server Environment There are a number of additional best practices that can maximize the security of the Application Server environment:

    ƒ

    In order to protect any program code from being visible, options such as NOMPRINT, NOMLOGIC, NOSOURCE, and NOSOURCE2 should be included at the top of the program specified in REQUEST INIT. The use of these options prevents any of the SAS source code from being included in the SAS log. They can be used in combination with the DebugMask directive to limit access to the SAS log.

    ƒ

    Use compiled macro code or SCL to minimize the likelihood of the source code being viewable by a user.

    ƒ

    Use the ADMINLIBS statement to specify program libraries that can be restricted to Administrator access. The _ADMINPW option should also be specified in the PROC APPSRV statement.

    ƒ

    Use the VERIFY option of the SESSIONS statement to ensure that only the original user can reconnect to a session. The VERIFY option is a space-delimited list of variable names that the Application Server can check when reconnecting to session. It checks that the names have the same value as the original request that created the session. Fields that should be considered for inclusion in the list are _RMTUSER and _RMTADDR (the TCP/IP address where the user’s browser is running) or _RMTHOST (the DNS name corresponding to _RMTADDR). The use of VERIFY makes it difficult for a user to acquire another user’s URL and access that person’s session information.

    ƒ

    Store data and programs in separate SAS libraries and, if the application does not require Write access, use the operating system to make the libraries Read-only. Alternatively, use the READONLY option.

    ƒ

    The Xplore sample application shipped with SAS/IntrNet allows a user to browse data in any library defined to an Application Server. If such access exposes security issues, the Xplore application can be disabled by removing (or commenting out) the ALLOCATE LIBRARY statement for the SAMPLIB libref (or simply by removing it from the PROGLIBS statement). Alternatively, the techniques described in the next section can be used to ensure that users have access only to the data or reports that they are authorized to see. Note: The SASHELP library is, by default, excluded from the list of libraries displayed by Xplore. SASHELP should not be displayed as this enables a user to browse data sets that contain information that could expose security risks (e.g., pathnames for libraries).

    ƒ

    If SAS programs are stored in SAS catalog entries, do not specify just the libref in the PROGLIBS statement. The catalog names should be specified in the PROGLIBS statements. That is, do not enable any entry in an entire library to be specified as the program to run (e.g., the value of _PROGRAM). Specifying both the library and the catalog allows other catalogs to contain code or (HTML templates) without allowing them to be run or viewed directly by a user. The appstart.sas file provided with the sample environment specifies the allowed catalog names in the PROGLIBS statement.

    212 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    Specify an administrative password using the ADMINPW option when starting the Application Server. This prevents users from running administrative programs such as STOP (e.g., a Denial of Service attack) without supplying the password.

    ƒ

    Limit access to the Application Broker configuration file so that only authorized staff can update (or read) it. Protecting this file increases the likelihood that the security put in place through the use of DebugMask and _RMTUSER is effective.

    ƒ

    Since the Application Broker configuration file is stored in the same directory as the Application Broker executable, some Web servers allow it to be downloaded just like an HTML file. This possibility can be tested by trying to access it via a URL (e.g., http://yourserver/scripts/broker.cfg). If this URL allows the Application Broker configuration file to be downloaded, the Web server configuration should be modified to prevent this. For example, in IIS (Internet Information Service), you can enable Execute access but turn off Read access (for the scripts or for the cgi-bin virtual directory).

    12.3 Controlling Access to Data and Reports There are a number of approaches that can be used to ensure that users are allowed access only to certain data or reports. Assuming the measures described previously have been implemented, the use of _RMTUSER can ensure that users see only the data and reports which they are authorized to see using a simple metadata approach. The metadata defines rules for which reports any user is allowed to access or run. A sample of one such metadata structure is included in the examples for this chapter. To see this metadata, go to the sample environment and select Chapter 12. Then select Sample User Access Data. Note: The sample environment does not use _RMTUSER because that would require you to define such users in your environment. Thus, these examples use a field (user ID) whose value is passed in as a name/value pair. Before you use the provided samples, change the examples to use the value of _RMTUSER.

    ƒ

    The first table (see Figure 12.4) maps users to groups of users who have common privileges. This eliminates the need to define privileges for each user; the privileges need only to be defined for each group. In this example, three users are defined with each user included in one or more groups.

    Chapter 12: Tips for Safeguarding Security 213

    Figure 12.4 Data Set That Defines Groups for Each User

    ƒ

    The second table (see Figure 12.5) defines the data libraries each group is allowed to access. Note that the paths are set assuming a Windows installation. It is also assumed that the paths enable different users to have access to different components of the SASHELP library (e.g., superusers have access to the views that correspond to dictionary tables). The use of this table is conceptually the same as the idea presented in Section 9.3.1 where the available librefs were defined in the INIT program. The key difference here is that _rmtuser is used to determine which libraries to assign.

    Figure 12.5 Data Libraries to Define Based on Group

    214 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    The third table (see Figure 12.6) defines the programs (and thus, what reports) users in each group are allowed to run. The table includes a prefix value to be compared to the value of _program. If the prefix value matches the value of _PROGRAM (i.e., the SAS =: operator), then users in the group are allowed to run that program. This sample data imposes the following restrictions: …

    Browsers are allowed to run only the programs in the Chapter12 catalog whose names begin with RPTS.

    …

    Administrators are allowed to run any program in the Chapter12 catalog.

    …

    Superusers are allowed to run any program in the SASPRESS library that is in a catalog whose name begins with CHAP (subject, of course, to it being defined in a PROGLIBS statement).

    Figure 12.6 Allowed Program Mask Based on Group

    A simple, but effective, security scheme can be implemented by combining the use of a metadata structure similar to the above with the technique presented in Section 9.3.4 to terminate a request by an unauthorized user. The following sample macro, defineAccess, implements this scheme of defining what data libraries each user is allowed to access. It also generates a message (to the user’s browser) if the user is not authorized to access any data libraries. Note: You can adapt and customize the following sample macro as needed to use other metadata data sets or structures as well as different templates. %macro defineAccess (users = sampdata.chapter12Users, datalibs = sampdata.chapter12Libraries, programs = sampdata.chapter12Programs, notAuthorizedTemplate = saspress.template12.notAuthorized.source ); %local groups authorized; %let _rmtuser = &user;

    n

    Chapter 12: Tips for Safeguarding Security 215

    proc sql noprint; /* determine the groups for this user */ select distinct(quote(upcase(trim(group)))) into: groups separated by ' ' Y from sampdata.chapter12users where upcase(userid) = "%upcase(&_rmtuser)"; quit; data _null_; /* assign the libname for the authorized library */ set &datalibs; where upcase(group) in (&groups); Z rc = libname(libname,path); run; %let authorized = 0; data _null_; /* check the prefix on the program name to see if user is authorized */ set &programs; where upcase(group) in (&groups) and q "%upcase(&_program)" =: upcase(trim(program)); /* if at least one row passed user is authorized */ call symput('AUTHORIZED','1'); stop; run; %if not &authorized %then r %do; /* terminate the request */ %externalHTML(type=catalog, source=¬AuthorizedTemplate ) endsas; %end; /* terminate the request */ %mend defineAccess;

    n For purposes of demonstration, _RMTUSER is assigned from a value defined in the calling demo HTML page. In a real application, _RMTUSER itself would be compared with the metadata. You should remove the %LET statement to use the http-authenticated value of _rmtuser. Y Create a macro variable which contains a list of the quoted values for the groups this user belongs to. Z Assign all the librefs for data sets that the groups this user belongs to are allowed to access. q Check to see if any of the groups the user belongs to are allowed to access this program. The macro variable is reset if the user belongs to at least one group with access. r If the user is not authorized, return a message to that effect using the externalHTML macro (Section 7.5.1) and then terminate the request using an ENDSAS statement as discussed in Section 9.3.4. To run the examples demonstrating this, go to the sample environment and select Chapter 12. Then select one or more of the choices under Limiting Access to Reports. The results can be seen in Figures 12.7 through 12.15.

    216 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 12.7 Reader1 Allowed to Run Programs in Chapter12 Catalog Whose Names Begin with RPTS Using COREDATA

    Figure 12.8 Reader2 Allowed to Run Programs in Chapter12 Catalog Whose Names Begin with RPTS Using COREDATA and WEBDATA

    Figure 12.9 Author Allowed to Run Programs in Chapter12 Catalog Whose Names Begin with PTS Using COREDATA, VIEWS, and WEBDATA

    Chapter 12: Tips for Safeguarding Security 217

    Figure 12.10 Reader1 Not Allowed to Run Other Programs in Chapter12 Catalog

    Figure 12.11 Reader2 Allowed to Run Other Pprograms in Chapter12 Catalog Using COREDATA and WEBDATA

    Figure 12.12 Author Allowed to Run Other Programs in Chapter12 Catalog Using COREDATA, VIEWS, and WEBDATA

    218 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 12.13 Reader1 Not Allowed to Run Programs in Other Catalogs Whose Names Begin with CHAP

    Figure 12.14 Reader2 Not Allowed to Run Programs in Other Catalogs Whose Names Begin with CHAP

    Figure 12.15 Author Allowed to Run Programs in Other Catalogs Whose Names Begin with CHAP Using COREDATA, VIEWS, and WEBDATA

    Chapter 12: Tips for Safeguarding Security 219

    The same SAS code (with the exception of the FOOTNOTE statement) is used in each of the three programs. Note that the code displays a list of the librefs defined in the metadata shown above that each user is authorized to access. You can compare the metadata with the results to validate that the %defineAccess macro limits both a user’s access to certain programs as well as to certain data libraries. Note that in the sample environment, the defineAccess macro is invoked in the individual programs instead of the INIT program. If the %defineAccess macro was included in the INIT program for the sample environment, none of them would have been runnable without the dummy user field being provided with a value of SuperUser. In an application environment it is usually best to run defineAccess in the INIT program. However, if only certain sets of programs need to be restricted, defineAccess can be used in just those programs, just as it is called in the sample environment. The metadata approach described above can easily be extended to provide additional security options, e.g.,

    ƒ ƒ

    limiting access to specific data sets

    ƒ

    menu options

    applying WHERE clauses to data sets based on who the user is

    Best Practice: Consider the use of data set passwords to provide security for the metadata tables themselves.

    Adding metadata tables or modifying the tables used in the example above should enable a variety of security schemes to be implemented in an Application Dispatcher environment.

    12.3.1 Customizing Menu Choices A common application requirement is to limit menu choices based on who the user is. You can do that using techniques similar to those described in Section 12.3. This technique is also commonly used for security (e.g., showing users only the options they are authorized to use). Note: Limiting menu choices, while commonly used for security reasons, is not a robust security implementation. If a user received a link (e.g., via e-mail) to a report program, she would still be able to access it. Thus, limiting menu choices should be combined with the techniques discussed in Section 12.3 (assuming the choices were implemented using _RMTUSER). Generating user-specific menus can be easily done using a metadata approach. Such a data set should contain the appropriate menu choices for each group (as defined in Section 12.3) of users, along with (at a minimum) the following information:

    ƒ ƒ

    the group

    ƒ

    the label or description for the menu item

    the information needed to generate the link (e.g., the name of the program to run along with any name/value pairs needed by the program)

    The following program demonstrates how to implement a simple metadata-driven menu system. The SAS Server Pages program that enabled a user to select a library, a data set, and the variables described in Section 7.6, is the example program used for this example. Depending on the group, the user is allowed to do the following things:

    ƒ

    Select variables and view the Menu Choices data set (if Group = Browser). See Figure 12.16.

    ƒ

    Select any data set and then its variables from the SAMPDATA library (if Group = Admin). See Figure 12.17.

    ƒ

    Select any library, any data set and then its variables (if Group = SuperUser). See Figure 12.18.

    220 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    To run this example, go to the sample environment and select Chapter 12. Then select one or more of the choices for Customizing Menu Choices.

    Figure 12.16 Generated Menu Choices for the Browser Group

    Figure 12.17 Generated Menu Choices for the Admin Group

    Chapter 12: Tips for Safeguarding Security 221

    Figure 12.18 Generated Menu Choices for the SuperUser Group

    Figure 12.19 No Menu Choices Generated for an Unknown User

    Figure 12.19 shows the results for a user not authorized for any of the menu items defined in the metadata. %setDefaultValue(user,Not Defined)

    n

    %externalHTML (type=catalog, source=saspress.template12.menuHeader.source ) %let _rmtuser = &user;

    p

    %let groups = "-NONE-"; proc sql noprint; /* determine the groups for this user */ q select distinct(quote(upcase(trim(group)))) into: groups separated by ' '

    o

    222 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    from sampdata.chapter12users where upcase(userid) = "%upcase(&_rmtuser)"; quit; %let trailerTemplate = saspress.template12.menuTrailer.source; r data _null_; file _webout; if _n_ = 1 and lr then call symput('trailerTemplate', s 'saspress.template12.nochoices.source'); set sampdata.chapter12MenuChoices end=lr; where upcase(group) in (&groups); put '

  • ' Description ''; run; %externalHTML(type=catalog, u source=&trailerTemplate )

    n Define a default value for User. The reader should use _RMTUSER instead of User which is used in the sample environment. o Use a template to generate the appropriate header text for the menu system. p As in the above example, remove the %LET statement in order to use the http-authenticated value of _RMTUSER. q Determine what group the user belongs to using the same data sets and logic as used in the example in Section 12.3. \ Assign the appropriate template for the text to follow the menu choices based on whether the user will have menu choices assigned to him or her. ] If no menu choices are defined for the user, use a template that indicates that no menu choices are defined. ^ Generate the link for the report program. The program to run is stored in one variable (Program), and any other name/value pairs are stored in a second variable. Any number of data structures could be used with a similar program used to generate the menu. _ Using a template, generate the text to appear below the menu choices. This sample demonstrates how a single program can offer different views and functionality based on the name/value pairs passed to the program. A simple variation of this technique could be used to enable drilling down through data where the user’s level in the organizational hierarchy defines what he is allowed to see. A program that enables drill-down analysis could be used to enable a user to view the data starting at her level and below.

    Chapter 12: Tips for Safeguarding Security 223

    12.3.2 Using AUTH=HOST The AUTH option of PROC APPSRV is another option that can limit access to data sets to only authorized users. This option requires a username and password (defined using the name/value pairs _username and _password) be provided with each request. Reconnecting to an existing session uses the username and password value from the original request that created the session. The request runs using the operating system credentials for the user that was defined using _username. All access to catalogs, datasets, and external files is validated against the operating system using the provided values for _username and _password. The AUTH option is specified in the PROC APPSRV statement. Thus, it applies to every request made to the Application Server. The PROC APPSRV statement has two options that provide default values to use. The values specified in the GUESTUSER and GUESTPASS options are used for requests where a request-specific value is not provided. The AUTH option also requires that the userID under which the Application Server is running have specific operating system privileges. Any user running a request (i.e., the values for _username) must also have specific operating system privileges in order for the AUTH options to work. This AUTH option provides security that can be as robust as the security for the operating system where the Application Server is running. This option requires that each program handle scenarios where the operating system authentication failed (e.g., due to typos in the username or password, an unauthorized user, an authorized user’s username or password has expired, etc.). The complexity of this requirement and its resulting overhead may limit the utility of this option. Note: See the SAS/IntrNet Web site for the Application Server for additional information on using the AUTH=HOST option.

    224 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    13

    Integrating Application Dispatcher Applications with Other Applications, Products, and Environments 13.1 Introduction 225 13.2 htmSQL 226 13.2.1 Integrating htmSQL with the Application Dispatcher 228 13.2.2 Integrating the Application Dispatcher with htmSQL 230 13.3 Single Sign-On Environments 235 13.4 External Session Facilities 236

    13.1 Introduction Applications developed using the Application Dispatcher often need to be integrated with other applications, products, and environments. Many of the techniques discussed earlier can be used to facilitate such integration. This chapter presents three selected examples of such external integration.

    226 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    13.2 htmSQL As discussed in Chapter 1, htmSQL provides a facility to run SQL queries and embed the results in HTML pages (as well as XML, CSV, etc.). Recall that htmSQL is a CGI program that resides on the Web server and can be used to access and update a SAS data set using either SAS/SHARE or the SAS Scalable Performance Data Server (which can be run on the same or a different server than the Application Dispatcher). An input file (e.g., a file whose name includes the .hsql extension) contains SQL statements embedded in HTML. Note: See the SAS IntrNet documentation for complete information about how htmSQL works and the statements and directives that can be included in hsql files. If either SAS/SHARE or the SAS Scalable Performance Data Server is available, htmSQL should be considered for any requirement to generate a dynamic HTML page where the data to be included can be generated from SQL queries. Like the SAS Server Pages examples, and the use of external HTML templates discussed in Section 7.6, htmSQL does not require any complicated quoting requirements. For example, the following hsql file produces the report shown in Figure 13.1.

    htmSQL Example

    Sample htmSQL Report
    Name Sex Age Height Weight {query server="localhost:5011"} no {sql} select name, sex, age, height format=5.1, weight format=5.1, case (sex) p when('M') then 'Blue' when('F') then 'Green' else 'Red' end as color from sashelp.class where age le 12 {/sql} {eachrow} q
    {&name} {&sex} {&age} {&height} {&weight} {/eachrow} {/query}
    Generated {&sys.fulldate}



    s

    n htmSQL directives are included in curly braces ({}). Text not included in curly braces is assumed to be text to be sent to the user’s browser as is. o The {query} directive defines what server to send the query to. In this case, the TCP/IP name and port are used. The {sql} directive, which ends with the {/sql} directive, defines the SQL statement to be sent to the data server. p The CASE expression is used to create a conditional variable that can be used to customize the HTML display. q The {eachrow} . . .{/eachrow} directives instruct that the embedded text is to be repeated for each row returned by the query. r Variable values returned by the SQL query are referenced by prefixing the variable name with an ampersand (&) and embedding the reference in {}. s System variables are referenced the same way. Name/value pairs from the URL can also be referenced as {&name}. Figure 13.1 htmSQL Report

    Another example is shown in the following hsql file which generates a select tag in the form of a drop-down list of unique values. The data set and variable are passed as name/value pairs on the URL.

    228 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    {query server="localhost:5011"} {sql} select distinct {&variable} as value from {&dataset} {/sql} {eachrow} {&value} {/eachrow} {/query}

    The results of running the above hsql file can be seen in Figure 13.2 for the case where Dataset and Variable are defined to be SASHELP.SHOES and Product. This use of htmSQL to produce an HTML file with a data-driven select tag provides similar functionality to the SAS Server Page example (Section 7.6) that used the generateOptionTag macro but has the added flexibility of being able to use any SQL query to return the values. For example, the generateOptionTag macro could not select distinct values from a data set.

    Figure 13.2 HTML Select Tag Created Using htmSQL

    13.2.1 Integrating htmSQL with the Application Dispatcher The Application Dispatcher can invoke or execute htmSQL as long as the following two pieces of information are known:

    ƒ ƒ

    the name of the htmSQL executable (on the Web server) the path to and the name of the HSQL file

    Note: Some Web servers (e.g., IIS) enable hsql files to be mapped to the htmSQL executable. In such cases, there is no need to define the location of the htmSQL executable. While the name of the htmSQL executable can be hard-coded, ideally there should be a parameter (like _url for the Application Broker) that specifies the location of the htmSQL executable. A simple method is to use the Application Broker Set directive (see

    Best Practice: Eliminate hard-coding of such parameters whenever possible. Such values can be specified using any of the following approaches: • Set directive in the Application Broker config file (see Section 5.3.3). • %LET statements in the Application Server INIT program (see Section 9.3.2). • Metadata table that contains name/value pairs that can be loaded in the INIT program (see Section 14.3).

    Chapter 13: Integrating Application Dispatcher Applications 229

    Section 5.3.3). For example, the following directive can be added to the Application Broker configuration file: Set _htmSQL "/scripts/htmSQL.exe"

    This value can then be used in a SAS program as &_htmSQL. A ServiceSet directive can also be used. The use of this field can be seen in the following sample program. The data set and variable name are passed in from the link. The output is the same as is shown in Figure 13.2. To run the example in this section, go to the sample environment and select Chapter 13. Then select Distinct Values via htmSQL - Product in SASHELP.SHOES. filename htmsql url n o "%completeBrokerName(url=&_htmSQL)&saspressRoot/distinctvalues.hsql?da taset=&dataset%nrstr(&variable)=&variable"; p data _null_; q infile htmSQL; file _webout; input; put _infile_; run;

    n The URL access method of the FILENAME statement is used to define a URL which invokes htmSQL. o The URL value uses _htmSQL as well as another macro variable, saspressRoot, which defines the project root (in the Web server directory tree) for this application (as suggested in the Best Practice above). p The macro completeBrokerName is used to generate the complete URL, including http, the server name, and port. The value of the macro variable reference &_htmSQL is used to override the macro parameter which defaults to the location of the Application Broker. q A simple DATA step is used to read the file (the output of htmSQL) and write the contents directly to _webout. If you have downloaded and installed the sample environment locally, this program runs correctly only if:

    ƒ

    htmSQL is installed and configured to connect to a Data Server.

    ƒ

    The hsql files included with the sample environment have been updated to define the appropriate server (e.g., replace localhost as appropriate) and port for the Data Server (e.g., a SAS/SHARE server).

    ƒ

    The variables _htmSQL and saspressRoot are defined as discussed in the above Best Practice.

    Best Practice: If htmSQL is an option, i.e., you have a license for a Data Server (either SAS/SHARE or the SAS Scalable Performance Data Server), consider developing a set of tools as hsql files that can be used in htmSQL applications using the {include} directive as well as in Application Dispatcher applications using the URL access method of the FILENAME statement. Having a common set of shared tools can greatly facilitate consistency in applications.

    Instead of using htmSQL to generate HTML as part of an HTML page generated by the Application Dispatcher, another (and perhaps more common) way to integrate them is to have an Application Dispatcher program generate a link to a report or page produced by htmSQL. To run the example in this section, go to the sample environment and select Chapter 13. Then select Application Dispatcher Generating a Link for an htmSQL Report.

    230 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    data _null_; file _webout; put ''; put '’; put ‘htmSQL Link’; put ‘'; put ''; put 'Sample htmSQL Report'; put ''; put ''; run;

    Clicking the generated link produces the same report shown in Figure 13.1. Note: All the techniques presented earlier for generating links for Application Dispatcher generated reports (including the use of the externalHMTL and SASServerPages macros) can be used by Application Server programs to generate links for htmSQL reports. Here are the only differences:

    ƒ ƒ

    _htmSQL is used instead of _url instead of _service, _debug, and _program, the path (e.g., &saspressRoot) and the name of the hsql file (e.g., datatable.hsql) are used

    The hsql files include the directives to define what server to submit the request to. These can be customized by including the values in the URL and then using {&name} references in the hsql files.

    13.2.2 Integrating the Application Dispatcher with htmSQL In order to generate links that can access the Application Dispatcher from an htmSQL page, htmSQL must know the values for:

    ƒ ƒ ƒ

    the location of the Application Broker (e.g., _url) the service to use other application-dependent values

    While such values can be hard-coded (as mentioned in Section 13.2.1), that is neither desirable nor easily maintained. Unfortunately, htmSQL does not include a facility like the Application Broker Set directive. And since the values must be resolved when the input hsql file is processed on the Web server, setting macro variables on the Data Server is not possible either. There is a technique that can be used to define such values using a metadata approach. First, create a metadata data set that contains the name/value pairs. Then include statements like the following in any hsql file that needs to know the values for such parameters. These statements query the metadata SAS data set to create {&name} values corresponding to the required parameters:

    Chapter 13: Integrating Application Dispatcher Applications 231 {query server="localhost:5011"} n {sql} select _url, _service, saspressRoot from sampdata.htmSQLParameters {/sql} {/query}

    n Since there is no {eachrow} section, {&name} references resolve to the value from the last observation returned by the SELECT statement. The metadata table should have one observation with a separate variable for each parameter. Note: The htmSQL Include directive cannot be used as there is one namespace for variable names (that is, there is only one list in which variable names are stored). Each hsql file has a separate scope. Include files have separate scopes so they also have separate namespaces. Thus, variables defined in an included file are only available as {&name} references within the included file. The following hsql files demonstrate the use of this technique. The first one, liblist.hsql, generates the list of libraries similar to what was produced by the SAS Server Pages example (Section 7.6). To run this example, go to the sample environment and select Chapter 13. Then select htmSQL Invoking the Application Dispatcher. Tip: Since the overhead of querying all the name/value pairs vs. querying just the ones that are needed is minimal, you should consider using names that won’t overlap with your other queries and then creating {&name} references for all of them instead of trying to customize this file based on the particular query. The following two hsql files query all three parameters even though liblist.hsql only uses saspressRoot while memlist.hsql uses only _url and _service.

    htmSQL - Generate a list of Libraries n

    This hsql file allows the user to select which library contains the data to be paged through. After clicking Get List of Data Sets, the user is given a choice of data sets in the selected library.

    {query server="localhost:5011"} {sql} select _url, _service, saspressRoot from sampdata.htmSQLParameters {/sql} {/query}

    o

    Select the Library: {query server="localhost:5011"} {sql} q select distinct libname from dictionary.tables {/sql}

    {eachrow} {&libname} r

    Z

    232 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    {/eachrow} {/query}





    n This file is very similar to the liblist template discussed in Section 7.6.2. Instead of macros to generate the data-driven content, htmSQL directives are used. o The hsql statements to create name/value pairs for the parameters are included in the file. They are then available as {&name} references in the rest of the hsql file. Z The htmSQL automatic variable sys.url is used for the name of the htmSQL executable. saspressRoot (from the metadata table) is used to define the directory path for the hsql files. q Get the list of available libraries. r Generate a select tag that passes the selected value to the next hsql file (that generates the list of data sets in the library). The results of running the liblist.hsql file can be seen in Figure 13.3.

    Figure 13.3 List of Libraries Produced by htmSQL

    When you click Get List of Data Sets, the memlist.hsql file is then processed:

    htmSQL - Generate a list of Libraries n

    This hsql file allows the user to select the data set from a previously selected library. When the user clicks Select Variables, the Application Dispatcher is invoked to enable the user to select the variables to display, along with the number of observations to display at a time.

    Chapter 13: Integrating Application Dispatcher Applications 233

    {query server="localhost:5011"} {sql} select _url, _service, saspressRoot from sampdata.htmSQLParameters {/sql} {/query}

    o

    p



    Select the Data Set from {&libname}: {query server="localhost:5011"} {sql} select distinct memname r from dictionary.tables where libname = "{&libname}" {/sql}

    {eachrow} ] {&memname} {/eachrow} {/query}





    q

    n This hsql file is very similar to the memlist template discussed in Section 7.6.4 The program uses htmSQL directives instead of macros to generate the data-driven content. o The hsql statements to create name/value pairs for the parameters are included in the file. They are then available as {&name} references in the rest of the hsql file. p {&_url} and {&_service} are used to provide the location of the Application Broker and which set of Application Servers the request should be sent to. q The {&libname} r eferences resolve since libname was one of the name/value pairs from the form submitted by liblist.hsql. r Get the list of available data sets. ] Generate a select tag that passes the selected value to the Application Dispatcher. The results of processing the memlist.hsql file can be seen in Figure 13.4.

    234 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 13.4 List of Data Sets in Selected Library Produced by htmSQL

    Click Select Variables to invoke the same SAS Server Page macro and template as seen in Figures 7.11 and then 7.12. Those figures are included here as Figures 13.5 and 13.6.

    Figure 13.5 htmSQL Invoking the Application Dispatcher (sasServerPage Macro)

    Chapter 13: Integrating Application Dispatcher Applications 235

    Figure 13.6 PROC PRINT Output

    13.3 Single Sign-On Environments Many organizations have (or will) implement a single sign-on environment where the user enters a user ID and a password as soon as she connects to the network (or the intranet). In such environments, in-house applications (e.g., like those developed with the Application Dispatcher) are not supposed to have any logon function; instead they need to use the ID from the user’s initial logon. Making applications developed with Application Dispatcher compliant with such environments often involves one or more of the following requirements:

    ƒ ƒ

    using the single sign-on ID to uniquely identify the user to the application

    ƒ

    using the single sign-on ID, instead of another ID, in any metadata tables created by the application

    using any metadata from the single sign-on product in order to define the user’s privileges

    Meeting each of these requirements can be straightforward with an Application Dispatcher application:

    ƒ

    When the user connects to the intranet (needed before he can connect to any Web-based application), the user ID is validated by the single sign-on product. Such products typically have an environment variable that the Application Broker can access. The variable may be Remote User (which is exported as _rmtuser) or some other applicationspecific environment variable (e.g., for RSA ClearTrust, the environment variable is

    236 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    HTTP_CT_REMOTE_USER) which can then be exported (using the Application Broker Export directive) and used by the Application Dispatcher application.

    ƒ

    If necessary, the extensive data access facilities of SAS can be used to query the appropriate metadata tables (e.g., in an external data base, XML, LDAP, etc.)

    ƒ

    Export the ID as _rmtuser and use it. Alternatively, as discussed in Section 12.2, export the value for the single sign-on user ID to some other name and use it instead of _rmtuser.

    13.4 External Session Facilities In Chapter 10 the use of sessions created and maintained by the Application Server was discussed. There may be occasions where an Application Dispatcher application is to be integrated with an externally maintained session environment. This section describes an example of how the Application Dispatcher might be integrated with a session environment maintained on the Web server via a JSP (or ASP) application. The typical requirements for such integrations are these:

    ƒ

    The user signs-in to the application via a JSP page that validates the user (e.g., they have an active session and they are allowed to access the requested report/URL).

    ƒ

    A JSP (or ASP) object exists that examines each request (e.g., the URL including all the name/value pairs) before allowing the user to access the URL.

    This requirement can be addressed by ensuring that any Application Dispatcher request, including links generated by an Application Dispatcher program, are funneled through the relevant JSP object and are processed by the JSP object before invoking the Application Broker. Assuming that the Best Practice of using &_url (Section 5.3.1) has been used when generating links or forms, routing requests through such a JSP object is straightforward. For example, assuming the path to the JSP page that does the validation is /path/validate.jsp, the Application Broker SelfURL directive can be used to redefine the value for _url, e.g., SelfURL “/path/validate.jsp”

    Note: If _url cannot be globally redefined (i.e., other programs run by the Application Server use the default value), another field can be defined in the Application Broker using the Set directive or using a %LET statement in the Application Server INIT program. Alternatively, a ServiceSet directive can be used if the change applies to all requests for a specific service. This results in multiple values (see Section 5.2.4.1) for a _url with the first being the value from the ServiceSet directive and the second being the original value of _url. This causes any HTML forms or links that are generated by Application Dispatcher to use /path/validate.jsp as the location of the Application Broker. Any name/value pairs defined in the form or the link are defined just as they would be if the form or link directly invoked the Application Broker. 1

    A sample /path/validate.jsp page that you can modify to meet your needs follows. The sample does not include an actual authentication mechanism since that is specific to the external application. Note: The following example uses JSP. You could do a similar implementation using ASP if desired. 1

    The author wishes to thank Scott Wood of Zencos Corporation for contributing the sample.

    Chapter 13: Integrating Application Dispatcher Applications 237

    n Check to determine if the user has access and what his access level is. o A null value for the user object means there is no active session; a zero value for getAccessLevel() means the user does not have access; any other value is the user’s access level. p If the users are authorized for the request, they are redirected to a URL that invokes the Application Broker along with any name/value pairs from the original request, augmented by a name/value pair for getAccessLevel(). q Otherwise, they are redirected to the appropriate JSP or HTML page (depending on the reason for the failure).

    238 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    14

    Maintaining an Applications Environment 14.1 Introduction 239 14.2 Supporting Development, Test, and Production Environments and Applications 239 14.2.1 Using a Developer’s Workstation 240 14.2.2 Distinct Services 240 14.2.3 Distinct Brokers 241 14.2.4 Customized INIT Program 242 14.3 Metadata Approaches to Minimize Code Changes 244 14.3.1 The getTextString Macro 249

    14.1 Introduction An important component of any application is the environment for its development, testing, and maintenance. This chapter presents a number of Best Practices for Application Dispatcher applications, including supporting multiple applications.

    14.2 Supporting Development, Test, and Production Environments and Applications Ideally, separate physical servers are available to keep development and production (and optionally, test) environments completely independent from one another. These servers include

    240

    Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    both Web servers and servers to host the SAS/IntrNet Application Server. While this type of separation may be desirable, it is not required, and you can choose from a number of techniques that can be employed regardless of how many physical servers are available. The effectiveness of these techniques depends upon all programs using _url, _service, etc., instead of hard-coding these values as was discussed as a Best Practice in Section 11.6.

    14.2.1 Using a Developer’s Workstation Using a developer’s workstation (i.e., a PC) to host both the development Application Server and the Web Server is one strategy to consider for applications that are supported by a single developer. If the developer’s workstation is a PC, the workstation may not require an additional license since SAS may allow SAS/IntrNet to be installed on a PC for development purposes when SAS/IntrNet is licensed on a server. Note: Using a developer’s workstation does not preclude other users from accessing the development environment. Such users can access the developer’s Web Server via the corporate intranet. Alternatively, since the Application Broker configuration file can point to any Application Server via TCP/IP (see Section 2.2), the broker configuration file on a centralized Web Server could include a service that points to the developer’s Application Server.

    14.2.2 Distinct Services Separate services should be defined for each environment (e.g., development, test, and production. Using the techniques discussed in Section 9.3.1, the Application Server or servers for a service can support multiple applications Best Practice: Use an acronym for the application (or set of while keeping the data independent (e.g., applications) combined with the environment for the by defining the data libraries in the service name: e.g., appname_dev, appname_test, program specified in the INIT parameter appname_prod. of the SESSION statement instead of in the ALLOCATE and DATALIBS statements in the PROC APPSRV statement). Deciding whether a service should support a single or multiple applications can depend on a number of criteria. The decision as to which option to choose should be made on a case-by-case basis. Consider the following criteria when making that determination:

    ƒ

    High-volume critical applications should likely have a dedicated service, and thus a pool of Application Servers to support them.

    ƒ

    Applications that have different periods of high demand (e.g., a timesheet application which is expected to have higher demand at the end of the day or week or a reporting application that is expected to have higher demand first thing in the morning) are good candidates to support with a single service (i.e., a single pool of Application Servers). For example, if the two applications each need a maximum of four Application Servers (determined using the Queuing Model presented in Section 2.3.4), it may be possible to support both with four or five Application Servers instead of four Application Servers each.

    ƒ

    Applications with differing levels of security requirements should probably not be supported via a single service. For example, consider an application where the primary security concern is what data is available. Such applications can be candidates to share a single service (assuming the techniques presented in Section 9.3.1 are used to define the available data libraries).

    Caution: If the Load Manager is used, separate services should not point to the same server and port (e.g., for socket services) as the Load Manager associates a server/port combination with a single service definition.

    Chapter 14: Maintaining an Applications Environment 241

    The Application Broker directives DebugMask or ServiceDebugMask should be used to limit what values for _debug are allowed in each environment. Here are some details:

    ƒ

    For the production service, a DebugMask value of 34 that allows only the following values for _Debug: 0 (no debugging output), 2 (Broker Version, Time, and the SAS Powered Logo), or 32 (only the SAS Powered Logo), should be considered.

    ƒ

    For the test service, values of 1 (echo the name/value pairs passed from the Application Broker to the Application Server) and 128 (show the SAS log) may be appropriate. Allowing 1 and 128 in addition to 0, 2, and 32 would imply a DebugMask value of 163.

    ƒ

    For the development service, all values should be enabled.

    As discussed in Sections 11.2 and 11.6, it may also be desirable to use the Debug or ServiceDebug directive to set the default value for _debug specific to the environment, e.g.,

    ƒ ƒ ƒ

    0 (or 2 or 32) for production 1 (or 3 or 33) for test 129 (or 131 or 161) for development

    These default values can be overridden by providing a value for _debug in the URL subject to the restrictions imposed by the DebugMask or ServiceDebugMask values.

    14.2.3 Distinct Brokers Separate Application Brokers (and their associated configuration files) can be used for each of the development, test, and production environments. Likewise, separate Application Brokers can be used for each distinct application (or set of applications). Separate versions of the Application Broker can be supported in several ways. One strategy is to copy the Application Broker executable and configuration file to separate cgi-bin (or script) directories on the Web Server. Alternatively, multiple copies can be created in the same directory by copying them to files under new names in the same directory. For example, on PC platforms:

    ƒ

    Use broker.exe and broker.cfg for production, including only the definition of the production service.

    ƒ

    Copy broker.exe to broker_test.exe and create its associated file, broker_test.cfg, defining only the test service.

    ƒ

    Copy broker.exe to broker_dev.exe and create its associated file, broker_dev.cfg, defining only the development service.

    Caution: Multiple brokers that share a common Load Manager should not define services with the same names. The Load Manager is not able to distinguish between them. Note: As discussed in Section 3.5, the Application Broker looks for a CFG file in the same directory as the Application Broker. The CFG filename is the same as that of the Application Broker (with the exception of the .cfg extension). These separate Application Brokers (see Figure 14.1), whether they are in the different directories or the same directory with different names, can be restricted to authorized users of each environment by enabling http authentication (on the Web Server) and using the operating system security on the Web Server to limit access to the test and development Application Brokers to only testers and developers. For example, under Windows, right-click on the Application Broker executable in Windows Explorer, select Properties, and then select the Security tab. Then specify which users and groups should be able to access the Application Broker.

    242

    Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 14.1 Separate Application Brokers for Development, Test, and Production

    14.2.4 Customized INIT Program Chapter 9 discussed the use of the INIT program as a Best Practice to perform a range of set-up activities such as defining the data libraries to be made available to programs and checking access. However, an Application Server can have only one INIT program, which is defined in the PROC APPSRV REQUEST statement. This can be a problem if some of the logic to be run in the INIT program is dependent on the application or service or on the environment. While it may be desirable (or preferable) to customize the INIT program so it is specific to a particular application or service or environment, another possible strategy is to use a standard INIT program that performs the basic set-up functions that are required regardless of the environment or application (if the Application Server supports multiple applications). Such an INIT program can include or call another program to define any environment- or application-specific parameters. Assuming the Best Practice in Section 14.2.2 has been used to have application- or environment-specific service names, the following macro (provided with the sample environment) can be called in the INIT program to facilitate customizing the logic based on either environment or application. %macro customInit(entry=); %local initpgm node1 node2 node3 node4 init_type; %let %let %let %let %let

    initpgm node1 = node2 = node3 = node4 =

    = %sysfunc(appsrvgetc(request init)); %scan(&initpgm,1,.); %scan(&initpgm,2,.); %scan(&initpgm,3,.); %scan(&initpgm,4,.);

    n

    %if %length(&entry) = 0 %then %let entry = &_service;

    o

    %if &node4 = %then /* init program is a .sas file */ %let initpgm = %sysfunc(pathname(&node1))/&entry..&node3; p %else %do; /* init program is a SAS catalog entry */ %let initpgm = &node1..&node2..&entry..&node4; q %let init_type = catalog; %end; /* init program is a SAS catalog entry */

    q

    Chapter 14: Maintaining an Applications Environment 243 filename _init &init_type "&initpgm";r %if %sysfunc(fexist(_init)) %then %str(%inc _init;); %mend customInit;

    n customInit assumes the code to be included is another entry of the same type in the same location as the current INIT program. The APPSRVGETC function is used to get its name. The name is parsed into its components. o If no value is provided for the entry to use, the value defaults to the service (e.g., the value of the macro variable reference &_service). p The / separator works under both Windows and UNIX. q Build the filename (the path to the .sas external file or catalog) and then associate a SAS fileref with it. r Include the entry or file only if it exists. This check allows the macro to be called unconditionally even if there is no entry because there are no requirements specific to the application or environment. The use of this macro is illustrated by the following scenarios:

    ƒ

    The Application Server supports only one application, with separate development and production environments where the value of _service has _dev and _prod as suffixes. The INIT program specified in the PROC APPSRV REQUEST statement can do the set-up functions that are not specific to development vs. production (e.g., checking _rmtuser to determine what librefs to assign). Including the following macro call causes the logic that is specific to the environment to be invoked: %customInit( entry = %scan(&_service,2,_) )

    The macro causes catalog entries (or .sas files) called dev or prod to be included (if they exist) and run before each request. For example, code similar to what was used in the defineAccess macro (Section 12.3) could be included in the dev program to terminate the request if the value of _rmtuser is not one of the developers or authorized testers, e.g.,

    Best Practice: Even if distinct brokers as discussed in Section 14.2.3 are used (with http authentication) to limit which users can access the development environment, it may be a good idea to add this extra check in the Application Server. For example, other users may be granted http access for other reasons, but are not authorized to access the application.

    %if not &authorized %then %do; /* terminate the request */ %externalHTML(type=catalog, source=¬AuthorizedTemplate ) endsas; %end; /* terminate the request */

    The code that was used in Section 11.4 to set the debugFlag could also be included in the dev entry: data _null_; if &_debug = '1.......'b then call symput('debugFlag','Yes'); run;

    244

    Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    The Application Server supports multiple applications and environments with different data libraries to be defined for each application. The macro can be called twice. First, it can be called with the application name or acronym from the value of _service used to include or execute an entry to define the librefs for each application, e.g., %customInit( entry = %scan(&_service,1,_) )

    Second, it can be called to do any special setup for the specific environment (e.g., development versus production as discussed above): %customInit( entry = %scan(&_service,2,_) )

    ƒ

    The Application Server supports multiple applications and environments and each combination has a unique set of set-up tasks. The macro can be invoked once and includes the appropriate code for each relevant combination, e.g., %customInit()

    which will include a file whose name is the same as the value of the service. Having a program that can be called by the INIT program that is specific to the environment enables the setting of values and parameters specific to that environment. For example, in a production environment, the following OPTIONS statement may be appropriate: Options nomprint nosource nosource2 nosymbolgen nomlogic;

    By comparison, in the development and/or test environments the following OPTIONS statements may be appropriate: options mprint source source2; /* logic to check a name/value pair to turn on symbolgen and/or mlogic */ %global symbolgen mlogic; n %iif(%length(&symbolgen) ne 0, options symbolgen;); %iif(%length(&mlogic) ne 0, options mlogic;); o /* See the debugFlag discussion in Section 11.4 */ %let debugFlag = Y;

    n %global is used to ensure the macro variables exist. o The iif macro (Section 7.3.1) is used to generate options. The customInit macro, like all of the other macros and code included in the sample environment, is not intended to be a robust implementation. It can, however, provide a foundation that you can enhance to meet your application needs.

    14.3 Metadata Approaches to Minimize Code Changes Metadata techniques (sometimes referred to as data-driven) to generate application code are well known and widely used. Updating metadata is easier than updating code (e.g., it can be done by a non-programmer). This is also true for applications generated using the SAS/IntrNet Application

    Chapter 14: Maintaining an Applications Environment 245

    Dispatcher. For example, HTML generated by the use of the externalHTML macro via template HTML files (Section 7.5) can be more easily updated and maintained by changing the template as opposed to having to deal with PUT statements and the associated quoting requirements. Such HTML templates represent one type of metadata. The metadata approach can also include the use of SAS data sets that contains values that meet any of the following specifications:

    ƒ

    to be included in the output, e.g., a default title or footnote or both to be used for all output

    ƒ ƒ

    messages

    ƒ

    the names of HTML templates to be used for certain common functions

    parameter values for the SAS code, e.g., the ODS style to be used for all ODS output

    Best Practice: Use a metadata approach for any value that is used in many programs, e.g., define the ODS style using such an approach. Should a change to a new style be required, it is easier to edit the value once in a metadata table instead of reviewing and updating every program in an application.

    This approach also facilitates changes required to support, for example, multiple languages or locations. It allows the values to be customized or changed based on language or location (e.g., the name or e-mail address of the contact person to be included in any output is different for different locations or countries). Updating or adding such data once is easier and more maintainable than reviewing and updating every program. The text values defined in the metadata could include macro functions, macro calls, and macro variable references that are resolved by the SAS word-scanner when the value is inserted into the SAS program. To run the examples in this section, go to the sample environment and select Chapter 14. Then select Message Text Metadata as Macro Variables or Message Text Metadata using getTextString Macro. These Best Practice: Loading all the values into macro variables is examples provide two preferred for values used many times in many programs, while the alterative implementations of macro approach is preferred for less frequently used values. An a metadata approach and application can use both techniques by having two data sets (or one produce the output shown in data set with another variable that designates the technique): one that is loaded into macro variables and one that uses macro calls to insert Figure 14.2. The only the values. difference between them is how the values from the metadata are referenced. The first link uses a program that assumes that all the values have been loaded into SAS macro variables. The desired text is inserted into the program by using a macro variable reference for the key or mnemonic value. The second link uses a program that calls a macro to query the data set for the value for a particular key or mnemonic. The macro inserts the returned text into the SAS program.

    246

    Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 14.2 Using Metadata to Customize or Set Parameter Values

    Both examples show a sample metadata data set with two columns: a key or mnemonic value (which serves as the name of the macro variable) and the appropriate text that contains a select set of values such as these:

    ƒ

    appname, apptitle, appfoot, and copyright contain text that is to be used in any or

    all generated output.

    ƒ

    dsnf, notauth, and notauth_rmtuser define the message text for particular situations. spt_app_name and spt_app_email can provide contact information for the

    support person responsible for solving application-specific problems.

    ƒ

    odsStyle defines the value to use for the style attribute for all ODS output. images and projectRoot define paths that might need to be referenced in generated programs. For example, projectRoot can be used in place of the field saspressRoot used in Section 13.2.1. stylesheet defines the location for the standard stylesheet that should be used.

    ƒ

    refreshSession, notauth_template, and trailer provide the name of the HTML

    templates to use for specific scenarios (the user is not authorized for the request, a standard trailer block of HTML is to be included at the bottom of any generated HTML page, etc.).

    Chapter 14: Maintaining an Applications Environment 247

    The following program is run when the first link is selected and uses the technique of loading all the values into macro variables: %restoreMvars(lib=sampdata,data=messageMetaData)

    n

    ods listing close; ods html file = _webout (no_bottom_matter title = "&appname")o style = &odsStyle; proc print data = sampdata.messageMetaData noobs label style(data)=[protectspecialchars=yes]; p title "&apptitle"; o footnote "&appfoot"; run; ods html close; %externalHTML(source=&trailer,type=catalog)

    Best Practice: Call restoreMvars to load the values of the macro variables in the INIT program instead of in each individual program (as is done in this sample program).

    q

    n The restoreMvars macro (Section 10.3) is used to load the metadata values into SAS macro variables. o The values are referenced as macro variables. p protectspecialchars=yes is used so that any HTML metadata values are shown as is. q The name of the template for the standard trailer block is referenced as a macro variable. If the location of the trailer HTML is changed, only the metadata needs to be updated. A virtually identical program is used for the second technique. It uses a macro (discussed in Section 14.3.1 below) to get the desired value and insert it into the program. The restoreMvars macro is not called to create the macro variables; instead the getTextString macro is used at each point in the program where a value is to be inserted. The macro also queries the metadata to get the value to be inserted into the program: ods listing close; ods html file = _webout (no_bottom_matter title = "%getTextString(keyvalue=appname)") style = %getTextString(keyvalue=odsStyle); n proc print data = sampdata.messageMetaData noobs label style(data)=[protectspecialchars=yes]; o title "%getTextString(keyvalue=apptitle)"; p footnote "%getTextString(keyvalue=appfoot)"; p run; ods html close; %externalHTML(source=%getTextString(keyvalue=trailer), type=catalog)

    q

    n The getTextString macro provides the value for the ODS style to use. o protectspecialchars=yes is used so that any HTML metadata values are shown as is. p The getTextString macro also provides the value for the default title and footnote. q The getTextString macro provides the value for the name of the HTML template that contains the standard trailer text to be included at the bottom of each page.

    248

    Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The HTML template referenced by the externalHTML macro illustrates some techniques of interest. The output produced by the externalHTML macro call using the template listed below is shown in Figure 14.3 (and is the rest of the output corresponding to Figure 14.2). standard HTML text deleted The values are loaded from a SAS data set and two ways of using this metadata are illustrated

    • all the values are loaded into macro variables which can then be referenced in the code
    • a macro is used to query the data set for the required value
    This page results from using %iif((%lowcase(&_pgm)=valuesasmvars),macro variable references to get the values out of the metadata.) n %iif((%lowcase(&_pgm)=getvaluesusingmacro),the getTextString macro to get the values out of the metadata.)

    standard HTML text deleted Applications Development Using the SAS/IntrNet Applications Dispatcher%getTextString(keyvalue=copyright) o

    standard HTML text deleted

    n The iif macro (Section 7.3.1) customizes the message in this template to reference which approach (macros variables vs. macro calls) was invoked. Figure 14.3 was generated using the getTextString macro. o The getTextString macro is used since it cannot be assumed that every reference to this template will have the macro variable Copyright defined. Calling getTextString should always work, while macro variable references will work only if the values have been loaded into macro variables from the metadata.

    Chapter 14: Maintaining an Applications Environment 249

    Figure 14.3 Metadata Values Used in HTML Processed by the externalHTML Macro

    14.3.1 The getTextString Macro Here is a list of the macro parameters.

    ƒ

    msgdata provides the name of the metadata data set that contains the message text. In the sample environment this parameter defaults to sampdata.messageMetadata. You can customize the default value to point to your metadata table.

    ƒ

    keyname the name of the variable that contains the unique identifier. The default value is Name.

    ƒ

    msgtext the name of the variable that contains the text to be returned. The default value is Value.

    ƒ

    keyvalue the value that identifies which text string value is to be included.

    ƒ

    default a text value to be used if there is no row in the data set corresponding to the key value. The default value is %nrstr(&keyvalue) Not Found. The use of %nrstr(&keyvalue) allows the default value to include the value passed in for the keyvalue parameter while preventing SAS from attempting to resolve the value at macro compile time.

    250

    Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The parameter names may need to be changed if they conflict with macro variable references in a text string defined in the metadata. For example if there is a text value that references &msdata or &msgtext, the macro will resolve it to the value passed in for this parameter instead of the expected macro variable. Note: Name and Value were specified as the default for keyname and msgtext so the restoreMvars macro (Section 10.3) could be used to load the values. If other names are desired, the restoreMvars macro could be changed so the names it uses are parameters (instead of assuming they will always be Name and Value). The macro source follows. Note that it is conceptually similar to the generateOptionTag (Section 7.6.3) and getVarsAsTextBoxes (Section 7.6.6). SCL functions that can access values in a SAS data set are invoked using the %sysfunc macro. %macro getTextString (msgdata=sampdata.messageMetadata, keyname=name, msgtext=value, keyvalue=, default= %nrstr(&keyvalue) Not Found ); %local _rc _dsid _varnum _value;

    n

    %let _dsid = %sysfunc(open(&msgdata(where=(&keyname="&keyvalue"))));o %let _varnum = %sysfunc(varnum(&_dsid,&msgtext)); %let _rc = %sysfunc(fetch(&_dsid)); %if &_rc = 0 %then %let _value = %sysfunc(getvarc(&_dsid,&_varnum)); p %else %let _value = &default; q %let _dsid = %sysfunc(close(&_dsid)); %unquote(&_value) r %mend getTextString;

    n The _ prefix for the local macro variables is used to provide protection against those values 1blocking access to global macro variables that may be referenced in the returned message text. o Open the data set using a WHERE clause to subset the data to the one observation for the desired key value. p If the value is found, use it. q Otherwise use the value for the default parameter. r %unquote is used to ensure that macro references or calls are resolved. The getTextString macro, as for all the other macros and code included in the sample environment, is not intended to be a robust implementation. It can provide the foundation that you can enhance to meet your application needs.

    P a r t

    5

    Selected Examples Chapter 15

    Generating Static Versions of Dynamic Reports 253

    Chapter 16

    Simulating a Pause and Resume Capability for the Application Server 259

    Chapter 17

    Techniques for Handling Long-Running Processes 267

    Chapter 18

    Using Scheduled Execution to Handle Long-Running Processes 275

    Chapter 19

    Handling On-Demand Long-Running Requests 281

    Chapter 20

    Using Metadata-Driven Reporting to Construct Reports 307

    Chapter 21

    Packaging the Application Dispatcher as a Web Service 327

    Part 5 provides a number of examples that use the techniques, samples, and approaches described in previous chapters. Part 5 also illustrates how these techniques, samples, and approaches can be integrated to address commonly encountered requirements. Each example describes a scenario or requirement along with a suggested approach for implementing it using the SAS/IntrNet Application Dispatcher. The following examples are included: Chapter 15 provides an example of generating static versions of dynamic reports (e.g., run by the Application Dispatcher). Chapter 16 demonstrates building a simple facility to simulate a pause and resume capability for the Application Server.

    252 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Chapters 17, 18, and 19 discuss a variety of techniques for long-running processes or programs:

    ƒ

    Chapter 17 demonstrates two techniques that display a “Please Wait” message in the user’s browser while the program is running.

    ƒ

    Chapter 18 shows how the parameters for a user’s request can be saved and processed in a separate SAS session (e.g., a scheduled batch SAS process).

    ƒ

    Chapter 19 presents a reusable framework that enables the spawning of an independent SAS session to handle long-running requests on demand.

    Chapter 20 illustrates how to build a metadata-driven reporting application. Chapter 21 shows Application Dispatcher packaged so its services are available as a Web service. The examples are not intended to be robust implementations of the functionality described. Instead, they are intended to provide insight about how the tools and techniques presented in this book can be used to address some typical application requirements. The examples can be the foundation for building robust applications. The examples presented in Part 5, as well as the techniques presented earlier in the book, can be combined in a number of ways to meet your needs.

    C h a p t e r

    15

    Generating Static Versions of Dynamic Reports 15.1 Introduction 253 15.2 Sample Program 254 15.3 Ensuring That Generated Links Function Correctly 256 15.4 E-mailing Static Copies of Dynamic Reports 256

    15.1 Introduction Suppose an organization has a nightly process that extracts new data and stores it in a data warehouse. In addition, a summary table is created that is used to provide drill-down functionality. Everyone in the organization can access these reports, but a large number of users in the sales organization access the report as soon as they log on to the corporate intranet and the sales portal. This has caused a performance bottleneck since the demand is orders-of-magnitude higher at the beginning of the day. Adding additional capacity (e.g., more SAS/IntrNet Application Servers) to address this once-a-day bottleneck is not an option. Here is a solution to this dilemma. Since the data is typically updated nightly, and everyone starts with the same high-level summary, there is no need to generate that high-level summary dynamically for each user as he logs on to the portal. Instead the decision is made to create a static version of the top-level page at the end of the nightly process. Because the program has been set up to run via the SAS/IntrNet Application Server, and because some users will still need to run the report request dynamically (e.g., they do not have access to the sales portal), there is no need to create another program to produce the same report. Instead a simple program is run that uses the URL access method to generate a static copy of the report each night after the data is updated. The URL access method can access the specified URL, which calls the desired program via the Application Dispatcher and can save the results in an HTML file accessible from the sales portal. The technique uses the URL access method of the

    254 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    FILENAME statement (shown in an earlier example in Section 13.2.1). The example presented here demonstrates that the URL access method has broad applicability; it can be used to access any dynamic report in order to generate a static copy of a dynamically generated report.

    15.2 Sample Program The template code to generate a static copy of a dynamically generated report is shown below. The template code is stored in staticReport.source in the Chapter 15 catalog of the sample environment. To run the code, download it to an interactive SAS session and fill in the appropriate values. %let lrecl = 512; n filename static url o 'http://[SERVERNAME]:80/[BROKERLOCATION]?_service=appdisp&_program=sam plib.websamp.drill.macro&dataset=sampdata.shoeSummary&classlst=Region+ Product&vars=Sales+Returns' lrecl=&lrecl; filename out '[SPECIFY OUTPUT LOCATION]' lrecl=&lrecl; data _null_; infile static; input; file out; put _infile_; run;

    The sample runs the Xplore drill-down macro against a summary data set that is provided in the sample environment. It assumes that the samplib is specified in a PROGLIB statement (it is included in the default appstart.sas file). Otherwise, the program SAMPLIB.WEBSAMP.DRILL.MACRO is not found. Note: If the Xplore sample is not available, the URL can be replaced by any URL that generates an HTML-based report. Before submitting the code for execution, you should make the following changes to it. n The value specified for LRECL in the %LET statement may need to be updated if the generated HTML contains lines that are longer than the provided value of 512.

    o The following placeholder values should be replaced with appropriate values for your site: …

    [SERVERNAME] – The Web server name.

    …

    [BROKERLOCATION] – The location of the Application Broker, e.g., /scripts/broker.exe or /cgi-bin/broker, etc.

    …

    [SPECIFY OUTPUT LOCATION] – The location to write the static copy to.

    Chapter 15: Generating Static Versions of Dynamic Reports 255

    Note: The FTP access method of the FILENAME statement can be used to write the static version of the report to the Web server if the server where this program is run does not have file system access to the Web server directory tree. See Figure 15.1 to see the SAS log for the template code. The code has been modified to use the FTP access method of the FILENAME statement.

    Figure 15.1 SAS Log Showing the Use of the URL and FTP Access Methods to Create a Static Copy of Output

    256 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    15.3 Ensuring That Generated Links Function Correctly If the static copy is not written to a location on the Web server where the Application Broker is available, in all likelihood the links will not work since they won’t include the server name. If the Best Practice of using &_url to generate links has been followed, the links will include the complete path. The completeBrokerName macro (Section 5.3.2) can be used to replace the value of _url with a value that includes the complete path. For example, %let _url = %completeBrokerName;

    This can be done using either of the following techniques:

    ƒ

    using the complete broker name by executing the above %LET statement unconditionally at the beginning of the program

    ƒ

    using a value (e.g., completeBrokerName) having a non-blank value passed in on the URL, e.g.: %global completeBrokerName; %iif((%length(&completeBrokerName) gt 0),%nrstr(%let _url = %completeBrokerName;))

    n

    n The iif macro (Section 7.3.1) is used to set the value based on the parameter. %nrstr is used to prevent the %LET statement from being executed when the macro parameters are being scanned. This code can be included in either of the following locations: …

    the specific program(s) that may need this option

    …

    the INIT program

    Best Practice: If only one (or a few) programs need this functionality, include this code in those programs. Otherwise, include this code in the INIT program.

    Note: It is assumed that such reports contain links as otherwise there may be little or no reason to make the reports available dynamically.

    15.4 E-mailing Static Copies of Dynamic Reports The EMAIL option of the FILENAME statement can be used with an e-mail address (or distribution list) as the value for [SPECIFY OUTPUT LOCATION] (see Section 15.2 above) if a static version of reports is to be distributed via e-mail instead of via the company’s intranet (e.g., the sales portal for the scenario described above). If the static version is to be e-mailed, any generated links must include the full path as discussed above in Section 15.3. The SAS log that shows the use of the EMAIL option of the FILENAME statement is shown in Figure 15.2.

    Chapter 15: Generating Static Versions of Dynamic Reports 257

    Figure 15.2 SAS Log Showing the Use of the URL and EMAIL Access Methods to E-mail a Static Copy of Output

    258 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    16

    Simulating a Pause and Resume Capability for the Application Server 16.1 Introduction 259 16.2 Sample Implementation 260 16.2.1 The Metadata Control Table 262 16.2.2 The Toggle Program 263 16.2.3 Macro to Check Status 264 16.2.4 The HTML Templates 265 16.3 Doing More with the Sample 266

    16.1 Introduction Most if not all production applications have scheduled downtimes that allow updates or promotions to be done on the application code. In an ideal world all updates could be applied during those scheduled downtimes. Occasionally, however, a situation arises when a quick update (i.e., something that can be done and tested in a few minutes) needs to be made to an application before the next regularly scheduled downtime. If an update is being made to an Application Server program while the Application Server is processing a request, a user could get a message similar to the message that is shown in Figure 16.1. And if the user does not get such a message, he might get results from a program (or set of programs) for which the updates have not yet been completed. Since the Application Server does not include a built-in facility to handle temporarily not running (i.e., pausing) requests, the only way to make such changes without allowing any requests is to shut down the Application Servers, apply updates, and restart the Application Servers.

    260 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 16.1 Error Seen by a User If a Program Is Being Edited

    Such an approach may not be viable if there are active sessions since users can lose their sessions (and thus their current work) if the Application Servers are shut down and restarted. An alternative is needed where requests can be intercepted before the programs are run by the Application Server. A metadata-based approach that can intercept requests in the INIT program is described in this chapter.

    16.2 Sample Implementation As discussed in Section 9.1, the INIT program is run before any requested program is run (the use of an INIT program was suggested as a Best Practice). A simple approach is to use a metadata control table to filter requests. Filtering prevents the requests from being run during the few minutes that it takes to make an update. Here is a way to implement such functionality:

    ƒ ƒ

    Use a metadata control table to indicate that requests should not be accepted. Add logic (e.g., a macro call) to the program specified in the INIT parameter of the REQUEST statement to determine if requests are to be allowed.

    ƒ

    Use an administrative program, i.e., one that requires the _ADMINPW password (discussed in Section 4.4.2.4), that toggles the status of the Application Server.

    ƒ

    Use HTML templates to provide the user with the current status of the Application Server.

    To run the examples in this section, download and use a local copy of the sample environment. Then select Chapter 16 and select the appropriate links for the task you want to perform.

    ƒ

    Select the Toggle Pause/Resume Status button to update the metadata so requests are not run (see Figure 16.2).

    ƒ

    Select any other of the example links (e.g., the link for Section 7.1) to see that the request has been intercepted (Figure 16.3).

    ƒ

    Select the Toggle Pause/Resume Status button again to allow requests to again be processed (Figure 16.4).

    ƒ

    Confirm that requests are now allowed by selecting, for example, the link for Section 14.3 again (Figure 16.5). Note that if the user leaves the browser window open as shown in Figure 16.3, it displays her requested output (e.g., the output shown in Figure 16.5) once the Application Server is available to process requests. The reason for this is the automatic refresh (discussed in Section 16.2.4.2).

    Chapter 16: Simulating a Pause and Resume Capability for the Application Server 261

    Note: The sample environment on the Web does not support this example because it requires knowledge of the Application Server administrative password. If you want to run this example, you must download and use a local copy of the sample environment.

    Figure 16.2 Application Server Is Paused

    Figure 16.3 Message Seen by a User When an Application Server Is Paused

    Figure 16.4 Application Server Status Reset to Resume

    262 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 16.5 User Output Automatically Displayed

    16.2.1 The Metadata Control Table To see the table saspress.pauseResume, go to the sample environment and select Chapter 7. Select SAS Server Pages, and then select a library and data set (Figure 16.6). The data set contains one observation and five variables:

    ƒ

    pauseResume a non-blank value indicates that requests should not be accepted. Since the value shown in Figure 16.6 is blank, requests are accepted.

    Best Practice: Consider storing applicationspecific metadata such as the pauseResume metadata in a specific library. That library can be the same one that the programs for an application are stored in. The libref is defined in the Request Executive (Section 4.2) whenever a program entry is used as the value of _program. As discussed in Section 4.4.2.3, the library is defined because of its being referenced in a PROGLIBS statement. However it is not available for programs in other libraries.

    ƒ

    templateON the name of the HTML template that displays a message to the user that the application is currently being updated.

    ƒ

    templateToggle the name of the HTML template that displays the current value of the pauseResume variable after it has been updated.

    ƒ

    timeChanged the datetime when the value was last toggled.

    ƒ

    available if the Application Server is in Pause mode, contains the estimated datetime when the application will be available. In Figure 16.6 the value is blank because the Application Server in not in Pause mode.

    Best Practice: It may be desirable to include the _service value in the data set name. This would allow the same program to control Application Servers for different services.

    Note: The code, chapter16PauseResume.source, that creates the sample metadata control table is located in the setup catalog, which included with the sample environment.

    Chapter 16: Simulating a Pause and Resume Capability for the Application Server 263

    Figure 16.6 Pause/Resume Metadata Control Table

    This data set is checked during the INIT program to determine if the request should be allowed.

    16.2.2 The Toggle Program The sample toggle program provided is stored Best Practice: Store code that runs updates in a similar in the same catalog as the INIT program since it way to this toggle program in a location defined by the ADMINLIBS (Section 4.4.2.4) statement, e.g., the is an administrative program and access to the same location where the INIT program is located. ability to run the program can be restricted by defining the location in an ADMINLIBS statement. The toggle program checks the current value and toggles the value (blank to non-blank and vice-versa). The program also calculates when the Application Dispatcher is expected to be available again by adding the estimated length of time during which the Application Server is expected to be in Pause mode (i.e., the value of the backOn macro variable) to the current datetime. %setDefaultValue(data,saspress.pauseResume) %setDefaultValue(backOn,300)

    n

    data &data; set &data; timeChanged = datetime();o call symput(‘timeChanged’, trim(left(put(timeChanged,datetime.)))); p if pauseResume = 'x' then do; /* on now, turn it off */q pauseResume = ' '; available = .; call symput('available',' '); call symput('pauseResumeMessage','Pause/Resume turned off.'); end; /* on now, turn it off */ else do; /* off now, turn it on */ pauseResume = 'x'; available = timeChanged + &backOn; r call symput('available', trim(left(put(available,datetime.)))); call symput('pauseResumeMessage','Pause/Resume turned on.'); end; /* off now, turn it on */ call execute('%externalHTML(type=catalog,source=' || trim(templateToggle) || ')'); run;

    264 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    n Use the setDefaultValue macro (Section 5.3.3) to define the values. They can be overridden by providing values in the request if necessary. o Set the value for when the value was toggled. p In SAS 9, the STRIP(string) can be used instead of TRIM(LEFT(string)). ®

    q Toggle the values and set a message to be used by the template. r Create a macro variable with the name of the template defined in the metadata and call the externalHTML macro (Section 7.5.1) to display the template to the (administrative) user.

    16.2.3 Macro to Check Status The following macro can be called in the INIT program to determine the current status and take the appropriate action: %pauseResume(data=saspress.pauseResume)

    The macro logic checks to see if the Application Server is in pause mode and either allows the request, or display a message that requests are not currently allowed. The macro source code follows: %macro pauseResume(data=); %if %sysfunc(exist(&data)) %then n %do; /* pause/resume data set exists */ data _null_; set &data; where pauseResume ne ' ' and o /* allow specific programs */ "%upcase(&_pgmlib..&_pgmcat)" ne: upcase(appsrvgetc ('request init'));p /* can only get here if the flag is set */ call symput('refresh','120'); call symput('available', q trim(left(put(available,best12.)))); call execute('%externalHTML(type=catalog,source=' || trim(templateOn) || ')');r call execute('endsas;'); s stop; run; end; /* pause/resume data set exists */ %mend pauseResume;

    n Verify the metadata control data set exists and do nothing if it does not. o If the toggle is set and the program is not in an allowed catalog, the WHERE clause allows the observation to be read. This causes the DATA step statements to be executed. p Programs in the same catalog as the INIT program are allowed to run. Other allowed values can be added. q Set values for macro variables used in the template.

    Chapter 16: Simulating a Pause and Resume Capability for the Application Server 265

    r Display the template specified in the metadata. s Issue an ENDSAS statement to end the request executive (and thus the request).

    16.2.4 The HTML Templates The sample Pause/Resume facility uses two templates. One is used by the toggle program to display the current status. The other is used by the macro to inform the user that the Application Server is in Pause mode. Using templates enables you to easily update and change the HTML to be generated.

    16.2.4.1 The pauseResumeToggle Template This template displays the current status. It is invoked by the toggle program that sets the value for the macro variables referenced in the template.

    Toggle Pause Resume Status

    Toggle Pause Resume Status &pauseResumeMessage n

    Status toggled at: &timeChanged..

    %iif(&available ne %str( ),Estimated time to be available: &available.)

    o

    n The template uses the macro variables created in the toggle program so the users know what the current status is. o When Pause mode is enabled, the iff macro (Section 7.3.1) is used to conditionally generate the estimated availability time.

    16.2.4.2 The pauseResumeMessage Template This template displays a message whenever the Application Server is in Pause mode and the submitted program is not an allowed program. The template includes macro %LET statements in order to perform selected calculations.

    Please Wait . . . n

    Application Being Updated - Please Wait The application is being updated and %let now = %sysfunc(datetime()); %let back = %sysfunc(ceil((&available-&now)/60)); o %iif(%eval(&back > 0), should be back online in &back minutes.,p

    266 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    estimated availability is TBD.)

    This request page will be refreshed every &refresh seconds. As soon as the maintenance is completed, your request will be processed with the next refresh.

    Current Datetime: %sysfunc(putn(&now,datetime.)) %global _sessionid; /* make sure it exists */

    %iif(%length(&_sessionid) ne 0,Your session is extended each time this page is refreshed.) q

    n The META tag causes the requested URL to be refreshed using the value defined in the toggle program. This allows the original request to be automatically submitted when the Application Server is no longer in Pause mode. o Determine how many more minutes Pause mode is expected to be enabled. p The iif macro is used to generate a customized message to tell the user how much longer before the Application Server is expected to be available to process a request. If the estimated end time has passed, the message indicates the wait time is TBD. q If the request uses sessions, the iif macro informs the user that refreshing this page keeps the session alive until Pause mode is ended. This assumes that the refresh time is shorter than the session timeout.

    16.3 Doing More with the Sample With minor changes to the metadata control table, the pauseResume macro, and the templates, a number of enhancements could be made to this facility, including:

    ƒ

    The toggle program could be enhanced to allow for a parameter to update the expected time when the Application Server will no longer be in Pause mode.

    ƒ ƒ ƒ ƒ

    Enabling Pause mode to be set specific to particular applications. Generalizing the logic for what programs can be run while in Pause mode. Providing contact information so users know who to contact. Sending an e-mail to interested parties when Pause mode is started and/or ended.

    You can adapt or enhance this facility to meet your particular application requirements.

    C h a p t e r

    17

    Techniques for Handling Long-Running Processes 17.1 Introduction 267 17.2 Using the Cascading Style Sheets Display Attribute 268 17.3 Using the JavaScript location.replace Function 271

    17.1 Introduction Users of Web applications expect results of their requests to be returned in a matter of seconds. For this reason, a Web-based application often uses a two-step approach to providing responses to users of a long-running process:

    ƒ ƒ

    First, provide an immediate response that says “Please Wait.” Then replace the “Please Wait” message with the output from the process once it has been completed.

    Such functionality is straightforward to implement using the Application Dispatcher.

    268 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    However, keep these caveats in mind. The first technique is simple to implement and uses industry standard Cascading Style Sheets. But, it produces HTML that may be considered invalid. The second technique produces valid HTML, but must generate the output to a temporary location (the &_tmpcat catalog), which is then replayed to the user’s browser once the request is complete. This result may be an issue for voluminous output. You can choose which technique to use based on your environment and user expectations.

    17.2 Using the Cascading Style Sheets Display Attribute Cascading Style Sheets (CSS) have a number of attributes that can be used when producing HTML output. One of them is the DISPLAY attribute, which allows parts of the HTML to be dynamically displayed or hidden. When combined with JavaScript, the display of specified parts of HTML can be toggled on or off. The following example demonstrates this technique using a program similar to the PROC PRINT examples discussed in Chapter 8. To run this example, go to the sample environment and select Chapter 17. Then select Please Wait Using CSS.

    Figure 17.1 “Please Wait” Message Using Cascading Style Sheets

    Once the program has completed, the output shown in Figure 17.2 is displayed.

    Chapter 17: Techniques for Handling Long-Running Processes 269

    Figure 17.2 Output Replaces “Please Wait” Message

    An HTML template (as discussed in Section 7.5) is used to display the “Please Wait” message so the message can be used easily by multiple programs. Using HTML templates eliminates complicated quoting issues and makes it easy to generate and update HTML with an HTML editor. The HTML must be saved in a location known by the Application Server.



    n




    Please Wait o

    p



    n Use a DIV tag to wrap the text to associate a unique identifier (pleaseWait) with the text. As a result, the DISPLAY attribute can be changed using JavaScript. o The &images macro variable was created in the INIT program (as discussed in Section 14.3) and is the location of the application images directory. This avoids having to hardcode the location.

    270 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    p A number of blank lines may be needed so the “Please Wait” message is seen by the user’s browser. The number of blank lines needed may be site dependent. The template in the sample environment has 25 blank lines. You may need to adjust the number of blank lines via trial and error. The following sample program demonstrates how to generate a “Please Wait” message using this template: %setDefaultValue(obs,5); %setDefaultValue(startAt,1); %setDefaultValue(data,sashelp.shoes); %externalHTMLn (type=catalog, source=saspress.template17.pleasewait.source ) data _null_; /* simulate a longer process */ x = sleep(15); o run; %generateOdsStmtp (type=html, htmlOptions=(title = "Please Wait Example") style=&OdsStyle ); proc print data = &data(firstobs=&startAt obs=&obs); title "Please Wait Example"; footnote ''; run;

    q

    ods html close;

    n Use the externalHTML macro (Section 7.5.1) to display the template asking the user to wait. o The Sleep function is used to simulate a longer running process (so the “Please Wait” message can be displayed before the output is returned). p The generateODSStmt macro (Section 8.3.3) is used to generate the ODS HTML statement. q Embed a JavaScript command in the FOOTNOTE statement so that once the output is returned, the display of the “Please Wait” message (as defined by the ID pleaseWait) is turned off.

    Chapter 17: Techniques for Handling Long-Running Processes 271

    17.3 Using the JavaScript location.replace Function The Display attribute is a flexible technique that can be used in most, if not all, scenarios where a “Please Wait” message is to be displayed. An alternative implementation of the functionality using the JavaScript location.replace function is also straightforward to implement. The program should be structured as follows:

    ƒ ƒ ƒ

    Generate the “Please Wait” message (just as in the example in Section 17.1).

    ƒ

    The argument to the location.replace function is the URL that should replace the current content (e.g., the “Please Wait” message). Construct that URL by using &_replay to replay the output written to the &_tmpcat catalog.

    Generate the content, writing it to the &_tmpcat catalog instead of the _webout catalog. Send JavaScript that calls the location.replace function back to the user’s browser once the program has been completed.

    Note: The location Content-type header (Section 8.2.1.1) cannot be used in this situation since the “Please Wait” message has already been returned to the user’s browser. The following example demonstrates this. It produces the same “Please Wait” message as shown in Figure 17.1. See Figure 17.3 to see the generated output. To run this example, go to the sample environment and select Chapter 17. Then select Please Wait Using a Re-Direct.

    Figure 17.3 JavaScript location.replace Function Used to Replace the “Please Wait” Message

    272 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The program, which is shown below, uses ODS to generate a table and a graph (similar to the example in Section 8.4). Both the HTML and the graph are written to the &_tmpcat catalog. The only other content (other than the “Please Wait” message) written directly to the user’s browser (i.e., to _webout) is a location.replace function call with a URL that uses the Replay facility. The Replay facility returns the ODS-generated HTML (from the &_tmpcat catalog) to the user’s browser. That HTML then includes an ODS-generated replay to display the graph. %externalHTML n (type=catalog, source=saspress.template17.pleasewait.source ) data _null_; /* simulate a longer process */ x = sleep(15); run; filename results catalog "&_tmpcat..results.html";

    o

    goptions dev=gif xpixels=800 ypixels=400 transparency; %generateOdsStmtp (type=html, file=results, htmlOptions=(title = "Please Wait Example") path=&_tmpcat (url=&_replay) style = &OdsStyle ); proc gchart data = sashelp.shoes; title; hbar3D product/sumvar=salesq subgroup=region shape=cylinder nostats discrete; run; quit; proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; columns product region, sales; define product / group ' '; define region / across ' '; define sales / sum ' '; rbreak after / summarize; run; ods html close; data _null_; file _webout; url = &_replay || 'results.html'; put '' ; run;

    Chapter 17: Techniques for Handling Long-Running Processes 273

    n As in the above example:

    ƒ ƒ

    Use the externalHTML macro to display the “Please Wait” message. Use the Sleep function to simulate a longer process.

    o Generate a FILENAME statement for the generated output that points to the &_tmpcat catalog. p Use the generateODSStmt macro (Section 8.3.3) to generate the ODS HTML statement. q As discussed in Section 8.4, the graph will be written to the &_tmpcat catalog. Since the ODS statement points the fileref results, the HTML output will also be written to the catalog. r Generate HTML that includes a call to the JavaScript location.replace function with a URL as its argument that replays the generated HTML to the user’s browser. While only the JavaScript example includes a graph (using the ODS-generated Replay facility), both techniques presented in this chapter support pages produced by ODS that have mixed text and graphics.

    274 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    18

    Using Scheduled Execution to Handle LongRunning Processes 18.1 Introduction 275 18.2 Sample Implementation 276 18.3 Doing More with Scheduled Execution 279

    18.1 Introduction This example demonstrates the use of the Application Dispatcher to handle a long-running program or process. This example provides the first of two alternative approaches to the “Please Wait” example presented in Chapter 17. This approach allows the user to enter parameter values via a Web browser. It is appropriate for long-running requests that will take a substantial amount of time (for example, submitting a request for a daily/weekly/monthly batch process on a mainframe). It is typically more convenient for the user to do this than to log on remotely to that machine and then edit or enter the parameter values. This scenario involves using a separate SAS session (not using the SAS/IntrNet Application Dispatcher) that can be run on a scheduled basis (e.g., nightly or hourly) to process the request. If necessary, the results can be sent to the user via e-mail (e.g., using the filename e-mail access method).

    276 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The result can take either of two forms:

    ƒ ƒ

    include the full report provide a link to a Web site that includes the complete report

    Best Practice: Sending the full report is usually the best approach when the report is not too voluminous and when the report is intended for only the requesting user. When reports are large, or when others may access the report, the SAS process should write the report to a Web server (using the FTP access method if necessary) and should include a link to the report in the e-mail.

    18.2 Sample Implementation To run this example, go to the sample environment and select Chapter 18. Then enter values for the form fields and click the Run button.

    Figure 18.1 HMTL Input Form

    The sample code that does this is straightforward (the runLater source entry in the Chapter 18 catalog of the sample environment). %savemMVars n (lib=work, data=mvars, saveflag=y ) data runLater/view=runLater; o format email $64. datetime datetime.; retain datetime %sysfunc(datetime()); set work.mvars;

    Chapter 18: Using Scheduled Execution to Handle Long-Running Processes 277

    label email = "Email Results To" datetime = "Submitted At" name = "Parameter Name" value = "Parameter Value" ; email = symget("email"); run; proc append base = sampdata.runLater data=runLater; run;

    Z

    %externalHTML q (type=catalog, source=saspress.template18.runLater.source )

    n The saveMVars macro (Section 10.3) is used to save all the name/value pairs from the request in a data set in the SAS WORK library. Excluding the includeAuto parameter causes the parameters whose names begin with an underscore (_) to not be saved. This is the default behavior of the macro. o In order for the request to be processed later, it is necessary to uniquely identify each request. The requesting e-mail and the datetime values of the request are assumed in this example to provide unique keys. A view is used to eliminate extra passes of the data. Z The data (i.e., the name/value pairs) is appended to a cumulative data set so the name/value pairs are available to the stand-alone SAS session to be run later. For convenience of the sample environment, the library used here is the sample data library for the sample environment. As a Best Practice, data should be stored in some other data library (e.g., one that is not available to other Application Server requests). q The externalHTML macro (Section 7.5.1) is used to display a message to the user. This template reports that the request data has been saved for later processing. Submitting this request will cause the results shown in Figure 18.2 to be sent to the user’s browser. The captured parameter values are shown in Figure 18.3.

    Figure 18.2 Confirmation Message Following Request

    278 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 18.3 SAS Data Set Containing the Parameter Values for the Request

    The data shown in Figure 18.3 includes all the macro parameters (excluding those prefixed with an _). The values to be saved could be further subset by the program if desired. To see the table saspress.runLater, go to the sample environment and select Chapter 7. Select SAS Server Pages, and then select the library and the data set (Figure 18.3). The only other required component is another SAS program that:

    ƒ ƒ

    runs on a scheduled basis

    ƒ

    processes each request and, optionally, e-mails the results (or a link to the results) to the e-mail address provided by the requesting user

    uses the restoreMvars macro to make the name/value pair available as macro variables to the program to be run (which is itself defined as the value of program2Run)

    A program that does this is not included in the sample environment. It is a relatively straightforward SAS program that you can develop to meet your exact requirements.

    Chapter 18: Using Scheduled Execution to Handle Long-Running Processes 279

    18.3 Doing More with Scheduled Execution The example presented is the second of three techniques to handle long-running processes. It can be used as-is to implement a facility to capture parameters for reports and requests to be processed later. It used the externalHTML macro (Section 7.5) as well as the saveMvars (Section 10.3) macro. There are a number of enhancements that you may wish to consider before implementing this facility in your environment.

    ƒ ƒ

    Define a specific library where the requests to be processed are stored.

    ƒ

    Create a more robust set of keys (vs. e-mail and datetime) for the request. The ® UUIDGEN function can be used to do this in SAS 9.

    ƒ

    Include a parameter in the request to provide a description for the request (e.g., to be used in the e-mail sent to the requestor).

    ƒ

    Include an opportunity to specify that a request should be grouped with other requests by the same user into a single e-mail vs. a separate e-mail for each request.

    ƒ

    Include an opportunity to specify that a link to the output location should be e-mailed (instead of e-mailing the actual output). Optionally, the report location can be defined as a report parameter. This could be used by an administrator to run reports and have them saved on the Web server. The e-mail notification the administrator receives could then be forwarded to any interested parties.

    Ensure that the data set name where the requests are saved is specific to the request date (e.g., libref.runLater_DDMMMYY) or the hour (e.g., libref.runLater_DDMMMYY_HH). Using the date or time can allow for: … a daily request to be run shortly after midnight to process all the requests from the previous day … an hourly request to be run, for example, five minutes after the top of the hour to run all the requests from the previous hour.

    280 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    19

    Handling On-Demand Long-Running Requests 19.1 Introduction 281 19.2 E-mailing Results from an On-Demand Long-Running Process 282 19.1.1 Notifying the User 284 19.2 Updating Process Status by Refreshing the User’s Browser 285 19.2.1 Notifying the User 287 19.3 The Sample Framework—Spawning a Separate SAS Session 290 19.3.1 Sample Program to Submit a Long-Running Program— spawnSAS.source 295 19.3.2 Sample spawnSAS Macro 298 19.3.3 Sample Long-Running Program 300 19.4 Doing More with Independent Sessions 305

    19.1 Introduction Ideally, requests submitted to the SAS/IntrNet Application Dispatcher are programs that run relatively quickly. When this is not the case, consider using the Application Dispatcher to spawn an independent SAS session to run a long-running program or process. An independent session is an alternative to the scheduled execution approach discussed in Chapter 18 and the “Please Wait” message approach discussed in Chapter 17. Why is it not a good idea to allow on-demand long-running programs to be directly processed by the Application Dispatcher?

    282 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ ƒ

    Users don’t expect to submit a request and then wait a long time for the results.

    ƒ

    Long-running programs can tie up the Application Server for an extended period, making it unavailable for other requests.

    The Application Dispatcher times out after a specified time period. While this timeout can be overridden, it is not always reliable: … Long timeouts disable protection against run-away processes. … Browsers have a built-in timeout.

    Instead of allowing on-demand long-running requests to be run directly by the Application Server, a better approach is to have the Application Server spawn a separate SAS session to do this processing. Once the Application Server has submitted the code to this separate SAS session, the current request can be ended, thus freeing the Application Server so that it is available for other requests. This chapter presents a sample implementation of a framework to spawn asynchronous separate SAS sessions to handle long-running requests. In addition to the two examples that use this framework to spawn a separate SAS session, this chapter also provides an overview of how the sample framework enables the generated results to be returned to the requesting user:

    ƒ ƒ

    by sending the user an email by providing updates via the user’s browser on the status of the request, with the final output available once the separate SAS session has completed its processing

    The sample framework (which is included in the sample environment) is described next.

    19.2 E-mailing Results from an On-Demand Long-Running Process To run the example in this section, go to the sample environment and select Chapter 19. Then select Long-Running Request: E-mail the Results (see Figure 19.1) for a demonstration of spawning a separate SAS session to process a request. Here are the results:

    ƒ

    A message is returned to the browser almost immediately, informing the user that the request has been submitted (see Figure 19.2).

    ƒ

    The request to the Application Server is completed, and it is immediately available to respond to other requests.

    ƒ

    The separate SAS session produces the desired output and e-mails it to the address provided by the user (see Figure 19.3).

    Chapter 19: Handling On-Demand Long-Running Requests 283

    Figure 19.1 HTML Input Form—E-mailing Results On-Demand

    Figure 19.2 User Notification

    Figure 19.3 E-mailed Results from a Request for an On-Demand Process

    284 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The output shown in Figure 19.3 is produced by the following program. This program looks like any other program that can be run via the Application Dispatcher; i.e., nothing special needs to be added to the program to allow it to be run by a separate SAS process. Running this program via the sample framework (discussed in detail in Section 19.3), instead of directly, is what enables this program to have its results e-mailed to the requesting user. %generateOdsStmt (type=html, htmlOptions=(title = "Email Results") style = &OdsStyle ); proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; title 'Sample E-mailed Output'; footnote "Completed at %sysfunc(datetime(),datetime.)"; columns product region, sales; define product / group ' '; define region / across ' '; define sales / sum ' '; rbreak after / summarize; run; ods html close;

    Note: Programs that e-mail their results to the user cannot use the SAS Output Delivery System (ODS) to generate graphs. As discussed in Section 4.3.1, lightweight sessions are used when ODS generates an HTML page that has an embedded graph. In most cases, the lightweight session will have expired before the user opens the e-mail message containing the reference to the graph. For results with mixed text and graphics, a stand-alone program, as illustrated in Section 8.4, is suggested for pages with embedded graphs.

    19.1.1 Notifying the User The sample framework notifies the requesting user that the request has been submitted with the results e-mailed to the address that was specified. The externalHTML macro (Section 7.5.1) produces the output (see Figure 19.2) in the user’s browser using the following template. The details of how the program’s output is delivered via e-mail will be discussed in Section 19.3.

    Results Will be Emailed

    Request Submitted Your request to run &program2Run has been submitted n and will be emailed to &email when it is completed.

    Request Sumbitted at: %sysfunc(datetime(),datetime.)

    n The template references the macro variable program2Run. As discussed in Section 19.3, the sample framework uses this parameter to specify the name of the long-running program that is to be run on demand.

    Chapter 19: Handling On-Demand Long-Running Requests 285

    19.2 Updating Process Status by Refreshing the User’s Browser E-mailing the results of a long-running process may not be an acceptable solution in some cases. For example, consider the following scenario:

    ƒ ƒ

    Large data sets are subset in the process.

    ƒ

    The resulting custom summary data set or cube is then made available for drill-down analysis.

    The user specifies the class and analysis variables to be used to summarize the data (e.g., by using the SUMMARY procedure or by creating an OLAP cube).

    This scenario is demonstrated later in Section 19.3.3. A simpler example is presented here. To run this example, go to the sample environment and select Chapter 19. Then select LongRunning Request: Browser Refresh. Upon running the example, the user sees the display in Figure 19.4. This page will be automatically refreshed, thus keeping the user informed about the status of his request (e.g., the output shown in Figure 19.5). Finally, when the program has finished running, the output shown in Figure 19.6 is displayed.

    Figure 19.4 Browser Refresh Notification—Session Spawned

    Figure 19.5 Updated Status—Requested Program Has Started

    286 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 19.6 Updated Status—Results Returned

    This example uses a program similar to the one used in Section 19.2, except a graph is included in the output. Other than the calls to the updateStatus macro (discussed in Section 19.2.1), this program also looks like any other Application Server program. %updateStatus n (statusDataSet = &statusDataSet, msg = Request Beginning Execution ) data _null_; x = sleep(&refresh); run;

    o

    goptions dev=gif xpixels=600 ypixels=300 transparency; %generateOdsStmt (type=html, htmlOptions=(title = "Browser Refresh") path=&_tmpcat (url=&_replay) style = &OdsStyle ); proc gchart data = sashelp.shoes; title; hbar3D product/sumvar=sales subgroup=region shape=cylinder nostats discrete; run; quit;

    Chapter 19: Handling On-Demand Long-Running Requests 287

    proc report data=sashelp.shoes nowd style(summary)=[foreground=green]; columns product region, sales; define product / group ' '; define region / across ' '; define sales / sum ' '; rbreak after / summarize; run; ods html close; %updateStatus (statusDataSet = &statusDataSet, msg = Report Generated, done = Y )

    p

    n The updateStatus macro adds an observation to a SAS data set containing an update on the program’s status. The name of the data set is made available as a macro variable. The macro should be called, with an appropriate value for the Msg parameter, at key checkpoints in the program. This call to the macro generates the observation in the status data set shown in Figures 19.4 and 19.5 (the first row is inserted by the sample framework that spawns the independent SAS session). o Use the Sleep function to simulate a long-running process. p When the Done parameter is set to a non-blank value, the STATUS data set has an observation that indicates that the request has been completed. The sample framework interprets this as a trigger to display the output of the program instead of redisplaying the STATUS data set (e.g., the output seen in Figure 19.6). The details of how this works are discussed in Section 19.3.

    19.2.1 Notifying the User As in the e-mail example, the sample framework takes care of notifying the user about the status of her request. The example uses two components that are specific to this case, where the user is notified by forcing a refresh of a browser page and uses sessions so the SAVE library is available. The first component is the updateStatus macro, which is called to add a row to the STATUS data set at key points during the execution of the long-running program. The updateStatus macro is used in the sample code listed above. The second component is a program which displays the STATUS data set. The program also includes HTML META tags that force an automatic refresh to show the current status of the request. The data set that is displayed is the one that is updated by the calls to the updateStatus macro in the long-running program that is being processed by the separate SAS session. Note: The sample framework uses sessions, and the STATUS data set is stored in the SAVE library.

    288 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The refresh forced by the HTML META tag causes the program to be rerun, and thus causes the current STATUS data set to be redisplayed. This use of the META tag performs two important functions:

    ƒ

    It keeps the user updated about the status of his request, as long as he does not navigate away from this page.

    ƒ

    It causes the end time of the session to be extended, as long as the refresh time is shorter than the session time. In the sample spawnSAS driver program discussed in Section 19.3.1, the session time is set to be 10 times longer than the refresh interval.

    Although running this program repeatedly places additional demands on the Application Server, the program runs very quickly. Thus, this approach enables the Application Server to accept other requests between the refresh intervals while the separate SAS session is executing the longrunning request. The impact on the Application Server of repeatedly running this short program is significantly less than the impact of tying up the Application Server to run the long-running request. Here is the program (part of the sample framework) that displays the STATUS data set: data _null_; set &statusDataSet; where done ne ' '; n /* do a location replace */ file _webout; rc = appsrv_header('Location', "http://&_srvname/" || &_replay || 'results.html'); put; call execute('endsas;'); run; %generateOdsStmt o (type=html, htmlOptions=(title = "Job status as of &sysdate:&systime") style = &OdsStyle metatext="http-equiv=""refresh"" content=""&refresh""" );

    p

    proc print data = &statusDataSet(drop=done) label noobs; q title "Job status as of &sysdate:&systime"; footnote "If you leave this page, the output may not be displayed.”;r run; ods html close;

    n If the status data set contains an observation with the Done flag set to a non-blank value, the additional SAS process has finished running and has written its output to the &_tmpcat catalog (details discussed below in Section 19.3). In this case, this DATA step redirects the browser to the generated output using a location header (Section 8.2.1.1) using the Replay facility. Since no more content should be returned to the browser, the Request Executive for the current request is terminated using an ENDSAS statement. o The statements starting with the generateOdsStmt (Section 8.3.3) are run only if the longrunning request has not yet completed.

    Chapter 19: Handling On-Demand Long-Running Requests 289

    p The refresh rate is specified by the macro variable &refresh. q The PROC PRINT output (with the META REFRESH tag) is returned to the user’s browser. The PROC PRINT will be refreshed as long as the additional SAS session is still processing the long-running request. r If the user navigates away from this page she may lose the link that enables the output to be delivered to her browser. As seen in the above example, the updateStatus macro is used to add an observation to a SAS data set. This is the data set that provides the status information that is displayed by the program discussed above. Here are the macro parameters:

    ƒ

    Msg the text of the message for the new observation to be added.

    ƒ

    statusDataSet the name of the STATUS data set. When you specify a value in the SAVE library, the value is available until the session ends, and is cleared by the Application Server when the session ends (as discussed in Section 10.2). The STATUS data set contains three variables: … the datetime the observation was added (obtained using the datetime DATA step function) … the message text (the value of this parameter) … a flag indicating the request (i.e., the spawned SAS session) has finished running.

    ƒ

    Done a non-blank value for this parameter specifies that the processing has finished running.

    The macro source follows. As with all the macros included in the sample environment, this macro is not intended to be a robust implementation. Rather, it provides core functionality that you can update. %macro updateStatus (msg=, done=, statusDataSet= ); %if %length(&email) ne 0 %then /* do nothing as results being emailed */; n %else %if not %sysfunc(exist(&statusDataSet)) %then %do; /* status data set does not exist - create it */ data &statusDataSet; o format AsOf datetime. Status $256. Done $1.; Done = symget('done'); AsOf = datetime(); Status = symget('msg'); run; %end; /* status data set does not exist - create it */ %else %do; /* status data exists - add a new row */ data; /* use DATAn convention to generate name */ if 0 then set &statusDataSet; AsOf = datetime();

    290 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Status = symget('msg'); Done = symget('done'); output; stop; run; proc append base = &statusDataSet data = _last_ force; run;

    p

    %end; /* status data exists - add a new row */ %mend updateStatus;

    n Do nothing if there is a value for e-mail as that indicates that the user is to receive her results via e-mail. Embedding this logic here allows the long-running program to always call the macro regardless of whether the output it returned via e-mail or the browser refresh method. This is desirable, as it allows the user to control how she is to receive the output, while allowing the long-running program to handle either scenario transparently. o If the data set does not exist, create it with one observation. p Append the observation containing the updated status information to the STATUS data set.

    19.3 The Sample Framework—Spawning a Separate SAS Session The examples presented in Sections 19.1 and 19.2 used the sample framework to spawn a separate SAS process for the long-running request. Unlike the technique presented in Chapter 18, a separate SAS session is asynchronously spawned by the Application Server to process the request. That separate SAS session executes independently of the Application Server Request Executive that spawned this SAS session. Thus the Application Server is immediately available and can accept new requests. The sample framework provides two different methods to spawn this separate SAS session:

    ƒ

    the SYSTASK statement, which issues an operating system command. The advantage of the SYSTASK command is that it is simple and requires no additional products. However, it may not scale well as too many SAS sessions can degrade overall performance, and it is not available on all SAS platforms, e.g., Z/OS. MP CONNECT requires that an additional product be licensed (SAS/CONNECT), but MP CONNECT works on all platforms and can take advantage of any additional processors on the server.

    ƒ

    MP CONNECT, which spawns a separate SAS session on the same SAS server that the Application Server is running on. Note: The X command cannot be used to start another SAS session since the Request Executive environment that is used by the APPSRV procedure does not support X commands. The sample framework for long-running programs contains four reusable components that you can use and modify to meet your requirements:

    ƒ ƒ

    The checkStatus source entry (discussed in Section 19.2.1) The updateStatus macro (discussed in Section 19.2.1)

    Chapter 19: Handling On-Demand Long-Running Requests 291

    ƒ

    A source entry (spawnSAS.source) that sets up the environment for the separate SAS process. Figure 19.7 shows the logical flow illustrating how it works. Figure 19.8 provides a more detailed description for the process to create the actual program that will be executed by the separate SAS session.

    ƒ

    A macro (also called spawnSAS) is called by the spawnSAS.source entry. Its process flow is shown in Figure 19.9. The spawnSAS macro issues the operating system command to start the separate SAS session.

    Figure 19.7 Process Flow—Set Up the Environment: spawnSAS.source Entry

    292 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 19.8 Detailed Process Flow to Create the .sas File

    Figure 19.9 Process Flow for the spawnSAS Macro

    As seen in the examples presented in Sections 19.1 and 19.2, the program that was run using this approach looks like a typical Application Server program. There are only three requirements that the proposed framework imposes on the programs:

    ƒ

    The generated output should be written to the fileref _webout (the default behavior for any Application Server program).

    Chapter 19: Handling On-Demand Long-Running Requests 293

    ƒ

    As illustrated in the example shown in Section 19.2, the updateStatus macro should be used to add an observation to the SAS data set that contains the current status information.

    ƒ

    Upon completion, the updateStatus macro should be called with a non-blank value for the Done flag.

    Another example (shown in Figures 19.10 through 19.13) runs a program similar to the one run in Section 19.1 (invocations of the updateStatus macro have been added). This example allows the user to select whether he wants the results delivered by e-mail or by a browser refresh (if no email address is provided) as well as whether the separate SAS session is spawned with the SYSTASK command or MP CONNECT. To run the example in this section, go to the sample environment and select Chapter 19. Then select Spawn a new SAS Session.

    Figure 19.10 Query User for Parameters for How to Run the Long-Running Program

    294 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 19.11 Initial Display Informing User That a Session Was Spawned

    Figure 19.12 Updated Display Informing User That the Requested Program Has Started

    Chapter 19: Handling On-Demand Long-Running Requests 295

    Figure 19.13 Results Displayed in User’s Browser Due to Automatic Refresh

    19.3.1 Sample Program to Submit a Long-Running Program— spawnSAS.source The process flow for the spawnSAS.source program in the sample environment was shown in Figures 19.7 and 19.8 above. The spawnSAS.source program does the following things:

    ƒ

    sets up the environment to allow the requested program to be run by a separate SAS process

    ƒ

    invokes the spawnSAS macro (described below in Section 19.3.2) to start the separate SAS process

    ƒ

    depending on whether the user has chosen to be notified by e-mail or by having the browser display the current status, the spawnSAS.source program will take one of the following actions: … inform the user that the request has been submitted and that the results will be e-mailed … enable the display of the STATUS data set (using the checkStatus program discussed in Section 19.2.1)

    296 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    This Driver program handles all four scenarios (e-mail vs. refresh and SYSTASK vs. MP CONNECT). The following logic is used to determine which scenario to use:

    ƒ

    If a value for e-mail is passed in from the request, it is assumed that the results are to be e-mailed. Otherwise, the user is kept up-to-date on the status by a refresh of the browser.

    ƒ

    If a non-blank value for MP CONNECT is passed in from the request, MP CONNECT is used to spawn the separate SAS process. Otherwise SYSTASK is used.

    Note: The framework presented here can be extended to use different control logic. For example, the requestor may wish to be notified both by e-mail and a browser refresh. This would require a separate parameter that specifies notification by e-mail, refresh, or both. The spawnSAS.source code follows: %setDefaultValue(refresh,30) %global email MPConnect;

    n

    data _null_; rc = appsrv_session('create',&refresh*10); run;

    o

    %spawnedSASSessionBegin()p %let statusDataSet = save.jobStatus;

    q

    %updateStatus (statusDataSet=&statusDataSet, msg=Session Spawned r ) %saveMvars (data=nameValuePairs, saveFlag=Y, includeAuto=Y )

    s

    filename code2run "%sysfunc(pathname(save)).sas"; filename source catalog "&program2Run";

    t

    data _null_; infile source end=lr; file code2run; u if lr then put '%spawnedSASSessionEnd()' / 'endsas;'; if _n_ = 1 then do; /* generate setup statements */

    v

    put 'libname save "' "%sysfunc(pathname(save))" '";' / w "options sasautos=%sysfunc(getoption(sasautos));" / 11 '%restoreMvars(data=nameValuePairs)'; 12 if %length(&email) = 0 then 13 put 'filename _webout catalog "' "&_tmpcat" '.results.html";'; else put 'filename _webout email "' "&email" '" Subject="Emailed Results" CT="text/html";' / '%let _url = %completeBrokerName();';

    Chapter 19: Handling On-Demand Long-Running Requests 297

    end; /* generate setup statements */ input; put _infile_; 14 run; %spawnSAS 15 (program=%sysfunc(pathname(code2Run)), MPConnect=&MPConnect ) data _null_; 16 file _webout; if "&email" = " " then do; url = 'http://' || "&_srvname" || "%superq(_thissession)" || '&_debug=' || "&_debug" || '&_program=' || "%programName(pgm=checkStatus)" || 17 '&statusDataset=' || "&statusDataset" || '&refresh=' || "&refresh"; rc = appsrv_header('Location',url); put; /* blank line needed to force header to be generated */ end; else call execute ( '%externalHTML(type=catalog,source=saspress.template19.emailResults.source)' ); run; 18

    n A default refresh value is specified using the setDefaultValue macro (Section 5.3.3). For requests where the results are not e-mailed, this parameter controls how often the page informing the user of status is updated. The %GLOBAL statement is used to make sure the control parameters exist with null values. o Create a session so that the SAVE library is available to store the STATUS data set as well as a data set to contain the name/value pairs from the request. p Call a user-exit macro so any needed set-up processing can be easily integrated by adding the needed logic to the spawnedSASSessionBegin macro. q A macro variable is used to specify the name of the STATUS data set. r Add a row to the STATUS data set stating that the session will be spawned. s Save all the name/value pairs from the request, including those set previously in the program, using the saveMvars macro (Section 10.3). The includeAuto option is set to non-blank to indicate that the automatic variables (i.e., those beginning with an _) are to be saved. The macro variable statusDataset is also saved. t Set up filenames for the requested program (only source entries are supported in this example) as well as a .sas file (stored in the SAVE directory) to contain the statements to be processed by the spawned SAS session. u Generate the .sas file containing the code to run. The name of the .sas file is the session name. The file is saved in the parent directory of the SAVE library.

    298 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    v Call a user-exit macro to perform so any needed clean-up processing. Since additional requests are not handled by this process, generate an ENDSAS statement. w Prefix that code with the location of the SAVE library. 11

    SET the sasautos options and the location of the macro auto call library.

    12

    Include a call to the restoreMvars macro (Section 10.3). The %restoreMvars macro call makes all the parameters available to the program that is run in the generated SAS session.

    13

    Prefix the code to be run with a FILENAME statement for _webout that, depending on how the output is to be returned, uses the e-mail access method to e-mail the results or uses the catalog access method to write the results to the &_tmpcat catalog in the SAVE library.

    14

    Copy the code from the specified source entry (&nextProgram).

    15

    The spawnSAS macro (discussed in Section 19.3.2 below) starts the separate SAS session. The location of the program to run and the method to use to initiate the separate SAS session are passed in as parameters.

    16

    The spawnSAS macro started the separate session asynchronously, so this DATA step executes immediately and either uses the location header (Section 8.2.1.1) to redirect the user’s browser to a program that updates him on the status of the program, or uses the externalHTML macro (Section 7.5.1) to inform him that the request has been submitted.

    17

    The programName macro (Section 5.4) is used to provide the value for _program (instead of hard-coding the library and catalog) that points to another entry, checkStatus, in the same catalog as the spawnSAS.source entry.

    18

    The current request finishes running, while the separate SAS session is processing the user’s request. The Application Server is thus available for other requests.

    19.3.2 Sample spawnSAS Macro The spawnSAS macro starts the separate SAS session that runs the desired code. As with all the macros included in the sample environment, this macro is not intended to be a robust implementation. It provides the core functionality needed and you can update it as needed. In particular, this macro is specific to the Windows environment. When the starting of the separate SAS session is encapsulated into a macro (stored in a SAS autocall library) changing the macro so it is specific to the operating system is straightforward. Here are the macro parameters:

    ƒ

    Program the path to where the program is stored.

    ƒ

    Log used only by the SYSTASK option. This parameter supplies the path to where the SAS log should be written. If no value is specified, no log is written.

    ƒ

    MPConnect non-blank values indicate that the separate SAS session is to be an MP CONNECT session. The default is to use the SYSTASK option instead of MP CONNECT.

    Chapter 19: Handling On-Demand Long-Running Requests 299

    Note: The spawnSAS macro has been tested only on Windows systems using localhost. Before using the macro to start a SAS session on another host, your SAS license should be checked to see if that is allowed by the site’s SAS license agreement. %macro spawnSAS (program=, log=, MPConnect= ); %local sasexe; %let sasexe = %sysget(sasroot)\sas.exe;

    n

    options noxwait noxsync; %if %length(&MPConnect) = 0 %then Y %do; /* use systask to start a new SAS session */ %if %length(&log) = 0 %then %let log = -nolog; %else %let log = -altlog ""&log""; systask command """&sasexe"" ""&program"" -rsasuser -noterminal &log"; %end; /* use systask to start a new SAS session */ Z %else %do; /* use MP Connect to start a new SAS session */ options autosignon = yes; filename rsubcode catalog "&_tmpcat..mpconnect.source"; filename code2Run "&program"; data _null_; infile code2Run truncover end=lr; file rsubcode; if _n_ = 1 then put 'rsubmit '\ "%scan(%sysfunc(pathname(save)),-1,/\)" ' sascmd = "' %if %substr(&sysver,1,1) = 8 %then '""'; "&sasexe" %if %substr(&sysver,1,1) = 8 %then '""'; '" log = purge output = purge wait = no;'; input; put _infile_; s if lr then put 'endrsubmit;'; run; %include rsubcode;

    t

    u

    %end; /* use MP Connect to start a new SAS session */ %mend spawnSAS;

    q

    300 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    n Assign a macro variable to be the command needed to start another SAS session. This will need to be adjusted based on the operating system and may need a custom SAS configuration file (e.g., if the default config file is used, there may be security issues with the location of the SASUSER library if the SYSTEM started the Application Server because the spawned SAS session will inherit those privileges. Y SYSTASK is used to start the additional SAS session using the .sas program file generated by the spawnSAS.source entry. Z If MP CONNECT is used the code to be run must have an RSUBMIT command at the beginning and an ENDRSUBMIT command at the end. The RSUBMIT command includes options that enable the additional SAS session to be run asynchronously. Note that if the UPLOAD procedure or the %sysput macro were used to provide the macro variable values, the spawned SAS session could not be run asynchronously. q Define a FILENAME statement so the code, including the SUBMIT and ENDRSUBMIT statements, will be written to an entry in the &_tmpcat catalog. \ Generate the RSUBMIT command. The last node of the SAVE library name is used to provide a unique value for the remote session ID. In SAS 8, the SAS command needs to be ® embedded in triple double quotes, while in SAS 9 only one set of double quotes is needed. s Copy the original program entry. t Generate the ENDRSUBMIT command. u Execute the generated code which has been wrapped with the RSUBMIT and ENDRSUBMIT commands to start the SAS session using MP CONNECT.

    19.3.3 Sample Long-Running Program To run the example for the scenario described in Section 19.2, go to the sample environment and select Chapter 19. Then under Long-Running Request: Drill-Down Analysis, select Using SYSTASK (see Figures 19.14 through 19.17). The sample framework uses sessions (and the SAVE library) to pass information to the separate SAS session that creates the summary table. It then generates a link to the drill-down functionality available in the Xplore sample application (see Section 1.2.3). Xplore should be available as long as the Application Server has defined the location (i.e., the SAMPLIB proglib) of the Xplore sample application in a PROGLIBS statement.

    Chapter 19: Handling On-Demand Long-Running Requests 301

    Figure 19.14 Initial Display for Long-Running Request: Drill-Down Analysis

    Figure 19.15 Updated Display—Program Is Running

    302 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 19.16 Updated Display—Summary Process Completed

    Figure 19.17 Drill-Down Using the Xplore Sample Program

    Chapter 19: Handling On-Demand Long-Running Requests 303

    The following sample program (which is run by the separate SAS process when Using SYSTASK is selected) runs the SUMMARY procedure and creates an output data set that can be passed to the drill-down facility in the Xplore sample application. A common application requirement is to provide functionality where a large data set is selected, perhaps with a WHERE clause applied, different statistics calculated in the summary process which is then used in drilldown analysis. Note that the initial step, creating the summary table, can be time-consuming. %updateStatus (statusDataSet = &statusDataSet, msg = Program &program2Run Beginning Execution ) data _null_; x = sleep(&refresh); run;

    n

    proc summary data=sashelp.shoes; o class Region Product Subsidiary; var Stores Sales Inventory Returns; output out=save.shoeSummary p (drop=_freq_ label='Shoe Sales') sum=; run; %updateStatus q (statusDataSet = &statusDataSet, msg = Summary Step Completed ) data _null_; x = sleep(&refresh); run;

    n

    %updateStatus (statusDataSet = &statusDataSet, msg = Processing Completed - Report Being Generated )

    304 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher data _null_; r url = "&_url" || "?_service=&_service" || '&_server=' || "&_server" || '&_port=' || "&_port" || '&_sessionid=' || "&_sessionid" || '&_debug=' || "&_debug" || '&_program=samplib.websamp.mkdrlhtm.source' || '&dataset=save.shoeSummary'; file _webout lrecl=512; put '' ; run; %updateStatus t (statusDataSet = &statusDataSet, msg = Results from spawned SAS Session, done = Y )

    n Use the Sleep function to simulate a long-running process. o Run PROC SUMMARY to create the summary table. In a real application the name of the data set, the classification variables, the analysis variables, and the summary statistic to calculate would likely be passed in as parameters. p The output data set is stored in the SAVE library so it is available for further analysis. q The updateStatus macro is used to update the STATUS data set so the user is told about the status of his request. r Generate a URL to reconnect to the session, passing the name of the summary data set to the Xplore drill-down programs. s The JavaScript location.replace function is used to redirect the browser to the Xplore drilldown program on the summary data set in the SAVE library. The SAVE library is available since the URL includes the session ID. The location header cannot be used since the browser refresh facility is being used to deliver the results to the user’s browser. t The updateStatus macro is called one last time to set the Done flag so the checkStatus program will display the generated output.

    Chapter 19: Handling On-Demand Long-Running Requests 305

    19.4 Doing More with Independent Sessions The framework described in this chapter can be used as-is to implement a facility for long-running requests. However, there are a number of enhancements that you may wish to consider before implementing the framework in your environment.

    ƒ

    Allow for the program (e.g., &program2Run) to be something other than a catalog source entry.

    ƒ

    Instead of assuming that the absence of an e-mail address implies that the user wants to be notified via a browser refresh, use an explicit parameter to control this behavior.

    ƒ ƒ ƒ ƒ

    Update the spawnSAS macro for other operating systems.

    ƒ

    Provide a location for the generated output so it can be access later via a URL (that can be e-mailed to the requesting user).

    Allow the session time to be passed in as a parameter value. Enable the user to enter a description for the subject line for the generated e-mail. Provide a mechanism that keeps track of the number of currently running asynchronous SAS sessions. If the maximum number is currently running, the user can be informed (via a template) to try again later.

    Check the sample environment for updates on supporting long-running programs via the Application Dispatcher. The techniques described in this chapter may meet your requirements for applications with ondemand long-running requests. However, for the most current techniques and Application Dispatcher features that address these requests, see the sample environment for examples. Spawning separate SAS sessions to handle long-running processes should be done with care. Too many such requests may seriously degrade the performance of all the applications, including the Application Server or servers, running on the server.

    306 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    C h a p t e r

    20

    Using Metadata-Driven Reporting to Construct Reports 20.1 Introduction 307 20.2 Sample Implementation 309 20.2.1 The Logon Template 311 20.2.2 The Logon Program 312 20.2.3 Selecting the Case or Individual to Examine 315 20.2.4 Report Components 319 20.2.5 The assembleReport Macro 323

    20.1 Introduction This chapter examines a sample application that provides a data review facility to enable various users to see relevant data and reports. While the example provided here is specific to patient reporting, the approach is broadly applicable. This example demonstrates the use of a sample framework that uses metadata to define and construct reports. This sample framework is not robust (e.g., it does very little error handling) and demonstrates the techniques presented earlier to facilitate the development of such applications. One of the keys to this is the use of small components, each of which performs a simple function.

    308 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Here are some selected reporting requirements where the use of a framework like the one presented here might be useful:

    ƒ

    A clinical trials reporting environment, where the user may need to see patient demographics, visit history, treatment history, adverse events history, etc.

    ƒ

    A CRM environment where a call center representative might need to see the customer’s profile, current services, offer history, payment history, etc.

    ƒ

    A manufacturing environment where a quality control analyst might need to see the product history, information about the supplier of the raw materials, or details about the production line.

    ƒ

    A financial services environment, where a compliance officer might need to see summary and detailed information on transactions for a particular client or with a particular broker.

    The reports are to be built from individual components, and those components can be combined depending on the user’s access level (i.e., what they are allowed to see). Certain report components are included in multiple reports. For example, the example presented here includes the patient’s demographics on every report. The reports are conceptually similar to a portal in that a single page is produced containing a variety of information about the selected entity (in this case, a patient). To run this example, go to the sample environment and select Chapter 20 and then select Metadata-Driven Reporting. See Figure 20.1 for an example report constructed using this framework.

    Figure 20.1 Sample Metadata-Driven Report

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 309

    20.2 Sample Implementation The framework and example reports that are provided with the sample environment are built using the following components:

    ƒ

    Metadata that defines each report by listing the individual report components that it contains, as well as information about what groups of users are allowed to see each component of the report.

    ƒ

    The security model discussed in Section 12.3. There are any number of approaches to implemented security that can be used. You can replace the provided one with an alternative that better meets your requirements.

    ƒ

    A logon facility that captures the user ID and creates a session for the user’s reporting requests.

    ƒ

    The extending sessions feature discussed in Section 10.6, including a template that will automatically direct the user to the logon page if a session has timed-out because of no activity.

    ƒ

    A sample macro, assembleReport, which uses the metadata to build the report by calling all the component programs.

    ƒ

    A variety of sample reporting programs that perform specific functions.

    The list of users and their assigned groups for the available reports in the sample environment are shown in Figure 20.2.

    Figure 20.2 Users Mapped to One or More Groups

    310 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Note: As discussed in Section 12.2, _rmtuser should be used if possible to identify the user. This example illustrates the alternative use of a logon page for these reasons:

    ƒ ƒ

    to demonstrate how it can be done using the SAS Server Pages facility (Section 7.6) since the sample environment cannot assume HTTP authentication is available and, therefore, _rmtuser cannot be used

    The process flow for the framework is shown in Figure 20.3 and described below.

    Figure 20.3 Metadata-Driven Reporting Framework Process Flow

    1. The SASServerPage macro is used to generate a logon page. 2. A logon program checks the ID and password and if either is invalid, the logon page is redisplayed. 3. Otherwise, the following actions occur: 3.1 A session is created. 3.2 The metadata is subset to include only those report components or reports the user is authorized to see. This subset of the data is saved in the SAVE library created for the session. 3.3 The user is redirected to a page where she can select the individual or entity to be reviewed. 4. The report is assembled and displayed for the selected individual or entity. The generated report includes forms to allow the user to select a different report (if he is authorized for multiple reports) or a different individual or entity to report on.

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 311

    20.2.1 The Logon Template The first step in the process is to log on. The logon page is generated using the SAS Server Page macro discussed in Section 7.6 using the following template. Any number of logon pages (e.g., the structure and format) can be supported by adding new templates and making the appropriate changes.

    Logon - Metadata Driven Reporting

    Logon - Metadata Driven Reporting %global failed; n &failed

    o

    UserID:
    Valid ids are receptionist, nurse and physician
    Password: Z


    Current Datetime: %sysfunc(datetime(),datetime.)

    n The template may be called recursively and can include a message (the macro variable reference &failed) if the calling program sets it. The %global statement ensures the macro variable exists. o &_pgmlib is used instead of hard-coding the library for the program. This facilitates maintenance and re-use of the logon template. In a real application, the catalog would have a more descriptive name than chapter20. It can also be stored as a file in the file system. Z For your convenience only, the password is pre-populated into the template so that you do not need to know/remember the password to run the example. To run this example, go to the sample environment and select Chapter 20, which shows the Metadata Driven Reporting example. As seen in Figure 20.4, entering receptionist, nurse, or physician for the user ID and then clicking the Logon button causes the program to run.

    312 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 20.4 Logon Screen Using Customizable Template HTML Page

    20.2.2 The Logon Program As mentioned in the previous section, this step in the process accomplishes several things.

    ƒ

    Checks the user ID and password to make sure they are valid. Note that the sample code uses an IF statement to check the ID/password combination. In a real application, a more robust scheme should be used. The SCL approach discussed in Section 12.2.2 allows for flexibility and security in checking passwords. The user ID/password combinations can be stored in a password-protected SAS data set. The password for that data set can be provided by a macro variable reference whose value is provided when the SCL is compiled (also discussed in Section 12.2.2).

    ƒ

    If the user ID or password is not valid, the user is returned to the logon page so he can reenter his user ID and password (see Figure 20.5).

    Figure 20.5 Invalid User ID/Password Combination Entered

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 313

    ƒ

    Otherwise, the following actions occur: …

    A session is created so the SAVE library is available.

    …

    The SAVE library is used for the user-specific subset of the metadata that defines the reports (and their components) that the user is authorized for.

    …

    The request is redirected to a page where the user can begin the process to select the data and the report she wants to review.

    The logon program follows. %global userID Password;

    n data _null_; if lowcase(symget('userID')) not in ('receptionist' 'nurse' 'physician') or symget('Password') ne 'paSSword' then do; /* invalid login */ call symput('failed','Invalid UserID/Password Combination'); call execute('%externalHTML(type=catalog,source=’ o|| ”&_pgmlib”||’.template20.logon.source)'); call execute('endsas;'); p stop; end; /* invalid login */ rc = appsrv_session('create'); q run; %let groups = "-NONE-"; proc sql noprint; /* determine the groups for this user */ r select distinct(quote(upcase(trim(group)))) into: groups separated by ' ' from sampdata.chapter20users where upcase(userid) = "%upcase(&userID)"; s quit; proc sql; rt create table save.layouts as select components.Type, components.Entry, reports.Layout, reports.Order, reports.newRow, reports.columns from sampdata.chapter20ReportComponents components, sampdata.chapter20ReportLayouts reports where upcase(components.group) in (&groups 'ALL') and components.componentKey = reports.componentKey order reports.Layout, reports.Order; proc sql; create table save.reportList as select * from u

    314 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    sampdata.chapter20reportList where layout in (select distinct layout from save.layouts); quit; data _null_; file _webout; v url = 'http://' || "&_srvname" || "%superq(_thissession)" || '&_debug=' || "&_debug" || '&_program=' || "&_pgmlib..chapter20.assembleReport.macro"; rc = appsrv_header('Location',url); put; /* blank line needed to force header to be generated */ run;

    n For simplicity, an IF statement is used to check the ID/password combination. A more robust scheme to validate such values is mentioned above. o If the entered combination is not valid, the externalHTML macro is used to display the logon template (originally displayed by the SASServerPage macro), with a custom message provided by the value of the macro variable (failed). p An ENDSAS statement is used to terminate the current request so the rest of the code to subset the reporting metadata for this user is not run. q A session is created so the SAVE library is available. r The logic for the example in Section 12.3 (customizing menu choices based on who the user is), with the data shown above in Figure 20.2, is used to subset the report metadata to include only the report components the specific user is allowed to see. s _rmtuser could be used instead of userID if there is no logon page. t This SQL step creates the report layout data needed by the assembleReport macro and stores it in the SAVE library for the session so that it is available to later requests. See Figure 20.6 to see this data for a user ID of nurse. The source data used here is discussed in Section 20.2.4 below. To learn more about PROC SQL, see the SAS documentation. u The list of reports is subset to include only those reports for which the current user is authorized to see at least one of the report components. It is also saved in the SAVE library for the session. See Figure 20.7. v The user is redirected to a page generated by the assembleReport macro (Section 20.2.5). The location header (Section 8.2.1.1) is used to do the redirect. The data sets created by the SQL step (corresponding to a user ID of nurse are shown in Figures 20.6 and 20.7. Note that the select layout is not included in Save.ReportList as, by design, it is not in the sampdata.chapter20ReportList data set. It is a default used by the assembleReport macro when no layout is specified (e.g., upon initial entry).

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 315

    Figure 20.6 Report Layouts for the Current User (nurse)

    Figure 20.7 Available Reports for the Current User (nurse)

    20.2.3 Selecting the Case or Individual to Examine The assembleReport macro is used with the select layout seen in Figure 20.6 as the default report (i.e., when the user first enters the reporting application). The select layout includes two customizable templates that can be displayed by the externalHMTL macro (see Section 7.5). The

    316 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    selectKey.source entry contains the HTML form that allows the user to select the individual or entity as well as the report he wants to see. The overview.source entry includes a description of the reporting facility (e.g., it could serve as a Help page). This use demonstrates how the assembleReport macro can be used to combine components to assemble the page to be seen by the user, including components that are not reports generated from the data using ODS.

    The sasServerPage macro could be used instead if only a single HTML template (e.g., the selectKey.source template) is to be displayed. The selectKey template is shown below. It contains the HTML that allows the user to select the key value for the individual to be examined.

    Consolidated Data Review as of %sysfunc(datetime(),datetime13.) for: %global Name_Key Layout; %generateOptionTag n o (data=sampdata.chapter20Demographics, var=Name_Key, label=Name, selected=&Name_Key) %generateOptionTag (data=save.ReportList, var=Layout, label=Description, selected=&Layout)





    n For readability and formatting purposes, the calls to the generateOptionTag macro have been split across multiple lines. In the actual template, the complete macro call must be specified on a single line. o The generateOptionTag macro (Section 7.6.3) is used to generate the option tags that allow the user to select the key for the case or individual, as well as the report layout. The SELECTED argument shows the current value (if the template is called recursively). The reportList data set is the subset version in the SAVE library that includes only reports and report components the user is authorized to see. The second template, overview.source, contains a text description of the reporting framework that is to be included when the user enters the reporting application. Figure 20.8 shows the output produced when the assembleReport macro is called with this default report layout. When the user selects Lab History and then clicks the Review button, the output shown in Figure 20.9 is displayed.

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 317

    Figure 20.8 Initial Display—Select Patient with Overview or Help Text

    The sample environment will be expanded to include reports other than the one shown here (see Figure 20.8) that can be produced from this facility. Download and install the sample environment (and check http://hcsbi.com/IntrNetAppDev regularly) to see these additional examples. Check the sample environment on the Web for updates.

    318 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 20.9 Partial Output—Lab History Report

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 319

    20.2.4 Report Components Three metadata data sets that define the reports that are used to create the SAVE.Layouts and SAVE.reportList data sets are displayed in Figures 20.6 and 20.7. They can be seen using the data set paging facility discussed in Section 7.6. To run the example in this section, go to the sample environment and select Chapter 7. Select SAS Server Pages, and then select the SAMPDATA library. The results are shown in Figure 20.10.

    ƒ

    Chapter20ReportComponents is the list of available report components (see Figure 20.10).

    Figure 20.10 Report Definition Metadata—Report Components

    ƒ

    Chapter20ReportLayouts is the data set that defines which components are included in each report and how they are laid out (see Figure 20.11).

    320 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 20.11 Report Definition Metadata—Report Layouts

    ƒ

    Chapter20ReportList is the list of layouts that the user can select, depending on his level of access (see Figure 20.12).

    Figure 20.12 Report Definition Metadata—Available Reports

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 321

    The example reports can be constructed by combining any of the eight listed report components seen in Figure 20.10. Two of the components are HTML templates which are processed by the externalHTML macro (corresponding to a value for Type of T) and the rest are programs (Type values of P) that generate HTML using ODS. The variable Group defines the security group the user must be part of in order to see the individual component. Figure 20.11 shows the report layouts data set that defines the reports that can be generated by the assembleReport macro. It has variables that define the following:

    ƒ

    which report layout it is and the component identifier (componentKey), which provides a link to the component as seen in Figure 20.10.

    ƒ ƒ

    the order in which the component is included in the report. two columns (newRow and Columns) that are used by the assembleReport macro to specify that the report component does not take up the full width of the page (i.e., if Columns has a value). For example, components 4 (printMeds) and 5 (printDiagnoses) are displayed in side-by-side panels. The field newRow is used to force the display of the component below the previous component (e.g., it forces a new report component row if the previous row contained multiple components).

    The select layout, as discussed above, includes the component (componentKey=1, selectKey), which is the HTML template that allows the user to select which individual and which report is to be generated. It also includes another HTML template (componentKey=2, overview) which is a description of this metadata reporting framework. This is the default layout used by the assembleReport macro when the user first logs in. The selectKey entry is included in every report so the user can change the case or individual being examined, as well as the report contents, while reviewing any of the defined reports. Figure 20.12 lists reports that can be selected by the user as long as she is authorized for at least one of the components of a report. The function of this data set is to provide a more meaningful description to present to the user than the value of the layout variables. The logic used in the logon program (Section 20.2.2) results in including only report1 and report2 in this data set. The select component is not included in the pull-down list the user is allowed to select from. As discussed above, the select component is used as a default entry point for the assembleReport macro. The sample report shown earlier in Figure 20.1 (report1, which is Current Status) contains five components.

    ƒ

    The selectKey template (discussed above in Section 20.2.3) allows the user to select the individual as well the report to be displayed.

    ƒ

    The Demographics program (Section 20.2.4.1) is used to display the data for the selected case or individual and is discussed below.

    ƒ

    The following three simple programs (source available in the Chapter 20 catalog of the sample environment) that use a WHERE clause to subset the appropriate data sets to the selected case or individual are included next. Each program includes the ODS statements needed to produce the desired output. All three use the NO_TOP_MATTER and NO_BOTTOM_MATTER options in the ODS statement. …

    printMeds

    …

    printDiagnoses whose output is displayed next to, rather than below, the output from printMeds

    …

    printCurrentLabs

    322 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    The sample report shown above in Figure 20.9 (report2, which is Lab History) contains four components. The first two are the same components included in report1. The other two are (like the programs in report1) simple programs that use a WHERE clause to subset the appropriate data sets to the selected case or individual and use ODS to produce the desired output. Both use the NO_TOP_MATTER and NO_BOTTOM_MATTER options in the ODS statement

    20.2.4.1 The dataReviewDemographics Program This entry is used to display the data for the selected case or individual. The program subsets the data and then loads all the values into macro variables. This allows a template to be used (with the externalHTML macro) to display the data. The use of macro variables embedded in a template HTML file gives great flexibility in formatting with none of the awkward quoting issues that would be encountered using PUT statements. The program follows. data _null_; n set sampdata.chapter20Demographics; where name_key = &name_key; array _n _numeric_; array _c _character_; do i = 1 to dim(_n); fmt = vformat(_n(i)); if fmt = ' ' then fmt = 'best12.'; call symput(vname(_n(i)),trim(left(putn(_n(i),fmt)))); o end; do i = 1 to dim(_c); fmt = vformat(_c(i)); if fmt ne ' ' then call symput(vname(_c(i)),trim(left(putc(_c(i),fmt)))); else call symput(vname(_c(i)),trim(left(_c(i)))); o end; run; %externalHTML (type=catalog, p source=saspress.template20.Demographics.source )

    n A generalized DATA step that creates macro variables corresponding to each variable in the data set for the selected individual and should work for virtually any data set. o If a variable has a format associated with it, the format is used to create the value of the macro variables. These macro variables are referenced in the template discussed below. p Display the data using a template specific to the data set. Note: If you are familiar with the FSLETTER procedure, you may see similarities (i.e., the use of & references) between FSLETTER letters (which get their values directly from a data set) and the use of macro variables in this template.

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 323

    The template used by the externalHTML macro follows and is specific to the data set being displayed, chapter20Demographics. The data set can be viewed using the data set paging facility discussed in Section 7.6:

    &Name(&Name_Key) %iif((%length(&Sex)>0), | Sex:&Sex) %iif((%length(&Height)>0), | n Height:%sysfunc(int(&height/12))%str(%')%sysfunc(mod(&height,12))%str( %"))o %iif((%length(&Weight)>0), | Weight:&Weight.lbs) %iif((%length(&Height)>0 and %length(&Weight)>0), | BMI: %sysfunc(putn(703*&Weight/&Height/&Height,4.1))) o %iif((%length(&Birth_Date)>0), | Date-of-Birth:&Birth_Date) %iif((%length(&Last_Visit)>0), | Last Office Visit:&Last_Visit) %iif((%length(&Phone)>0), | Phone:&Phone) %iif((%length(&Insurance)>0), | Insurance:&Insurance, | Insurance:No Insurance on File)

    n The iif macro (Section 7.3.1) conditionally generates labels for columns and values that may not have values. o %sysfunc is used to enable calculations within the template to generate values for display (i.e., BMI is calculated from Height and Weight; Height in inches is displayed in feet and inches).

    20.2.5 The assembleReport Macro The last component of the sample framework is the assembleReport macro that uses the metadata data sets to define what individual report components are to be included in a specific report. It assembles the report by invoking each component in the appropriate order. If the report component does not take up the full width of the display (as defined in the metadata), HTML tables are used to display components side-by-side. The macro is provided in the sample environment and, as for all the other provided macros, is meant to provide you with sample code that you can use as the foundation of a more robust reporting tool. It includes a very simple (with limited functionality) ability to lay out report components and is not intended to be a robust report generation facility. Depending on your release of SAS there are a variety of options, e.g., the ODS document object, that you may wish to consider. %macro assembleReport; %local entries closeTable; %setDefaultValue(layouts,save.Layouts) %setDefaultValue(layout,report1)

    n

    proc sort data=&layouts out=_report; o where "%upcase(&layout)" = upcase(layout); by order; run;

    p

    ods html file = _webout(no_bottom_matter title=”Consolidated Data Review) style = &OdsStyle; ods html close; %let entries = 0; %let closeTable =0;

    324 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    data _null_; length putText $256; retain cnum '0 ' lastColumns; if lr then call symput('entries',cnum); set _report end=lr; type cnum call call

    = upcase(type); = left(put(_n_,3.)); symput('type'||cnum,trim(upcase(type))); symput('entry'||cnum,trim(entry));

    [

    if lastColumns and (not Columns or newRow) then putText = ''; \a if (Columns and not lastColumns) or newRow then putText = trim(putText) || '

    '; \b if Columns then putText = trim(putText) || '
    ';

    \c

    call symput('putText'||cnum,trim(putText)); if lr and (Columns or newRow) then call symput('closeTable','1'); s lastColumns = Columns; run; %do i = 1 %to &entries;

    t

    %if %length(&&putText&i) ne 0 %then %do; /* table tags needed */ u data _null_; file _webout; put %sysfunc(quote(&&putText&i)); run; %end; /* table tags needed */ %if &&type&i = T %then %externalHTML(type = catalog,source = &&entry&i); %else %if &&type&i = P %then %do; /* content is SAS Code - execute it */ filename _entry catalog "&&entry&i"; %include _entry;

    w

    %end; /* content is SAS Code - execute it */ %else %do; /* Unknown type */ 11 /* Error Handling The externalHTML macro could be used */ %put ERROR: Unrecognized Type: &&type&i; %end; /* Unknown type */ %end;

    v

    Chapter 20: Using Metadata-Driven Reporting to Construct Reports 325

    %if &closeTable %then 12 %do; /* last row had columns - close the table */ data _null_; file _webout; put '
    '; run; %end; /* last row had columns - close the table */ %externalHTML 13 (type=catalog, source=%getTextString(keyvalue=refreshSession) ) ods html file =

    15

    14

    _webout(no_top_matter); ods html close;

    %mend assembleReport;

    n Define default values using the setDefaultValue macro (Section 5.3.3). o Subset and order the data to the selected report. p Use an empty ODS block to create all the top matter for the report. This allows the individual programs to all use both no top and bottom matter. q Create arrays of macro variables for all the report components. r Generate the text needed (into another array of macro variables) to generate table cells for report components that should appear side-by-side (i.e., as report panels): a. Close the table for the previous row if the current component should take the full width. b. Generate a table tag if this is the first component on the row. c. Generate a table cell if the component if the component has the Columns flag set.

    s There is no next row to close the table if the last component is a component to be displayed in a panel. t Loop through all the components. u If the report component is to be displayed in a panel, generate the necessary table tags. v Use the externalHTML macro if the component is a template. w If the report component is a program to run (this sample framework supports only catalog source entries), use %include to run it. 11

    Produce an error for an unrecognized component. The externalHTML macro could be used if the error message is to be included in the generated output (instead of just the SAS log).

    12

    If the last component is displayed as a panel, a CLOSE TABLE tag is needed.

    13

    Use the externalHTML macro to display the template that pops up the window to ask the user if they want to extend their session (Section 10.6). It is only displayed if there has been no activity for a specified time window. Including this template is all that is needed to provide a pop-up window asking the user if they want to extend their session.

    326 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    14

    getTextString (Section 14.3.1) is used to retrieve the name of the template that pops the refresh window.

    15

    Use another empty ODS block to generate the bottom matter.

    C h a p t e r

    21

    Packaging the Application Dispatcher as a Web Service 21.1 Introduction 327 21.2 Solution Architecture 329 21.3 Sample Application 330 21.3.1 The Sample .NET Client Application 331 21.3.2 The .NET Web Service 332 21.4 Using the Sample Client Application 335 21.5 Final Thoughts 338

    21.1 Introduction Recall that Section 1.4.3 briefly described Web services as a means of packaging applications as services so they can be made available over the Web. All communication between services (i.e., the request for a service, and the results of running a service) are handled via a number of XMLbased technologies. Web services provide a robust platform for distributed application development that facilitates interoperability. Specifically, a Web service can be described as an application component that has the following characteristics:

    ƒ ƒ ƒ ƒ

    communicates via open protocols (e.g., HTTP) processes XML messages using the Simple Object Access Protocol (SOAP) describes its messages using defined XML schemas provides a description of its services using the Web Services Definition Language (WSDL), which enables other applications to discover what the Web service does and how to request those services

    328 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    ƒ

    can be discovered in a variety of ways, e.g., using Universal Description, Discovery, and Integration (UDDI). Note, however, that most Web services are known by their clients, and so searching for them is not typical.

    Note: Web services are an advanced topic. This chapter assumes you understand Web services and are interested in seeing how SAS, and specifically the Application Dispatcher, can be packaged as a Web service. Web services have gained wide acceptance and interest. Demand for Web services is growing. As of the summer of 2006, for example, there were hundreds of vendors (including SAS) that are part of the Web Services Interoperability Organization (www.ws-i.org). This represents an expanding opportunity for SAS and its users to make the facilities of SAS available to a broad range of business applications. Web services that can provide access to SAS functionality and data may offer the same kind of growth opportunities that the release of SAS/ACCESS did (e.g., by allowing SAS to access operational databases). Programmers who make use of the interoperability of Web services to provide access to SAS add extraordinary depth and power to the rapidly growing world of Web services. ®

    Note: In SAS 9, the Stored Process Server includes some built-in facilities to make the results of specified Stored Processes available via Web services. Although the Application Dispatcher is not a Web service (its services are not available using the Web services architecture described above), a wrapper application can be written for the Application Dispatcher that meets the above criteria while internally using the services provided by the Application Dispatcher. This concept is examined more closely in this chapter, using a simple hypothetical scenario for a fictitious organization where SAS is used to build an analytical model. (This model might include a neural network using SAS Enterprise Miner, a forecast using the High Performance Forecasting tools, a propensity to buy using the LOGISTIC procedure, and a descriptive analysis of historical data using the FREQ or UNIVARIATE procedures, etc.). The model is run regularly as part of the organization’s warehouse infrastructure (and can be rerun on demand when new information, data, or both become available). The organization would like to make the forecast data available to other Web services-enabled applications. To demonstrate accessing SAS using a Web service, the sample environment includes a .NET 1 application that connects to a Web service to access SAS data. The .NET application allows the user to pick a SAS library and a data set. The application then displays the resulting data as either an HTML table or a data grid. This example shows that the SAS results:

    ƒ ƒ

    can be surfaced via a Web service to a client application can be leveraged by the client application to perform any number of functions on that content

    Note: A Web service that can provide SAS data can also be used to provide access to any file structure or database that SAS can read (e.g., any database for which there is a SAS/ACCESS product).

    1

    The sample .NET applications were developed by Alan Churchill of Savian and can be downloaded with the sample environment. They are also available from the Savian Web site at http://utilities.savian.net.

    Chapter 21: Packaging the Application Dispatcher as a Web Service 329

    21.2 Solution Architecture The architecture of the provided sample contains three components:

    ƒ

    The client application (i.e., the consumer of the Web service) as described in Section 21.1.

    ƒ

    An application that is the Web service itself. It is a wrapper for the Application Dispatcher and is located on the same Web server as the Application Broker.

    ƒ

    The Application Dispatcher along with any appropriate programs needed to provide the SAS results or data.

    Figure 21.1 illustrates the process flow when the Application Dispatcher is invoked as a Web service.

    Figure 21.1 Process Flow—Invoking the Application Dispatcher as a Web Service

    The following processing steps are shown in Figure 21.1. 1. The client application (e.g., a .NET application, a Java application, etc.) initiates a request using a SOAP wrapper, with the input to the Web server defined via XML. 2. The Web service wrapper makes a request to the Application Broker (located on the same Web server) using HTTP. It simply makes a request by passing it a URL, exactly like a user’s browser does. 3. The Application Broker forwards the request to an appropriate Application Server (as discussed in Part 2 of this book). The Application Server can be any machine that the Application Broker can access via TCP/IP. This means the Web service need not be running on the same server as SAS. 4. The Application Server processes the requests and returns the results to the Application Broker. The results are typically XML but can be anything (e.g., ODS HTML output) as long as the client knows how to consume it. 5. The Application Broker relays the returned data stream to the Web service running on the Web server. 6. The Web service relays the results to the client application after putting them in a SOAP wrapper.

    330 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Note: Only steps 1 and 6 are known to the client application. The internal processes of what the Web service does (in this case sending a request to the SAS/IntrNet Application Dispatcher) are transparent to the client. This is one of the advantages of Web services architecture. The sample Web service has a number of methods that can be requested by the client. To run this example, go to the sample environment and select Chapter 21. Then select one of the methods listed under Web Service Properties and Methods. When the user selects the link, a Web service page is displayed (see Figure 21.2) that lists the available Web services. Each of the listed services can be viewed as analogous to methods, functions, etc.

    Figure 21.2 List of Available Methods and Services

    21.3 Sample Application The sample environment contains two applications implemented using the .NET 2.0 (or higher) framework. One of the applications is the client, which will be the requestor of Web services; the other is the Web service wrapper. The two sample applications need to know the following information:

    ƒ

    The client application needs to know the URL for the Web service. A default value is provided. It can be changed using the ToolsXOptions pull-down menu.

    ƒ

    The Web service needs to know the HTTP address for the Application Broker as well as the service to use (it does not need to know where the SAS Application Server or Servers

    Chapter 21: Packaging the Application Dispatcher as a Web Service 331

    reside). These values are provided during the installation of the Web service on the Web server. Note: Typically the _service to use should not be of interest to the client application. However there may be instances where a single Web service can provide access to data or functionality that is provided by distinct services. In such cases, it may be appropriate to expose methods that might allow the client to select the service. The sample application allows the user to select any data library defined to the Application Server (e.g., similar to the SAS Server Pages example in Section 7.6) and then a data set in that library. The following subsections discuss selected code snippets from the two applications. Instructions for installing the sample client application and the sample Web service are available from the sample environment at http://hcsbi.com/IntrNetAppDev. Note: You need not install the Web service application to run the example. The client application, however, must be downloaded and installed if you wish the run the samples. Assuming you have Internet access, the client will contact the Web service running on the Web site for the sample environment.

    21.3.1 The Sample .NET Client Application The development environment provided by Microsoft’s Visual Studio 2005 was used to develop the .NET applications. It includes a wizard that defines Web services and builds the needed infrastructure so the application can access the Web services illustrated in the Figure 21.3. The details regarding the implementation of the .NET applications using Visual Studio are included here for the convenience of readers who are familiar with .NET development. The details of how to implement a .NET application are not covered in this book.

    Figure 21.3 Building the .NET Client Using Visual Studios

    The URL for the Web service is defined in the wizard. It can be updated programmatically as well: SasServices.Service sas = new SasServices.Service(); sas.Url = "http://demo.savian.net/SasWebServicesDemo/Service.asmx"

    332 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Invoking one of the Web services (aka a method) is also straightforward: XmlDocument xdoc = sas.GetTableNames(cbSasLibrary.Text) ;

    21.3.2 The .NET Web Service As discussed in the process flow above (Figure 21.1), the Web service submits a URL to the Application Broker and passes it the appropriate name/value pairs, including the values for _service and _program. Note: The security issues discussed in Chapter 12 should be taken into account if the Web service is allowed to submit code using a technique similar to the provided sample. For example, the Web service could provide an application ID or password that is known only by the Web service and that can be checked by the Application Server, as discussed in Sections 12.2.1 and 12.2.2. The RunCode example presented in Section 6.2.2.1 (that allowed any arbitrary code to be submitted) is used by the sample Web service. This allows the Web service to have complete control over what information is needed and how the results are to be returned since it is building and submitting the SAS code instead of simply passing parameters to specific programs. The Web service could also be implemented so that its methods invoke specific programs that it just passes parameters (i.e., name/value pairs) to. The following subsections discuss the code for several of the methods, both externally accessible as well as internal methods.

    21.3.2.1 The GetTableNames Method This method creates the lines of code that, when submitted to the RunCode program, return an XML file containing the list of SAS data sets in the selected library. [WebMethod] n public XmlDocument GetTableNames(string library) {//Check for alphanumerics only Regex regex = new Regex(@"\W"); if (regex.Match(library).Success) return null; pgm = o "saspress.requires_security.runCode.source"; StringBuilder code = new StringBuilder(); code.Append(@"%let rc = %sysfunc(appsrv_header(Contenttype,text/xml));"); code.Append(@"ods xml file=_webout;"); p code.Append(@"proc sql;"); code.Append(@"select distinct memname").Append(" "); code.Append(@"from dictionary.tables").Append(" "); code.Append(@"where libname='" + library.ToUpper() + "';"); code.Append(@"quit;"); code.Append(@"ods xml close;"); XmlDocument xdoc = new XmlDocument(); xdoc.LoadXml(SubmitCode(code.ToString()));q return xdoc; }

    Chapter 21: Packaging the Application Dispatcher as a Web Service 333

    n [WebMethod] defines this to be a method of a Web service and builds the needed infrastructure so it can be called as a Web service. o Define the progam to run. The field pgm is used for the value of _program in the submitted URL. p ODS XML is used to get the results as XML. The use of the appsrv_header function ensures the Content-type header is correct. q The (internal) SubmitCode method is used to send the request to the Application Broker (and then to the Application Server). The GetTableNames Web service returns a type of XmlDocument. When the Web service is called (e.g., from a client application), it is invoked as XmlDocument tables=sas.GetTableNames(“sashelp”);

    As a result, the service creates a new XmlDocument instance, called tables, which accesses the Web service GetTableNames and returns an XmlDocument type.

    21.3.2.2 The GetSasDataSetasXml Method This method creates lines of code (as above) that return a SAS data set as an XML file. Note that this method uses a different technique to generate the XML than what was used in the GetTableNames method. GetTableNames used ODS XML which contained no schema information at the beginning of the XML file. Schema information was not needed since the table name and column name are known. However, when retrieving an arbitrary SAS data set, schema information is needed. Thus, the data is returned in the form used by the XML LIBNAME engine. [WebMethod] public string GetSasDataSetAsXml(string library, string dataSetName) {//Check for alphanumerics only Regex regex = new Regex(@"\W"); if (regex.Match(library).Success | regex.Match(dataSetName).Success) return null; pgm = "saspress.requires_security.runCode.source"; StringBuilder code = new StringBuilder(); code.Append(@"libname mydata xml ""%sysfunc(pathname(work))\mydata.xml"";"); n code.Append(@""); code.Append(@"data mydata.analysis; "); code.Append(@" set " + library + "." + dataSetName + "; "); code.Append(@"run; "); code.Append(@""); code.Append(@"%let rc = %sysfunc(appsrv_header(Contenttype,text/xml)); "); code.Append(@"data _null_; "); o code.Append(@" infile ""%sysfunc(pathname(work))\mydata.xml""; "); code.Append(@" file _webout;"); code.Append(@" input;"); code.Append(@" put _infile_; "); code.Append(@"run;"); string results = SubmitCode(code.ToString()); return results; }

    334 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    n The XML LIBNAME engine is used to save the data set as XML, including the schema information. The data is saved in the same directory as the WORK library. o A DATA step is used to copy the XML to the _webout fileref. This enables the data to be returned as XML with the needed schema information.

    21.3.2.3 Submitting a Request to the Application Broker The CallBroker method is used to submit a request to the Application Broker. private string CallBroker (string pgm, n string codelines, ) { string location = IntrNet.Properties.Settings.Default.BrokerLocation; string service = IntrNet.Properties.Settings.Default.IntrNetService; StringBuilder uri = new StringBuilder("http://"); p uri.Append(location); uri.Append("?DEBUG=0"); url.Append("&_SERVICE=").Append(service); uri.Append("&_PROGRAM=").Append(pgm); uri.Append("&line=").Append(Server.UrlEncode(codelines));

    o

    q

    System.Net.WebRequest req = System.Net.WebRequest.Create(uri.ToString()); System.Net.WebResponse resp = req.GetResponse(); System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream(),Encoding.UTF8); string test = sr.ReadToEnd(); r return test; }

    n The parameters pgm and codelines are defined by the calling method and provide the name of the SAS program that should be run, as well as the name/value pairs (in this case, the lines of code). o The Application Broker location and the service to use were defined during the set-up and installation of the Web service. p Build the URL to invoke the Application Broker. q The lines of code need to be URL encoded so they can include &s, etc. r Invoke the Application Broker and retrieve the generated results. Note: The CallBroker method is available internally only. Making the CallBroker method public would allow any client to submit any SAS code. That is both a serious security risk as well as a possible violation of the site’s license for SAS/IntrNet.

    Chapter 21: Packaging the Application Dispatcher as a Web Service 335

    21.4 Using the Sample Client Application Since this is an advanced technique, this section includes a number of screen shots that illustrate how the .NET client application can access the SAS/IntrNet Application Dispatcher as a Web service. Upon launching the client application you will be see a display similar to Figure 21.4. Click About and then How this works to bring up a diagram on Web services and to see how the application works.

    Figure 21.4 Invoking the .NET Client

    Click Tools to specify the URL for the Web service that this client application connects to (e.g., the .NET wrapper for the Application Dispatcher). See Figure 21.5.

    Figure 21.5 Specify the URL for the Web Service

    Click OK to return to a display like the one shown in Figure 21.4. Selecting a demo and a wrapper technology (e.g., the SAS/IntrNet Application Dispatcher in this case) causes the client to invoke a Web service, which, in turn, supplies the list of available libraries, which this client then uses to populate a list box as seen in Figure 21.6.

    336 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 21.6 Select HTML Output Instructing the Web Service to Use the Application Dispatcher

    When you select a library, the client calls another Web service to get the list of members (the GetTableNames method discussed above) in the library as seen in Figure 21.7.

    Figure 21.7 List of Available Data Sets in the SAMPDATA Library

    Next, the client invokes another Web service to get the MESSAGEMETADATA data set as HTML (the same data shown in Figure 14.2). See Figure 21.8. The Web service submits SAS code that uses ODS to generate the HTML.

    Chapter 21: Packaging the Application Dispatcher as a Web Service 337

    Figure 21.8 Display the Chapter 14 messageMetadata as HTML

    Figure 21.9 shows the client requesting data as XML (using the GetSasDataSetasXml method discussed above).

    Figure 21.9 Request Data as XML for Display in a .NET Grid Object

    After you select the library and the data set, the results are displayed by the Web service client. See Figure 21.10.

    338 Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher

    Figure 21.10 Display the Chapter 14 messageMetadata in a .NET Grid Object

    21.5 Final Thoughts This chapter illustrated how the SAS/IntrNet Application Dispatcher could be used to provide SAS content to a broad range of client applications that can consume Web services (e.g., Microsoft Excel). With the exception of the RunCode example (Section 6.2.2.1), the sample code in this chapter is C# code developed in Visual Studio. The details of how to develop such code are beyond the scope of this book. However, this chapter demonstrated how easily the Application Dispatcher can be used by virtually any tool or product that can access Web services. Note: The Utilities page on the Savian Web site (http://utilities.savian.net) includes several examples of Web services integration with Excel.

    Index A ACTION parameter, generateFormTag macro 101 Active Server Pages See ASP (Active Server Pages) ActiveX controls defined 19 DTC support 5, 127 ODS support 7 _ADMAIL field (Application Broker) 72, 208 _ADMIN field (Application Broker) 72, 208 ADMINLIBS statement, APPSRV procedure 51, 53 best practice 211 toggle program and 263 ADMINPW option, APPSRV procedure 53 best practice 53, 211–212 pause/resume for Application Servers 260 ALL option, APPSRV procedure 197 ALLOCATE FILE statement, APPSRV procedure 51–52 defining log file names 196 _program name/value pair 64 ALLOCATE LIBRARY statement, APPSRV procedure 51–52 _program name/value pair 64 Xplore sample application and 211 ampersand (&) as unsafe character 89 generating HTML 95 htmSQL support 227 macro variable references and 84 SCL programs and 92, 108 angle brackets 103 AppDev Studio 6, 9 AppendFile directive 32–33, 161 Application Broker 5 administrative fields 72–73 application environments and 241–242 best practice 34, 212 CGI support 5, 32 configuration files 24–26, 241 _debug mnemonic values 67 defining Application Servers 202–203 directives and 31 escape character for 33

    external session facilities 236 name/value pairs defined by 72–75 process flow 24–25, 40–46 security and 206–208 submitting requests to 334 TCP/IP support 240 Application Dispatcher 5, 61–64 determining application fit 4 integrating applications 225–237 metadata approaches and 244–245 process flow 23–29 stored processes 9 application distribution 18 Application Servers 5, 47–48 See also APPSRV procedure application environments and 240, 244 clean-up processing 60–61 dedicating for debugging 202–203 defining 202–203 Executives and 49 invoking functions 60 launch servers 39–40 name/value pairs defined by 76–78 pause and resume capability for 259–266 pool servers 37–39 process flow 55–59 security and 206–212 sessions in 50 socket servers 35–37 applications See also Web applications debugging tools 191–203 integrating 225–237 maintaining environment 239–250 metadata approach 175 out-of-the-box 5 pause/resume for Application Servers 259–266 security and 205–223 sessions and 166 APPSRV procedure See also REQUEST INIT statement, APPSRV procedure ADMINLIBS statement 51, 53, 211, 263 ADMINPW option 53, 211–212, 260 ALL option 197 ALLOCATE FILE statement 51–52, 64, 196

    340 Index

    APPSRV procedure (continued) ALLOCATE LIBRARY statement 51–52, 64, 211 Application Server support 48–49 assigning libraries 51–53 AUTH option 223 DATALIBS statement 51–52, 157 Executives and 49 GUESTPASS option 223 GUESTUSER option 223 LOG statement 196–197, 203 pool server directives and 38 PORT= option 51, 203 PROGLIBS statement 51–53, 211, 254 REQUEST statement 49, 54–55, 60–61, 206–207 REQUEST TERM statement 49, 54–55, 60, 155–164 SESSION INIT statement 54–55, 60, 186–188, 240 SESSION statement 54–55, 180, 184–186, 211 SESSION TERM statement 54–55, 61, 186–188 STATISTICS option 203 TCP/IP port 51 APPSRVGETC function 60, 243 APPSRVGETN function 60 APPSRV_HEADER function 60, 132, 135 APPSRV_SESSION function 60, 166–170 APPSRVSET function 60–61 APPSRV_UNSAFE function 60, 87–91 accessing parameters 83–85 best practice 68 hiding passwords 209 WHERE clause and 151–152 appstart.sas program 51, 111 _apslist macro variable 78 APSWORK library 50 ASP (Active Server Pages) defined 19 dynamic reports 5 external session facilities 236 Stored Program DTC and 129–130 assembleReport macro 314–316, 323–326 attachments 133 AUTH option, APPSRV procedure 223 authentication 206, 215, 310

    B backward slash (\) 33 best practices accessing parameters as macro variables 84 ADMINLIBS statement 54 Application Broker administrative fields 73 Application Server clean-up processing 60–61 Application Server environment 206 APPSRV_UNSAFE function 88 checking generated links 256 controlling access to data and reports 219 customized INIT program 242 DATALIBS statement 52 _debug 67, 192 defining socket servers with directives 36 distinct services 240 environment variables 73 for secure Application Server environment 211–212 header and trailer blocks 161 hiding passwords 210 HTML name/value pairs 68, 69, 70 HTML output 33 including static HTML from external sources 111 initiating and terminating requests 54, 55 installation dependent fields 74, 75 integrating htmSQL with Application Dispatcher 228, 229 launch servers 39 limiting Application Broker acces to Application Servers 207 Load Manager command 34 metadata approaches to minimizing code changes 245, 247 metadata control table 262 name/value pairs defined by Application Server 77 PROBLIBS statement 53 reconnecting to a previous state of a session 179 sending reports 276 _service 65 toggle program 263 BG parameter, Xplore application 159 BGTYPE parameter, Xplore application 159

    Index 341

    BR tag 125 browsers, refreshing 285–290 Business Intelligence Platform 9–12

    C cache-control header 135 CALL EXECUTE statement 163, 172, 180 CallBroker method 334 Cascading Style Sheets (CSS) 268–270 CASE expression 227 catalogs 52–53, 64–65 CGI (Common Gateway Interface) Application Broker support 5, 32 htmSQL support 6, 226 character variables 105–107 CHECKED parameter, getVarsAsCheckbox macro 124 checkStatus source entry 291 cHTML (compact HTML) 6 Churchill, Alan 328n CLOSE TABLE tag 325 Common Gateway Interface See CGI compact HTML (cHTML) 6 completeBrokerName macro 229 compute services 5–6, 18 configuration files Application Broker 24–26, 241 application environments and 241 debugging and 199, 202–203 directives in 31, 33–35, 39–40, 229 name/value pairs 64–67, 72–74 security and 206–208, 210, 212 SET parameter 199 TCP/IP support 240 Constellation Chart HTML Generator (ds2const) 8 Content-disposition header 133–135 content types defining 132–135 generating pages with mixed 146–154 generating pages with other 135–146 COPY procedure 200 Critical Success Factor DTC 127 CSS (Cascading Style Sheets) 268–270 CSV output format 7, 131, 136 curly braces {} 227 customInit macro 242–244

    D data access, controlling 212–223 data-driven techniques See metadata approaches DATA parameter generateOptionTag macro 119 getVarsAsCheckbox macro 124 restoreMvars macro 172 saveMvars macro 172 data services 5, 18 Data Set Formatter (%ds2htm) 8 data sets accessing information in 116 inserting records into 187 listing variables in 122–124 metadata approaches for code changes 245 paging through selected 125–126 partitioned 53 DATA step APPSRV_UNSAFE function in 87–88 content types and 132 generating HTML 97 DATALIBS statement, APPSRV procedure 51–52 best practice 52 defining libraries and files 157 DATA_NULL_ step 168 dataReviewDemographics program 322–323 DATETIME function 154 _debug parameter 65–67, 191–196 conditional debugging output 199 dedicating Application Servers 203 generateFormTag macro 100–101 security and 206 DEBUG parameter, generateFormTag macro 101 debugging 191 conditionally generating output 197–200 configuration files and 199, 202–203 dedicating Application Server for 202–203 SAS Display Manager and 200–202 with _debug parameter 192–196 with LOG statement, APPSRV procedure 196–197 DebugMask directive 67, 211–212, 241 default parameter, getTextString macro 249 defineAccess macro 214–215, 219, 243

    342 Index

    Demographics program 321 design-time controls 5–6, 127–130 developer's workstation 240 development environment 239–244 DHTML (dynamic HTML) 15–16, 19, 226 directives Application Broker support 31 in configuration files 31, 33–35, 39–40, 229 launch servers 40 Load Manager support 31 ODS statement options and 33 pool servers 38–39 samples of 32 socket servers 36–37 DISPLAY attribute 268–270 Display Manager (SAS) 200–202 DISPLAY option, LOG statement (APPSRV) 196–197, 203 DISPLAY procedure 88, 201 DIV tag 103, 269 Done parameter, updateStatus macro 289 downloading reports and results 137–138 ds2const (Constellation Chart HTML Generator) 8 %ds2csf (RangeView HTML Generator) 8 DS2CSV macro 9, 135, 137 %ds2graf (GraphApplet HTML Generator) 8 %ds2htm (Data Set Formatter) 8 %ds2tree (TreeView HTML Generator) 8 DSVAR parameter, generateInputTag macro 102 DTCs (design-time controls) 5–6, 127–130 dynamic HTML (DHTML) 15–16, 19, 226 dynamic reports 5, 253–257

    environment variables 73–74 security and 206 single sign-on environments 235–236 ETL (Extract-Transform-Load) 10 ExcelXP tagset 137 EXC_LIBS parameter, Xplore application 159 Executives (Application Server) 49, 155 EXIST function (SCL) 91 Expected # of Requests in the System statistics 28 Expected # of Total Waiting Requests statistic 28 Expected Time Spent in System statistic 28 Expected Waiting Time in Queue statistic 28 expires header 135 Export directive 73, 75 Extensible Markup Language (XML) 6–7, 332–334 external sources, static HTML from 110–115 externalHTML macro 114–115 e-mailing process results 284 extending sessions 183 header and trail blocks 162–163 hiding passwords 208–209 htmSQL support 230 long-running processes and 277 messages for expired sessions 180, 182, 184 metadata approaches for code changes 248 metadata-driven reporting 314, 323, 325 "Please Wait" message 270 SAS Server Pages 116–117 Extract-Transform-Load (ETL) 10

    E

    File Download dialog box 133–134 FILE parameter, generateOdsStmt macro 140 FILE statement 96–102 FILENAME statement ALLOCATE statement and 52 EMAIL option 256 FTP access method 255 location.replace function and 273 MOD option 200 spawning sessions 300 URL access method 229, 253–255 filerefs 52–53 files, defining 157–159 FILESRV macro 9 firewalls 7 FOOTNOTE statement 102–105, 270

    e-mailing results from on-demand processes 282–284 static copies of dynamic reports 256–257 submitting long-running programs 295 {eachrow} directive 227 EMAIL option, FILENAME statement 256 ENDRSUBMIT command 300 ENDSAS statement controlling data access 215 Executives and 49 logon program example 314 terminating requests 163 updating process status 288

    F

    Index 343

    FORM tag character variables and 105–107 FOOTNOTE statement and 102–105 quotation marks in 97–98 TITLE statement and 102–105 forward slash (/) 33 FREQ procedure 328 FROMADR option, REQUEST statement (APPSRV) 206–207 FSLETTER procedure 322 FTP access method, FILENAME statement 255

    G generateFormTag macro 100–102 generateInputTag macro 100–102 generateOdsStmt macro generating content for printing 139–143 header and trailer blocks 161 long-running processes 270, 273 generateOptionTag macro 119–121, 228, 316 GET function 109 GETNITEMN function (SCL) 91 GetSasDataSetasXml method 333–334, 337 GetTableNames method 332–333, 336 getTextString macro 247–250, 326 getVarsAsCheckbox macro 124–125 GIF format content types and 132 debugging output 195–196 generating 131 ODS support 7 GIFBASE parameter, Xplore application 159 %GLOBAL statement application environments 244 best practice 69 conditional debugging output 198, 200 _grafloc parameter 74 GraphApplet HTML Generator (%ds2graf) 8 graphics, and IMG tag 146–153 graphs, ensuring current 153–154 _grfaplt parameter 74 GUESTPASS option, APPSRV procedure 223 GUESTUSER option, APPSRV procedure 223 GWAPI 31

    H HDML (Handheld Device Markup Language) 6 header blocks 161–163 .hsql file 6, 226 hsql files 226–233

    HTML Application Broker and 236 Content-disposition header and 134 content types and 132 CSS support 268–270 DTC support 127–130 dynamic HTML (DHTML) 15–16, 19, 226 extending ODS 102–107 externalHTML macro 162–163 FILE statement 96–102 from external sources 110–115 generateOdsStmt macro 141 generating 94–95 generating multiple output types 143–146 htmSQL support 228 Java applets and 19 macro tool support 8–9 metadata approaches for code changes 245 Microsoft Excel and 137 pause/resume for Application Servers 265–266 problems generating 95 program-specific name/value pairs 68–72 PUT statement 96–102 SAS Server Pages 116–126 SCL submit blocks 107–110 htmlOptions parameter, generateOdsStmt macro 140 htmSQL (CGI gateway) 6, 226–228 Application Dispatcher and 228–235 overview 6, 226–228 http authentication 206, 215, 310 HTTP headers 132, 194, 209 HTTP tunneling 7 HTTP_CT_REMOTE_USER environment variable 236

    I IdleTimeout directive 39 IFC DATA step function 103 IFN DATA step function 103 IIF macro conditional processing with 103–105, 113 generating options 244 metadata approaches for code changes 248 metadata-driven reporting 323 static copies of dynamic reports 256 IMG tag 33, 146–154 INC_LIBS parameter, Xplore application 159 Include directive (htmSQL) 231 %include statement 325

    344 Index

    includeAuto parameter, saveMvars macro 172 inetcfg See SAS/IntrNet Service Configuration Utility (inetcfg) INIT option See REQUEST INIT statement, APPSRV procedure See SESSION INIT statement, APPSRV procedure initiating requests See REQUEST INIT statement, APPSRV procedure input parameters See parameters INPUT tag 99–100, 125 installation, fields dependent on 74–75 intranets 240 INVSESS option best practice 55 REQUEST statement, APPSRV procedure 55 SESSION statement, APPSRV procedure 55, 180, 184–186 IP addresses, and security 207 ISAPI 31

    L

    J2EE 11 Java applets 19 Java Database Connectivity (JDBC) 6 Java programming language 6–7, 19 Java Server Pages See JSP (Java Server Pages) JavaBeans 19 JavaScript 12–14 defined 19 extending sessions example 183 HTML output support 268–270 location.replace function 271–273 JDBC (Java Database Connectivity) 6 JPEG format 7, 131–132 JSP (Java Server Pages) defined 19 dynamic reports 5 external session facilities 236–237 Stored Program DTC and 129–130

    LABEL parameter, generateOptionTag macro 119 launch servers 39–40 LaunchService directive 40 %LET statement assigning macro variable values 200 content types and 132 LRECL option 254 resetting parameter values 159, 161 Lib parameter restoreMvars macro 172 saveMvars macro 171 LIBNAME statement 52 libraries assigning with APPSRV procedure 51–53 defining 157–159 listing 118–119 PROGLIBS statement, APPSRV procedure 52 librefs 52–53, 64 lightweight sessions 50, 54, 78, 145–146, 148, 150, 169, 171, 207, 284 links, generating 256 Load Manager 5 administrative functions 46 best practice 34 defining 34–35 directives and 31 performance benefits 27–29 process flow 25–27 Queuing Model 28–29, 240 Load Manager command 34–35 LoadManager directive 32, 34–35 location headers 132 location.replace function (JavaScript) 271–273 LOG statement, APPSRV procedure 196–197, 203 LOGISTIC procedure 328 logon template/program 310–315 long-running processes handling 267–273 on-demand 281–305 scheduled execution 275–279 LRECL option, %LET statement 254

    K

    M

    keyname parameter, getTextString macro 249 keyvalue parameter, getTextString macro 249

    MACRO catalog entry 53 macro tools 8–9 See also externalHTML macro assembleReport 314–316, 323–326

    J

    Index 345

    completeBrokerName 229 customInit 242–244 defineAccess 214–215, 219, 243 generateFormTag 100–102 generateInputTag 100–102 generateOdsStmt 139–143, 161, 270, 273 generateOptionTag 119–121, 228, 316 getTextString 247–250, 326 getVarsAsCheckbox 124–125 including external HTML 114–115 multipleNames 70–71, 85–87, 126 pauseResume 262–264 programName 77, 101, 106, 123, 153–154, 167–168, 297–298 quoteHTML 103–104 restoreMvars 172–175, 200, 247, 278 sasServerPage 116–117, 230 saveMvars 171–175, 200, 277 spawnedSASSessionBegin 297 spawnedSASSessionEnd 296 spawnSAS 291–292, 295, 298–300 updateStatus 286–290, 304 macro variable references accessing parameters 83–85 best practice 73 generating HTML and 95 RESOLVE function and 112 sessions and 168–170 templates and 116–117 macro variables ampersand (&) and 92 conditional debugging output 198 input parameters as 82–91 inserting with SYMGET function 97 metadata approaches for code changes 245 restoring 171–175 saving 171–175 Macromedia Drumbeat 2000 5 MAILTO tag 72 maxreq-minutes option, Load Manager command 35 maxrun=minutes option, Load Manager command 35 maxstart=minutes option, Load Manager command 35 MDDB Report DTC 127 MEMTYPES parameter, Xplore application 159–160 menus, customizing 219–222

    messages for expired sessions 179–182 pause/resume for Application Servers 261, 265–266 "Please Wait" 268–273 META REFRESH tag 289 META tag 287–288 metadata approaches applications and 175 Business Intelligence Platform and 9 constructing reports 307–326 controlling data access 215–222 defining libraries and files 157–159 htmSQL and 230 messages and 180–182 MetaView HTML Generator 8 minimizing code changes 244–250 pause/resume for Application Servers 260, 262–264 SAS Information Delivery Portal 11 single sign-on environments 235 MetaView HTML Generator (%meta2htm) 8 METHOD parameter, generateFormTag macro 101 Microsoft Excel downloading reports and results 137–138 generateOdsStmt macro 141 Web services integration with 338 Microsoft FrontPage 5 Microsoft Office 10–11 Microsoft Word 138 MinRun directive 39 MOD option, FILENAME statement 200 MP CONNECT 290, 293, 296, 300 _mrvimg parameter 74 Msg parameter, updateStatus macro 289 msgdata parameter, getTextString macro 249 msgtext parameter, getTextString macro 249 multipleNames macro 70–71, 85–87, 126 MVAR parameter, generateInputTag macro 102

    N NAME= option, INPUT tag 100 NAME parameter generateFormTag macro 101 generateOptionTag macro 119 getVarsAsCheckbox macro 124 name/value pairs defined by Application Broker 72–75 defined by Application Server 76–78

    346 Index

    name/value pairs (continued) defined by user request 64–72 external session facilities 236 htmSQL support 227 in configuration files 64–67, 72–74 multiple values for parameters 85 SCL programs and 201–202 WHERE clause and 87–88 .NET architecture defined 19 sample client application 331–332, 335–338 Web services and 328, 332–334 NO_BOTTOM_MATTER OPTION (ODS) 33, 145, 321–322 nokill option, Load Manager command 35 NOMLOGIC system option 211 NOMPRINT system option 211 NOSOURCE system option 207, 211 NOSOURCE2 system option 211 NOTOC option ODS HTML statement 139 _WEBOUT fileref 194 NO_TOP_MATTER OPTION (ODS) 33, 321–322 %nrstr macro function 99

    O ODS (Output Delivery System) content types and 132, 135 extending 102–107 generating HTML 94 pages with mixed content types 146–153 Pareto charts 16–17 sessions and 50 Web application support 7–8 ODS HTML statement 139 ODS statement directives and 33 generating content 139 metadata-driven reporting 321 OLAP cube 165, 285 on-demand processes e-mailing results from 282–284 independent sessions and 305 spawning separate sessions 291–304 updating process status 285–290 OPTIONS parameter generateInputTag macro 102 generateOdsStmt macro 140

    otherOptions parameter, generateOptionTag macro 120 out-of-the-box applications 5 %out2htm (Output Formatter) 8 Output Delivery System See ODS Output Formatter (%out2htm) 8

    P parameters 81–82 accessing as macro variables 82–91 accessing in SCL programs 91–92 metadata approaches for code changes 246 overriding values 159–161 overview 81–82 supplying values 159–161 Pareto charts 16–17 partitioned data set (PDS) 53 passwords AUTH option, APPSRV procedure 223 best practice 212 hiding 208–210 logon program example 312 sessions and 165 single sign-on environments 235 path names 33 pattern matching 9 pauseResume macro 262–264 pauseResumeMessage template 265–266 pauseResumeToggle template 265 PDF (Portable Document Format) content types and 132 debugging output 195 generateOdsStmt macro 141 generating 131, 138–139 generating multiple output types 143–146 ODS support 7 pdfOptions parameter, generateOdsStmt macro 140 PDS (partitioned data set) 53 percent sign (%) 84, 89 performance, Load Manager 27–29 _pgm name/value pair 77 _pgmcat name/value pair 77 _pgmlib name/value pair 77 _pgmtype name/value pair 77 “Please Wait” message 270 pool servers 37–39 PoolService directive 32, 38 Port directive 36, 38 _PORT macro variable 50, 168

    Index 347

    port numbers, and security 206–207 PORT= option, APPSRV procedure 51, 203 PORT option, Load Manager command 34 Portable Document Format See PDF POSTSCRIPT format 132 PrependFile directive 32–33, 161 PRINT procedure 197–200, 289 processes See long-running processes See on-demand processes See stored processes production environment 239–244 PROGLIBS statement, APPSRV procedure 51–53, 211, 254 programName macro 77, 101, 106, 123, 153–154, 167–168, 297–298 _program name/value pair 64–65 best practice 211 controlling data access 214 generateFormTag macro 100–101 INIT option and 54 PUT statement and 97–98 PROGRAM parameter, generateFormTag macro 101 PUT statement 96–102, 132

    Q %qsysfunc macro function 87–88 {query} directive 227 questionnaires 165 Queuing Model (Load Manager) 28–29, 240 quotation marks (") ending literals 84 generating valid HTML 95 in FORM tag 97–98 in QUOTE function 102–103 QUOTE function 102–103 quoteHTML macro 103–104

    R RangeView HTML Generator (%ds2csf) 8 READONLY option 211 refreshFailed template 185–186 refreshing browsers 285–290 refreshSession template 184, 186 refreshSuccess template 185–186 REMOTE_USER environment variable 73, 206, 211 _REPLAY macro variable 60, 78, 148 REPLAY program utility 78, 194

    report distribution 18 REPORT procedure 138 reports constructing 307–326 controlling access 212–223 downloading 137–138 dynamic 5, 253–257 static 5 Request Executives 49, 155 REQUEST INIT statement, APPSRV procedure 54–55, 155–164 best practice 211 customized programs 242–244 macro variables and 68, 74, 91 pause/resume for Application Servers 260 SASAUTOS option 156, 201 REQUEST statement, APPSRV procedure 54 FROMADR option 206–207 INVSESS option 55 TIMEOUT option 60–61 REQUEST TERM statement, APPSRV procedure 54–55, 155–164 APPSRVGETN function and 60 ENSAS statement and 49 RESOLVE function 111–113, 116 restoreMvars macro 172–175 application environments and 247 long-running processes and 278 SAS Display Manager and 200 submitting long-running programs and 298 Rich Text Format See RTF _RMTADDR field 211 _RMTHOST field 211 _RMTUSER macro variable best practices 211–212 controlling data access 212–215, 222 environment variables and 73, 75 single sign-on environments 236 RSUBMIT command 300 RTF (Rich Text Format) debugging output 195 generateOdsStmt macro 141 generating 138 generating multiple output types 143–146 ODS support 7 rtfOptions parameter, generateOdsStmt macro 140 RX function 9

    348 Index

    S SAS/ACCESS 328 SAS Add-In for Microsoft Office 10–11 SAS/CONNECT 6 SAS Critical Success Factor DTC 127 SAS Display Manager 200–202 SAS Enterprise Guide 10–12 SAS Enterprise Miner 328 SAS/GRAPH 7 SAS Information Delivery Portal 10–11 SAS/IntrNet components 5–7 SAS/IntrNet Service Configuration Utility (inetcfg) APPSRV procedure and 51 default SAS jobs 48 defining Application Servers 202 SasCommand directive 38 SAS MDDB Report DTC 127 SAS Server Pages 116–126, 310–312 SAS/SHARE 6, 226 SAS Stored Process Web Application 10–11 SAS Stored Program DTC 127, 129–130 SAS Table DTC 127 SAS Tabular Report DTC 127 SAS Thin-Client Graphics DTC 127 SAS TreeView DTC 127 SAS Web Report Studio 10, 12 SASAUTOS option, REQUEST INIT statement (APPSRV) 156, 201 SasCommand directive 38, 40 SASPoweredAlt directive 32–33 SASPoweredHref directive 32–33 SASPoweredLogo directive 32–33 SASPoweredTarget directive 32–33 sasServerPage macro 116–117 htmSQL and 230 metadata-driven reporting 310–312, 316 SAVE library APPSRV_SESSION function 60, 166 logon program example 314 sessions and 50 saveFlag parameter, saveMvars macro 172 saveMvars macro 171–175 debugging and 200 long-running processes and 277 submitting long-running programs 298 Savian Web site 338 Scalable Performance Data Server 6, 226 scheduled process execution 275–279 SCL (SAS Component Language) accessing input parameters in 91–92

    APPSRV_UNSAFE function 87 best practice 211 EXIST function 91 GETNITEMN function 91 hiding passwords 208–210 name/value pairs 68 PROGLIBS statement, APPSRV procedure 53 SETTITLE function 88 special handling for programs 201–202 stored processes 9 submit blocks 107–110 scripting languages 12–14 security application environments and 240 Application Servers and 206–212 applications and 205–223 best practices 211–212 configuration files and 206–208, 210, 212 controlling data access 212–223 controlling report access 212–223 hiding passwords 208–210 tunneling and 7 SELECT statement 231 SELECT tag generateOptionTag macro and 119–120 HTML name/value pairs 69 htmSQL support 227–228 SELECTED parameter, generateOptionTag macro 120, 316 selectKey template 321 SelfURL directive 73 semicolon (;) 84, 89 Server directive 36, 38 _SERVER macro variable 50, 168 SERVER_NAME environment variable 73 SERVER_PORT environment variable 73 SERVER_PROTOCOL environment variable 73 _service name/value pair 65 dedicating Application Servers 203 generateFormTag macro 100–101 sessions and 50 SERVICE parameter, generateFormTag macro 101 ServiceAppendFile directive 32–33, 161 ServiceDebugMask directive 67, 206, 241 ServiceLoadManager directive 34 ServicePrependFile directive 32–33, 161 ServiceSASPoweredLogo directive 33 ServiceSet directive 74–75

    Index 349

    external session facilities 236 htmSQL support 229 resetting parameter values 159 SESSION INIT statement, APPSRV procedure 54–55, 186–188 application environments and 240 executing programs and 60 SESSION statement, APPSRV procedure 54 INVSESS option 55, 180, 184–186 VERIFY option 211 SESSION TERM statement, APPSRV procedure 54–55, 186–188 executing programs and 61 sessionCounter macro variable 179 _SESSIONID macro variable 78 APPSRV_SESSION function 60, 168 sessions and 50 sessions 165–166 creating 166–170 defined 50 Executives and 49 extending 182–186 external facilities 236–237 independent 305 initiating 186–188 lightweight 50, 54, 78, 145–146, 148, 150, 169, 171, 207, 284 macro variables and 171–175 messages for expired 179–182 ODS and 50 reconnecting to previous states 175–179 SAS Display Manager and 200–202 spawning separate 291–304 terminating 186–188 Set directive 74–75, 159 SET parameter, configuration files 199 setDefaultValues macro 75, 94 SETTITLE function (SCL) 88 Simple Object Access Protocol (SOAP) 327 single sign-on environments 235–236 Sleep function 270, 273, 287 SOAP (Simple Object Access Protocol) 327 socket servers 35–37, 202–203 SocketService directive 32, 36 SoftQuad HoTMetalL Pro 5 5 SOURCE catalog entry 53 SOURCE parameter, externalHTML macro 114–115 spawnedSASSessionBegin macro 297

    spawnedSASSessionEnd macro 296 SpawnerPort directive 39 spawnSAS macro 291–292, 295, 298–300 spawnSAS.source program 291–292, 295–298 SQL procedure 314 SQL queries 226–235 {sql} directive 227 _SRVNAME macro variable 73 state maintaining 50 preserving information 165 reconnecting to previous session 175–179 static HTML from external sources 110–115 static reports 5 STATISTICS option, APPSRV procedure 203 statusDataSet parameter, updateStatus macro 289 STOP command best practice 212 Load Manager support 46 pool server directives and 39 stored processes accessing 10 Business Intelligence Platform support 9 functionality 10 Web applications and 10–11 Web services and 328 Stored Program DTC 127, 129–130 suffix variables 85–87 SUMMARY procedure 285, 303–304 SUPERQ macro function 82–83, 87 surveys 165 SYMBOLS option, LOG statement (APPSRV) 196–197, 203 SYMGET function accessing parameters 83, 85, 92 insert records into data sets 187 macro variables and 97, 208 SYMGETC function 92 SYMGETN function 92 SYMPUTX function 172 %SYSFUNC macro function accessing data set information 116 APPSRV_HEADER function and 135 APPSRV_UNSAFE function and 87 content types and 132 generateOptionTag macro and 120 getVarsAsCheckbox macro and 125 IFC DATA step function and 103 IFN DATA step function and 103

    350 Index

    %SYSFUNC macro function (continued) metadata-driven reporting 323 pages with mixed content types 153–154 %syslput macro 300 SYSTASK statement 290, 293, 296, 300 sys.url automatic variable 232

    T %tab2htm (Tabulate Formatter) 8 Table DTC 127 Tabular Report DTC 127 Tabulate Formatter (%tab2htm) 8 tagsetOptions parameter, generateOdsStmt macro 140 TARGET= option (IMG tag) 33 TARGET parameter, generateFormTag macro 101 TCP/IP Application Broker and 240 APPSRV procedure support 51 security and 206 templates logon 310–312 macro variable references and 116–117 messages for expired sessions 181 metadata approaches for code changes 245–248 pause/resume for Application Servers 260, 265–266 refreshFailed 185–186 refreshSession 184, 186 refreshSuccess 185–186 selectKey 321 TERM option See REQUEST TERM statement, APPSRV procedure See SESSION TERM statement, APPSRV procedure terminating requests See REQUEST TERM statement, APPSRV procedure test environment 239–244 testPrint macro 198–199 text, IMG tag and 146–153 TEXT parameter, generateInputTag macro 102 TEXTAREA tag 69 Thin-Client Graphics DTC 127 _thissession name/value pair 78, 184 _thissrv name/value pair 77 TIMEOUT option, REQUEST statement (APPSRV) 60–61

    TITLE statement 102–105 _TMPCAT macro variable 78 APPSRV_SESSION function 60, 169 generating multiple output types 143–146, 150 toggle program 263–264 trailer blocks 161–163 TreeView DTC 127 TreeView HTML Generator (%ds2tree) 8 tunneling 7 TYPE parameter externalHTML macro 114–115 generateInputTag macro 102 generateOdsStmt macro 140

    U UDDI (Universal Description, Discovery, and Integration) 328 underscore (_) 69, 171 Uniform Resource Locator (URL) 18 UNIVARIATE procedure 328 Universal Description, Discovery, and Integration (UDDI) 328 %UNQUOTE macro 250 updateStatus macro 286–290, 304 UPLOAD procedure 300 URL (Uniform Resource Locator) 18 URL access method, FILENAME statement 229 static copies of dynamic reports 253–255 _URL field Application Broker and 73 dedicating Application Servers 203 &_url macro variable reference 73, 95, 256 usernames 223, 312 USESESSION program 168–170 UUIDGEN function 279

    V VAR parameter, generateOptionTag macro 119 variables See also macro variables character 105–107 environment 73–74, 206, 235–236 listing in selected data sets 122–124 suffix 85–87 VBScript 12 VERIFY option, SESSION statement (APPSRV) 211 _VERSION field (Application Broker) 73

    Index 351

    W

    Symbols

    Web applications See also Xplore sample Web application long-running processes 267–273 SAS/IntrNet support 5–7 SAS support 7–12 stored processes and 10–11 Web publishing 8–9, 18 Web servers developer's workstations and 240 environment variables 73 http authentication 206 Web services 16 Application Dispatcher as 327–338 defined 19 .NET architecture and 328, 332–334 Web Services Definition Language (WSDL) 327 webAF 5, 9 webEIS 9 _WEBOUT fileref conditional debugging output 198 copying HTML to 112 htmSQL support 229 macro variables and 9 NOTOC option 194 PUT statement and 132 SAS Display Manager and 200 WHERE clause APPSRV_UNSAFE function and 151–152 controlling data access 219 getTextString macro and 250 metadata-driven reporting 321 name/value pairs 87–88 WHERE parameter, generateOptionTag macro 120 WML (Wireless Markup Language) 6–7 Wood, Scott 236n WSDL (Web Services Definition Language) 327

    & (ampersand) See ampersand (&) (angle brackets) 103 \ (backward slash) 33 {} (curly braces) 227 / (forward slash) 33 % (percent sign) 84, 89 " (quotation marks) See quotation marks (") ; (semicolon) 84, 89 _ (underscore) 69, 171

    X X command 290 XML (Extensible Markup Language) 6–7, 332–334 Xplore sample Web application 6 best practices 211 JavaScript support 13–14 resetting parameter values 159–160 static copies of dynamic reports 254

    352 Index

    Books Available from SAS Press

    Advanced Log-Linear Models Using SAS ®

    Debugging SAS ® Programs: A Handbook of Tools and Techniques

    by Daniel Zelterman

    by Michele M. Burlew

    Analysis of Clinical Trials Using SAS®: A Practical Guide by Alex Dmitrienko, Geert Molenberghs, Walter Offen, and Christy Chuang-Stein

    Decision Trees for Business Intelligence and Data Mining: Using SAS® Enterprise MinerTM by Barry de Ville

    Annotate: Simply the Basics by Art Carpenter

    Efficiency: Improving the Performance of Your SAS ® Applications

    Applied Multivariate Statistics with SAS® Software, Second Edition

    by Robert Virgile

    by Ravindra Khattree and Dayanand N. Naik

    Elementary Statistics Using JMP®

    Applied Statistics and the SAS ® Programming Language, Fifth Edition

    The Essential Guide to SAS ® Dates and Times

    by Sandra D. Schlotzhauer

    by Ronald P. Cody and Jeffrey K. Smith

    The Essential PROC SQL Handbook for SAS ® Users

    An Array of Challenges — Test Your SAS ® Skills by Robert Virgile

    Building Web Applications with SAS/IntrNet®: A Guide to the Application Dispatcher by Don Henderson

    by Katherine Prairie

    Fixed Effects Regression Methods for Longitudinal Data Using SAS ® by Paul D. Allison

    Genetic Analysis of Complex Traits Using SAS ®

    Carpenter’s Complete Guide to the SAS® Macro Language, Second Edition by Art Carpenter

    Carpenter’s Complete Guide to the

    by Derek P. Morgan

    SAS®

    REPORT Procedure

    Edited by Arnold M. Saxton

    A Handbook of Statistical Analyses Using SAS®, Second Edition by B.S. Everitt and G. Der

    by Art Carpenter

    Health Care Data and SAS®

    The Cartoon Guide to Statistics

    by Marge Scerbo, Craig Dickstein, and Alan Wilson

    by Larry Gonick and Woollcott Smith

    Categorical Data Analysis Using the Second Edition

    The How-To Book for SAS/GRAPH ® Software SAS ®

    System,

    by Maura E. Stokes, Charles S. Davis, and Gary G. Koch

    by Thomas Miron

    In the Know ... SAS ® Tips and Techniques From Around the Globe, Second Edition by Phil Mason

    Cody’s Data Cleaning Techniques Using SAS® Software by Ron Cody

    Instant ODS: Style Templates for the Output Delivery System

    Common Statistical Methods for Clinical Research with SAS ® Examples, Second Edition

    Integrating Results through Meta-Analytic Review Using SAS® Software by Morgan C. Wang and Brad J. Bushman

    by Glenn A. Walker

    by Bernadette Johnson

    The Complete Guide to SAS ® Indexes by Michael A. Raithel

    Introduction to Data Mining Using SAS® Enterprise MinerTM by Patricia B. Cerrito

    Data Management and Reporting Made Easy with SAS ® Learning Edition 2.0

    Learning SAS® by Example: A Programmer’s Guide

    by Sunil K. Gupta

    by Ron Cody

    Data Preparation for Analytics Using SAS®

    Learning SAS ® in the Computer Lab, Second Edition

    by Gerhard Svolba

    by Rebecca J. Elliott

    support.sas.com/pubs

    The Little SAS ® Book: A Primer by Lora D. Delwiche and Susan J. Slaughter

    Professional SAS ® Programmer’s Pocket Reference, Fifth Edition

    The Little SAS ® Book: A Primer, Second Edition by Lora D. Delwiche and Susan J. Slaughter (updated to include SAS 7 features)

    Professional SAS ® Programming Shortcuts, Second Edition

    The Little SAS ® Book: A Primer, Third Edition by Lora D. Delwiche and Susan J. Slaughter (updated to include SAS 9.1 features)

    by Arthur L. Carpenter and Charles E. Shipp

    The Little SAS ® Book for Enterprise Guide® 3.0 by Susan J. Slaughter and Lora D. Delwiche The Little SAS ® Book for Enterprise Guide® 4.1 by Susan J. Slaughter and Lora D. Delwiche Logistic Regression Using the SAS® System: Theory and Application by Paul D. Allison Longitudinal Data and SAS®: A Programmer’s Guide by Ron Cody Maps Made Easy Using SAS® by Mike Zdeb

    by Rick Aster

    by Rick Aster

    Quick Results with SAS/GRAPH ® Software

    Quick Results with the Output Delivery System by Sunil K. Gupta

    Reading External Data Files Using SAS®: Examples Handbook by Michele M. Burlew

    Regression and ANOVA: An Integrated Approach Using SAS ® Software by Keith E. Muller and Bethel A. Fetterman

    SAS ® for Forecasting Time Series, Second Edition by John C. Brocklebank and David A. Dickey

    SAS ® for Linear Models, Fourth Edition by Ramon C. Littell, Walter W. Stroup, and Rudolf J. Freund

    SAS ® for Mixed Models, Second Edition Models for Discrete Data by Daniel Zelterman

    by Ramon C. Littell, George A. Milliken, Walter W. Stroup, Russell D. Wolfinger, and Oliver Schabenberger

    Multiple Comparisons and Multiple Tests Using SAS® Text and Workbook Set

    SAS ® for Monte Carlo Studies: A Guide for Quantitative Researchers

    (books in this set also sold separately) by Peter H. Westfall, Randall D. Tobias, Dror Rom, Russell D. Wolfinger, and Yosef Hochberg

    Multiple-Plot Displays: Simplified with Macros by Perry Watts

    Multivariate Data Reduction and Discrimination with SAS ® Software by Ravindra Khattree and Dayanand N. Naik

    by Xitao Fan, Ákos Felsovályi, Stephen A. Sivo, ˝ and Sean C. Keenan

    SAS ® Functions by Example by Ron Cody

    SAS ® Guide to Report Writing, Second Edition by Michele M. Burlew

    SAS ® Macro Programming Made Easy, Second Edition by Michele M. Burlew

    SAS ® Programming by Example Output Delivery System: The Basics by Lauren E. Haworth

    Painless Windows: A Handbook for SAS ® Users, Third Edition by Jodie Gilmore (updated to include SAS 8 and SAS 9.1 features)

    Pharmaceutical Statistics Using SAS®: A Practical Guide Edited by Alex Dmitrienko, Christy Chuang-Stein, and Ralph D’Agostino

    The Power of PROC FORMAT by Jonas V. Bilenas

    PROC SQL: Beyond the Basics Using SAS®

    by Ron Cody and Ray Pass

    SAS ® Programming for Researchers and Social Scientists, Second Edition by Paul E. Spector

    SAS ® Programming in the Pharmaceutical Industry by Jack Shostak

    SAS ® Survival Analysis Techniques for Medical Research, Second Edition by Alan B. Cantor

    by Kirk Paul Lafler

    SAS ® System for Elementary Statistical Analysis, Second Edition

    PROC TABULATE by Example

    by Sandra D. Schlotzhauer and Ramon C. Littell

    by Lauren E. Haworth

    support.sas.com/pubs

    SAS ® System for Regression, Third Edition by Rudolf J. Freund and Ramon C. Littell

    SAS ® System for Statistical Graphics, First Edition by Michael Friendly

    The SAS ® Workbook and Solutions Set (books in this set also sold separately)

    JMP® Books

    JMP® for Basic Univariate and Multivariate Statistics: A Step-byStep Guide by Ann Lehman, Norm O’Rourke, Larry Hatcher, and Edward J. Stepanski

    JMP® Start Statistics, Third Edition

    by Ron Cody

    by John Sall, Ann Lehman, and Lee Creighton

    Selecting Statistical Techniques for Social Science Data: A Guide for SAS® Users

    Regression Using JMP®

    by Frank M. Andrews, Laura Klem, Patrick M. O’Malley, Willard L. Rodgers, Kathleen B. Welch, and Terrence N. Davidson

    by Rudolf J. Freund, Ramon C. LIttell, and Lee Creighton

    Statistical Quality Control Using the SAS ® System by Dennis W. King

    Statistics Using SAS ® Enterprise Guide® by James B. Davis

    A Step-by-Step Approach to Using the SAS ® System for Factor Analysis and Structural Equation Modeling by Larry Hatcher

    A Step-by-Step Approach to Using SAS ® for Univariate and Multivariate Statistics, Second Edition by Norm O’Rourke, Larry Hatcher, and Edward J. Stepanski

    Step-by-Step Basic Statistics Using SAS®: Student Guide and Exercises (books in this set also sold separately) by Larry Hatcher

    Survival Analysis Using SAS ®: A Practical Guide by Paul D. Allison

    Tuning SAS ® Applications in the OS/390 and z/OS Environments, Second Edition by Michael A. Raithel

    Univariate and Multivariate General Linear Models: Theory and Applications Using SAS ® Software by Neil H. Timm and Tammy A. Mieczkowski

    Using SAS ® in Financial Research by Ekkehart Boehmer, John Paul Broussard, and Juha-Pekka Kallunki

    Using the SAS ® Windowing Environment: A Quick Tutorial by Larry Hatcher

    Visualizing Categorical Data by Michael Friendly

    Web Development with SAS® by Example, Second Edition by Frederick E. Pratter

    Your Guide to Survey Research Using the SAS® System by Archer Gravely

    support.sas.com/pubs