Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-expressionengine-creating-photo-gallery
Packt
23 Oct 2009
6 min read
Save for later

ExpressionEngine: Creating a Photo Gallery

Packt
23 Oct 2009
6 min read
Install the Photo Gallery Module The photo gallery in ExpressionEngine is considered a separate module, even though it is included with every personal or commercial ExpressionEngine license. Installing it is therefore very simple: Log into the control panel using http://localhost/admin.php or http://www.example.com/admin.php, and select Modules from the top of the screen. About a quarter of the way down the page, we can see the Photo Gallery module. In the far-right column is a link to install it. Click Install. We will see a message at the top of the screen indicating that the photo gallery module was installed. That's it! Setting Up Our Photo Gallery Now that we have installed the photo gallery module, we need to define some basic settings and then create categories that we can use to organize our photos. Define the Basic Settings Still in the Modules tab, the photo gallery module should now have become a clickable link. Click on the Photo Gallery. We are presented with a message that says There are no image galleries. Select to Create a New Gallery. We are now prompted for our Image Folder Name. For our photo galleries, we are going to create a folder for our photos inside the images folder that should already exist. Navigate to C:xampphtdocsimages (or /Applications/MAMP/htdocs/images if using MAMP on a Mac) or to the images folder on your web server, and create a new folder called photos. Inside that folder, we are going to create a specific subfolder for our toast gallery images. (This will keep our article photos separate from any other galleries we may wish to create). Call the new folder toast. If doing this on a web server, set the permissions of the toast folder to 777 (read, write, and execute for owner, group, and public). This will allow everyone to upload images to this folder. Back in ExpressionEngine, type in the name of the folder we just created (toast) and hit Submit. We are now prompted to name our template gallery. We will use the imaginative name of toastgallery so that it is distinguishable from any other galleries we may create in the future. This name is what will be used as the default URL to the gallery and will be used as the template group name for our gallery templates. Hit Submit. We are now prompted to update the preferences for our new gallery. Expand the General Configuration option and define a Photo Gallery Name and Short Name. We are going to use Toast Photos as a Photo Gallery Name and toastphotos as a Short Name. The short name is what will be used in our templates to reference this photo gallery Next, expand the Image Paths section. Here the Image Folder Name should be the same name as the folder we created earlier (in our case toast). For XAMPP users, the Server Path to Image Directory is going to be C:/xampp/htdocs/images/photos/toast, and the Full URL to Image Directory is going to be http://localhost/images/photos/toast. For MAMP users on a Mac or when using a web server, these paths are going to be different depending on your setup. Verify these settings for correctness, making adjustments as necessary. Whenever we upload an image into the image gallery, ExpressionEngine creates three copies of the image—a medium-sized and a thumbnail-sized version of the image, in addition to the original image. The thumbnail image is fairly small, so we are going to double the size of the thumbnail image. Expand the Thumbnail Resizing Preferences section, and instead of a Thumbnail Width of 100, choose a width of 200. Check the box (the one outside of the text box) and the height should update to 150. Hit Submit to save the settings so far. We will review the rest of the settings later. We have now created our first gallery. However, before we can start uploading photos, we need to create some categories. Create Categories For the purposes of our toast website, we are going to create categories based on the seasons: spring, summer, autumn, and winter. We are going to have separate subfolders for each of the categories; these are created automatically when we create the categories. To do this, first select Categories from the new menu that has appeared across the top of the screen. We will see a message that says No categories exist. Select Add a New Category. We are going to use a Category Name of Spring and a Description that describes the category—we will later display this description on our site. We are going to create a Category Folder of spring. Leave the Category Parent as None, and hit Submit. Select Add a New Category, and continue to add three more categories: summer, autumn, and winter in the same way. After we a re done with creating all the categories, use the up and down arrows to order the categories correctly. In our case, we need to move Autumn down so that it appears after Summer. We now have the beginnings of a photo gallery. Next, we will upload our first photos so that we can see how the gallery works. Upload Our First Photos To upload a photo to a photo gallery is pretty straightforward. The example photos we are working with can be downloaded from the Packtpub support page at http://www.packtpub.com/files/code/3797_Graphics.zip. To upload a photo, select New Entry from the menu within the photo gallery module. For the File Name, click the Browse...> button and browse to the photo spring1.jpg. We are going to give this an Entry Title of Spring Flower. For Date, we could either leave it as a default or enter the date that the photo was taken on. We are going to use a date of 2006-04-22. Click on the calendar icon to expand the view to include a calendar that can be easily navigated. We are going to use a Category of Spring and a Status of Open. Leave the box checked to Allow Comments, and write a Caption that describes the photo. The Views allows us to indicate how many times this image has been viewed—in this case we are going to leave it at 0. Hit Submit New Entry when everything is done. We are presented with a message that reads Your file has been successfully submitted, and the image now appears underneath the entry information. In the folder where our image is uploaded, three versions of the same image are made. There is the original file (spring1.jpg), a thumbnail of the original file (spring1_thumb.jpg), and a medium-sized version of the original file (spring1_medium.jpg). Now, click on New Entry and repeat the same steps to upload the rest of the photos, using appropriate categories and descriptions that describe the photos. There are four example photos for each season (for example, winter1.jpg, winter2.jpg, winter3.jpg, and winter4.jpg). Having a few example photos in each category will better demonstrate how the photo gallery works.
Read more
  • 0
  • 0
  • 3602

article-image-creating-view-mysql-query-browser
Packt
23 Oct 2009
2 min read
Save for later

Creating a View with MySQL Query Browser

Packt
23 Oct 2009
2 min read
Please refer to an earlier article by the author to learn how to build queries visually. Creating a View from an Existing Query To create a view from a query, you must have executed the query successfully. To be more precise, the view is created from the latest successfully executed query, not necessarily from the query currently in the Query Area. To further clarify, the following three examples are cases where the view is not created from the current query: Your current query fails, and immediately after you create a view from the query. The view created is not from the failed query. If the failed query is the first query in your MySQL Query Browser session, you can’t create any view. You have just moved forward or backward the query in the Query Area without executing it, and then your current query is not the latest successfully executed. You open a saved query that you have never executed successfully in your active Resultset. Additionally, if you’re changing your Resultset, the view created is from the latest successfully executed query that uses the currently active Resultset to display its output. To make sure your view is from the query you want, select the query, confirm it as written in the Query Area, execute the query, and then, immediately create its view. You create a view from an existing query by selecting Query | Create View from Select from the Menu bar. Type in the name you want to give to the view, and then click Create View. MySQL Query Browser creates the view. When successfully created, you can see the view in the Schemata. You can modify a view by editing it: Right-click the view and select Edit View. You can edit the CREATE view statement by right-clicking it and select Edit View. The CREATE view statement opens in its Script tab. When you finish editing, you can execute the modified view. If successful, the existing view is replaced with the modified one. To replace the view you’re editing with the modified view, change the name of the view before you execute it. If you want to keep the view you’re editing, remove the DROP VIEW statement.
Read more
  • 0
  • 0
  • 5211

article-image-introduction-re-host-based-modernization-using-tuxedo
Packt
23 Oct 2009
19 min read
Save for later

Introduction to Re-Host based Modernization Using Tuxedo

Packt
23 Oct 2009
19 min read
Introduction SOA enablement wraps key application interfaces in services, and integrates it into the SOA. This largely leaves the existing application logic intact, minimizing changes and adding risk only to those components that needed restructuring work to become SOA-ready. While the interfaces are modernized, without subjecting the core application components to a lot of change, the high costs and the various legacy risks associated with the mainframe platform remain. In addition, the performance and scalability of the new interfaces needs to be well-specified and tested, and the additional load they place on the system should be included in any planned capacity upgrades, potentially increasing the overall costs. Reducing or eliminating the legacy mainframe costs and risks via re-host based modernization also helps customers to fund SOA enablement, and the re-architecture phases of legacy modernization, and lay the groundwork for these steps. SOA-enabling a re-hosted application is a much easier process on an open-systems-based, SOA-ready software stack, and a more efficient one as well in terms of system resource utilization and cost. Re-architecting selected components of a re-hosted application based on specific business needs is a lower risk approach than re-architecting the entire applications en masse, and the risk can be further reduced by ensuring that target re-hosting stack provides rugged and transparent integration between re-hosted services and new components. Keeping It Real: Selective re-architecture is all about maximizing ROI by focusing re-architecture investment in the areas with the best pay-off. Undertaking a change from one language or development paradigm to another shouldn't be undertaken lightly—the investment and risks need to be well understood and justified. It is the right investment for components that require frequent maintenance changes but are difficult to maintain, because of poor /structure and layered changes. The payback on re-architecture investment will come from reducing the cost of future maintenance. Similarly, components that need significant functional changes to meet new business requirements can benefit from substantial productivity increase after re-architecture to a more modern development framework with richer tools to support future changes. The payback comes from greater business agility and time-to-market improvements. On the other hand, well-structured and maintainable COBOL components that do not need extensive changes to meet business needs will have very little return to show for the significant re-architecture investment. Leaving them in COBOL on a modern, extensible platform saves significant re-architecture costs that can be invested elsewhere, reduces risk, and shortens payback time. These considerations can help to optimize ROI for medium to large modernization projects where components measure in hundreds or thousands and contain millions or tens of millions lines of code. Re-Hosting Based Modernization For many organizations, mainframe modernization has become a matter of 'how', and not 'if'. Numerous enterprises and public sector organizations choose re-hosting as the first tangible step in their legacy modernization program precisely because it delivers the best ROI in the fastest possible manner, and accelerates the move to SOA enablement and selective re-architecture. Oracle together with our services partners provides a comprehensive re-hosting-based modernization solution that many customers have leveraged for a successful migration of selected applications or complete mainframe environments ranging from a few hundred MIPS to well over 10,000 MIPS. Two key pillars support successful re-hosting projects: Optimal target environment that lowers the Total Cost of Ownership (TCO) by 50–80 percent and maintains mainframe-class Quality of Service (QoS) using open, extensible, SOA-ready, future-proof architecture Predictable, efficient projects delivered by our SI partners with proven methodologies and automated tools Optimal target environment provided by Oracle is powered by proven open systems software stack leveraging Oracle Database and Oracle Tuxedo for a rock-solid, mainframe-class transaction processing (TP) infrastructure closely matching mainframe requirements for online applications. Mainframe-compatible Transaction Processing: Support for IBM CICS or IMS TM applications in native COBOL or C/C++ language containers with mainframe-compatible TP features. RASP: Mainframe-class performance, reliability, and scalability provided by Oracle Real Application Clusters (RAC) and Tuxedo multi-node and multi-domain clustering for load-balancing and high availability despite failure of individual nodes or network links. Workload and System Management: End-to-end transaction and service monitoring to support 24X7 operations management provided by Oracle's Enterprise Manager Grid Control and Tuxedo System and Application Monitor. SOA Enablement and Integration: Extensibility with Web services using Oracle Services Architecture Leveraging Tuxedo (SALT), J2EE integration (using WebLogic-Tuxedo Connector (WTC), Enterprise Service Bus (ESB), Portal, and BPM technologies to enable easy integration of re-hosted applications into modern Service-Oriented Architectures (SOAs). Scalable Platforms and Commodity Hardware: Scalable, Linux/UNIX-based open systems from HP, Dell, Sun, and IBM, providing: Performance on a par with mainframe systems for most workloads at significantly reduced TCO Reliability and workload management similar to mainframe installations, including physical and logical partitioning Robust clustering technologies for high availability and fail-over capabilities within a data center or across the world The diagram below shows conceptual mapping of mainframe environment to compatible open systems infrastructure: Predictable, efficient projects delivered by leading SIs and key modernization specialists use risk-mitigation methodologies, and automated tools honed over numerous projects to address a complete range of Online, Batch, and Data architectures, and the various technologies used in them. These project methodologies and automated tools that support them encompass all phases of a migration project: Preliminary Assessment Study Application Asset Discovery and Analysis Application and Data Conversion (pilot or entire application portfolio) System and Application Integration Test Engineering Regression and Performance Testing Education and Training Operations Migration Switch-Over Combining a proven target architecture stack that is well-matched to the needs of mainframe applications with mature methodologies supported by automated tools has led to a large and growing number of successful re-hosting projects. There is a rising interest to leverage the re-hosting approach to mainframe application modernization, as a way to get off a mainframe fast, and with minimal risk, in a more predictable manner for large, business-critical applications evolved over a long term and multiple development teams. Re-hosting based modernization approach preserves an organizations long term investment in critical business logic and data without risking business operations or sacrificing the QoS, while enabling customers to: Reduce or eliminate mainframe maintenance costs, and/or defer upgrade costs, saving customers 50–80 percent of their annual maintenance and operations budget Increase productivity and flexibility in IT development and operations, protecting long-term investment through application modernization Speed up and simplify application integration via SOA, without losing transactional integrity and the high performance expected by the users The rest of this article explores the critical success factors and proven transformation architecture for re-hosting legacy applications and data, describes SOA integration options and considerations when SOA-enabling re-hosted applications, highlights key risk mitigation methodologies, and provides a foundation for the financial analysis and ROI model derived from over a hundred, mainframe re-hosting projects. Critical Success Factors in Mainframe Re-Hosting Companies considering a re-hosting-based modernization strategy that involves migrating some applications off the mainframe have to address a range of concerns, which can be summarized by the following questions: How to preserve the business logic of these applications and their valuable data? How to ensure that migrated applications continue to meet performance requirements? How to maintain scalability, reliability, transactional integrity, and other QoS attributes in an open system environment? How to migrate in phases, maintaining robust integration links between migrated and mainframe applications? How to achieve predictable, cost-effective results and ensure a low-risk project? Meeting these challenges requires a versatile and powerful application infrastructure—one that natively supports key mainframe languages and services, enables automated adaptation of application code, and delivers proven, mainframe-like QoS on open system platforms. For re-hosting to enable broader aspects of the modernization strategy, this infrastructure must also provide native Web services and ESB capabilities to rapidly integrate re-hosted applications as first-class services in an SOA. Equally important is a proven, risk-mitigation methodology, automated tools, and project services specifically honed to address automated conversion and adaptation of application code and data, supported by cross-platform test engineering and execution methodology, strong system and application integration expertise, and deep experience with operations migration and switch-over. Preserving Application Logic and Data The re-hosting approach depends on a mainframe-compatible transaction processing and application services platform supporting common mainframe languages such as COBOL and C, which preserves the original business logic and data for the majority of mainframe applications and avoids the risks and uncertainties of a re-write. A complete re-hosting solution provides native support for TP and Batch programs, leveraging an application server-based platform that provides container-based support for COBOL and C/C++ application services, and TP APIs similar to IBM CICS, IMS TM, or other mainframe TP monitors. Online Transaction Processing Environment Oracle Tuxedo is the most popular TP platform for open systems, as well as leading re-hosting platform that can run most of mainframe COBOL and C applications unchanged in container-based framework that combines common application server features, including health monitoring, fail-over, service virtualization, and dynamic load balancing critical to large-scale OLTP applications together with standard TP features, including transaction management and reliable coordination of distributed transactions (a.k.a. Two-Phase Commit or XA standard). It provides the highest possible performance and scalability, and has been recently benchmarked against a mainframe at over 100,000 transactions per second, with sub-second response time. Oracle Tuxedo supports common mainframe programming languages, that is, COBOL and C, and provides comprehensive TP features compatible with CICS and IMS TM, which makes it a preferred application platform choice for re-hosting CICS or IMS TM applications with minimal changes and risks. In the Tuxedo environment, COBOL or C business logic remains unchanged. The only adaptation required is automated mapping of CICS APIs (CICS EXEC calls) to equivalent Tuxedo API functions. This mapping typically leverages a pre-processor and a mapping library implemented on Tuxedo platform, and using a full range of Tuxedo APIs. The automated nature of pre-processing and comprehensive coverage provided by the library ensures that most CICS COBOL or C programs are easily transformed into Tuxedo services. Unlike other solutions that embed this transformation in their compiler coupled with a proprietary emulation run-time, Tuxedo-based solution provides this mapping as a compiler-independent source module, which can be easily extended as needed. The resultant code uses Tuxedo API at native speed, allowing it to reach tens of thousands of transactions per second, while taking advantage of all Tuxedo facilities. In a re-hosted application CICS transactions become Tuxedo services, registered for processing by Tuxedo server processes. These services can be deployed in a single machine or across multiple machines in a Tuxedo domain (SYSPLEX-like cluster.). The services are called by front-end Java, .Net, or Tuxedo/WS clients, or UI components (tn3270 or web-based converted 3270/BMS screens), or by other services in case of transaction linking. Deferred transactions are handled by Tuxedo's/Q component, which provides in-memory and persistent queuing services. The diagram below shows Oracle Tuxedo and its surrounding ecosystem of SOA, J2EE, ESB, CORBA, MQ, and Mainframe integration components:   User Interface Migration The UI elements in these programs are typically defined using CICS Basic Mapping Support (BMS) for 3270 "green screen" terminals. While it is possible to preserve these using tn3270 emulation, many customers in re-hosting projects choose to take advantage of automated conversion of BMS macros into JSP/HTML for Web UI. Supported by a specialized Javascript library, these Web screens mimic the appearance and the behavior of "green screens" in a web browser, including tab-based navigation and PF keys. These UI components can connect to re-hosted CICS transactions running as Tuxedo services using Oracle Jolt (Java client interface for Tuxedo), Weblogic-Tuxedo Connector (WTC), or Tuxedo's Web services gateway provided by Oracle Services Architecture Leveraging Tuxedo (SALT) product. The diagram on the next page depicts a target re-hosting architecture for a typical mainframe OLTP application. The architecture uses Tuxedo services to run re-hosted CICS programs and a web application server to run re-hosted BMS UI. The servlets or JSPs containing the HTML that defines the screens, connect with Tuxedo services via Oracle Jolt, WTC, or SALT. Customers using mainframe 4GLs or languages such as PL/I or Assembler frequently choose to convert these applications to COBOL or C/C++. The adaptation of CICS or IMS TM API calls is automated through a mapping layer, which minimizes overall changes for the development team and allows them to maintain the familiar applications. For more significant extensions and new capabilities, customers incrementally leverage Tuxedo's own APIs and facilities, or leverage a tightly-linked J2EE environment provided by the WebLogic Server, and even transparently make Web services calls. The optimal extensibility options depend on application needs, availability of Java or C/COBOL skills, and other factors.   Feature or Action CICS Verb Tuxedo API Communications Area DFHCOMMAREA Typed Buffer Transaction Request LINK tpcall Transaction Return RETURN tpreturn Transfer Control XCTL tpforward Allocate Storage GETMAIN tpalloc Queues READQ / WRITEQ TD,TS /Q tpenqueue / tpdequeue Begin new transaction START TRANID /Q and TMQFORWARD Abort transaction ISSUE ABEND tpreturn TPFAIL Commit or Rollback SYNCPOINT / SYNCPOINT ROLLBACK tpcommit / tpabort     Keeping it Real:For those familiar with CICS, this is a very short example of the CICS verbs. CICS has many functions, most of which either map natively to a similar Tuxedo API or are provided by migration specialists based on their extensive experience with such migrations. In summary, Tuxedo provides a popular platform for deploying, executing, and managing COBOL and C re-hosted transactional applications requiring any of the following OLTP and infrastructure services: Native, compiler-independent support for COBOL, C, or C++ Rich set of infrastructure services for managing and scaling diverse workloads Feature-set compatibility and inter-operability with IBM CICS and IMS/TM Two-Phase Commit (2PC) for managing transactions across multiple application domains and XA-compliant resource managers (databases, message queues) Guaranteed inter-application messaging and transactional queuing Transactional data access (using XA-compliant resource managers) with ACID qualities Services virtualization and dynamic load balancing Centralized management of multiple nodes in a domain, and across multiple domains Communications gateways for multiple traditional and modern communication protocols SOA Enablement through native Web services and ESB integration Workload Monitoring and Management An important aspect of the mainframe environment is workload monitoring and management, which provides information for effective performance analysis and capabilities that enable mainframe systems to achieve better throughput and responsiveness. Oracle's Tuxedo System and Application Monitor (TSAM) provides similar capabilities too. Define monitoring policies and patterns based on application requests, services, system servers such as gateways, bridges, and XA-defined stages of a distributed transaction Define SLA thresholds that can trigger a variety of events within Tuxedo event services including notifications, and instantiation of additional servers Monitor transactions on an end-to-end basis from a client call through all services across all domains involved in a client request Collect service statistics for all infrastructure components such as servers and gateways Detail time spent on IPC queues, waiting on network links, and time spent on subordinate services TSAM provides a built-in, central, web-based management and monitoring console, and an open framework for integration with third-party performance management tools. Batch Jobs Mainframe batch jobs are a response to a human 24-hour clock on which many businesses run. It includes beginning-of-period or end-of-period (day, week, month, quarter) processing for batched updates, reconciliation, reporting, statement generation, and similar applications. In some industries, external events tied to a fixed schedule such as intra-day, opening or closing trade in a stock exchange, drive specific processing needs. Batch applications are an equally important asset, and often need to be preserved and migrated as well. The batch environment uses Job Control Language (JCL) jobs managed and monitored by JES2 or JES3 (Job Entry System), which invoke one or more programs, access and manipulate large datasets and databases using sort and other specialized utilities, and often run under the control of a job scheduler such as CA-7/CA-11. JCL defines a series of job steps—a sequence of programs and utilities, specifies input and output files, and provides exception handling. Automated parsing and translation of JCL jobs to UNIX scripts such as Korn shell (ksh) or Perl, enables the overall structure of the job to remain the same, including job steps, classes, and exception handling. Standard shell processing is supplemented with required utilities such as SyncSort, and support for Generation Data Group (GDG) files. REXX/CLIST/PROC scripting environments on the mainframe are similarly converted to ksh or other scripting languages. Integration with Oracle Scheduler, or other job schedulers running in UNIX/Linux or Windows provides a rich set of calendar and event-based scheduling capabilities as well as dependency management similar to mainframe schedulers. In some cases, reporting done via batch jobs can be replaced using standard reporting packages such as Oracle BI Publisher. The diagram below shows a typical target re-hosting architecture for batch. It includes a scheduler to control and trigger batch jobs, scripting framework to support individual job scripts, and an application server execution framework for the batch COBOL or C programs. Unlike other solutions that run these programs directly as OS processes without the benefit of application server middleware, Oracle recommends using container-based middleware to provide higher reliability, availability, and monitoring to the batch programs. The target batch programs invoked by the scripts can also run directly as OS processes, but if mainframe-class management and monitoring similar to JES2 or JES3 environment is a requirement, these programs can run as services under Tuxedo, benefiting from the health monitoring, fail-over, load balancing, and other application server-like features it provides. Files and Databases When moving platforms (mainframe to open systems), the application and data have to be moved together. Data schemas and data stores need to be moved in a re-hosted mainframe modernization project just as with a re-architecture. The approach taken depends on the source data store. DB2 is the most straightforward, since DB2 and Oracle are both relational databases. In addition to migrating the data, customers sometimes choose to perform data cleansing, field extensions, merge columns, or other data maintenance practices leveraging the automated tooling that synchronizes all data changes with changes to the application's data access code. Mainframe DB2 DB2 is a predominant relational database on IBM mainframes. When migrating to Oracle Database, the migration approach is highly automated, and resolves all discrepancies between the two RDBMS in terms of field formats as well as error codes returned to applications, so as to maintain application behavior unchanged, including stored procedures if any. IMS IMS/DB (also known as DL/1) is a popular hierarchical database for older applications. Creating appropriate relational data schema for this data requires an understanding of the application access patterns so as to optimize the schema for best performance based on the most frequent access paths. To minimize code impact, a translation layer can be used at run-time to support IMS DB style data access from the application, and map it to appropriate SQL calls. This allows the applications to interface with the segments, now translated as DB2 UDB or ORACLE tables, without impacting application code and maintenance. VSAM VSAM files are used for keyed-sequential data access, and can be readily migrated to ISAM files or to Oracle Database tables wherever transactional integrity is required (XA features). Some customers also choose to migrate VSAM files to Oracle Database to provide accessibility from other distributed applications, or to simplify the re-engineering required to extend certain data fields or merge multiple data sources. Meeting Performance and Other QoS Requirements The mainframe's performance, reliability, scalability, manageability, and other QoS attributes have earned it pre-eminence for business-critical applications. How well do re-hosting solutions measure up against these characteristics? Earlier solutions based on IBM CICS emulators derived from development tools often did not measure up to the demands of mainframe workloads since they were never intended for true production environment and have not been exposed to large-scale applications. As a result, they have only been used for re-hosting small systems under 300 MIPS and not requiring any clustering or distributed workload handling. Oracle Tuxedo was built to scale ground up, to support high performance telecommunications operations. It has the distinction of being the only non-mainframe TP solution recognized for its mainframe-like performance, reliability, and QoS characteristics. Most large enterprise customers requiring such capabilities in distributed systems have traditionally relied on Tuxedo. Consistently rated by IDC and Gartner as the market leader, and predominant in non-mainframe OLTP applications, it has also become the preferred COBOL/C application platform and transaction engine for re-hosted mainframe applications requiring high performance and/or mission-critical availability and reliability. Reasons for the broad recognition of Tuxedo as the only mainframe-class application platform and transaction engine for distributed systems are based on mainframe-class performance, scalability, reliability, availability, and other QoS attributes proven in multiple customer deployments. The following table highlights some of these capabilities:   Reliability Availability Guaranteed messaging and transactional integrity Hardened code from 25 years of use in the world's largest transaction applications Transaction integrity across systems and domains through a two phase commit (XA) for all resources such as databases, queues, and so on. Proven in mainframe-to-mainframe transactions and messaging No single point of failure, 99.999% uptime with N+1/N+2 clusters Application services upgradeable in operation Self-monitoring, automated fail-over, datadriven routing for super high availability Centralized monitoring and management with clustered domains; automated, lights-out operations     Workload Management   Performance and Scalability   Resource management and prioritization across Tuxedo services Dynamic load balancing across domains based on load conditions Data-driven routing enables horizontally distributed database grids and differentiated QoS End-to-end monitoring of Tuxedo system and application services enables SLA enforcement Virtualization support enables spawning of Tuxedo servers on demand Parallel processing to maximize resource utilization with low latency code paths that provide sub-second response at any load Horizontal and vertical scaling of system resources yields linear performance increases Request multiplexing (synchronous and asynchronous) maximizes CPU utilization Proven in credit card authorizations at over 13.5K tps, and in telco billing at over 56K tps. Middleware of choice in HP, Fujitsu, Sun, IBM, and NEC TPC-C benchmarks    
Read more
  • 0
  • 0
  • 3006

article-image-configuring-and-securing-python-ldap-applications-part-2
Packt
23 Oct 2009
14 min read
Save for later

Configuring and securing PYTHON LDAP Applications Part 2

Packt
23 Oct 2009
14 min read
This is the second article in the article mini-series on Python LDAP applications by Matt Butcher. For first part please visit this link. In this article we will see some of the LDAP operations such as compare operation, search operation. We will also see how to change an LDAP password. The LDAP Compare Operation One of the simplest LDAP operations to perform is the compare operation. The LDAP compare operation takes a DN, an attribute name, and an attribute value and checks the directory to see if the given DN has an attribute with the given attribute name, and the given attribute value. If it returns true then there is a match, and if false then otherwise. The Python-LDAP API supports LDAP compare operations through the LDAPObject's compare() and compare_s() functions. The synchronous function is simple. It takes three string parameters (DN, attribute name, and asserted value), and returns 0 for false, and 1 for true: >>> dn = 'uid=matt,ou=users,dc=example,dc=com'>>> attr_name = 'sn'>>> attr_val = 'Butcher'>>> con.compare_s(dn, attr_name, attr_val)1 In this case, we check the DN uid=matt,ou=user,dc=example,dc=com to see if the surname (sn) has the value Butcher. It does, so the method returns 1. But let's set the attr_val to a different surname, one that the record does not contain: >>> attr_val = 'Smith'>>> con.compare_s(dn, attr_name, attr_val)0>>> Since the record identified by the DN uid=matt,ou=users,dc=example,dc=com does not have an SN attribute with the value Smith, this method returns 0, false. Historically, Python has treated the boolean value False with 0, and numeric values greater than zero as boolean True. So it is possible to use a compare like this: if con.compare_s(dn, attr_name, attr_val): print "Match"else: print "No match." If compare_s() returns 1, this will print Match. If it returns 0, it will print No match. Let's take a quick look, now, at the asynchronous version of the compare operation, compare(). As we saw in the section on binding, the asynchronous version starts the operation in a new thread, and then immediately returns control to the program, not waiting for the operation to complete. Later, the result of the operation can be examined using the LDAPObject's result() method. Running the compare() method is almost identical to the synchronized version, with the difference being the value returned: >>> retval = con.compare( dn, attr_name, attr_val )>>> print retval15 Here, we run a compare() method, storing the identification number for the returned information in the variable retval. Finding out the value of the returned information is a little trickier than one might guess. Any attempt to retrieve the result of a compare operation using the result() method will raise an exception. But, this is not a sign that the application has encountered an error. Instead, the exception itself indicates whether the compare operation returned true or false. For example, let's fetch the result for the previous operation in the way we might expect: >>> print con.result( retval )Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.5/site-packages/ldap/ldapobject.py", line 405, in result res_type,res_data,res_msgid = self.result2(msgid,all,timeout) File "/usr/lib/python2.5/site-packages/ldap/ldapobject.py", line 409, in result2 res_type, res_data, res_msgid, srv_ctrls = self.result3 (msgid,all,timeout) File "/usr/lib/python2.5/site-packages/ldap/ldapobject.py", line 415, in result3 rtype, rdata, rmsgid, serverctrls = self._ldap_call (self._l.result3,msgid,all,timeout) File "/usr/lib/python2.5/site-packages/ldap/ldapobject.py", line 94, in _ldap_call result = func(*args,**kwargs)ldap.COMPARE_TRUE: {'info': '', 'desc': 'Compare True'} What is going on here? Attempting to retrieve the value resulted in an exception being thrown. As we can see from the last line, the exception raised was COMPARE_TRUE. Why? The developers of the Python-LDAP API worked around a difficulty in the standard LDAP C API by providing the results of the compare operation in the form of raised exceptions. Thus, the way to retrieve information from the asynchronous form of compare is with a try/except block: >>> retval = con.compare( dn, attr_name, attr_val )>>> try: ... con.result( retval )...... except ldap.COMPARE_TRUE:... print "Returned TRUE."...... except ldap.COMPARE_FALSE:... print "Returned FALSE."... Returned TRUE. In this example, we use the raised exception to determine whether the compare returned true, which raises the COMPARE_TRUE exception, or returned false, which raises COMPARE_FALSE. Performing compare operations is fairly straightforward, even with the nuances of the asynchronous version. The next operation we will examine is search. The Search Operation LDAP servers are intended as high read, low write databases, which means that it is expected that most operations that the server handles will be “read” operations that do not modify the contents of the directory information tree. And the main operation for reading a directory, as we have seen throughout this book, is the LDAP search operation. As a reminder, the LDAP search operation typically requires five parameters: The base DN, which indicates where in the directory information tree the search should start. The scope, which indicates how deeply the search should delve into the directory information tree. The search filter, which indicates which entries should be considered matches. The attribute list, which indicates which attributes of a matching record should be returned. A flag indicating whether attribute values should be returned (the Attrs Only flag). There are other additional parameters, such as time and size limits, and special client or server controls, but those are less frequently used. Once a search is processed, the server will return a bundle of information including the status of the search, all of the matching records (with the appropriate attributes), and, occasionally, error messages indicate some outstanding condition on the server. When writing Python-LDAP code to perform searches, we will need to handle all of these issues. In the Python-LDAP API, there are three (functional) variations of the search function: search() search_s() search_st() The first is the asynchronous form, and the second is the synchronous form. The third is a special form of the synchronous form that allows the programmer to add on a hard time limit in which the client must respond. There are two other versions of the search method, search_ext() and search_ext_s(). These two provide parameter placeholders for passing client and server extension mechanisms, but such extension handling is not yet functional, so neither of these functions is performatively different than the three above. We will begin by looking at the second method, search_s(). The search_s() function of the LDAPObject has two required parameters (Base DN and scope), and three optional parameters (search filter, attribute list, and the attrs only flag). Here, we will do a simple search for a list of surnames for all of the users in our directory information tree. For this, we will not need to set the attrs only flag (which is off by default, and, when turned on, will not return the attribute values). But we will need the other four parameters: Base DN: The users branch, ou=users,dc=example,dc=com Scope: Subtree (ldap.SCOPE_SUBTREE) Filter: Any person objects, (objectclass=person) Attributes: Surname (sn) Now we can perform our search in the Python interpreter: >>> import ldap>>> dn = "uid=matt,ou=users,dc=example,dc=com">>> pw = "secret">>> >>> con = ldap.initialize('ldap://localhost')>>> con.simple_bind_s( dn, pw )(97, [])>>>>>> base_dn = 'ou=users,dc=example,dc=com'>>> filter = '(objectclass=person)'>>> attrs = ['sn']>>> >>> con.search_s( base_dn, ldap.SCOPE_SUBTREE, filter, attrs )[('uid=matt,ou=Users,dc=example,dc=com', {'sn': ['Butcher']}),('uid=barbara,ou=Users,dc=example,dc=com', {'sn': ['Jensen']}),('uid=adam,ou=Users,dc=example,dc=com', {'sn': ['Smith']}),('uid=dave,ou=Users,dc=example,dc=com', {'sn': ['Hume']}),('uid=manny,ou=Users,dc=example,dc=com', {'sn': ['Kant']}),('uid=cicero,ou=Users,dc=example,dc=com', {'sn': ['Tullius']}),('uid=mary,ou=Users,dc=example,dc=com', {'sn': ['Wollstonecraft']}),('uid=thomas,ou=Users,dc=example,dc=com', {'sn': ['Hobbes']})]>>> The first seven lines should look familiar – there is nothing in these lines not covered in the previous sections. Next, we declare variables for the Base DN (base_dn), filter (filter), and attributes (attrs). While base_dn and filter are strings, attrs requires a list. In our case, it is a list with one member: ['sn']. Safe FiltersIf you are generating the LDAP filter dynamically (or letting users specify the filter), then you may want to use the escape_filter_chars() and filter_format() functions in the ldap.filter module to keep your filter strings safely escaped. We don't need to create a variable for the scope, since all of the available scopes (subtree, base, and onelevel) are available as constants in the ldap module: ldap.SCOPE_SUBTREE, ldap.SCOPE_BASE, and ldap.SCOPE_ONELEVEL. The line highlighted above shows the search, and the lines following – that big long messy conglomeration of tuples, dicts, and lists – is the result returned from the server. Strictly speaking, the result returned from search_s() is a list of tuples, where each tuple contains a DN string, and a dict of attributes. Each dict of attributes has a string key (the attribute name), and a list of string values. While this data structure is compact, it is not particularly easy to work with. For a complex data structure like this, it can be useful to create some wrapper objects to make use of this information a little more intuitive. The ldaphelper Helper Module To better work with LDAP results, we will create a simple package with just one class. This will be our ldaphelper module, stored in ldaphelper.py: import ldiffrom StringIO import StringIOfrom ldap.cidict import cidictdef get_search_results(results): """Given a set of results, return a list of LDAPSearchResult objects. """ res = [] if type(results) == tuple and len(results) == 2 : (code, arr) = results elif type(results) == list: arr = results if len(results) == 0: return res for item in arr: res.append( LDAPSearchResult(item) ) return resclass LDAPSearchResult: """A class to model LDAP results. """ dn = '' def __init__(self, entry_tuple): """Create a new LDAPSearchResult object.""" (dn, attrs) = entry_tuple if dn: self.dn = dn else: return self.attrs = cidict(attrs) def get_attributes(self): """Get a dictionary of all attributes. get_attributes()->{'name1':['value1','value2',...], 'name2: [value1...]} """ return self.attrs def set_attributes(self, attr_dict): """Set the list of attributes for this record. The format of the dictionary should be string key, list of string alues. e.g. {'cn': ['M Butcher','Matt Butcher']} set_attributes(attr_dictionary) """ self.attrs = cidict(attr_dict) def has_attribute(self, attr_name): """Returns true if there is an attribute by this name in the record. has_attribute(string attr_name)->boolean """ return self.attrs.has_key( attr_name ) def get_attr_values(self, key): """Get a list of attribute values. get_attr_values(string key)->['value1','value2'] """ return self.attrs[key] def get_attr_names(self): """Get a list of attribute names. get_attr_names()->['name1','name2',...] """ return self.attrs.keys() def get_dn(self): """Get the DN string for the record. get_dn()->string dn """ return self.dn def pretty_print(self): """Create a nice string representation of this object. pretty_print()->string """ str = "DN: " + self.dn + "n" for a, v_list in self.attrs.iteritems(): str = str + "Name: " + a + "n" for v in v_list: str = str + " Value: " + v + "n" str = str + "========" return str def to_ldif(self): """Get an LDIF representation of this record. to_ldif()->string """ out = StringIO() ldif_out = ldif.LDIFWriter(out) ldif_out.unparse(self.dn, self.attrs) return out.getvalue() This is a large chunk of code to take in at once, but the function of it is easy to describe. Remember, to use a Python module, you must make sure that the module is in the interpreter's path. See the official Python documentation (http://python.org) for more information. The package has two main components: the get_search_results() function, and the LDAPSearchResult class. The get_search_results() function simply takes the results from a search (either the synchronous ones, or the results from an asynchronous one, fetched with result()) and converts the results to a list of LDAPSearchResult objects. An LDAPSearchResults object provides some convenience methods for getting information about a record. The get_dn() method returns the record's DN, and the following methods all provide access to the attributes or the record: get_dn(): return the string DN for this record. get_attributes(): get a dictionary of all of the attributes. The keys  are attribute name strings, and the values are lists of attribute value  strings. set_attributes(): takes a dictionary with attribute names for keys, and  lists of attribute values for the value field. has_attribute(): takes a string attribute name and returns true if that attribute name is in the dict  of attributes returned. get_attr_values(): given an attribute name, this returns all of the  values for that attribute (or none if that attribute does not exist). get_attr_names(): returns a list of all of the attribute names for this  record. pretty_print(): returns a formatted string presentation of the record. to_ldif(): returns an LDIF formatted representation of the record. This object doesn't add much to the original returned data. It just makes it a little easier to access. Attribute NamesLDAP attributes can have multiple names. The attribute for surnames has two names: surname and sn (though most LDAP directory entries use sn). Either one might be returned by the server. To make your application aware of this difference, you can use the ldap.schema package to get schema information. The Case Sensitivity Gotcha There is one noteworthy detail in the code above. The search operation returns the attributes in a dictionary. The Python dictionary is case sensitive; the key TEST is different than the key test. This exemplifies a minor problem in dealing with LDAP information. Standards-compliant LDAP implementations treat some information in a case-insensitive way. The following items are, as a rule, treated as case-insensitive: Object class names: inetorgperson is treated as being the same as inetOrgPerson. Attribute Names: givenName is treated as being the same as givenname. Distinguished Names: DNs are case-insensitive, though the all-lower-case version of a DN is called  Normalized Form. The main area where this problem surfaces is in retrieving information from a search. Since the attributes are returned in a dict, they are, by default, treated as case-sensitive. For example, attrs.has_key('objectclass') will return False if the object class attribute name is spelled objectClass. To resolve this problem, the Python-LDAP developers created a case-insensitive dictionary implementation (ldap.cidict.cidict). This cidict class is used above to wrap the returned attribute dictionary. Make sure you do something similar in your own code, or you may end up with false misses when you look for attributes in a case-sensitive way, e.g. when you look for givenName in an entry where the attribute name is in the form givenname.
Read more
  • 0
  • 1
  • 18229

article-image-front-page-customization-moodle
Packt
23 Oct 2009
11 min read
Save for later

Front Page Customization in Moodle

Packt
23 Oct 2009
11 min read
Look and Feel: An Overview Moodle can be fully customized in terms of layout and branding. It has to be stressed that certain aspects of changing the look and feel require some design skills. While you as an administrator will be able to make most of the relevant adjustments, it might be necessary to get a professional designer involved, especially when it comes to styling. The two relevant components for customization are the Moodle front page and Moodle themes, though this article will focus only on Moodle front page. Before going into further details, let's try to understand which part is responsible for which element of the look and feel of your site. Have a look at the screenshot that would follow. It shows the front page of Moodle site after you are logged in as an administrator. It is not obvious which parts are driven by the Moodle theme and by the front page settings. The next table sheds some light on this: Element Settings Theme Other Logos - x - Logged-in information (location and font) - x - Language Drop Down - - x Site Administration block (position) x - - Available Courses block (position) x - - Available Courses block (content) - - x Course categories and Calendar block (position) x - - Course categories and Calendar block (icons, fonts, colors) - x - Footer text - x - Footer logo - x - Copyright statement - x -   While this list is by no means complete, it hopefully gives you an idea that the look and feel of your Moodle site is driven by a number of different elemen In short, the settings (mostly front page settings as well as a few related parameters) dictate what content users will see before and after they log on. The theme is responsible for the design scheme or branding, that is, the header and footer as well as colors, fonts, icons, and so on used throughout the site. Now let's move towards the core part of this article. Customizing Your Front Page The appearance of Moodle's front page changes after a user has logged in. The content and layout of the page before and after login can be customized to represent the identity of your organization. Look at the following screenshot. It is the same site that the preceding screenshot was taken from, but before a user has logged in. In this particular example, a Login block is shown on the left and the Course categories are displayed in the center, as opposed to the list of available courses. Front Page Settings To customize the front page, you either have to be logged in as Moodle administrator, or have front-page-related permissions in the Front Page context. From the Site Administration block, select Front Page | Front Page Settings. The screen showing all available parameters will be loaded displaying your current settings that are changeable. ts. Setting Description Full site name This is the name that appears in the browser's title bar. It is usually the full name of your organization, or the name of the dedicated course, or qualification the site is used for. Short name for site This is the name that appears as the first item in the breadcrumb trail. Front Page Description This description of the site will be displayed on the front page via the Site Description block. It can, therefore, only be displayed in the left or right column, never in the center of the front page. The description text is also picked up by the Google search engine spider, if allowed. Front Page Moodle can display up to four elements in the center column of the front page when not logged in. List of courses List of categories News items Combo list(categories and courses) The order of the elements is the same as the one chosen in the pull-down menus. Front page items when logged in Same as "Front Page", but used when logged in. Include a topic section If ticked, an additional topic section (just like the topic blocks in the center column of a topics-format course) appears on top of the front page's center column. It can contain any mix of resources or activities available in Moodle. It is very often used to provide information about the site. News items to show Number of news items that are displayed. Courses per page This is a threshold setting that is used when displaying courses within categories. If there are more courses in a category than specified, page navigation will be displayed at the top of the page. Also, when a combo list is used, course names are only displayed if the number is less than the specified threshold. For all other categories, only the number of courses is shown after the category name. Allow visible courses within hidden categories By default, courses in hidden categories are not shown unless the said setting is applied. Default frontpage role If logged-in users should be allowed to participate in front page activities, a default front page role should be set. The default is None.   Arranging Front Page Blocks To configure the left and right column areas with blocks, you have to turn on editing (using the Blocks editing on button). The menu includes blocks that are not available in courses such as Course/Site description and Main menu. Blocks are added to the front page in exactly the same way as in courses. To change their position, use the standard arrows. The Main Menu block allows you to add any installed Moodle resource or activity inside the block. For example, using labels and links to (internal or external) websites, you are able to create a menu-like structure on your front page. If the Include a topic section parameter has been selected in the Front Page settings, you have to edit the part and add any installed Moodle activity or resource. This topic section is usually used by organizations to add a welcome message to visitors, often accompanied by a picture or other multimedia content. Login From a Different Website The purpose of the Login block is for users to authenticate themselves by entering their username and password. It is possible to log into Moodle from a different website, maybe your organization's homepage, effectively avoiding the Login block. To implement this, you will have to add some HTML code on that page as shown: <form class="loginform" name="login" method="post" action="http://www.mysite.com/login/index.php">   <p>Username :     <input size="10" name="username" />   </p>   <p>Password :     <input size="10" name="password" type="password" />   </p>   <p>     <input name="Submit" value="Login" type="submit" />   </p></form> The form will pass the username and password to your Moodle system. You will have to replace www.mysite.com with your URL. This address has to be entered in the Alternate Login URL field at Users | Authentication | Manage authentication in the Site Administration block. Other Front Page Items The Moodle front page is treated as a standalone component in Moodle, and therefore has a top-level menu with a number of features that can all be accessed via the Front Page item in the Site Administration menu. Having now looked in detail at the front page settings, let's turn to examining the other available options. Front Page Roles The front page has its own context in which roles can be assigned to users. This allows a separate user to develop and maintain the front page without having access to any other elements in Moodle. Since the front page is treated as a course, a Teacher role is usually sufficient for this. Front Page Backup and Restore The front page has its own backup and restore facilities to back up and restore all elements of the front page including any content. The mechanism of performing backup and restore is the same as for course backups.   Front page backups are stored in the backupdata folder in the Site Files area, and can be accessed by anybody who is aware of the URL. It is therefore best to move the created ZIP files to a more secure location. Front Page Questions Since the Moodle front page is treated in the same way as a course, it also has its own question bank, which is used to store any questions used on front-page quizzes. For more information on quizzes and the question bank, go to the MoodleDocs at http://docs.moodle.org/en/Quiz . Site Files The files areas of all courses are separate from each other, that is, files in Moodle belong to a course and can only be accessed by users who have been granted appropriate rights. The difference between Site files and the files area of any other course is that files in Site files can be accessed without logging in. Files placed in this location are meant for the front page, but can be accessed from anywhere in the system. In fact, if the location is known, files can be even be accessed from outside Moodle. Make sure that in the Site files area, you only place files that are acceptable to be seen by users who are not authenticated on your Moodle system. Typical files to be placed in this area are any images you want to show on the front page (such as the logo of your organization) or any document that you want to be accessed (for example, the curriculum). However, it is also used for other files that are required to be accessible without access to a course, such as the Site Policy Agreement, which has to be accepted before starting Moodle. To access these publicly available Site files elsewhere (for example, as a resource within other courses), you have to copy the link location that has the format: http://mysite.com/file.php/1/file.doc. Allow Personalization via My Moodle By default, the same front page is displayed for all users on your Moodle system. To relax this restriction and to allow users to personalize their own front page, you have to activate the My Moodle feature via the Force users to use My Moodle setting in Appearance | My Moodle in the Site Administration block. Once enabled, Moodle creates a /my directory for each user (except administrators) at their first login, which is displayed instead of the main Moodle front page. It is a very flexible feature that is similar to a customizable dashboard, but requires some more disk space on your server. Once logged in, users will have the ability to edit their page by adding blocks to their My Moodle area. The center of the page will be populated by the main front page, for instance displaying a list of courses, that users cannot modify. Making Blocks Sticky There might be some blocks that you wish to "stick", that is, display on each My Moodle page, making them effectively compulsory blocks. For example, you might want to pin the Calendar block on the top right corner of each user's My Moodle page. To do this, go to Modules | Blocks | Sticky blocks in the Site Administration block and select My Moodle from the pull-down menu. You can now add any item from the pull-down menu in the Blocks block. If the block is single instance (that is, only one occurrence is allowed per page), the block will not be available for the user to choose from. If the user has already selected a particular block, a duplicate will appear on their site, which can be edited and deleted. To prevent users from editing their My Moodle pages, change the moodle/my: manageblocks capability in the Authenticated user role from Allow to Not set. The sticky block feature is also available for course pages. A course creator has the ability to add and position blocks inside a course unless they have been made sticky. Select the Course page item from the same menu to configure the sticky blocks for courses, as shown in the preceding screenshot. Summary After providing a general overview of look and feel elements in Moodle, the article covered front page customization. As mentioned earlier, the front page in Moodle is a course. This has advantages (you can do everything you can do in a course and a little bit more), but it also has certain limitations (you can only do what you can do in a course and might feel limited by this). However, some organizations are now using the Moodle front page as their homepage.
Read more
  • 0
  • 0
  • 17368

article-image-jboss-plug-and-eclipse-web-tools-platform
Packt
23 Oct 2009
4 min read
Save for later

JBoss AS plug-in and the Eclipse Web Tools Platform

Packt
23 Oct 2009
4 min read
In this article, we recommend that you use the JBoss AS (version 4.2), which is a free J2EE Application Server that can be downloaded from http://www.jboss.org/jbossas/downloads/ (complete documentation can be downloaded from http://www.jboss.org/jbossas/docs/). JBoss AS plug-in and the Eclipse WTP JBoss AS plug-in can be treated as an elegant method of connecting a J2EE Application Server to the Eclipse IDE. It's important to know that JBoss AS plug-in does this by using the WTP support, which is a project included by default in the Eclipse IDE. WTP is a major project that extends the Eclipse platform with a strong support for Web and J2EE applications. In this case, WTP will sustain important operations, like starting the server in run/debug mode, stopping the server, and delegating WTP projects to their runtimes. For now, keep in mind that Eclipse supports a set of WTP servers and for every WTP server you may have one WTP runtime. Now, we will see how to install and configure the JBoss 4.2.2 runtime and server. Adding a WTP runtime in Eclipse In case of JBoss Tools, the main scope of Server Runtimes is to point to a server installation somewhere on your machine. By runtimes, we can use different configurations of the same server installed in different physical locations. Now, we will create a JBoss AS Runtime (you can extrapolate the steps shown below for any supported server): From the Window menu, select Preferences. In the Preferences window, expand the Server node and select the Runtime Environments child-node. On the right side of the window, you can see a list of currently installed runtimes, as shown in the following screenshot, where you can see that an Apache Tomcat runtime is reported (this is just an example, the Apache Tomcat runtime is not a default one). Now, if you want to install a new runtime, you should click the Add button from the top-right corner. This will bring in front the New Server Runtime Environment window as you can see in the following screenshot. Because we want to add a JBoss 4.2.2 runtime, we will select the JBoss 4.2 Runtime option (for other adapters proceed accordingly). After that, click Next for setting the runtime parameters. In the runtimes list, we have runtimes provided by WTP and runtimes provided by JBoss Tools (see the section marked in red on the previous screenshot). Because this article is about JBoss Tools, we will further discuss only the runtimes from this category. Here, we have five types of runtimes with the mention that the JBoss Deploy-Only Runtime type is for developers who start/stop/debug applications outside Eclipse. In this step, you will configure the JBoss runtime by indicating the runtime's name (in the Name field), the runtime's home directory (in the Home Directory field), the Java Runtime Environment associated with this runtime (in the JRE field), and the configuration type (in the Configuration field).In the following screenshot, we have done all these settings for our JBoss 4.2 Runtime. The official documentation of JBoss AS 4.2.2 recommends using JDK version 5. If you don't have this version in the JRE list, you can add it like this: Display the Preferences window by clicking the JRE button. In this window, click the Add button to display the Add JRE window. Continue by selecting the Standard VM option and click on the Next button. On the next page, use the Browse button to navigate to the JRE 5 home directory. Click on the Finish button and you should see a new entry in the Installed JREs field of the Preferences window (as shown in the following screenshot). Just check the checkbox of this new entry and click OK. Now, JRE 5 should be available in the JRE list of the New Server Runtime Environment window. After this, just click on the Finish button and the new runtime will be added, as shown in the following screenshot: From this window, you can also edit or remove a runtime by using the Edit and Remove buttons. These are automatically activated when you select a runtime from the list. As a final step, it is recommended to restart the Eclipse IDE.
Read more
  • 0
  • 0
  • 3541
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
Packt
23 Oct 2009
2 min read
Save for later

Network Configuration—Tunneling with Free BSD

Packt
23 Oct 2009
2 min read
Generic Routing Encapsulation (GRE) protocol GRE creates a virtual end-to-end network link between network nodes (that is routers), over a public network. GRE can be used to create simple VPN networks for customers that are connected to a service provider network, or between edge routers in a service provider environment, to exchange routing table updates. As this simple GRE interface does not support encryption, you may want to reconsider using GRE, if security is a priority. FreeBSD 7 natively supports creating and manipulating standard GRE tunnels. GRE support should be available in the FreeBSD's kernel. If you have not compiled GRE support into kernel, the GRE kernel module will be dynamically loaded into memory upon first usage. Creating gre(4) interface in a simple way usingifconfig(8) utility is shown here: # ifconfig gre0 create Note that if you do not specify the device node number, ifconfig takes the first available number and returns the new interface name, after creating the interface as follows: # ifconfig gre create gre1 You can also remove unnecessary gre(4) interface using ifconfig's destroy option: # ifconfig gre1 destroy Now that you have created a GRE interface, you should configure both sides of the GRE interface (on both hosts). This is basically done using the ifconfig utility. Consider the following scenario in the figure to establish a GRE tunnel between Host A and Host B:   To configure Host A, you should first create a gre0 interface and then specify both ends of the link on the interface.  
Read more
  • 0
  • 0
  • 2482

article-image-securing-xml-documents
Packt
23 Oct 2009
11 min read
Save for later

Securing XML Documents

Packt
23 Oct 2009
11 min read
XML Security Threats All the components in web services are described in XML. SOAP and all the WS -Security specifications are XML formats. Hence it just makes sense for expressing security data in XML format. Fortunately, there has been no need to invent new cryptography technologies for XML. The XML security standards have used existing cryptography directly. XML-based data transfer has emerged as the standard for organizations to exchange business data. As with all communications over the public Internet, XML-based transfers have their own set of vulnerabilities to confront. Like any other document exchange, XML document exchange must support the usual security measures which are Confidentiality, Integrity, Authenticity, and Non-Repudiation. The following list illustrates some specific XML security threats: Schema Altering — Manipulation of WS schema to alter the data processed by the application.      XML Parameter Tampering — Injection of malicious scripts or content into XML parameters      Coercive Parsing — Injection of malicious content into the XML      Oversized Payload — Sending oversized files to create an XDoS attack      Recursive Payload — Sending mass amounts of nested data to create an XDoS attack against an XML parser      XML Routing Detours — Redirecting sensitive data within the XML path      External Entity Attack — An attack on an application that parses XML input from suspicious sources using an incorrectly configured XML parser These threats pose potentially serious problems to developers creating applications, components, and systems that depend on XML data. The solution for the above problems is XML Encryption. XML Encryption XML Encryption provides end-to-end security for applications that require secure exchange of structured data. XML itself is the most popular technology for structuring data, and therefore XML-based encryption is the natural way to handle complex requirements for security in data interchange applications. XML Encryption is a process for encrypting and decrypting parts of XML documents. Most of today's encryption schemes use transport-level techniques that encrypt an entire request and response stream between a sender and receiver, offering zero visibility into contents of the interchange to intermediaries. Contentlevel encryption converts document fragments into illegible ciphertext, while other elements remain legible as plaintext. Some features of XML encryption are: The ability to encrypt a complete XML file The ability to encrypt a single element of an XML file The ability to encrypt only the contents of an XML element The ability to encrypt binary data within an XML file Encrypting an XML File Here's a short sample XML file that can serve to demonstrate XML encryption:     <?xml version='1.0'?>    <POInfo >        <Name>FJ</Name>        <Amount>125.00</Amount>        <CreditCardNumber>1234-5678-4564-4321</CreditCardNumber>    <Date>July 6, 2006</Date>    </POInfo> When you encrypt an entire XML file, the process simply replaces the root element (<POInfo> in the sample) with an <EncryptedData> element that contains the encryption details, including the encrypted content. Here is how the encrypted file will look:     <?xml version="1.0" encoding="UTF-8"?>    <xenc:EncryptedData                Type="http://www.w3.org/2001/04/xmlenc#Element">        <xenc:EncryptionMethod            Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"            />        <ds:KeyInfo >            <xenc:EncryptedKey                >                <xenc:EncryptionMethod                    Algorithm="http://www.w3.org/2001/04/xmlenc#kw-tripledes"                    />                <xenc:CipherData                    >                    <xenc:CipherValue                        >                        MKeT0ZmHFLwnZaSXO+oZSxlSJ5/BqvblqG76B3nOMU0=                    </xenc:CipherValue>                </xenc:CipherData>            </xenc:EncryptedKey>        </ds:KeyInfo>        <xenc:CipherData            >            <xenc:CipherValue                >                    +M/Tamk/62Lut4HqLpU/es9sdhnNTTpasbeszN8GN8EAJZsX0vvClcKEW                    UAgIdbvyJpprQ+jUIiWJKTz1X3L6VAefHqO963pU3bzmGMo                    pHLqS1Eg7iAPFhKV1PJclyswyyepEjyu+bOgqzgGnS1XA0/V                    NP7kLK70rB2Zb0DSbaCi+7HjTNGWF9YKtPIP5bvrs5xw+x                    HnKO++2EuqzK+deD7mCu8w6sG9vmRCrUR99Mx1QDZon9a2962ZD                    FSwoIJKg5I83GzOU+RObBBUme+yTf7UWybEiwtHp5ZgvuaQYJA=            </xenc:CipherValue>        </xenc:CipherData>    </xenc:EncryptedData> Encrypting a Single Element To encrypt a single element of an XML file, you specify the desired child element, rather than the root element of the input file as the element to encrypt. The following snippet shows the results of encrypting only the <CreditCardNumber> element of the sample file.     <?xml version="1.0" encoding="UTF-8"?>    <POInfo >        <Name>John Doe</Name>        <Amount>125.00</Amount>        <xenc:EncryptedData                        Type="http://www.w3.org/2001/04/xmlenc#Element">            <xenc:EncryptionMethod                Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"                />            <ds:KeyInfo >                <xenc:EncryptedKey                    >                    <xenc:EncryptionMethod                        Algorithm=                            "http://www.w3.org/2001/04/xmlenc#kw-tripledes"                        />                    <xenc:CipherData                        >                        <xenc:CipherValue                            >                                6zhAcEW7KIKrbSjEOkXDrVkmws5zhQQLDO4YYW+RfRY=                        </xenc:CipherValue>                    </xenc:CipherData>                </xenc:EncryptedKey>            </ds:KeyInfo>            <xenc:CipherData                >                <xenc:CipherValue                    >                    JqsRmdSoS+PXqCe80Y8zNiQ49sHTLNaAgHX1Ja7d+u9fv                    TFBrkBMK7C7EHsQTglZ3yT9yCZDuFnjBoQTLULKqOy71Qw                    EPRPObtYLPIJgy1vUdNrw47uDmJ/R5r/B0SH37HN8mfNv                    i50zPt1qPxxRwA==                </xenc:CipherValue>            </xenc:CipherData>        </xenc:EncryptedData>        <Date>July 6, 2005</Date>    </POInfo> Notice that the encryption process replaced the <CreditCardNumber> tag and its contents with an <EncryptedData> tag, while leaving the siblings of the <CreditCardNumber> element unaltered. This type of encryption can be performed using XML Signature and Encryption. The interested reader may look up the implementation at the Apache site (http://xml.apache.org/security/). Best practices for XML encryption, can be summarized as follows: It is good to have standard element tags for representing encrypted elements within the XML documents. This will enable parsers to better understand encrypted elements and data during the validation process.      It is necessary to provide means for encrypting only the desired elements within an XML document instead of encrypting the whole document. This will pave the way for incorporating several confidential data elements that are intended for different recipients within a single XML document.      There should be standard mechanisms for exchanging the secret keys used for encryption and decryption processes.      The standard should allow encryption of different parts of the document with different keys, so that multiple recipients can decrypt only those portions that are intended for them.      The standards should be adaptable to both ASCII and binary data.      The standards should be adaptable to different cryptographic algorithms.      The standards should work along with other XML security standards and specifications.
Read more
  • 0
  • 0
  • 5295

article-image-catalyst-web-framework-building-your-own-model
Packt
23 Oct 2009
12 min read
Save for later

Catalyst Web Framework: Building Your Own Model

Packt
23 Oct 2009
12 min read
Extending a DBIx::Class Model A common occurrence is a situation in which your application has free reign over most of the database, but needs to use a few stored procedure calls to get at certain pieces of data. In that case, you'll want to create a normal DBIC schema and then add methods for accessing the unusual data. As an example, let's look back to the AddressBook application and imagine that for some reason we couldn't use DBIx::Class to access the user table, and instead need to write the raw SQL to return an array containing everyone's username. In AddressBook::Model::AddressDB, we just need to write a subroutine to do our work as follows:     package AddressBook::Model::AddressDB;    // other code in the package    sub get_users {        my $self = shift;        my $storage = $self->storage;        return $storage->dbh_do(            sub {                my $self = shift;                my $dbh = shift;                my $sth = $dbh->prepare('SELECT username FROM user');                $sth->execute();                my @rows = @{$sth->fetchall_arrayref()};                return map { $_->[0] } @rows;                });    } Here's how the code works. On the first line, we get our DBIC::Schema object and then obtain the schema's storage object. The storage object is what DBIC uses to execute its generated SQL on the database, and is usually an instance of DBIx:: Class::Storage::DBI. This class contains a method called dbh_do which will execute a piece of code, passed to dbh_do as a coderef (or "anonymous subroutine"), and provide the code with a standard DBI database handle (usually called $dbh). dbh_do will make sure that the database handle is valid before it calls your code, so you don't need to worry about things like the database connection timing out. DBIC will reconnect if necessary and then call your code. dbh_do will also handle exceptions raised within your code in a standard way, so that errors can be caught normally. The rest of the code deals with actually executing our query. When the database handle is ready, it's passed as the second argument to our coderef (the first is the storage object itself, in case you happen to need that). Once we have the database handle, the rest of the code is exactly the same as if we were using plain DBI instead of DBIx::Class. We first prepare our query (which need not be a SELECT; it could be EXEC or anything else), execute it and, finally, process the result. The map statement converts the returned data to the form we expect it in, a list of names (instead of a list of rows each containing a single name). Note that the return statement in the coderef returns to dbh_do, not to the caller of get_users. This means that you can execute dbh_do as many times as required and then further process the results before returning from the get_users subroutine. Once you've written this subroutine, you can easily call it from elsewhere in your application:     my @users = $c->model('AddressDB')->get_users;    $c->response->body('All Users' join ', ', @users); Custom Methods Without Raw SQL As the above example doesn't use any features of the database that DBIC doesn't explicitly expose in its resultset interface, let us see how we can implement the get_users function without using dbh_do. Although the preconditions of the example indicated that we couldn't use DBIC, it's good to compare the two approaches so you can decide which way to do things in your application. Here's another way to implement the above example:     sub get_users { # version 2        my $self = shift;        my $users = $self->resultset('User');        my @result;        while(my $user = $users->next){                push @result, $user->username;        }        return @result;    } This looks like the usual DBIC manipulation that we're used to. (Usually we call $c->model('AddressDB::User') to get the "User" resultset, but under the hood this is the same as $c->model('AddressDB')->resultset('User'). In this example, $self is the same as $c->model('AddressDB').) The above code is cleaner and more portable (across database systems) than the dbh_do method, so it's best to prefer resultsets over dbh_do unless there's absolutely no other way to achieve the functionality you desire. Calling Database Functions Another common problem is the need to call database functions on tables that you're accessing with DBIC. Fortunately, DBIC provides syntax for this case, so we won't need to write any SQL manually and run it with dbh_do. All that's required is a second argument to search. For example, if we want to get the count of all users in the user table, we could write (in a controller) the following:     $users = $c->model('AddressDB::User');    $users->search({}, { select => [ { COUNT => 'id' } ],                                                    as => [ 'count' ],});    $count = $users->first->get_column('count'); This is the same as executing SELECT COUNT(id) FROM user, fetching the first row and then setting $count to the first column of that row. Note that we didn't specify a WHERE clause, but if we wanted to, we could replace the first {} with the WHERE expression, and then get the count of matching rows. Here's a function that we can place in the User ResultSetClass to get easy access to the user count:     sub count_users_where {        my $self = shift;        my $condition = shift;        $self->search($condition,                { select => [ { COUNT => 'id' } ],                        as => [ 'count' ], });        my $first = $users->first;        return $first->get_column('count') if $first;        return 0; # if there is no "first" row, return 0    } Now, we can write something like the following:     $jons = $c->model('AddressDB::User')->        count_users_where([ username => {-like => '%jon%'}]); to get the number of jons in the database, without having to fetch every record and count them. If you only need to work with a single column, you can also use the DBIx::Class:: ResultSetColumn interface. Creating a Database Model from Scratch In some cases, you'll have no use for any of DBIC's functionality. DBIC might not work with your database, or perhaps you're migrating a legacy application that has well-tested database queries that you don't want to rewrite. In this sort of situation, you can write the entire database model manually. In the next example, we'll use Catalyst::Model::DBI to set up the basic DBI layer and the write methods (like we did above) to access the data in the model. As we have the AddressBook application working, we'll add a DBI model and write some queries against the AddressBook database. First, we need to create the model. We'll call it AddressDBI: $ perl script/addressbook_create.pl model AddressDBI DBI DBI:SQLite: database When you open the generated AddressBook::Model::AddressDBI file, you should see something like this:     package AddressBook::Model::AddressDBI;    use strict;    use base 'Catalyst::Model::DBI';    __PACKAGE__->config(            dsn => 'DBI:SQLite:database',            user => '',            password => '',            options => {},    );    1; # magic true value required Once you have this file, you can just start adding methods. The database handle will be available via $self->dbh, and the rest is up to you. Let's add a count_users function:     sub count_users {        my $self = shift;        my $dbh = $self->dbh;        my $rows = $dbh->            selectall_arrayref('SELECT COUNT(id) FROM user');        return $rows->[0]->[0]; # first row, then the first column    } Let's also add a test Controller so that we can see if this method works. First, create the Test controller by running the following command line: $ perl script/addressbook_create.pl controller Test And then add a quick test action as follows:     sub count_users : Local {        my ($self, $c) = @_;        my $count = $c->model('AddressDBI')->count_users();        $c->response->body("There are $count users."); } You can quickly see the output of this action by running the following command line:   $ perl script/addressbook_test.pl /test/count_users  There are 2 users. The myapp_test.pl script will work for any action, but it works best for test actions like this because the output is plain-text and will fit on the screen. When you're testing actual actions in your application, it's usually easier to read the page when you view it in the browser. That's all there is to it—just add methods to AddressDBI until you have everything you need. The only other thing you might want to do is to add the database configuration to your config file. It works almost the same way for DBI as it does for DBIC::Schema:     ---    name: AddressBook    Model::AddressDBI:        dsn: "DBI:SQLite:database"        username: ~        password: ~            options:                option1: something                # and so on    # the rest of your config file goes here Implementing a Filesystem Model In this final example, we'll build an entire model from scratch without even the help of a model base class like Catalyst::Model::DBI. Before you do this for your own application, you should check the CPAN to see if anyone's done anything similar already. There are currently about fifty ready-to-use model base classes that abstract data sources like LDAP servers, RSS readers, shopping carts, search engines, Subversion, email folders, web services and even YouTube. Expanding upon one of these classes will usually be easier than writing everything yourself. For this example, we'll create a very simple blog application. To post the blog, you just write some text and put it in a file whose name is the title you want on the post. We'll write a filesystem model from scratch to provide the application with the blog posts. Let's start by creating the app's skeleton:   $ catalyst.pl Blog After that, we'll create our Filesystem model:   $ cd Blog  $ perl script/blog_create.pl model Filesystem We'll also use plain TT for the View:   $ perl script/blog_create.pl view TT TT
Read more
  • 0
  • 0
  • 2456

article-image-photoshop-foundation-difference-between-vector-and-bitmap-graphics
Packt
23 Oct 2009
4 min read
Save for later

Photoshop Foundation - The Difference between Vector and Bitmap Graphics

Packt
23 Oct 2009
4 min read
Introduction Welcome to the first in a new series of articles on Photoshop - the Photoshop Foundations series. The aim of this series is to give both beginners and more experienced users all the information they need to use Photoshop as efficiently as possible. Photoshop is a huge application, and there is usually more than one way to look at a given subject, or perform a certain action. This series aims to both, guide you through the more confusing aspects of Photoshop and show you the very best ways to use this application. In this first article we are going to look at the difference between vector and bitmap graphics, which is one of the most important principles to understand when working with graphics on a computer, inside or outside of Photoshop. Although Photoshop primarily is a bitmap image editor, it is capable of handling vector graphics to a certain extent. This can be a little confusing for people new to creating graphics on a computer, but by the end of this article you should have a clear idea of the difference between these two types of graphics. Bitmap Graphics Bitmap graphics are made up of colored pixels. Pixels are very small rectangles (usually square, although in some video applications they are wider than they are tall) of varying colors that once put together give you an image. You can see from the example below that zooming in on a bitmap image reveals the pixels that make up the image when viewed at 100%.   Bitmap graphics are usually (but not always) photographic in nature, capable of subtle graduated tones - often in the range of millions of colors per image. The problem with bitmap graphics is that they don't enlarge well as Photoshop needs to guess what color the extra pixels should be - this can result is loss of definition and a dramatic lowering in quality, depending on how much you enlarge the image. Common file formats for bitmap image data include GIF, JPEG and PNG for Internet usage and TIFF for print usage. As you can see from the example below, physically enlarging an image will degrade quality. Pixels are also used to display the image on your computer screen. Common pixel dimensions of computer displays are 1024 wide by 768 high and 1600 wide by 1200 high. The size of a bitmap graphic when viewed on your computer screen is defined by the number of pixels that make up the image - so an image that is 50 pixels wide will look very small on your screen at 100% viewing percentage, whereas an image that is 4000 pixels wide will be larger than your screen at 100% viewing percentage. The printable dimensions of an image are defined by the DPI (dots per inch) - this information is invisibly embedded in the image file. Digital cameras often embed information such as this, that may include the conditions the image was taken in, and even the camera model used. This information is not actually visible in the image, and requires software such as Photoshop to read it. You should not confuse the output DPI of your printer with this figure, which may range from 600-2400DPI - this refers to the density of the dots of ink laid down on the page by the printer. You don't have to prepare your images to 2400 DPI to get the best results - in fact doing so will significantly slow down printing as your file could potentially be huge! Often an image DPI in the range of 175-250 will give very good results on home printers. Images prepared for high quality commercial print are usually prepared at 300 DPI for up to A3 in size; whereas very large images (for instance on billboards) can be as low as 50 DPI, as they are not made to be viewed as closely as a magazine or small poster. There is no need to go above 300 DPI when creating images as you will yield virtually no improvement in output quality, only increasing the size of your file when saved. It is easy to understand the relationship between pixel dimensions and DPI - put simply, the DPI is how many pixels will be printed in an inch - so you could actually think of DPI as PPI (pixels per inch). Indeed, many experts believe this to be the true definition of DPI, and that Photoshop should refer to it as such. However, the term DPI is used throughout the professional print industry, so this is why it is referred to as DPI in Photoshop, not PPI.
Read more
  • 0
  • 0
  • 12849
article-image-customizing-default-theme-drupal
Packt
23 Oct 2009
3 min read
Save for later

Customizing the Default Theme in Drupal

Packt
23 Oct 2009
3 min read
Let's look at the default theme (garland) and customize it. We can customize the following features: Color scheme, either based on a color set, or by changing the individual colors If certain elements, such as the logo, are displayed The logo The favicon Back in the Themes section of the Administer area, there is a configure link next to each theme; if we click this we are taken to the theme's configuration page. Although Doug ideally wants a new theme that is unique to his website, he also wants to have a look at a few different options for the default theme. In particular, he wants to add his company's logo to the website and try a number of red color schemes as those are his corporate colors. Color Scheme The color scheme settings are quite intuitive and easy to change. We can either: Select a color set Change each color by entering the hexadecimal color codes (the # followed by 6 characters) Select the colors from the color wheel To change a color using the color wheel, we need to click on the color type (base color, link color, etc.) to select it and then chose the general color from the wheel and the shade of the color from the square within. When we change the colors or color set, the preview window below the settings automatically updates to reflect the color change. The following color sets are available: Blue Lagoon (the default set) Ash Aquamarine Belgian Chocolate Bluemarine Citrus Blast Cold Day Greenbeam Meditarrano Mercury Nocturnal Olivia Pink Plastic Shiny Tomato Teal Top Custom Quite a number of these are red-based color schemes, let's look into them, they are: Belgian Chocolate Meditarrano Shiny Tomato Belgian Chocolate Color Set The Belgian Chocolate color set uses a dark red header with a gradient starting with black flowing into a dark red color. The page's background is a cream color and the main content area has a white background as illustrated by the picture below: Mediterrano Color Set The Mediterrano color set uses a lighter red color where the gradient in the header starts with a light orange color which then flows into a light red color. Similar to the Belgian Chocolate color scheme the background is cream in color with a white background for the content area. Shiny Tomato Color Set The Shiny Tomato color set has a gradient header that starts with deep red and flows into a bright red color. The page's background is light grey with white background for the main content area, reflecting a professional image. The Shiny Tomato color set uses a red scheme which is in Doug's logo and he feels this set is the most professional of the three and wants us to use that.  
Read more
  • 0
  • 0
  • 1733

article-image-enhancing-user-interface-ajax
Packt
22 Oct 2009
32 min read
Save for later

Enhancing the User Interface with Ajax

Packt
22 Oct 2009
32 min read
Since our project is a Web 2.0 application, it should be heavily focused on the user experience. The success of our application depends on getting users to post and share content on it. Therefore, the user interface of our application is one of our major concerns. This article will improve the interface of our application by introducing Ajax features, making it more user-friendly and interactive. Ajax and Its Advantages Ajax, which stands for Asynchronous JavaScript and XML, consists of the following technologies: HTML and CSS for structuring and styling information. JavaScript for accessing and manipulating information dynamically. XMLHttpRequest, which is an object provided by modern browsers for exchanging data with the server without reloading the current web page. A format for transferring data between the client and server. XML is sometimes used, but it could be HTML, plain text, or a JavaScript-based format called JSON. Ajax technologies let code on the client-side exchange data with the server behind the scenes, without having to reload the entire page each time the user makes a request. By using Ajax, web developers are able to increase the interactivity and usability of web pages. Ajax offers the following advantages when implemented in the right places: Better user experience. With Ajax, the user can do a lot without refreshing the page, which brings web applications closer to regular desktop applications. Better performance. By exchanging only the required data with the server, Ajax saves bandwidth and increases the application's speed. There are numerous examples of web applications that use Ajax. Google Maps and Gmail are perhaps two of the most prominent examples. In fact, these two applications played an important role in spreading the adoption of Ajax, because of the success that they enjoyed. What sets Gmail from other web mail services is its user interface, which enables users to manage their emails interactively without waiting for a page reload after every action. This creates a better user experience and makes Gmail feel like a responsive and feature-rich application rather than a simple web site. This article explains how to use Ajax with Django so as to make our application more responsive and user friendly. We are going to implement three of the most common Ajax features found in web applications today. But before that, we will learn about the benefits of using an Ajax framework as opposed to working with raw JavaScript functions. Using an Ajax Framework in Django In this section we will choose and install an Ajax framework in our application. This step isn't entirely necessary when using Ajax in Django, but it can greatly simplify working with Ajax. There are many advantages to using an Ajax framework: JavaScript implementations vary from browser to browser. Some browsers provide more complete and feature-rich implementations, whereas others contain implementations that are incomplete or don't adhere to standards. Without an Ajax framework, the developer must keep track of browser support for the JavaScript features that they are using, and work around the limitations that are present in some browser implementations of JavaScript. On the other hand, when using an Ajax framework, the framework takes care of this for us; it abstracts access to the JavaScript implementation and deals with the differences and quirks of JavaScript across browsers. This way, we concentrate on developing features instead of worrying about browser differences and limitations. The standard set of JavaScript functions and classes is a bit lacking for fully fledged web application development. Various common tasks require many lines of code even though they could have been wrapped in simple functions. Therefore, even if you decide not to use an Ajax framework, you will find yourself having to write a library of functions that encapsulates JavaScript facilities and makes them more usable. But why reinvent the wheel when there are many excellent Open Source libraries already available? Ajax frameworks available on the market today range from comprehensive solutions that provide server-side and client-side components to light-weight client-side libraries that simplify working with JavaScript. Given that we are already using Django on the server-side, we only want a client-side framework. In addition, the framework should be easy to integrate with Django without requiring additional dependencies. And finally, it is preferable to pick a light and fast framework. There are many excellent frameworks that fulfil our requirements, such as Prototype, the Yahoo! UI Library and jQuery. I have worked with them all and they are all great. But for our application, I'm going to pick jQuery, because it's the lightest of the three. It also enjoys a very active development community and a wide range of plugins. If you already have experience with another framework, you can continue using it during this article. It is true that you will have to adapt the JavaScript code in this article to your framework, but Django code on the server-side will remain the same no matter which framework you choose. Now that you know the benefits of using an Ajax framework, we will move to installing jQuery into our project. Downloading and Installing jQuery One of the advantages of jQuery is that it consists of a single light-weight file. To download it, head to http://jquery.com/ and choose the latest version (1.2.3 at the time of writing). You will find two choices: Uncompressed version: This is the standard version that I recommend you to use during development. You will get a .js file with the library's code in it. Compressed version: You will also get a .js file if you download this version. However, the code will look obfuscated. jQuery developers produce this version by applying many operations on the uncompressed .js file to reduce its size, such as removing white spaces and renaming variables, as well as many other techniques. This version is useful when you deploy your application, because it offers exactly the same features as the uncompressed one, but with a smaller file size. I recommend the uncompressed version during development because you may want to look into jQuery's code and see how a particular method works. However, the two versions offer exactly the same set of features, and switching from one to another is just a matter of replacing one file. Once you have the jquery-xxx.js file (where xxx is the version number), rename it to jquery.js and copy it to the site_media directory of our project (Remember that this directory holds static files which are not Python code). Next, you will have to include this file in the base template of our site. This will make jQuery available to all of our project pages. To do so, open templates/base.html and add the highlighted code to the head section in it: <head> <title>Django Bookmarks | {% block title %}{% endblock %}</title> <link rel="stylesheet" href="/site_media/style.css"type="text/css" /> <script type="text/javascript"src="/site_media/jquery.js"></script></head> To add your own JavaScript code to an HTML page, you can either put the code in a separate .js file and link it to the HTML page by using the script tag as above, or write the code directly in the body of a script tag: <script type="text/javascript"> // JavaScript code goes here.</script> The first method, however, is recommended over the second one, because it helps keep the source tree organized by putting HTML and JavaScript code in different files. Since we are going to write our own .js files during this article, we need a way to link .js files to templates without having to edit base.html every time. We will do this by creating a template block in the head section of the base.html template. When a particular page wants to include its own JavaScript code, this block may be overridden to add the relevant script tag to the page. We will call this block external, because it is used to link external files to pages. Open templates/base.html and modify its head section as follows: <head> <title>Django Bookmarks | {% block title %}{% endblock %}</title> <link rel="stylesheet" href="/site_media/style.css" type="text/css"/> <script type="text/javascript" src="/site_media/jquery.js"> </script> {% block external %}{% endblock %}</head> And we have finished. From now on, when a view wants to use some JavaScript code, it can link a JavaScript file to its template by overriding the external template block. Before we start to implement Ajax enhancements in our project, let's go through a quick introduction to the jQuery framework. The jQuery JavaScript Framework jQuery is a library of JavaScript functions that facilitates interacting with HTML documents and manipulating them. The library is designed to reduce the time and effort spent on writing code and achieving cross-browser compatibility, while at the same time taking full advantage of what JavaScript offers to build interactive and responsive web applications. The general workflow of using jQuery consists of two steps: Select an HTML element or a group of elements to work on. Apply a jQuery method to the selected group Element Selectors jQuery provides a simple approach to select elements; it works by passing a CSS selector string to a function called $. Here are some examples to illustrate the usage of this function: If you want to select all anchor (<a>) elements on a page, you can use the following function call: $("a") If you want to select anchor elements which have the .title CSS class, use $("a.title") To select an element whose ID is #nav, you can use $("#nav") To select all list item (<li>) elements inside #nav, use $("#nav li") And so on. The $() function constructs and returns a jQuery object. After that, you can call methods on this object to interact with the selected HTML elements. jQuery Methods jQuery offers a variety of methods to manipulate HTML documents. You can hide or show elements, attach event handlers to events, modify CSS properties, manipulate the page structure and, most importantly, perform Ajax requests. Before we go through some of the most important methods, I highly recommend using the Firefox web browser and an extension called Firebug to experiment with jQuery. This extension provides a JavaScript console that is very similar to the interactive Python console. With it, you can enter JavaScript statements and see their output directly without having to create and edit files. To obtain Firebug, go to http://www.getfirebug.com/, and click on the install link. Depending on the security settings of Firefox, you may need to approve the website as a safe source of extensions. If you do not want to use Firefox for any reason, Firebug's website offers a "lite" version of the extension for other browsers in the form of a JavaScript file. Download the file to the site_media directory, and then include it in the templates/base.html template as we did with jquery.js: <head> <title>Django Bookmarks | {% block title %}{% endblock %}</title> <link rel="stylesheet" href="/site_media/style.css" type="text/css"/> <script type="text/javascript" src="/site_media/firebug.js"> </script> <script type="text/javascript" src="/site_media/jquery.js"> </script> {% block external %}{% endblock %}</head> To experiment with the methods outlined in this section, launch the development server and navigate to the application's main page. Open the Firebug console by pressing F12, and try selecting elements and manipulating them. Hiding and Showing Elements Let's start with something simple. To hide an element on the page, call the hide() method on it. To show it again, call the show() method. For example, try this on the navigation menu of your application: >>> $("#nav").hide()>>> $("#nav").show() You can also animate the element while hiding and showing it. Try the fadeOut(), fadeIn(), slideUp() or slideDown() methods to see two of these animated effects. Of course, these methods (like all other jQuery methods) also work if you select more than one element at once. For example, if you open your user page and enter the following method call into the Firebug console, all of the tags will disappear: >>> $('.tags').slideUp() Accessing CSS Properties and HTML Attributes Next, we will learn how to change CSS properties of elements. jQuery offers a method called css() for performing CSS operations. If you call this method with a CSS property name passed as a string, it returns the value of this property: >>> $("#nav").css("display") Result: "block" If you pass a second argument to this method, it sets the specified CSS property of the selected element to the additional argument: >>> $("#nav").css("font-size", "0.8em") Result: <div id="nav" style="font-size: 0.8em;"> In fact, you can manipulate any HTML attribute and not just CSS properties. To do so, use the attr() method which works in a similar way to css(). Calling it with an attribute name returns the attribute value, whereas calling it with an attribute name/value pair sets the attribute to the passed value. To test this, go to the bookmark submission form and enter the following into the console: >>> $("input").attr("size", "48") Results: <input id="id_url" type="text" size="48" name="url"> <input id="id_title" type="text" size="48" name="title"> <input id="id_tags" type="text" size="48" name="tags"> (Output may slightly differ depending on the versions of Firefox and Firebug). This will change the sizes of all input elements on the page at once to 48. In addition, there are shortcut methods to get and set commonly used attributes, such as val() which returns the value of an input field when called without arguments, and sets this value to an argument if you pass one. There is also html() which controls the HTML code inside an element. Finally, there are two methods that can be used to attach or detach a CSS class to an element; they are called addClass() and removeClass(). A third method is provided to toggle a CSS class, and it is called toggleClass(). All of these class methods take the name of the class to be changed as a parameter. Manipulating HTML Documents Now that you are comfortable with manipulating HTML elements, let's see how to add new elements or remove existing elements. To insert HTML code before an element, use the before() method, and to insert code after an element, use the after() method. Notice how jQuery methods are well-named and very easy to remember! Let's test these methods by inserting parentheses around tag lists on the user page. Open your user page and enter the following in the Firebug console: >>> $(".tags").before("<strong>(</strong>")>>> $(".tags").after("<strong>)</strong>") You can pass any string you want to - before() or after() - the string may contain plain text, one HTML element or more. These methods offer a very flexible way to dynamically add HTML elements to an HTML document. If you want to remove an element, use the remove() method. For example: $("#nav").remove() Not only does this method hide the element, it also removes it completely from the document tree. If you try to select the element again after using the remove() method, you will get an empty set: >>> $("#nav") Result: [] Of course, this only removes the elements from the current instance of the page. If you reload the page, the elements will appear again. Traversing the Document Tree Although CSS selectors offer a very powerful way to select elements, there are times when you want to traverse the document tree starting from a particular element. For this, jQuery provides several methods. The parent() method returns the parent of the currently selected element. The children() method returns all the immediate children of the selected element. Finally, the find() method returns all the descendants of the currently selected element. All of these methods take an optional CSS selector string to limit the result to elements that match the selector. For example, $("#nav").find ("li") returns all the <li> descendants of #nav. If you want to access an individual element of a group, use the get() method which takes the index of the element as a parameter. $("li").get(0) for example returns the first <li> element out of the selected group. Handling Events Next, we will learn about event handlers. An event handler is a JavaScript function that is invoked when a particular event happens, for example, when a button is clicked or a form is submitted. jQuery provides a large set of methods to attach handlers to events; events of particular interest in our application are mouse clicks and form submissions. To handle the event of clicking on an element, we select this element and call the click() method on it. This method takes an event handler function as a parameter. Let's try this using the Firebug console. Open the main page of the application, and insert a button after the welcome message: >>> $("p").after("<button id="test-button">Click me!</button>") (Notice that we had to escape the quotations in the strings passed to the after() method.) If you try to click this button, nothing will happen, so let's attach an event handler to it: >>> $("#test-button").click(function () { alert("You clicked me!"); }) Now, when you click the button, a message box will appear. How did this work? The argument that we passed to click() may look a bit complicated, so let's examine it again: function () { alert("You clicked me!"); } This appears to be a function declaration but without a function name. Indeed, this construct creates what is called an anonymous function in JavaScript terminology, and it is used when you need to create a function on the fly and pass it as an argument to another function. We could have avoided using anonymous functions and declared the event handler as a regular function: >>> function handler() { alert("You clicked me!"); }>>> $("#test-button").click(handler) The above code achieves the same effect, but the first one is more concise and compact. I highly recommend you to get used to anonymous functions in JavaScript (if you are not already), as I'm sure you will appreciate this construct and find it more readable after using it for a while. Handling form submissions is very similar to handling mouse clicks. First, you select the form, and then you call the submit() method on it and pass the handler as an argument. We will use this method many times while adding Ajax features to our project in later sections. Sending Ajax Requests Before we finish this section, let's talk about Ajax requests. jQuery provides many ways to send Ajax requests to the server. There is, for example, the load() method which takes a URL and loads the page at this URL into the selected element. There are also methods for sending GET or POST requests, and receiving the results. We will examine these methods in more depth while implementing Ajax features in our project. What Next? This wraps up our quick introduction to jQuery. The information provided in this section will be enough to continue with this article, and once you finish the article, you will be able to implement many interesting Ajax features on your own. But please keep in mind that this jQuery introduction is only the tip of the iceberg. If you want a comprehensive treatment of the jQuery framework, I highly recommend the book "Learning jQuery" from Packt Publishing, as it covers jQuery in much more detail. You can find out more about the book at: http://www.packtpub.com/jQuery Implementing Live Searching of Bookmarks We will start introducing Ajax into our application by implementing live searching. The idea behind this feature is simple: when the user types a few keywords into a text field and clicks search, a script works behind the scenes to fetch search results and present them on the same page. The search page does not reload, thus saving bandwidth, and providing a better and more responsive user experience. Before we start implementing this, we need to keep in mind an important rule while working with Ajax: write your application so that it works without Ajax, and then introduce Ajax to it. If you do so, you ensure that everyone will be able to use your application, including users who don't have JavaScript enabled and those who use browsers without Ajax support. Implementing Searching So before we work with Ajax, let's write a simple view that searches bookmarks by title. First of all, we need to create a search form, so open bookmarks/forms.py and add the following class to it: class SearchForm(forms.Form): query = forms.CharField( label='Enter a keyword to search for', widget=forms.TextInput(attrs={'size': 32})) As you can see, it's a pretty straightforward form class with only one text field. This field will be used by the user to enter search keywords. Next, let's create a view for searching. Open bookmarks/views.py and enter the following code into it: def search_page(request): form = SearchForm() bookmarks = [] show_results = False if request.GET.has_key('query'): show_results = True query = request.GET['query'].strip() if query: form = SearchForm({'query' : query}) bookmarks = Bookmark.objects.filter (title__icontains=query)[:10] variables = RequestContext(request, { 'form': form, 'bookmarks': bookmarks, 'show_results': show_results, 'show_tags': True, 'show_user': True })return render_to_response('search.html', variables) Apart from a couple of method calls, the view should be very easy to understand. We first initialize three variables, form which holds the search form, bookmarks which holds the bookmarks that we will display in the search results, and show_results which is a Boolean flag. We use this flag to distinguish between two cases: The search page was requested without a search query. In this case, we shouldn't display any search results, not even a "No bookmarks found" message. The search page was requested with a search query. In this case, we display the search results, or a "No bookmarks found" message if the query does not match any bookmarks. We need the show_results flag because the bookmarks variable alone is not enough to distinguish between the above two cases. bookmarks will empty when the search page is requested without a query, and it will also be empty when the query does not match any bookmarks. Next, we check whether a query was sent by calling the has_key method on the request.GET dictionary: if request.GET.has_key('query'): show_results = True query = request.GET['query'].strip() if query: form = SearchForm({'query' : query}) bookmarks = Bookmark.objects.filter(title__icontains=query)[:10] We use GET instead of POST here because the search form does not create or change data; it merely queries the database, and the general rule is to use GET with forms that query the database, and POST with forms that create, change or delete records from the database. If a query was submitted by the user, we set show_results to True and call strip() on the query string to ensure that it contains non-whitespace characters before we proceed with searching. If this is indeed the case, we bind the form to the query and retrieve a list of bookmarks that contain the query in their title. Searching is done by using a method called filter in Bookmark.objects. This is the first time that we have used this method; you can think of it as the equivalent of a SELECT statements in Django models. It receives the search criteria in its arguments and returns search results. The name of each argument must adhere to the following naming convention: field__operator Note that field and operator are separated by two underscores: field is the name of the field that we want to search by and operator is the lookup method that we want to use. Here is a list of the commonly-used operators: exact: The value of the argument is an exact match of the field. contains: The field contains the value of the argument. startswith: The field starts with the value of the argument. lt: The field is less than the value of the argument. gt: The field is greater than the value of the argument. Also, there are case-insensitive versions of the first three operators: iexact, icontains and istartswith. After this explanation of the filter method, let's get back to our search view. We use the icontains operator to get a list of bookmarks that match the query and retrieve the first ten items using Python's list slicing syntax. Finally we pass all the variables to a template called search.html to render the search page. Now create the search.html template in the templates directory with the following content: {% extends "base.html" %}{% block title %}Search Bookmarks{% endblock %}{% block head %}Search Bookmarks{% endblock %}{% block content %}<form id="search-form" method="get" action="."> {{ form.as_p }} <input type="submit" value="search" /></form><div id="search-results"> {% if show_results %} {% include 'bookmark_list.html' %} {% endif %}</div>{% endblock %} The template consists of familiar aspects that we have used before. We build the results list by including the bookmark_list.html like we did when building the user and tag pages. We gave the search form an ID, and rendered the search results in a div identified by another ID so that we can interact with them using JavaScript later. Notice how many times the include template tag saved us from writing additional code? It also lets us modify the look of the bookmarks list by editing a single file. This Django template feature is indeed very helpful in organizing and managing templates. Before you test the new view, add an entry for it in urls.py: urlpatterns = patterns('', # Browsing (r'^$', main_page), (r'^user/(w+)/$', user_page), (r'^tag/([^s]+)/$', tag_page), (r'^tag/$', tag_cloud_page), (r'^search/$', search_page),) Now test the search view by navigating to http://127.0.0.1:8000/search/ and experiment with it. You can also add a link to it in the navigation menu if you want; edit templates/base.html and add the highlighted code: <div id="nav"> <a href="/">home</a> | {% if user.is_authenticated %} <a href="/save/">submit</a> | <a href="/search/">search</a> | <a href="/user/{{ user.username }}/"> {{ user.username }}</a> | <a href="/logout/">logout</a> {% else %} <a href="/login/">login</a> | <a href="/register/">register</a> {% endif %}</div> We now have a functional (albeit very basic) search page. Thanks to our modular code, the task will turn out to be much simpler than it may seem. Implementing Live Searching To implement live searching, we need to do two things: Intercept and handle the event of submitting the search form. This can be done using the submit() method of jQuery. Use Ajax to load the search results in the back scenes, and insert them into the page. This can be done using the load() method of jQuery as we will see next. jQuery offers a method called load() that retrieves a page from the server and inserts its contents into the selected element. In its simplest form, the function takes the URL of the remote page to be loaded as a parameter. First of all, let's modify our search view a little so that it only returns search results without the rest of the search page when it receives an additional GET variable called ajax. We do so to enable JavaScript code on the client-side to easily retrieve search results without the rest of the search page HTML. This can be done by simply using the bookmark_list.html template instead of search.html when request.GET contains the key ajax. Open bookmarks/views.py and modify search_page (towards the end) so that it becomes as follows: def search_page(request): [...] variables = RequestContext(request, { 'form': form, 'bookmarks': bookmarks, 'show_results': show_results, 'show_tags': True, 'show_user': True }) if request.GET.has_key('ajax'): return render_to_response('bookmark_list.html', variables) else: return render_to_response('search.html', variables) Next, create a file called search.js in the site_media directory and link it to templates/search.html like this: {% extends "base.html" %}{% block external %} <script type="text/javascript" src="/site_media/search.js"> </script>{% endblock %}{% block title %}Search Bookmarks{% endblock %}{% block head %}Search Bookmarks{% endblock %}[...] Now for the fun part! Let's create a function that loads search results and inserts them into the corresponding div. Write the following code into site_media/search.js: function search_submit() { var query = $("#id_query").val(); $("#search-results").load( "/search/?ajax&query=" + encodeURIComponent(query) ); return false;} Let's go through this function line by line: The function first gets the query string from the text field using the val() method. We use the load() method to get search results from the search_page view, and insert the search results into the #search-results div. The request URL is constructed by first calling encodeURIComponent on query, which works exactly like the urlencode filter we used in Django templates. Calling this function is important to ensure that the constructed URL remains valid even if the user enters special characters into the text field such as &. After escaping query, we concatenate it with /search/?ajax&query=. This URL invokes the search_page view and passes the GET variables ajax and query to it. The view returns search results, and the load() method in turn loads the results into the #search-results div. We return false from the function to tell the browser not to submit the form after calling our handler. If we don't return false in the function, the browser will continue to submit the form as usual, and we don't want that. One little detail remains; where and when to attach search_submit to the submit event of the search form? A rule of a thumb when writing JavaScript is that we cannot manipulate elements in the document tree before the document finishes loading. Therefore, our function must be invoked as soon as the search page is loaded. Fortunately for us, jQuery provides a method to execute a function when the HTML document is loaded. Let's utilize it by appending the following code to site_media/search.js: $(document).ready(function () { $("#search-form").submit(search_submit);}); $(document) selects the document element of the current page. Notice that there are no quotations around document; it's a variable provided by the browser, not a string. ready() is a method that takes a function and executes it as soon as the selected element finishes loading. So in effect, we are telling jQuery to execute the passed function as soon as the HTML document is loaded. We pass an anonymous function to the ready() method; this function simply binds search_submit to the submit event of the form #search-form. That's it. We've implemented live searching with less than fifteen lines of code. To test the new functionality, navigate to http://127.0.0.1:8000/search/, submit queries, and notice how the results are displayed without reloading the page: The information covered in this section can be applied to any form that needs to be processed in the back scenes without reloading the page. You can, for example, create a comment form with a preview button that loads the preview in the same page without reloading. In the next section, we will enhance the user page to let users edit their bookmarks in place, without navigating away from the user page. Editing Bookmarks in Place Editing of posted content is a very common task in web sites. It's usually implemented by offering an edit link next to content. When clicked, this link takes the user to a form located on another page where content can be edited. When the user submits the form, they are redirected back to the content page. Imagine, on the other hand, that you could edit content without navigating away from the content page. When you click edit, the content is replaced with a form. When you submit the form, it disappears and the updated content appears in its place. Everything happens on the same page; edit form rendering and submission are done using JavaScript and Ajax. Wouldn't such a workflow be more intuitive and responsive? The technique described above is called in-place editing. It is now finding its way into web applications and becoming more common. We will implement this feature in our application by letting the user edit their bookmarks in place on the user page. Since our application doesn't support the editing of bookmarks yet, we will implement this first, and then modify the editing procedure to work in place. Implementing Bookmark Editing We already have most of the parts that are needed to implement bookmark editing. This was easy to do thanks to the get_or_create method provided by data models. This little detail greatly simplifies the implementation of bookmark editing. Here is what we need to do: We pass the URL of the bookmark that we want to edit as a GET variable named url to the bookmark_save_page view. We modify bookmark_save_page so that it populates the fields of the bookmark form if it receives the GET variable. The form is populated with the data of the bookmark that corresponds to the passed URL. When the populated form is submitted, the bookmark will be updated as we explained earlier, because it will look like the user submitted the same URL another time. Before we implement the technique described above, let's reduce the size of bookmark_save_page by moving the part that saves a bookmark to a separate function. We will call this function _bookmark_save. The underscore at the beginning of the name tells Python not to import this function when the views module is imported. The function expects a request and a valid form object as parameters; it saves a bookmark out of the form data, and returns this bookmark. Open bookmarks/views.py and create the following function; you can cut and paste the code from bookmark_save_page if you like, as we are not making any changes to it except for the return statement at the end. def _bookmark_save(request, form): # Create or get link. link, dummy = Link.objects.get_or_create(url=form.clean_data['url']) # Create or get bookmark. bookmark, created = Bookmark.objects.get_or_create( user=request.user, link=link ) # Update bookmark title. bookmark.title = form.clean_data['title'] # If the bookmark is being updated, clear old tag list. if not created: bookmark.tag_set.clear() # Create new tag list. tag_names = form.clean_data['tags'].split() for tag_name in tag_names: tag, dummy = Tag.objects.get_or_create(name=tag_name) bookmark.tag_set.add(tag)# Save bookmark to database and return it.bookmark.save()return bookmark Now in the same file, replace the code that you removed from bookmark_save_page with a call to _bookmark_save: @login_requireddef bookmark_save_page(request): if request.method == 'POST': form = BookmarkSaveForm(request.POST) if form.is_valid(): bookmark = _bookmark_save(request, form) return HttpResponseRedirect( '/user/%s/' % request.user.username )else: form = BookmarkSaveForm()variables = RequestContext(request, { 'form': form})return render_to_response('bookmark_save.html', variables) The current logic in bookmark_save_page works like this: if there is POST data:Validate and save bookmark.Redirect to user page.else:Create an empty form.Render page. To implement bookmark editing, we need to slightly modify the logic as follows: if there is POST data: Validate and save bookmark. Redirect to user page.else if there is a URL in GET data: Create a form an populate it with the URL's bookmark.else: Create an empty form.Render page. Let's translate the above pseudo code into Python. Modify bookmark_save_page in bookmarks/views.py so that it looks like the following (new code is highlighted): from django.core.exceptions import ObjectDoesNotExist@login_requireddef bookmark_save_page(request): if request.method == 'POST': form = BookmarkSaveForm(request.POST) if form.is_valid(): bookmark = _bookmark_save(request, form) return HttpResponseRedirect( '/user/%s/' % request.user.username ) elif request.GET.has_key('url'): url = request.GET['url'] title = '' tags = '' try: link = Link.objects.get(url=url) bookmark = Bookmark.objects.get( link=link, user=request.user ) title = bookmark.title tags = ' '.join( tag.name for tag in bookmark.tag_set.all() ) except ObjectDoesNotExist: pass form = BookmarkSaveForm({ 'url': url, 'title': title, 'tags': tags }) else: form = BookmarkSaveForm() variables = RequestContext(request, { 'form': form }) return render_to_response('bookmark_save.html', variables) This new section of the code first checks whether a GET variable called url exists. If this is the case, it loads the corresponding Link and Bookmark objects of this URL, and binds all the data to a bookmark saving form. You may wonder why we load the Link and Bookmark objects in a try-except construct that silently ignores exceptions. Indeed, it's perfectly valid to raise an Http404 exception if no bookmark was found for the requested URL. But our code chooses to only populate the URL field in this situation, leaving the title and tags fields empty.
Read more
  • 0
  • 0
  • 11304

article-image-how-recover-active-directory-failure
Packt
22 Oct 2009
19 min read
Save for later

How to Recover from an Active Directory Failure

Packt
22 Oct 2009
19 min read
Active Directory (AD) failure, which includes corruption, is something that is dreaded by any administrator. Simply put, it means that the directory service can no longer read the Active Directory database that it has locally. This will prevent logon and authentication as well as any directory-dependent services. Essentially, it renders the domain controller (DC) useless. What's worse, though, is that several times, the replication keeps going so that the corrupted database spreads out to the other DCs. In this article by Florian Rommel, we will look at the different options and approaches available to recover a DC that has a database corruption. In addition, this article outlines, symptoms, causes, and solutions for this scenario. Problems and Symptoms Let's take a look at the symptoms and then the causes. Symptoms The database gets corrupted and the DC is no longer able to process and authenticate or perform directory lookups. This becomes apparent when replication fails for some reason, or a large amount of specific event log errors appear. Another symptom could be that the AD services don't start on the DC. Causes This scenario can be caused by:      A software glitch, which can due to upgrading the schema, but having customized schema entries      An unclean AD write      A replication that has been interrupted      An accidental or malicious change in the AD schema with low level tools such as ADSIedit or something similar. Solution Process The recovery process is as follows:      You will have to verify that it is, in fact, a failure or corruption within the AD database, and not a network-related problem or other problem.      You will then have to perform a specific directory restore mode recovery, where you have to decide between an authoritative and a non-authoritative mode.      Once the recovery is complete, you will need to verify whether the DC is replicating and functioning properly. Solution Details What follows is a complete outline of what to do during each part of the solution process. Verification of Corruption If the AD on a domain controller becomes corrupted, or stops replicating to other DC's, or both, find out the root cause for this. A good starting point is to check again that the DNS is in order, and revert any manual changes that may have been made recently. Also, ensure that it is not a network-related problem—this means that no routers and routes have been changed, or firewalls re-configured, and the connection is not down. These are more often than not the main causes, and not an actual corruption. If you can safely rule out those causes, you can use utilities such as ReplMon and DCDiag, which are included in the Windows 2003 support tools, available free from Microsoft's website or on your install CD. Although ReplMon is a graphical utility, it is pretty small, and one of the best tool for checking whether or not there are replication errors within an entire domain. It shows which DCs are not replicating and why. The other utility, DCDiag, scans every DC, and determines if and why they have replication and other errors. When you have checked that all other DCs replicate just fine, you should check the Event log for specific event IDs (467 and 1018), that only occur when you have a real database corruption, and the AD jet database, which AD uses, is unreadable. Tools for Verification The Windows 2003 support tools (found on your installation CD under the SUPPORT folder) and the Windows 2003 resource kit tools (found at http://www.microsoft.com/downloads/details.aspx?FamilyID=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en ) provide a variety of tools to verify if the DC is still operational, if there is actually a problem, and where the problem lies. Although the usage of these programs can also be described as part of an AD health check, in this case, we will focus on a single DC. The output from some of these tools is fairly long, and for brevity's sake, we will focus on only the relevant parts. ReplMon ReplMon, short for Replication Monitor, is essential in your arsenal of tools for detecting replication errors within a domain. It can also provide you with a good view of replication partners for each DC, and allows you to run a check against an entire domain for replication errors. The following screenshot shows the default ReplMon window, while the next screenshot shows the right-click action menu for a monitored DC. To run ReplMon,  simply type replmon from the command line. An example of where ReplMon becomes very useful is to detect errors while trying to replicate to other DCs. There are different errors for different scenarios. but, for example, a server that cannot be reached, or is offline, would show the following error in the domain-wide search for errors: DCDiag DCDiag is a command line utility that performs a full check of the DC as regards AD. These tests include forest DNS tests, to check that the DNS is okay at the forest level, domain DNS tests, to do the same on a domain level, configuration test, schema test, and the FSMO test to check that all FSMO servers are available. Running it is as simple as typing dcdiag at the command line, and watching the output. You might want to add an output file such as dcdiag > c:dcdiag_output.txt at the end, to save everything to a text file that you can read easily. A successful DCDiag test would have the following results: NetDiag and DNSDiag Both of these utilities check the network connectivity of the DC on which they are installed. DNSDiag is more geared towards Exchange, and checks that all of the essential DNS records are valid and are working, by pretending to follow the MX records. It also gives us a lot of output with regard to the domain DNS structure, and identifies if there are any problems. NetDiag checks the local networking stack. It tests all the installed protocols and services, including WINS, NetBT, and TCP/IP. Sonar The Sonar.exe utility provides a GUI, which shows exactly where a replication failed, and what the state of the File Replication Service (FRS) is on each of the DCs. This is particularly useful for large environments. It also has different views that allow you to troubleshoot your FRS on a DC. Options to Recover and Stop the Spread of Corruption If, in effect, you have a corrupted or failed AD database, and it hasn't spread yet, meaning it is only on one DC, you should remove that DC from the replication chain as quickly as possible. A good way is, of course, to disconnect the network connection, though this will have an impact if you are working remotely on the machine. The other option is to isolate the DC with firewall rules. This is also the safest way that still gives you access to the machine remotely. If neither option is available to you, you can use the Repadmin utility to stop outbound and inbound replication. The two options are repadmin /options DCNAME +DISABLE_INBOUND_REPL and repadmin /options DCNAME +DISABLE_OUTBOUND_REPL, where DCNAME is the name of the DC that should be disabled. If you want to enable the replication, simply retype the command with a - instead of a + so as repadmin /options DCNAME -DISABLE_INBOUND_REPL. The following screenshot shows the command line for disabling outbound replication with Repadmin. Please note that when you disable outbound replication, errors with Event ID 1115 will appear in the event log, just as errors with event ID 1113 will show when inbound replication is disabled. When either one is re-enabled, informative Event IDs 1116 (outbound) and 1114 (inbound) will appear in the event log. The fastest way to recover from a corrupted AD database is to forcefully demote the DC to become a member server, and to promote it again to replicate off of another DC, if there is one in the same network. You should take this step only if you are sure, and have verified that the AD is actually working on the other DC. You can use DCDiag to easily verify whether everything is correctly order. You should not replicate from another DC within the same site if you cannot verify whether the DC is actually operational. Make sure that the event log does not contain any Jet database, or FRS errors such as 1173, which would indicate an internal error in the Jet Database. You will have to perform a metadata cleanup if you force-demote the DC because a clean demotion is not an option. If you use Dcpromo normally, it will replicate its "change" out to another DC. In other words, you are spreading the corrupted or non-functioning database this way. You can easily force-demote it by running  dcpromo /forceremoval, and then do a metadata cleanup. Non-Authoritative and Authoritative Restore To speed up the data replication of the AD, for example, for sites that have a slow or saturated network connection, or to make the whole restore process much quicker, you should perform a non-authoritative restore of the AD database. Non-authoritative restore means that you are restoring a database, but that database will not assert authority in the AD. This means that it will take all changes that the replication partners send it, where an authoritative restore is set to be the master replication in your domain. It will restore itself, and give itself such high update sequence numbers to objects that every other DC in the domain replicates from it because it is assumed it has the newest copy. Effectively, you are restoring a backup of the AD database to its original location, overwriting the current database. This can only be done when you are in Directory Restore mode. After the database is restored, reboot your DC normally. If you have changed the boot order, reverse the order again. Please make sure your backups are run regularly, whether it is on tape or on disk, according to your organization's backup policy. After the restore is complete, and you reboot the DC, the next replication will be much faster because only changed objects will be replicated to the server. This is because it has most of the AD database already, depending on how much the AD has changed, and how recent the backup was. In a non-authoritative restore, data that is restored includes AD objects with original update sequence numbers. This requires a lot of caution, as any data that is restored with a non-authoritative restore will appear to be outdated to the AD replication system. Hence, the data will not get replicated to other domain controllers. You therefore run the risk of having the data overwritten from DCs that have not yet been restored to, and contain somewhat newer data, including other corruptions. Since this article is meant for a corruption or AD failure on a single DC only, with a replication partner relatively close by, we will skip the authoritative restore. The following figure illustrates how authoritative and non-authoritative restores work. Option One: Restoring AD from a Backup In order to restore an AD database on a Domain Controller, you have to go into the "Directory Services Restore" mode. To do this, reboot the DC, and at the boot prompt, which is where the boot process waits for a second before the splash screen of Windows 2003 comes up, press the F8 key, after which you will be presented with a menu similar to the following: Windows 2003 Advanced Options MenuPlease select an option: Safe ModeSafe Mode with NetworkingSafe Mode with Command Prompt Enable Boot LoggingEnable VGA ModeLast Known Good ConfigurationDirectory Services Restore Mode (Windows NT domain controllers only)Debugging Mode Use | and | to move the highlight to your choice.Press Enter to choose. At the menu, select the Directory Services Restore Mode, by moving up and down with the arrow keys on your keyboard, and then press ENTER. You will then be in the same menu again. Press ENTER again, after which, your display will show Directory Restore Mode at the bottom of the screen. Your DC will now boot, but no AD-related services will be started. Once booted, restore the AD/system state from a trusted and recent backup, and reboot the machine. After the machine is rebooted, you should have working AD services with a slightly outdated AD database. Wait for the replication to take effect, and your AD will be updated. No Physical Access to the Machine If you do not have physical access to the machine in question, you can achieve the same effect by editing the boot.ini file, which is located in c:. This file, bydefault, is hidden and may be write-protected. To see the hidden files and system files, you will need to change your Windows Explorer settings, as shown in the following screenshot. When you can see the file, check its properties, that is, whether it is read-only or not, by right-clicking on it and selecting Properties. However, instead of changing file permissions of system files, you can edit this quite easily with the Graphical User Interface. Some people are not comfortable editing protected files, and then you can also run into problems if you forget to un-mark or re-mark the read-only flag. To get to the GUI editor, right-click on My Computer in your Start menu, or click the icon with the server name in the right hand upper-right, and then select Properties. Once you have done this, the following screen will appear: On the properties window, go to the Advanced tab, as shown in the following screenshot: And then click on Settings in the Startup and Recovery section of the  Advanced tab, as shown in the screenshot below: In the resulting window, which contains quite a few options and sections, you can click on the  Edit button and the boot.ini file will be opened in Notepad. Once editing is completed, and the file has been saved and closed, all permissions will be reset to their original settings. The editing of this file is pretty straightforward, yet not easily understandable for someone who has never done this. In the following screenshot, you can see the actual file displayed: To edit the file in order to reboot in Directory Restore Mode, it is recommended to just copy and paste the last line again, and perform two changes:      Change the display name by adding something like Recovery.      Add the /safeboot:dsrepair at the end of the line, as shown in the following screenshot: When editing is done, save and close the file as you would with any other text file, and click  OK in the Startup and Recovery window so that it closes. To select the option on which you want to boot, click SETTINGS in the  Startup and Recovery section again. And As you can see in the following screenshot, you can now select the boot default from the drop-down menu in the opening window: Select the Recovery line and click OK. Then click OK again to reboot the server. It will now boot automatically into the Directory Restore Mode. Restoring from a Backup Once you are in Directory Restore Mode (DRM), you can use your company's backup software to recover the AD database. If you use Windows backup, you can safely backup and restore the system state of the server, as you can see in the following screenshot. This will allow you to fully revert to a completely working system. However, if you do not need to, or do not want to, recover the whole system state, you can easily choose to restore only AD from the list, as shown in the following screenshot. We assume that we have a complete backup of the server, and that we need to restore the AD database. You can restore the AD or system state to a different location, or overwrite the original files, depending on whether you want to perform an authoritative or a non-authoritative restore, or even an install from media. Option Two: Replication In case you have no valid recent backup media from which to restore the AD, or you need to act very fast, you can restore the AD by first force-demoting the DC in question with dcpromo /forceremoval. This way, demoting will prevent replication to any replication partners, but will successfully demote the DC from the domain. If you have any important Flexible Server Master Operations (FSMO) roles running on the DC that you are removing by force, you will be presented with the following warning: Make sure that you seize the FSMO roles after you have demoted the DC, or after you disconnect its network cable. Do not forget this though, as the FSMO roles are quite important. As you can see in the following screenshot, the Active Directory wizard will now proceed to remove AD without updating the forest. This means that it will not replicate out its own data or changes. Once demoted, reboot the server, and remove its leftover information from AD as a DC. Then, verify whether the network connection is fully functional, and promote the DC again with the same name. You should encounter no problems in completing the promotion. The AD will replicate to it during the next scheduled replication. If your AD is very large and contains many records or files, such as pictures in the user information, the replication to a "blank" DC will take a long time and stress your network quite heavily. The replication option in a larger environment with a large dataset can take along time if the other DC is either busy, or the replication link is not a very high-speed connection. The size of the AD databases can start from 1 GB and may run up to several gigabytes. If your DC has to replicate from a DC over a leased line, or an average consumer Internet class connection (2 to 8mbit), you may have to consider the other option of  restoring from a backup, or performing a non-authoritative restore. Option Three: Rebuild DC with Install from Media Starting from Windows 2000 SP3, there is a dcpromo option available called "IFM" (install from media). This option adds a step to the DC promotion that will pre-populate the AD database on the promoted DC from a recent system state backup of a working DC, which is restored to a disk, CD or DVD. IFM is the fastest option if you have to install, recover, or re-install a DC that is connected by only a slow link to its closest replication partner. As you are pre-populating the local AD, the replication changes it will get from its partner are much smaller, and therefore will replicate much faster. To use IFM, restore the backup to the server BEFORE you make it a DC, and before you run dcpromo. A good way to do this, if you want to, or need to, restore several DCs with IFM, is to restore it to a network drive.. To prepare a backup and restore for IFM, please see Microsoft's Knowledge base article http://support.microsoft.com/kb/311078 IFM can only be used for additional DCs within a domain, not for the first DC. You can also only pre-populate, or restore a DC with a backup of the domain that you are building in. A good option for this is to force-demote the DC, reboot it, and start it as a normal member server. You then need to clean up all of the records from the AD regarding the DC, and then perform an install from Media. Restore the most recent backup to a separate directory, for example, c:restore. This restore should be a restore of the System State of a DC, and the directory will contain sub-directories such as AD, Boot Files, Registry and so on. Once the restore has succeeded, click on Start, Run, and enter dcpromo /adv as shown: The /adv flag in the Dcpromo command gives you advanced options within Dcpromo, such as the install from media options. Follow the dialog as you would normally do for a dcpromo. However, when you see the screen regarding Copying Domain Information, select the option:  From these restored backup files, and navigate to and select the directory to which you restored the system state. As you can see in this screenshot, you also have the option to pre-populate immediately from an existing DC. This is, of course, an option, but if your link is slow, or if your replication partner is busy serving other partners, it will be much slower than from media. Not just for disaster recovery! If you have to install several DCs for any reason, the /adv switch can save you the time spent waiting for the first replication, if you have a fast link, or a backup, that you can use to pre-populate. When following the wizard, you will be asked if you want to make this DC a Global Catalog, and then you may be required to use an account with Administrator privileges in order to proceed with the dcpromo. Once the Dcpromo wizard completes its task, and the AD records are copied from a previous backup, at the next replication only the changes since the last backup will be replicated. This saves a lot of bandwidth and time. This is especially useful for sites with a slow or saturated network connection, where replication would take far too long, so if you need to recover or install fast, this is probably your best bet. Summary In this article, we looked at the recovery procedure in the event of a corruption or failure of the AD database on a single domain controller. This is a scenario that happens more frequently than one might expect. So, instead of "just re-installing", these options and procedures will help you get a healthy DC back in no time while limiting the amount of errors in the event log. We also looked at some tools that will help you not only diagnose AD problems, but also allow you to perform, when used regularly, AD health tests to make sure your AD is always in perfect working order. This way, you might prevent failures because you might find the symptoms earlier.  
Read more
  • 0
  • 0
  • 17690
article-image-modeling-orchestration-and-choreography-service-oriented-architecture
Packt
22 Oct 2009
30 min read
Save for later

Modeling Orchestration and Choreography in Service Oriented Architecture

Packt
22 Oct 2009
30 min read
  Choreography versus Orchestration Choreography and orchestration, in an SOA context, pertain to the use of processes that span multiple participants, with message traffic moving in all directions according to a complex set of rules. Choreography and orchestration are attempts to coordinate or control all of this activity. They attack the problem by putting rigor on how message exchanges are represented, and by organizing the overall process using the right set of control flow patterns. Use cases in this area can be inter-organizational (for example, B2B commerce involving buyer, seller, and wholesaler), or intra-organizational if the organization is large enough and the participants act as separate organizations (for example, bank account processes spanning the front office, the back office, and the fraud department). By convention, choreography describes the global protocol governing how individual participants interact with one another. Each participant has its own process, but choreography is a master process that acts as a kind of traffic cop. Significantly, the choreography process does not actually run. It is not a central broker in the live message exchange, but merely a message exchange protocol. If the participants follow the protocol, the live exchange will run as smoothly as if there were a central broker. 'Traffic cop' is not exactly right then; choreography is more like a set of traffic rules. To mix metaphors, choreography teaches the participant processes how to dance as a group. The process for each participant is referred to as an orchestration process, whose principal job is to build a flow of control around (that is, to orchestrate) its interactions with partners. Orchestration processes are difficult to model, especially those faced with complex combinations of inbound events. If the process is subject to choreography, its structure can be derived from the choreography; in fact, as we'll see, there are tools that can generate skeletal orchestration processes from choreography definitions. The idea is simple: the choreography tells the complete story, so the participant can determine its role by isolating the parts in which it's involved. Not all orchestrations, alas, have a choreography to guide them (not all inter-organizational domains have a precise protocol defined). If the use case is sufficiently complex, the participant ought to create its own choreography anyway, not to share with its partners but simply to improve its own understanding of its orchestration. An orchestration process has public and private activities. The public activities are those that are required by the choreography. Private activities are there to meet internal requirements, but are not visible to partners. The next figure shows the public activities of the orchestration process for an energy retailer. The steps shown (for example, Send Request to Distributor) are those required by the enrollment choreography, in which the retailer is but one participant. The next figure shows the same process with private steps (for example, Update Account) included. In the figure, steps marked with a P are public steps. We examine the energy example in detail in this article. Web Services Choreography Description Language (WS-CDL) is the leading choreography language; Business Process Execution Language (BPEL) is the dominant process orchestration language. Although these XML-based languages feature a similar flow-oriented design style, only BPEL is meant to have an actual runtime platform: BPEL processes run; WS-CDL choreographies are protocols. BPEL is better known than WS-CDL in part because orchestration is more prevalent than choreography. BPEL's user community is much larger than WS-CDL's. Today, every company is building an SOA platform, and if they don't use BPEL as their SOA orchestration language, they use something similar. The user community for choreography consists of industry committees that publish protocols such as the enrollment and funds transfer choreographies we discuss in this article. Choreography might also work as part of a large organization's enterprise architecture, helping to sort out the communication of the organization's numerous systems. Few of these committees use WS-CDL to document their protocols anyway. Choreography is more often documented less formally using English descriptions, flowchart diagrams, and an XML message schema. Examples-Energy Enrollment and Email Bank Transfer The two examples from industry that showcase our technique for modeling choreography and orchestration are the enrollment of customers with retailers in a deregulated energy market and the procedure for transferring funds by email between two banks. In the energy market for a state or small country there are three parties: customers (who use electricity to power their homes), retailers (who sell electricity to customers), and the distributor (who supplies the electricity). Before deregulation, the distributor sold electricity directly to customers; there were no retailers back then. Deregulation introduced competition and broke up the distributor's monopoly. Customers can now buy electricity from one of many competing retailers. The distributor is now merely a supplier, having moved out of the retail sales business. When a customer enrolls with a retailer, the retailer uses the following protocol to complete the enrollment: The retailer submits the customer's request for enrollment to the distributor. The distributor responds in one of the three ways. If there is a problem with the request (for example, the customer has another enrollment in progress, or the customer has been flagged for fraud), the distributor sends a rejection to the retailer. If the request is valid and the customer is not currently enrolled with a retailer, the distributor sends an acceptance to the retailer. If the customer is currently enrolled with a competing retailer but intends to switch, the distributor sends a notice of pending switch to both the retailers. In the acceptance case, there is a 10-day waiting period during which the customer may cancel the enrollment. To cancel, the customer contacts the retailer, who forwards the cancellation request to the distributor. Assuming the customer does not cancel, at the end of the waiting period, the distributor sends a completion event to the retailer. The customer is now enrolled with the retailer. In the switch case, there is also a 10-day waiting period. To cancel, the customer contacts the initiating retailer (that is, the retailer to whom the customer is switching). The initiating retailer forwards the cancellation to the distributor, who then sends completion events to both retailers indicating that the customer will remain enrolled with the original retailer. Assuming the customer does not cancel, at the end of the waiting period, the distributor sends completion events to both retailers indicating that the customer is now enrolled with the initiating retailer. Email bank transfer is a protocol for wiring money by email. It works as follows: The person sending the money contacts his bank (the Sender bank), specifying from which account to draw the funds, how much money to send, and the name and email address of the recipient. The Sender bank sets aside the amount and sends an email to the recipient with instructions on how to complete the transfer. The Sender bank sets aside the amount and sends an email to the recipient with instructions on how to complete the transfer. The Recipient bank submits the transfer request to the Sender bank. The Sender bank accepts, and the funds are moved into the recipient's account, completing the transfer. At any point, either the sender or recipient may cancel the transfer, and the transaction is automatically canceled if not completed within 30 days. On cancellation, the funds are returned to the sender's account. (We assume both banks are members of the email transfer programme.) The following figure shows the most common scenarios in these examples: Modeling Choreography in BPMN In BPMN, two possible models for choreography are as follows: Invisible hub: Although choreography is fundamentally decentralized, we imagine there is a central hub through which all messages pass, and model the choreography as the process of that hub. Sum of parts: The public process of each participant (that is, the process containing steps required by the choreography, with private steps omitted) is drawn in a swim lane. Message flow (dashed lines) is used to show inter-participant communication. A sum-of-parts model for the enrollment choreography is shown in next figure. There are three swim lanes in the diagram: one for the distributor (referred to as Distributor), one for the initiating retailer (referred to as Retailer), and one for the current retailer (referred to as CurrentRetailer). Each lane contains the public process of the participant. The dashed arrows show the flow of messages between participants. The enrollment choreography is, according to this approach, the combination of the public processes of each participant plus the message flow that connects them. The choreography begins when the customer enrolls with the retailer (Cust Enrolls in the Retailer lane). The retailer then submits the enrollment request to the distributor by calling Dist.enroll. This call sends a message to the distributor, which triggers the event Enroll (the first step in the Distributor lane). The Distributor process is now underway, and it responds to the enrollment request either by rejecting the request, accepting it, or notifying the initiating retailer and the current retailer of a pending switch. The distributor rejects by calling Ret.reject, and, as the dashed line signifies, triggers the event Dist.reject in the retailer. The remaining steps are straightforward. The sum-of-parts method is intuitive, and variations on it can be found in business process literature (in the WSCI and BPMN specifications, for example). Sum-of-parts, however, has two disadvantages. First, the message flow creates an indecipherable clutter, making complex choreographies almost impossible to read; the swim lanes, for their part, use a lot of real estate. Aesthetics aside, sum-of-parts fails to present a global, consolidated view of the choreography. We grow bug-eyed trying to keep track of what each participant is doing. We are forced to watch each dancer rather than the group as a whole. The invisible-hub representation is comparatively compact. The next figure, which shows the enrollment choreography as a hub, has fewer steps than the sum-of-parts equivalent, and it makes do without lanes or dashed lines. The hub works as you would expect a hub to work: it listens for inbound events and routes them to their intended recipients. The act of receiving an event and sending it elsewhere is a single unit of work (shown in the figure as a rounded box with a dashed line), known as an interaction. The hub choreography represents the communication of its participants as a process of interactions. Before walking through this process, consider the following notational conventions: The event with a thin border (Ret.enroll(Dist)) is the process' start event. Events with a double-line border (for example, Dist.reject(Ret)) are intermediate events, which occur over the course of the process. Intermediate events can be used in three ways: for cancellation, for deferred choice, or simply to wait for the next message before continuing. The enrollment hub has examples of the latter two forms. We'll come back to cancellation while discussing the email transfer hub. Deferred choice, also known as an event pick, uses a diamond containing an inscribed star (known as an event-based gateway) with arrows leading to a set of intermediate events. The intent is to wait for one of those events to occur, execute its activities, and discard the remaining events. There are three deferred choices in the enrollment hub. The first occurs in three steps, and selects one of the three events: Dis.reject(Ret), Dist.accept(ret), or Dist.pendingSwitch(Ret, CurrRet). If, say, Dist.reject(Ret) occurs first, the activity Ret1.reject(Dist) is executed. The intermediate event Dist.switchCompleteCurrent(Ret, CurrRet) simply waits for something to happen before continuing. This event is sandwiched between the activities Dist.cancel(Ret) and Ret.switchCompleteCurrent, CurrRet.switchCompleteCurrent(Dist). Thus, when the first activity completes, that branch of the process waits for the event to occur before continuing with the second activity. Events have labels of the form Sender.msg (Recipients), meaning that the event received by the hub is a message from the sender bound for the specified recipients. (There must be at least one.) Thus, Dist.switchCompleteCurrent(Ret, CurrRet) is the message switchCompleteCurrent from the distributor (Dist) to both the initiating retailer (Ret) and the current retailer (CurrRet). Send tasks (rounded boxes with a straight border) are labeled Recipient.msg(Sender), meaning that hub is sending the specified message to the recipient and is indicating that the message originated with the specified sender. In Dist.enroll(Ret), for instance, the hub sends the message enroll to the distributor (Dist), and is indicating to the distributor that this message came from the retailer (Ret). If the event that preceded it specifies multiple recipients, the send task sends the message to each recipient. Each send counts as one interaction.Ret.pendingSwitch, CurrRet.pendingSwitch (Dist), for example, sends the message pendingSwitch to both the retailer (Ret) and the current retailer (CurrRet), and thus spans two interactions. A rounded box with a dashed border, known in BPMN as a group, pairs up an event and a send task. Thus, the grouping of Ret.enroll(Dist) and Dist.enroll(Ret) means that when the hub receives the message enroll from the retailer bound for the distributor, it sends that message to the distributor, indicating to the distributor that the message originated with the retailer. A group that contains multiple interactions has a label in the top-center of the dashed box indicating the number of interactions. The number of interactions is equal to the number of recipients. The enrollment hub diagram reads as follows: The choreography begins with the interaction in which the retailer sends an enrollment request to the distributor. For convenience, this interaction is labeled a in the figure. Exactly one of the three interactions can happen next: the distributor sends a rejection to the retailer (b); the distributor sends an acceptance to the retailer (c); or the distributor sends a notice of pending switch to both the initiating and current retailers (d). Exactly one of the two interactions can follow acceptance: the retailer sends a cancellation to the distributor e, or the distributor sends a completion event to the retailer (f). In the pending switch case, one of the two interactions follows the notice of pending switch: the initiating retailer sends a cancellation to the distributor (g); or the distributor sends a switch completion event to both the initiating and current retailers indicating that the current retailer won (h). If the switch is cancelled, the distributor sends a switch completion event to both retailers indicating that the initiating retailer won (i). The choreography has 12 interactions assembled in a process flow. (There are nine groups, but three of them have two interactions each.) Reading the diagram means spotting the 12 interactions and traversing the control flow that connects them. The email transfer choreography hub, shown in the next figure, is somewhat more complex. The email transfer hub reads as follows: The choreography begins when the sender submits the transfer request to the sender bank (a). The sender bank can reject the request (b), or accept it (c). The acceptance event in c is routed to both the sender and the recipient, and thus results in two interactions. The remainder of the hub process is a loop that continues until the transfer is completed. The loop is modeled as a BPMN embedded sub-process labeled Loop. The arched arrow pointing counter-clockwise in the bottom-center of the sub-process box denotes that this sub-process is iterative. In the first step of the loop, the recipient requests her bank to transfer the funds into his or her account (d). The recipient's bank either rejects (e) or accepts (f) the request. In the rejection case, the recipient's bank sends a rejection notice to the recipient. In the next iteration of the loop, the recipient can try again. In the acceptance case, the recipient's bank sends a transfer request to the sender's bank. The sender's bank can either accept (g) or reject (h) the request. In the acceptance case, the sender's bank sends a transferOK message to both the recipient's bank and the sender. The recipient's bank then notifies the recipient (i), and the choreography completes. (The Set Done task sets the loop's continuation condition to false, which causes the loop to exit and the hub process to complete.) In the rejection case, the sender's bank sends a rejectTransfer message to the recipient's bank, and the recipient's bank notifies the recipient of this (j). In the next iteration of the loop, the recipient can try again. While the loop is executing, any of the parties may cancel the request (k). The label in the event *.cancel (SenderBank) informs the hub to listen for a cancel message from any party—the * works as a wildcard—and to route that message to the sender's bank. The sender's bank, in turn, sends an abort message (l) to the sender, the recipient, and the recipient's bank (the bank into which the recipient is currently requesting the transfer). Interaction (k) is an example of a cancellation intermediate event; it terminates the loop and transition into a series of cancellation activities. Choreographies are not executable, as we discussed previously. A choreography is a protocol, a set of traffic laws. It is, emphatically, not a central hub through which all participant interactions flow. Our hub model is merely a specification of how the individual participants should communicate. There are countless senders, recipients, and banks in the world of email transfer, but there is no hub that helps them talk to each other. The invisible hub for email transfer is merely a model; it is every bit as hypothetical as the invisible hand of Adam Smith's free-market economy. The economy is self-powered, and does not require the intervention of a hand; email transfer goes on without a hub. Still, the BPMN hub model is more than an informative picture. As we'll see, it maps easily to WS-CDL, and it serves as the basis for the generation of participant stubs and a choreography 'protocol' tester. Our BPMN method is practical and built with implementation in mind. Choreography modeling is also a hot topic in the academic world. Useful papers in this arena include Inheritance of Interorganizational Workflows: How to agree to disagree without loosing control? (Wil van der Aalst," BETA Working Paper Series, BP46, Eindhoven University, http://is.tm.tue.nl/staff/wvdaalst/publications/p109.pdf) and 'Let's Dance' (servicechoreographies.com, http://sky.fit.qut.edu.au/~dumas/LetsDance/01-Overview.html). The Invisible Hub in BPEL BPEL is principally an orchestration language, as just discussed, but it can also be used to model invisible hub choreographies. The code shown in the next figure is a simplified version of an actual BPEL implementation of the enrollment hub. The mapping from the BPMN hub to this BPEL implementation is straightforward: The event that starts the choreography in BPMN (Ret.enroll(Dist)) is receive that creates the BPEL process instance, marked as Start Event in the figure. An intermediate event that simply waits for a message between activities (for example, Dist.switchCompleteCurrent (Ret, CurrRet) in the BPMN model) is a BPEL receive, such as the line marked Intermediate Event in the figure. Deferred choice is a BPEL pick. The events in the choice are onMessage handlers. For example, the deferred choice in the BPMN model of Dist.reject(Ret), Dist.accept(Ret) and Dist.pendingSwitch(Ret, CurrRet) is the pick marked as Deferred Choice in the figure. The handlers are the three onMessage blocks that sit underneath the pick. Send tasks are BPEL invoke activities. For example, Dist.enroll(Ret) in the BPMN representation becomes the invoke in the line marked Send Task in the figure. The set of partner links used in the BPEL process is the union of all sender and recipient participants in the hub. Many partner links are bidirectional: they can either call the BPEL process or the BPEL process can call the partner link. The three partner links in this example, which are referred to in each receive, invoke, and onMessage tag are Ret, Dist, and CurrRet. BPEL supports dynamic partner links (where the BPEL process determines the physical address of its partner service at runtime). The series of four steps marked Dynamic partner links in the figure provides an example. The initial receive is a message from the distributor intended for one of the two retailers (either the current or the initiating retailer). The invoke that follows sends the message to that retailer. The endpoint of that retailer is resolved at runtime, based on the contents of the receive message. The next receive is a message from the distributor intended for the second retailer, and the invoke that follows sends the message to that retailer, again resolving the endpoint at runtime. In the majority of BPEL processes, partner links are resolved at deployment time, but that approach does not work in scenarios like ours. An interaction in which the sender sends to N recipients is modeled in BPEL as N separate inbound events and invokes. The series of four steps discussed in the previous bullet (and marked with Send to Multiple in the figure) provides an example. These steps model the activity Dist.switchCompleteCurrent (Ret, CurrRet) from the BPMN hub. In the BPEL code, the effect of the distributor sending the message switchCompleteCurrent to both the initiating and current retailers is achieved by having the hub receive the message from the distributor twice (using a receive), in each case forwarding the message (using an invoke) to one of the retailers. Dynamic partner links are used to resolve the endpoint of the recipient. The figure maps lines of code in the BPEL hub to interaction groups in the BPMN model. The first two lines, for example, represent group A. The reader can easily verify the mapping for groups B to I. There are two advantages to having the hub model in BPEL form: BPEL's XML form is an alternative to the leading XML choreography representation, WS-CDL (discussed in the next section). If we require an XML representation of choreography, BPEL might be a better choice than WS-CDL, because it is more familiar and has broader tool support. The BPEL hub is executable! There are numerous BPEL runtime platforms that can run this process as an actual hub. Granted, choreographies are not meant to run as part of the live exchange of actual participants, but having an executable version enables two important types of testing, shown in the next figure: unit testing of the choreography itself, and protocol testing of a particular participant. In unit testing, we build a Test Harness, driven by scripted scenarios, that sends messages to the hub and compares responses received with those expected. In protocol testing, we build the public process of a participant (say the retailer), but point it to the hub rather than its actual partners. We can embellish the hub to use test scripts to control how it responds. Once we have tested all of the scenarios and verified that the participant behaves as required, we can point the participant process to the real partners and go live Choreography in WS-CDL with Pi4SOA The description of choreography in WS-CDL with P14SOA is as follows: Defining Roles and Relationships Web Services Choreography Description Language (WS-CDL) is a specification from the W3C (Web Services Choreography Description Language Version 1.0, http://www.w3.org/TR/ws-cdl-10) for building choreographies in XML form. Like our invisible hub model, WS-CDL takes the global view: a choreography is not the sum of the public processes of its participants, but a single control flow of interactions. The WS-CDL language is exceedingly rich and best learned by example. In this section, we study how the enrollment choreography is represented in WS-CDL. Rather than building the choreography's XML from scratch, we use a visual modelling tool known as pi4SOA. pi4SOA, an open-source implementation that plugs into Eclipse, is one of the few WS-CDL implementations available today. The first step in building a WS-CDL choreography is to define participants and their structural relationships. The following figure shows the enrollment choreography open in the Participants, Roles, and Relationships tab of the pi4SOA editor.   There are five participants (shown with building icons) in the figure: Distributor, Retailer, CurrentRetailer, Customer (to model a customer's interaction with a retailer), and DistributorBizCal (a subsystem of the distributor to model the management of business calendars for completion and switch periods). Each participant has a role of the same name (designated by a stick-man icon), and each role has a behavior named for its role: Distributor's behavior is DistributorBehavior, Retailer's behaviour is RetailerBehavior, and so on. In WS-CDL, a behavior is a web service interface, and a role is a group of behaviors. A role can have multiple behaviors and a participant can have multiple roles. In our case, each participant has one role and one participant. The lines connecting roles are called relationships. There are four relationships: RD is the relationship between Retailer and Distributor, CRD the relationship between CurrentRetailer and Distributor, RC the relationship between Retailer and Customer, and DInt the relationship between Distributor and DistributorBizCal. When two roles have a relationship, they can interact by calling each other's services. The next figure shows the Base Types tab of the choreography editor. To the participants, roles, and relationships defined above, we add four important elements: information types, tokens, token locators, and channel types. An information type is an XML data type (generally based on an XML schema) exchanged during interactions. A token is a field in an information type. A token locator defines how to extract—generally using an XPath expression—the token from the information type. Our choreography has one information type, called EnergyMsg with five tokens and token locators (custID, retailer, txID, currentRetailer, and reason). A channel type is an inbound communication endpoint for a role behavior. In the enrollment choreography, there are channels for the retailer, current retailer and distributor. Each channel type is configured for one-way asynchronous requests only. Hence, Retailer receives requests on its RetailerChannel; CurrentRetailer receives requests on its CurrentRetailerChannel; and Distributor receives requests on its DistributorChannel. Combining our definitions of relationships and channels, we have the following communication structure: In the relationship RD, Retailer sends to Distributor on DistributorChannel, and Distributor sends to Retailer on RetailerChannel   In the relationship CRD, Distributor sends to CurrentRetailer on CurrentRetailerChannel. (CurrentRetailer could also send to Distributor on DistributorChannel, but the use case does not require it.) In the relationship RC, Customer sends to Retailer on RetailerChannel. (Customer does not have a channel, so the reverse direction is not permitted.) In the relationship DInt, DistributorBizCal sends to Distributor on DistributorChannel. (DistributorBizCal does not have a channel, so the reverse direction is not permitted.) Building a Control Flow of Interactions The next figure shows the overall control flow that defines the behaviour of the choreography. There are three steps. The first, RequestC2R, is an interaction in which the Customer participant sends an enrollment request to the Retailer participant. The request has the information type EnrollmentMsg, and is sent on RetailerChannel as part of the RC relationship. In the interaction that follows, RequestR2D, the retailer forwards that request to the distributor; or, in the language of WS-CDL, Retailer sends the request with information type EnrollmentMsg on the DistributorChannel as part of the RD relationship. The step that follows, enrollmentResult, is a flow construct known as a choice. There are three possible outcomes of an enrollment request—acceptance, rejection, or a pending switch. The choice allows exactly one to occur. The next figure shows the acceptance and rejection paths; the switch path is omitted for brevity. The rejection path (housed in a sequence labelled rejectEnrollment) has one interaction, RejectD2R, in which the distributor sends a rejection message to the retailer. The more complicated acceptance path is housed in the sequence labelled newEnrollment, which begins with the interaction in which the distributor notifies the retailer that the enrollment is accepted (AcceptD2R). Next is a silent action, setCompletionTimer, in which the distributor sets a timer that expires at the end of the ten-day cancellation period. A silent action in WS-CDL is a private operation performed by a role. The acceptance path has a nested choice, labelled completionPeriod, which documents the two possible outcomes for an accepted enrolment: periodExpired is a sequence that specifies what happens when the ten-day timer expires, and cancel handles the case in which the customer cancels the enrollment during the cancellation period. Each path contains two interactions. In the periodExpired sequence, the periodExpired interaction (sent from DistributorBizCal to Distributor by the DInt relationship) notifies the distributor that time is up, whereupon the distributor sends a completion event to the retailer (CompleteD2R). In the cancel sequence, the customer cancels with the retailer (CancelC2R by the RC relationship), and the retailer, in turn, cancels with the distributor (CancelR2D by the RD relationship). The following is a snippet of the WS-CDL XML encoding of the enrollment choreography, covering the acceptance case only. For the sake of simplicity, numerous details are omitted: <package name="EnergyChoreo">   <choreography name="main" root="true">     <sequence>       <!-- Cust sends request to retailer -->       <interaction channelVariable="tns:retailerChannel"                    name="RequestC2R" operation="enroll">         <participate fromRoleTypeRef="tns:Customer"                      relationshipType="tns:RC" toRoleTypeRef="tns:Retailer"/>       </interaction>       <!-- Retailer sends request to distributor -->       <interaction channelVariable="tns:distributorChannel"                    name="RequestR2D" operation="enroll">         <participate fromRoleTypeRef="tns:Retailer"                relationshipType="tns:RD" toRoleTypeRef= "tns:Distributor"/>       </interaction>       <!-- Choose what comes next -->       <choice>         <!-- Path taken if distributor rejects -->         <sequence>           <!-- This is the interaction that heads the reject path -->             <interaction name="RejectD2R" operation="reject">             </interaction>         </sequence>         <!-- Path taken if distributor accepts -->         <sequence>           <!-- This is the interaction that heads the accept path -->             <interaction name="AcceptD2R" operation="accept">             </interaction>             <silentAction name="setCompletionTimer"                           roleType="tns:Distributor">             <choice>               <!-- Accept subpaths omitted -->             </choice>          </sequence>          <!-- Omitted: switch path -->       </choice>     </sequence>   </choreography> </package> The two key elements in this example are choice and interaction. They are mapped to our hub model as follows: A receive-send pair in the hub is a WS-CDL interaction. In WS-CDL, a relationship is defined between sender and recipient, and the recipient has a channel on which the sender sends the message. An example of this is the interaction shown in bold from the customer (fromRoleTypeRef="tns: Customer") to the retailer (toRoleTypeRef="tns:Retailer") on the RC relationship (relationshipType="tns:RC"). The event arrives on the retailer's channel (channelVariable="tns:retailerChannel"). A deferred choice in the hub is a choice structure in WS-CDL. Generally, a path in the choice is a sequence. The set of events from which we are choosing is the set of first interactions on each path. The main choice structure in the choreography, shown in bold, has two interactions from which to choose: acceptance and rejection interactions (also shown in bold). The interaction that actually occurs determines which path is taken.   Generating a BPEL Role Process In addition to producing conformant WS-CDL code, pi4SOA's capabilities include scenario testing and BPEL and Java endpoint generation. The BPEL generation feature is especially useful, as BPEL is a suitable implementation choice to build orchestration process of a given participant. Here, in pseudo-code, is the BPEL process that pi4SOA generates for the distributor: <process>   <sequence>     <receive operation="enroll" partnerLink="RD"              portType="DistributorBehavior"/>       <!-- Dist receives interaction -->     <switch name="enrollmentResult"> <!-- Dist choice is switch -->       <case condition="accepted">         <sequence>           <invoke operation="accept" partnerLink="RD"                   portType="RetailerBehavior"/> <!-- Dist is sender -->           <scope name="setCompletionTimer"/>           <pick name="completionPeriod">             <onMessage operation="periodExpired" partnerLink="DInt"                        portType="DistributorBehavior">               <sequence>                 <invoke operation="complete" partnerLink="RD"                         portType="RetailerBehavior"/>               </sequence>             </onMessage>             <onMessage operation="cancel" partnerLink="RD"                        portType="DistributorBehavior"/>           </pick>         </sequence>       </case>       <case condition="rejected ">         <invoke operation="reject" partnerLink="RD"                 portType="RetailerBehavior"/>       </case>       <case condition="switch">         <!-- omitted -->       </case>   </switch> </sequence> </process> Two salient features of this process are the mapping of interactions and the conversion of the WS-CDL choice to a BPEL switch as follows: An interaction in which the distributor is the sender is a BPEL invoke activity whose partner link is the WS-CDL relationship and whose port type is the recipient's behavior. The operation element in the invoke matches the one specified in the interaction. The invoke shown in bold, for example, which is sent to the retailer, has partner link RD, port type RetailerBehavior, and operation accept. An interaction in which the participant is the recipient is either a receive activity or an onMessage handler in a pick. The partner link is the name of the relationship, and port type is the participant's behavior. The operation element, as before, matches that specified in the interaction. The bolded receive in the BPEL sample is the enroll operation on the partner link RD and port type DistributorBehavior. The WS-CDL choice is a switch for the BPEL distributor process (as the bolded comment in the switch line indicates), because the distributor begins each path of the choice by sending a message. From the distributor's perspective, the choice represents its decision to reject the request, accept it, or initiate a pending switch. Each case in the switch handles one of these possibilities. The retailer, on the other hand, begins each path by waiting for an event from the distributor. From the retailer's point of view, the choice is a pick, as in the following snippet: <pick name="enrollmentResult"> <!-- For Ret, choice is a pick -->   <onMessage operation="accept" partnerLink="RD"              portType="RetailerBehavior">     <!-- code omitted -->   </onMessage>   <onMessage operation="reject" partnerLink="RD"              portType="RetailerBehavior" />   <onMessage operation="pendingSwitch" partnerLink="RD"              portType="RetailerBehavior" >     <!-- code omitted -->   </onMessage> </pick> We have only scratched the surface of WS-CDL. Other notable capabilities are state alignment, coordination, and channel passing. WS-CDL's supporters boast that their language has its foundations in the Pi Calculus, a mathematical scheme for describing concurrent processes and how they pass messages to each other. Robin Milner, the mathematician who devised the Pi Calculus, is an advisor to the WS-CDL working group. Sadly, WS-CDL has not gained much traction in the field. There are scant tools to build WS-CDL choreographies and, frankly, not many use cases that require choreography. Few people who practice SOA technology have even heard of this language. WS-CDL is winning the mathematics battle but losing the marketing war. Summary Choreography is the global protocol governing the interaction of SOA processes partnering to achieve some business ends. An orchestration process is a process whose principal job is to build a flow of control around its interactions with partners. WS-CDL is the dominant choreography standard. BPEL is the dominant orchestration standard. Both standards provide a way to build process flows in an XML form, though only BPEL processes are meant to actually execute. The modeling tool pi4SOA is ideal for building WS-CDL examples. Key WS-CDL elements are interaction (which corresponds to the receive-send pair in the invisible hub) and choice (a kind of deferred choice in the choreography's flow of control. pi4SOA generates skeletal BPEL code for each participant in the choreography.
Read more
  • 0
  • 0
  • 7700

article-image-oracle-web-rowset-part1
Packt
22 Oct 2009
6 min read
Save for later

Oracle Web RowSet - Part1

Packt
22 Oct 2009
6 min read
The ResultSet interface requires a persistent connection with a database to invoke the insert, update, and delete row operations on the database table data. The RowSet interface extends the ResultSet interface and is a container for tabular data that may operate without being connected to the data source. Thus, the RowSet interface reduces the overhead of a persistent connection with the database. In J2SE 5.0, five new implementations of RowSet—JdbcRowSet, CachedRowSet, WebRowSet, FilteredRowSet, and JoinRowSet—were introduced. The WebRowSet interface extends the RowSet interface and is the XML document representation of a RowSet object. A WebRowSet object represents a set of fetched database table rows, which may be modified without being connected to the database. Support for Oracle Web RowSet is a new feature in Oracle Database 10g driver. Oracle Web RowSet precludes the requirement for a persistent connection with the database. A connection is required only for retrieving data from the database with a SELECT query and for updating data in the database after all the required row operations on the retrieved data has been performed. Oracle Web RowSet is used for queries and modifications on the data retrieved from the database. Oracle Web RowSet, as an XML document representation of a RowSet facilitates the transfer of data. In Oracle Database 10g and 11g JDBC drivers, Oracle Web RowSet is implemented in the oracle.jdbc.rowset package. The OracleWebRowSet class represents a Oracle Web RowSet. The data in the Web RowSet may be modified without connecting to the database. The database table may be updated with the OracleWebRowSet class after the modifications to the Web RowSet have been made. A database JDBC connection is required only for retrieving data from the database and for updating the database. An XML document representation of the data in a Web RowSet may be obtained for data exchange. In this article, the Web RowSet feature in Oracle 10g database JDBC driver is implemented in JDeveloper 10g. An example Web RowSet will be created from a database. The Web RowSet will be modified and stored in the database table. In this article, we will learn the following: Creating a Oracle Web RowSet object Adding a row to Oracle Web RowSet Modifying the database table with Web RowSet In the second half of the article, we will cover the following : Reading a row from Oracle Web RowSet Updating a row in Oracle Web RowSet Deleting a row from Oracle Web RowSet Updating Database Table with modified Oracle Web RowSet Setting the Environment We will use Oracle database to generate an updatable OracleWebRowSet object. Therefore, install Oracle database 10g including the sample schemas. Connect to the database with the OE schema: SQL> CONNECT OE/<password> Create an example database table, Catalog, with the following SQL script: CREATE TABLE OE.Catalog(Journal VARCHAR(25), Publisher Varchar(25),Edition VARCHAR(25), Title Varchar(45), Author Varchar(25));INSERT INTO OE.Catalog VALUES('Oracle Magazine', 'OraclePublishing', 'July-August 2005', 'Tuning Undo Tablespace','Kimberly Floss');INSERT INTO OE.Catalog VALUES('Oracle Magazine', 'OraclePublishing', 'March-April 2005', 'Starting with Oracle ADF', 'SteveMuench'); Configure JDeveloper 10g for Web RowSet implementation. Create a project in JDeveloper. Select File | New | General | Application. In the Create Application window specify an Application Name and click on Next. In the Create Project window, specify a Project Name and click on Next. A project is added in the Applications Navigator. Next, we will set the project libraries. Select Tools | ProjectProperties and in the Project Properties window, select Libraries | Add Library to add a library. Add the Oracle JDBC library to project libraries. If the Oracle JDBC drivers version prior to the Oracle database 10g (R2) JDBC drivers version is used, create a library from the Oracle Web RowSet implementation classes JAR file: C:JDeveloper10.1.3jdbclibocrs12.jar. The ocrs12.jar is required only for JDBC drivers prior to Oracle database 10g (R2) JDBC drivers. In Oracle database 10g (R2) JDBC drivers OracleRowSet implementation classes are packaged in the ojdbc14.jar. In Oracle database 11g JDBC drivers Oracle RowSet implementation classes are packaged in ojdbc5.jar and ojdbc6.jar. In the Add Library window select the User node and click on New. In the Create Library window specify a Library Name, select the Class Path node and click on Add Entry. Add an entry for ocrs12.jar. As Web RowSet was introduced in J2SE 5.0, if J2SE 1.4 is being used we also need to add an entry for the RowSet implementations JAR file, rowset.jar. Download the JDBC RowSet Implementations 1.0.1 zip file, jdbc_rowset_tiger-1_0_1-mrel-ri.zip, from http://java.sun.com/products/jdbc/download.html#rowset1_0_1 and extract the JDBC RowSet zip file to a directory. Click on OK in the Create Library window. Click on OK in the Add Library window. A library for the Web RowSet application is added. Now configure an OC4J data source. Select Tools | Embedded OC4J Server Preferences. A data source may be configured globally or for the current workspace. If a global data source is created using Global | Data Sources, the data source is configured in the C:JDeveloper10.1.3jdevsystemoracle.j2ee.10.1.3.36.73embedded-oc4jconfig data-sources.xml file. If a data source is configured for the current workspace using Current Workspace | Data Sources, the data source is configured in the data-sources.xml file. For example, the data source file for the WebRowSetApp application is WebRowSetApp-data-sources.xml. In the Embedded OC4J Server Preferences window configure either a global data source or a data source in the current workspace. A global data source definition is available to all applications deployed in the OC4J server instance. A managed-data-source element is added to the data-sources.xml file. <managed-data-source name='OracleDataSource' connection-pool-name='Oracle Connection Pool' jndi-name='jdbc/OracleDataSource'/><connection-pool name='Oracle Connection Pool'><connection-factory factory-class='oracle.jdbc.pool.OracleDataSource' user='OE' password='pw'url="jdbc:oracle:thin:@localhost:1521:ORCL"></connection-factory></connection-pool> Add a JSP, GenerateWebRowSet.jsp, to the WebRowSet project. Select File | New | Web Tier | JSP | JSP. Click on OK. Select J2EE 1.3 or J2EE 1.4 in the Web Application window and click on Next. In the JSP File window specify a File Name and click on Next. Select the default settings in the Error Page Options page and click on Next. Select the default settings in the Tag Librarieswindow and click on Next. Select the default options in the HTML Options window and click on Next. Click on Finish in the Finish window. Next, configure the web.xml deployment descriptor to include a reference to the data source resource configured in the data-sources.xml file as shown in following listing: <resource-ref><res-ref-name>jdbc/OracleDataSource</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth></resource-ref>
Read more
  • 0
  • 0
  • 2372
Modal Close icon
Modal Close icon