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
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Front-End Web Development

341 Articles
article-image-displaying-data-grids-ext-js
Packt
15 Oct 2010
9 min read
Save for later

Displaying Data with Grids in Ext JS

Packt
15 Oct 2010
9 min read
What is a grid? Ext JS grids are similar to a spreadsheet; there are two main parts to each spreadsheet: Columns Rows Here our columns are Title, Released, Genre, and Price. Each of the rows contains movies such as The Big Lebowski, Super Troopers, and so on. The rows are really our data; each row in the grid represents a record of data held in a data store. GridPanel is databound Like many Ext JS Components, the GridPanel class is bound to a data store which provides it with the data shown in the user interface. So the first step in creating our GridPanel is creating and loading a store. The data store in Ext JS gives us a consistent way of reading different data formats such as XML and JSON, and using this data in a consistent way throughout all of the Ext JS widgets. Regardless of whether this data is originally provided in JSON, XML, an array, or even a custom data type of our own, it's all accessed in the same way thanks to the consistency of the data store and how it uses a separate reader class which interprets the raw data. Instead of using a pre-configured class, we will explicitly define the classes used to define and load a store. The record definition We first need to define the fields which a Record contains. A Record is a single row of data and we need to define the field names, which we want to be read into our store. We define the data type for each field, and, if necessary, we define how to convert from the raw data into the field's desired data type. What we will be creating is a new class. We actually create a constructor which will be used by Ext JS to create records for the store. As the 'create' method creates a constructor, we reference the resulting function with a capitalized variable name, as per standard CamelCase syntax: var Movie = Ext.data.Record.create([ 'id', 'coverthumb', 'title', 'director', 'runtime', {name: 'released', type: 'date', dateFormat: 'Y-m-d'}, 'genre', 'tagline', {name: 'price', type: 'float'}, {name: 'available', type: 'bool'} ]); Each element in the passed array defines a field within the record. If an item is just a string, the string is used as the name of the field, and no data conversion is performed; the field's value will be whatever the Reader object (which we will learn more about soon) finds in the raw data. If data conversion is required, then a field definition in the form of an object literal instead of a string may contain the following config options: Name: The name by which the field will be referenced. type: The data type in which the raw data item will be converted to when stored in the record. Values may be 'int', 'float', 'string', 'date', or 'bool'. dateFormat: If the type of data to be held in the field is a date type, then we need to specify a format string as used by the Date.parseDate function. Defining the data type can help to alleviate future problems, instead of having to deal with all string type data defining the data type, and lets us work with actual dates, Boolean values, and numbers. The following is a list of the built in data types:   Field type Description Information string String data   int Number Uses JavaScript's parseInt function float Floating point number Uses JavaScript's parseFloat function boolean True/False data   date Date data dateFormat config required to interpret incoming data. Now that the first step has been completed, and we have a simple Record definition in place, we can move on to the next step of configuring a Reader that is able to understand the raw data. The Reader A store may accept raw data in several different formats. Raw data rows may be in the form of a simple array of values, or an object with named properties referencing the values, or even an XML element in which the values are child nodes. We need to configure a store with a Reader object which knows how to extract data from the raw data that we are dealing with. There are three built in Reader classes in Ext JS. ArrayReader The ArrayReader class can create a record from an array of values. By default, values are read out of the raw array into a record's fields in the order that the fields were declared in the record definition we created. If fields are not declared in the same order that the values occur in the rows, the field's definition may contain a mapping config which specifies the array index from which we retrieve the field's value. JsonReader This JSONReader class is the most commonly used, and can create a record from raw JSON by decoding and reading the object's properties. By default, the field's name is used as the property name from which it takes the field's value. If a different property name is required, the field's definition may contain a mapping config which specifies the name of the property from which to retrieve the field's value. XmlReader An XMLReader class can create a record from an XML element by accessing child nodes of the element. By default, the field's name is used as the XPath mapping (not unlike HTML) from which to take the field's value. If a different mapping is required, the field's definition may contain a mapping config which specifies the mapping from which to retrieve the field's value. Loading our data store In our first attempt, we are going to create a grid that uses simple local array data stored in a JavaScript variable. The data we're using below in the movieData variable is taken from a very small movie database of some of my favorite movies. The data store needs two things: the data itself, and a description of the data—or what could be thought of as the fields. A reader will be used to read the data from the array, and this is where we define the fields of data contained in our array. The following code should be placed before the Ext JS OnReady function: var movieData = [ [ 1, "Office Space", "Mike Judge", 89, "1999-02-19", 1, "Work Sucks", "19.95", 1 ],[ 3, "Super Troopers", "Jay Chandrasekhar", 100, "2002-02-15", 1, "Altered State Police", "14.95", 1 ] //...more rows of data removed for readability...// ]; var store = new Ext.data.Store({ data: movieData, , reader: new Ext.data.ArrayReader({idIndex: 0}, Movie) }); If we view this code in a browser we would not see anything—that's because a data store is just a way of loading and keeping track of our data. The web browser's memory has our data in it. Now we need to configure the grid to display our data to the user. Displaying structured data with a GridPanel Displaying data in a grid requires several Ext JS classes to cooperate: A Store: A data store is a client-side analogue of a database table. It encapsulates a set of records, each of which contains a defined set of fields. A Record definition: This defines the fields (or "columns" in database terminology) which make up each record in the Store. Field name and datatype are defined here. A Reader which uses a Record definition to extract field values from a raw data object to create the records for a Store. A ColumnModel which specifies the details of each column, including the column header to display, and the name of the record field to be displayed for each column. A GridPanel: A panel subclass which provides the controller logic to bind the above classes together to generate the grid's user interface. If we were to display the data just as the store sees it now, we would end up with something like this: Now that is ugly—here's a breakdown of what's happening: The Released date has been type set properly as a date, and interpreted from the string value in our data. It's provided in a native JavaScript date format—luckily Ext JS has ways to make this look pretty. The Price column has been type set as a floating point number. Note that there is no need to specify the decimal precision. The Avail column has been interpreted as an actual Boolean value, even if the raw data was not an actual Boolean value. As you can see, it's quite useful to specify the type of data that is being read, and apply any special options that are needed so that we don't have to deal with converting data elsewhere in our code. Before we move on to displaying the data in our grid, we should take a look at how the convert config works, as it can come in quite useful. Converting data read into the store If we need to, we can convert data as it comes into the store, massage it, remove any quirky parts, or create new fields all together. This should not be used as a way to change the display of data; that part will be handled elsewhere. A common task might be to remove possible errors in the data when we load it, making sure it's in a consistent format for later actions. This can be done using a convert function, which is defined in the 'convert' config by providing a function, or reference to a function. In this case we are going to create a new field by using the data from another field and combining it with a few standard strings. var store = new Ext.data.Store({ data: movieData, reader: new Ext.data.ArrayReader({id:'id'}, [ 'id', 'title', 'director', {name: 'released', type: 'date', dateFormat: 'Y-m-d'}, 'genre', 'tagline', 'price', 'available', {name:'coverthumb',convert:function(v, rawData){ return 'images/'+rawData[0]+'m.jpg'; }} ]) }); This convert function when used in this manner will create a new field of data that looks like this: 'images/5m.jpg' We will use this new field of data shortly, so let's get a grid up and running.
Read more
  • 0
  • 0
  • 5106

article-image-marshalling-data-services-extdirect
Packt
14 Oct 2010
9 min read
Save for later

Marshalling Data Services with Ext.Direct

Packt
14 Oct 2010
9 min read
What is Direct? Part of the power of any client-side library is its ability to tap nearly any server-side technology. That said, with so many server-side options available there were many different implementations being written for accessing the data. Direct is a means of marshalling those server-side connections, creating a 'one-stop-shop' for handling your basic Create, Read, Update, and Delete actions against that remote data. Through some basic configuration, we can now easily create entire server-side API's that we may programmatically expose to our Ext JS applications. In the process, we end up with one set of consistent, predefined methods for managing that data access. Building server-side stacks There are several examples of server-side stacks already available for Ext JS, directly from their site's Direct information. These are examples, showing you how you might use Direct with a particular server-side technology, but Ext provides us with a specification so that we might write our own. Current stack examples are available for: PHP .NET Java ColdFusion Ruby Perl These are examples written directly by the Ext team, as guides, as to what we can do. Each of us writes applications differently, so it may be that our application requires a different way of handling things at the server level. The Direct specification, along with the examples, gives us the guideposts we need for writing our own stacks when necessary. We will deconstruct one such example here to help illustrate this point. Each server-side stack is made up of three basic components: Configuration— denoting which components/classes are available to Ext JS API— client-side descriptors of our configuration Router— a means to 'route' our requests to their proper API counterparts To illustrate each of these pieces of the server-side stack we will deconstruct one of the example stacks provided by the Ext JS team. I have chosen the ColdFusion stack because: It is a good example of using a metadata configuration DirectCFM (the ColdFusion stack example) was written by Aaron Conran, who is the Senior Software Architect and Ext Services Team Leader for Ext, LLC Each of the following sections will contain a "Stack Deconstruction" section to illustrate each of the concepts. These are to show you how these concepts might be written in a server-side language, but you are welcome to move on if you feel you have a good grasp of the material. Configuration Ultimately the configuration must define the classes/objects being accessed, the functions of those objects that can be called, and the length (number) of arguments that the method is expecting. Different servers will allow us to define our configuration in different ways. The method we choose will sometimes depend upon the capabilities or deficiencies of the platform we're coding to. Some platforms provide the ability to introspect components/classes at runtime to build configurations, while others require a far more manual approach. You can also include an optional formHandler attribute to your method definitions, if the method can take form submissions directly. There are four basic ways to write a configuration. Programmatic A programmatic configuration may be achieved by creating a simple API object of key/value pairs in the native language. A key/value pair object is known by many different names, depending upon the platform to which we're writing for: HashMap, Structure, Object, Dictionary, or an Associative Array. For example, in PHP you might write something like this: $API = array( 'Authors'=>array( 'methods'=>array( 'GetAll'=>array( 'len'=>0 ), 'add'=>array( 'len'=>1 ), 'update'=>array( 'len'=>1 ) ) ) ); Look familiar? It should, in some way, as it's very similar to a JavaScript object. The same basic structure is true for our next two methods of configuration as well. JSON and XML For this configuration, we can pass in a basic JSON configuration of our API: { Authors:{ methods:{ GetAll:{ len:0 }, add:{ len:1 }, update:{ len:1 } } } } Or we could return an XML configuration object: <Authors> <methods> <method name="GetAll" len="0" /> <method name="add" len="1" /> <method name="update" len="1" /> </methods> </Authors> All of these forms have given us the same basic outcome, by providing a basic definition of server-side classes/objects to be exposed for use with our Ext applications. But, each of these methods require us to build these configurations basically by hand. Some server-side options make it a little easier. Metadata There are a few server-side technologies that allow us to add additional metadata to classes and function definitions, using which we can then introspect objects at runtime to create our configurations. The following example demonstrates this by adding additional metadata to a ColdFusion component (CFC): <cfcomponent name="Authors" ExtDirect="true"> <cffunction name="GetAll" ExtDirect="true"> <cfreturn true /> </cffunction> <cffunction name="add" ExtDirect="true"> <cfargument name="author" /> <cfreturn true /> </cffunction> <cffunction name="update" ExtDirect="true"> <cfargument name="author" /> <cfreturn true /> </cffunction> </cfcomponent> This is a very powerful method for creating our configuration, as it means adding a single name/value attribute (ExtDirect="true") to any object and function we want to make available to our Ext application. The ColdFusion server is able to introspect this metadata at runtime, passing the configuration object back to our Ext application for use. Stack deconstruction—configuration The example ColdFusion Component provided with the DirectCFM stack is pretty basic, so we'll write one slightly more detailed to illustrate the configuration. ColdFusion has a facility for attaching additional metadata to classes and methods, so we'll use the fourth configuration method for this example, Metadata. We'll start off with creating the Authors.cfc class: <cfcomponent name="Authors" ExtDirect="true"> </cfcomponent> Next we'll create our GetAll method for returning all the authors in the database: <cffunction name="GetAll" ExtDirect="true"> <cfset var q = "" /> <cfquery name="q" datasource="cfbookclub"> SELECT AuthorID, FirstName, LastName FROM Authors ORDER BY LastName </cfquery> <cfreturn q /> </cffunction> We're leaving out basic error handling and stuff, but these are the basics behind it. The classes and methods we want to make available will all contain the additional metadata. Building your API So now that we've explored how to create a configuration at the server, we need to take the next step by passing that configuration to our Ext application. We do this by writing a server-side template that will output our JavaScript configuration. Yes, we'll actually dynamically produce a JavaScript include, calling the server-side template directly from within our <script> tag: <script src="Api.cfm"></script> How we write our server-side file really depends on the platform, but ultimately we just want it to return a block of JavaScript (just like calling a .js file) containing our API configuration description. The configuration will appear as part of the actions attribute, but we must also pass the url of our Router, the type of connection, and our namespace. That API return might look something like this: Ext.ns("com.cc"); com.cc.APIDesc = { "url": "/remote/Router.cfm", "type": "remoting" "namespace": "com.cc", "actions": { "Authors": [{ "name": "GetAll", "len": 0 },{ "name": "add", "len": 1 },{ "name": "update", "len": 1 }] } }; This now exposes our server-side configuration to our Ext application. Stack deconstruction—API The purpose here is to create a JavaScript document, dynamically, of your configuration. Earlier we defined configuration via metadata. The DirectCFM API now has to convert that metadata into JavaScript. The first step is including the Api.cfm in a <script> tag on the page, but we need to know what's going on "under the hood." Api.cfm: <!--- Configure API Namespace and Description variable names ---> <cfset args = StructNew() /> <cfset args['ns'] = "com.cc" /> <cfset args['desc'] = "APIDesc" /> <cfinvoke component="Direct" method="getAPIScript" argumentcollection="#args#" returnVariable="apiScript" /> <cfcontent reset="true" /> <cfoutput>#apiScript#</cfoutput> Here we set a few variables, that will then be used in a method call. The getAPIScript method, of the Direct.cfc class, will construct our API from metadata. Direct.cfc getAPIScript() method: <cffunction name="getAPIScript"> <cfargument name="ns" /> <cfargument name="desc" /> <cfset var totalCFCs = '' /> <cfset var cfcName = '' /> <cfset var CFCApi = '' /> <cfset var fnLen = '' /> <cfset var Fn = '' /> <cfset var currFn = '' /> <cfset var newCfComponentMeta = '' /> <cfset var script = '' /> <cfset var jsonPacket = StructNew() /> <cfset jsonPacket['url'] = variables.routerUrl /> <cfset jsonPacket['type'] = variables.remotingType /> <cfset jsonPacket['namespace'] = ARGUMENTS.ns /> <cfset jsonPacket['actions'] = StructNew() /> <cfdirectory action="list" directory="#expandPath('.')#" name="totalCFCs" filter="*.cfc" recurse="false" /> <cfloop query="totalCFCs"> <cfset cfcName = ListFirst(totalCFCs.name, '.') /> <cfset newCfComponentMeta = GetComponentMetaData(cfcName) /> <cfif StructKeyExists(newCfComponentMeta, "ExtDirect")> <cfset CFCApi = ArrayNew(1) /> <cfset fnLen = ArrayLen(newCFComponentMeta.Functions) /> <cfloop from="1" to="#fnLen#" index="i"> <cfset currFn = newCfComponentMeta.Functions[i] /> <cfif StructKeyExists(currFn, "ExtDirect")> <cfset Fn = StructNew() /> <cfset Fn['name'] = currFn.Name/> <cfset Fn['len'] = ArrayLen(currFn.Parameters) /> <cfif StructKeyExists(currFn, "ExtFormHandler")> <cfset Fn['formHandler'] = true /> </cfif> <cfset ArrayAppend(CFCApi, Fn) /> </cfif> </cfloop> <cfset jsonPacket['actions'][cfcName] = CFCApi /> </cfif> </cfloop> <cfoutput><cfsavecontent variable="script">Ext.ns('#arguments. ns#');#arguments.ns#.#desc# = #SerializeJson(jsonPacket)#;</ cfsavecontent></cfoutput> <cfreturn script /> </cffunction> The getAPIScript method sets a few variables (including the 'actions' array), pulls a listing of all ColdFusion Components from the directory, loops over that listing, and finds any components containing "ExtDirect" in their root meta. With every component that does contain that meta, it then loops over each method, finds methods with "ExtDirect" in the function meta, and creates a structure with the function name and number of arguments, which is then added to an array of methods. When all methods have been introspected, the array of methods is added to the 'actions' array. Once all ColdFusion Components have been introspected, the entire packet is serialized into JSON, and returned to API.cfm for output. One item to note is that the script, when introspecting method metadata, also looks for a "ExtFormHandler" attribute. If it finds the attribute, it will include that in the method struct prior to placing the struct in the 'actions' array.
Read more
  • 0
  • 0
  • 2299

article-image-creating-our-first-jquery-plugin
Packt
14 Oct 2010
7 min read
Save for later

Creating Our First jQuery Plugin

Packt
14 Oct 2010
7 min read
  jQuery Plugin Development Beginner's Guide A practical straightforward guide for creating your first jQuery plugin Utilize jQuery's plugin framework to create a wide range of useful jQuery plugins from scratch Understand development patterns and best practices and move up the ladder to master plugin development Discover the ins and outs of some of the most popular jQuery plugins in action A Beginner's Guide packed with examples and step-by-step instructions to quickly get your hands dirty in developing high quality jQuery plugins         Read more about this book       (For more resources on jQuery, see here.) Defining our own default plugin structure To make things easier to remember and apply, we are going to start off with the definition of what we will be referring to when speaking of the basic template that all the plugins we are going to develop should conform to. Actually, we have already had a quick look at it earlier in the previous chapter, but there's something more definitely worth saying. From now on, we will call the following code the default structure for our plugins. This is what we will promptly copy and paste into each file we're going to write a plugin into. jQuery.fn.PLUGINNAME = function() { return this.each(function() { // code });} Needless to say, the this.each loop iterates all of the matching elements. We return the jQuery object (this) to allow chaining. We extend the jQuery.fn object; all of the code will be put inside the function. Also: The file name of every plugin we're going to develop will bejquery.PLUGINNAME.js. For the moment, remember to always avoid referring to the jQuery object with the dollar sign ($), to prevent possible conflicts with other libraries. We'll get to using aliases very soon. All of the functions that we write to make our plugin work should be private and not accessible from outside, in an attempt to avoid cluttering and possible backwards incompatibility. If not from the very start, at least at the end, a user will be able to specify options to control the plugin behavior. Default options for the plugin will be publicly accessible to allow for easier customization with minimal code. The directory that the plugin resides in will also contain two other files, by default: index.html: This is our test page. jquery.js: This is the jQuery library that we need to make things work. Setting the basics for our first plugin As our first plugin, we might want to create something uncomplicated but somewhat impressive: what about something that, when the cursor is hovering over an element, substitutes the text contained with some words of our choice? Time for action – our first plugin, Part I Getting started in creating jQuery plugins in not difficult at all. For example, creating this simple plugin should help us in understanding how things actually work. Given that our plugin name is txtHover, create all the directories and files we need by default, and copy over the jquery.js file. Copy the default structure for our plugins to the plugin file and make sure the function is named accordingly. It should look like this: jQuery.fn.txtHover = function() { return this.each(function() { // code });}; Nothing's easier to do than change the text contained in some element. Inside the plugin function, write the following to let the trick happen: jQuery(this).text("Text changed"); To test this in action, we can modify the HTML document to look like this: <!DOCTYPE html><html><head> <script src="jquery.js"></script> <script src="jquery.txthover.js"></script> <script> $(document).ready(function() { $("p#one").txtHover(); }); </script></head><body> <p id="one">Some text.</p></body></html> Unfortunately, the result is neither fancy nor satisfactory—something we've experienced earlier too. But we're just getting started; we won't stop here this time! What just happened? The plugin is working correctly so far. When the page loads, the text is changed to what we had defined into the plugin code. However, there are a couple of things to pay attention to: The function text() from the jQuery API expects either one or no arguments to be passed: if there is no argument the function returns the current content of the selected element. The text string passed as an argument substitutes the element text otherwise. There are, however, some similarities with the html() function, which treats the text it operates on as if it were HTML code. That is, passing any tag to the html() function results in having the element possibly containing other elements after the operation (also, the same applies for getting HTML code from within the element), whereas the this function will just treat the HTML code as plain text and not affect any element hierarchy. The fact that the text cannot be changed, unless we directly modify the code of the plugin. Getting a step farther Despite the good realization, our plugin still misses the point. Our goal was to activate the text substitution whenever the mouse pointer hovered over the text to be replaced, whereas our current implementation does it right after the page is loaded. We put it inside the "document ready" statement, after all! Time for action – our first plugin, Part II: Hovering By adding little pieces of code one at a time, we can easily understand what we are going to do and which is the best layout for our plugin. Activating the plugin whenever the mouse pointer hovers over the selected elements is surely another little step that adds up to reach our final goal. Back to our plugin file, we have to change the code so that the whole mechanism activates when the hover event is triggered. jQuery provides a function called hover(), which we can use to achieve our objective: jQuery.fn.txtHover = function() { return this.each(function() { jQuery(this).hover(function() { jQuery(this).text("Mouse hovered"); }); });}; Now, on to testing. Once the mouse pointer hovers over the text, it is effectively replaced by our string. But even if the mouse is moved, the text doesn't revert to the original. In fact, looking at the hover documentation, we see the function can also take a second argument, that is, the pointer to a function to be executed when the mouse goes off the element. Our modified code will now look like the following: jQuery.fn.txtHover = function() { return this.each(function() { var oldTxt = jQuery(this).text(); jQuery(this).hover(function() { jQuery(this).text("Mouse hover"); }, function() { jQuery(this).text(oldTxt); }); });}; The result is somewhat better now: the text is changed when we leave the pointer on the paragraph, and is changed again to its original form once the pointer is moved away. What just happened? We might be a little more satisfied with this evolution of our plugin, even though it's far from complete. Its functioning is fairly straightforward: we have made use of a variable (oldTxt) to store the old content, and we then have proceeded to using two anonymous functions (function(){ }), passed as arguments to the hover() function, to handle the mouse hover event (write our string) and the mouse out event (text back to original). There's still something to do though: We have used far too many instances of $(this) in our code and, on a larger scale application, a lot of memory would be wasted this way. It will be incredibly better for performance to make up a variable and use it every time we refer to the element with $(this). The text cannot be changed, unless we directly modify the code of the plugin. Have a go hero – html () versus text () Read the documentation for the html() function. Create a plugin that, once the mouse pointer hovers over an element, displays the HTML code of its content. The content should then change back to normal once the mouse pointer is moved away from that space. What happens if the html() and text() functions are used one inside the other, that is, $(sel).text($(sel).html()) or $(sel).html($(sel).text())? Just play around a little bit.
Read more
  • 0
  • 0
  • 864
Banner background image

article-image-menus-toolbars-and-buttons-ext-js-32
Packt
13 Oct 2010
8 min read
Save for later

Menus, Toolbars, and Buttons in Ext JS 3.2

Packt
13 Oct 2010
8 min read
  Learning Ext JS 3.2 Build dynamic, desktop-style user interfaces for your data-driven web applications using Ext JS Learn to build consistent, attractive web interfaces with the framework components Integrate your existing data and web services with Ext JS data support Enhance your JavaScript skills by using Ext's DOM and AJAX helpers Extend Ext JS through custom components An interactive tutorial packed with loads of example code and illustrative screenshots         What's on the menu? We will begin by introducing the Menu class which will be used in all following examples. We are going to demonstrate usage of the Menu class as both a static component within a page, and as a popup. Both menus will be configured with the same options by using a technique where we define a variable called menuItems to reference an array which specifies the menu's items, and use it in both cases. The Menu class inherits from Container, so any menu options are child Components specified in the items config. It also inherits the usual Component config options such as renderTo, and the width option. The static menu will be rendered to the document body, and in order for it to be rendered as a visible, static element in the document, we configure it with floating: false. So the configuration we end up with is as follows: new Ext.menu.Menu({ renderTo: document.body, width: 150, floating: false, items: menuItems }); The popup menu needs no extra configuring aside from its items. We do need to decide when and where to display it. In this case we will add a contextmenu (right click) event listener to the document, and show the menu at the mouse event's position: var contextMenu = new Ext.menu.Menu({ items: menuItems }); Ext.getDoc().on({ contextmenu: function(eventObj) { contextMenu.showAt(eventObj.getXY()); }, stopEvent: true }); When we run this example, the static menu will be visible. When we right click on the document, the result should be the two menus shown below. Notice how only the second, popup menu has a shadow to indicate that it floats above the document. The menu's items The menuItems variable references an array which should be familiar by now. Just like the items config of a FormPanel, it's a list of child Components or config objects. In a menu, a config object with no xtype creates a MenuItem Component. The MenuItem class accepts the following config options in addition to those it inherits: icon: The URL of an image to display as an icon iconCls: A CSS class name which allows a stylesheet to specify a background image to use as an icon text: The text to display handler: A function to call when the item is clicked menu: A Menu object, or Menu configuration object or an array of menu items to display as a submenu when the item is clicked Because a menu inherits from Container, it can accept other Components as child items. If some complex, menu option dependent input is required, a menu may be configured with a panel as a child item. The menu config of "Menu Option 2" we're creating next contains a FormPanel as its sole child item: { text: 'Menu Option 2', iconCls: 'flag-green', menu: { plain: true, items: { xtype: 'form', border: false, bodyStyle: 'background:transparent;padding:5px', labelWidth: 70, width: 300, defaults: { anchor: '100%' }, items: [{ xtype: 'combo', editable: false, fieldLabel: 'Select', triggerAction: 'all', store: [ [0, 'One or...'], [1 ,'The other']], value: 0, getListParent: function() { return this.el.up('div.x-menu'); } }, { xtype: 'textfield', fieldLabel: 'Title' }], fbar: [{ text: 'Submit' }] } } } The configurations in the above object will mostly be familiar by now. There is one extra config we use for the menu which contains the FormPanel. plain: Specify as true so that the menu does not have to show the incised line for separating icons from text The panel within the menu has the following configs: border: Specify as false to produce a panel with no borders. bodyStyle: A CSS style string to apply to the document body. We want to make it transparent to allow the menu to show, and we apply padding. The ComboBox must render its dropdown list to the menu's element so that clicking on the list does not trigger the menu to hide: GetListParent: This is a function which a ComboBox may be configured with. It must return the HTML element to render the dropdown list into. By default a ComboBox renders its dropdown into the document. We call the up function of the Ext.Element class to find the ancestor node of the combo's element which is a DIV which has the CSS class "x-menu". The FormPanel as a child of a menu will display like this: A toolbar for every occasion An Ext JS Panel, and every Ext JS Component which inherits from the Panel class (This includes Window, TreePanel, and GridPanel) can be configured to render and manage a toolbar docked above, or below the panel's body—or both if really necessary. These are referred to as the top and bottom toolbars, or tbar and bbar for short. Panels and subclasses thereof may also be configured with a footer bar which renders buttons right at the bottom of the panel—below any bottom toolbar. The Toolbar class is also an Ext JS Component in its own way, and may when necessary be used on its own, or as a child Component of any Container. Our second example renders a toolbar standalone into the body of the document. We will use all the main button types to illustrate their usage before moving on to add handlers to react to user interaction. The toolbar will contain the following child components: A basic button A button configured with a menu which is displayed when the button is clicked A SplitButton which will display a menu only when its arrow glyph is clicked A CycleButton which on click, cycles between three different options A pair of mutually exclusive toggle buttons of which only one may be in a "pressed" state at once Ext.onReady(function(){ new Ext.Toolbar({ renderTo: Ext.getBody(), items: [{ xtype: 'button', text: 'Button' },{ xtype: 'button', text: 'Menu Button', menu: [{ text: 'Better' },{ text: 'Good' },{ text: 'Best' }] },{ xtype: 'splitbutton', text: 'Split Button', menu: [{ text: 'Item One' },{ text: 'Item Two' },{ text: 'Item Three' }] }, { xtype: 'cycle', showText: true, minWidth: 100, prependText: 'Quality: ', items: [{ text: 'High', checked: true }, { text: 'Medium' }, { text: 'Low' }] }, { text: 'Horizontal', toggleGroup: 'orientation-selector' }, { text: 'Vertical', toggleGroup: 'orientation-selector' }] }); }); As usual, everything is inside our onReady event handler. The items config holds our toolbar's entire child Components—I say child Components and not buttons because as we now know, the toolbar can accept many different types of Ext JS Components including entire forms or just form fields—which we will be implementing later on in this article. The result of the above code looks like this: The default xtype for each element in the items config is button. We can leave out the xtype config element if button is the type we want, but I like to include it just for clarity. Button configuration In addition to inherited config options, a button accepts the following configurations which we will be using in the following examples for this article: icon: The URL of an image to display as an icon iconCls: A CSS class name which allows a stylesheet to specify a background image to use as an icon text: The text to display handler: A function to call when the button is clicked menu: A Menu object, or Menu configuration object, or an array of menu items to display as a submenu when the button is clicked enableToggle: Specify as true to make a single button toggleable between pressed and unpressed state toggleGroup: A mnemonic string identifying a group of buttons of which only one may be in a "pressed" state at one time toggleHandler: A function to be called when a button's "pressed" state is changed A basic button Creating a button is fairly straightforward; the main config option is the text that is displayed on the button. We can also add an icon to be used alongside the text if we want to. A handler function is called when the button is clicked. Here is the most basic configuration of a button: { xtype: 'button', text: 'Button', handler: functionReference } The following screenshot shows what happens when the mouse is hovered over the Button button: Button with a menu A button may be configured to act as a trigger for showing a dropdown menu. If configured with a menu option, clicking the button displays a menu below the button. The alignment of the menu is configurable, but defaults to being shown below the button. Each option within the menu may itself be configured with a menu option allowing a familiar cascading menu system to be built very easily. The following is a config for a button which displays a dropdown menu upon click: { xtype: 'button', text: 'Button', menu: [{ text: 'Better' },{ text: 'Good' },{ text: 'Best' }] } The following screenshot shows what happens when the Menu Button is clicked on, and the mouse is hovered over the Best option:
Read more
  • 0
  • 0
  • 3553

article-image-integrating-discussions-wiki-and-blog-oracle-webcenter
Packt
23 Sep 2010
9 min read
Save for later

Integrating Discussions, Wiki, and Blog with Oracle WebCenter

Packt
23 Sep 2010
9 min read
  Web 2.0 Solutions with Oracle WebCenter 11g Learn WebCenter 11g fundamentals and develop real-world enterprise applications in an online work environment Create task-oriented, rich, interactive online work environments with the help of the comprehensive Oracle WebCenter Suite 11g Accelerate the development of Enterprise 2.0 solutions by leveraging the Oracle tools Apply the basic concepts of Enterprise 2.0 for your business solutions by understanding them completely Prepare development environments that suit your enterprise needs using WebCenter applications Define collaborative work environments for the members of your organization Read more about this book (For more resources on Oracle, see here.) The Oracle WebCenter Discussions Service provides a forum tool that can be used within the organization to share information and foster collaboration. Forums are one of the most popular information exchange mechanisms used on the Internet. We are all familiar with the power of forums to create user communities and disseminate information. If you have used the Oracle Technology Network Forums, the WebCenter Discussions Service will look very familiar to you—both use the same powerful forum software. (Move the mouse over the image to enlarge.) Some of the common challenges we have found with implementing forums for the enterprise have been related to the following: Integration into the existing technology stack User and group administration using existing LDAP Support Cultural adoption With Discussions, Oracle has successfully minimized the technical level of effort required during implementation by addressing the main technical challenges (numbers 1 and 2 above). And with formal support offered, as well as the general idea that forums integration using Discussions will not be unique for each implementation, organizations can feel more comfortable about viable long-term support. Given how well Discussions addresses the first three challenges, we now view the cultural adoption as the largest challenge, which can be overcome with effective training and strategic management decisions. Forums in general have become increasingly popular within organizations and Oracle has implemented an intuitive end-user approach. The Discussions Server is installed as part of Oracle WebCenter installation. It can be accessed directly at the default location http://hostname:8890/owc_discussions. While Discussions in itself is a useful forum tool, the real power comes from the Discussions Service that enables us to embed, view, and interact with the forums as part of custom WebCenter applications where forums can be used as a means to obtain and share information by the enterprise from within the current application being used. A Wiki is a website that allows a group of editors to easily create web pages in a collaborative manner using simple markup language. The markup language helps to organize and format the web pages. Many corporations use Wikis to create documentation internally. Each employee of the company has easy access to edit the information within the wiki, and can therefore incorporate new information for the entire community. A Blog is a website that allows an individual author to post a stream of content for a community to view. The word "Blog" is derived from the contraction of "Web Log". Each item in the Blog stream can be viewed as an article. An article can be something as simple as a video or image posting, or something as formal as a newspaper article. It is up to the author to decide what content to post. Typically, authors allow members of a community to engage in dialog related to the blog article by allowing them to post comments on the article. Both the Wiki Service and the Blog Service depend on the WebCenter Wiki and Blog Server. Both services use a single connection to the single backend server. Discussions configuration Our application is built with JDeveloper 11g and connects to the Oracle WebCenter Discussions Server. Ensure that the Discussions Server is running by connecting to the Discussions Server URL (http://hostname:8890/owc_discussions). It is a good idea to populate the Discussions Server with some example users, categories, and forums. The Forums Admin Console is used to create new users and forums. The admin console can be accessed at http://hostname:8890/owc_discussions/admin. The default admin username is weblogic and the password is weblogic. This is irrespective of any username/password specified at installation time. You can configure a wide variety of settings in the Admin Console, including but not limited to the following: Content Structure: Categories, Subcategories, Forums Users and Groups Permissions Filters and Interceptors Moderation User Interface (Colors and themes) Reports and Metrics System Settings (Cache, e-mail, locale, and so on) Plugins The following is a screenshot of the admin console home page: We will not focus on configuring all aspects of the forum, but instead the main pieces required to get started and integrated into WebCenter. Specifically, we will focus on users/groups, and the overall forum content structure. Content structure The general structure for Discussions is as follows: Categories: logical grouping of discussions content Subcategories: optional subgrouping(s) Forums: Lowest-level grouping where end users can create discussion threads Threads: Entries made by end users, which contain the original entry as well as replies Within the Forums Admin Console, you can define a structure that makes sense for your organization. The same principles that apply to other forum software will apply to Discussions content structure. For the purpose of this demo, we have created a Marketing category and a Marketing Ideas forum. The following is an image of that Category Summary that reflects the new Marketing Ideas forum that was just created: User and group structure We have worked on this Discussions Service as an independent service, and hence created our users and groups manually. In an enterprise solution, you will likely hook this into your LDAP (like Oracle Internet Directory) using Enterprise Manager. Following is a screenshots of the users we have configured within the Discussions administration: As shown previously, we have added two users— amit and jimmy—with the appropriate privileges to post to the Marketing Ideas forum. Integrating Discussions with WebCenter During the remainder of this article, we will create a custom WebCenter application, which will integrate with the Discussions Server and in which we will expose a view of the forum that allows you to interact with the forum using task flows. Oracle exposes multiple task flows for the Discussions service. We have listed all the out-of-box task flows in the following table and bolded the ones we will drop into our WebCenter application: Task Flow Description Discussions Forums This task flow shows all the topics associated with a specified forum. Users can create, read, update, and delete topics based on their privileges. Discussions - Popular Topics Shows the popular topics under a given category ID or forum ID. Discussions - Recent Topics Shows all the recent topics under a given category ID or forum ID. Discussions - Watched Forums Allows users to see all their watched forums under a given category ID. Discussions - Watched Topics Allows users to see all their watched topics under a given category ID or forum ID. Discussions - Sidebar View This shows a combined view of the Popular Topics, Recent Topics, Watched Topics, and Watched Forums task flows. The main steps we will complete are as follows: Ensure the Discussions Server is running Create a WebCenter application Create a connection to the Discussions Server Create a JSF page Select appropriate Discussions Service Task Flow and embed it in the page Deploy, run, and test the application Ensuring the Discussions Server is running Before we start developing the application, ensure that the WebCenter Discussions Server is running. If the Discussions Server is not running then start the WLS_Services managed server. Using a browser, open the URL for Discussions (for example, http://hostname:8890/owc_discussions). Log in with the newly created users and post some sample articles. Creating a new WebCenter application Using JDeveloper, create a new application, selecting the WebCenter application template. Creating a JSF page Next, we will create a JSF page to host the view for the Discussions Forum. In the Application Navigator, highlight the ViewController project, right-click on it, and from the context menu select New. In the New Gallery, select JSF Page to create a new JSF page. In the Create JSF Page dialog, select a name for the page and create the page. Creating a connection to the Discussion Forum In order to use the Discussions Service in our application, we need to create a Discussions Forum connection to the Discussions Server. This connection will be used by the application to connect to the backend Discussions Server. Note that it is possible to modify the connection after the application is deployed to the WebLogic server by using the Enterprise Manager Fusion Middleware Control. To set up the discussions connection, right-click on the Connections node in Application Resources pane and select New Connection | Discussions Forum. Select a unique name for the connection such as MyDiscussions. Ensure that the Make this the connection default checkbox is ticked. Next, we have to set the property values for the forum.url and admin.user. The value of forum.url should be the URL of the Discussions server. The default admin user is weblogic. Click on Test Connection to ensure that the connection is set up properly. Click Finish to complete this step. Click on the Resource Palette and open My Catalogs. You should now see the WebCenter Services Catalog. Expand the catalog to view the various task flows available. In the task flows shown, you see the various discussions-related task flows such as the Discussion Forums, Discussions – Popular Topics, Discussions - Recent Topics, and so on. We will use these task flows in our application to create a view of the Discussion Forum.
Read more
  • 0
  • 0
  • 1693

article-image-oracle-webcenter-11g-portlets
Packt
21 Sep 2010
6 min read
Save for later

Oracle WebCenter 11g: Portlets

Packt
21 Sep 2010
6 min read
(For more resources on Oracle, see here.) Portlets, JSR-168 specification Specification JSR-168, which defines the Java technologies, gives us a precise definition of Java portlets: Portlets are web components—like Servlets—specifically designed to be aggregated in the context of a composite page. Usually, many Portlets are invoked to in the single request of a Portal page. Each Portlet produces a fragment of markup that is combined with the markup of other Portlets, all within the Portal page markup. You can see more detail of this specification on the following page: http://jcp.org/en/jsr/detail?id=168 While the definition makes a comparison with servlets, it is important to note that the portlets cannot be accessed directly through a URL; instead, it is necessary to use a page-like container of portlets. Consequently, we might consider portlets as tiny web applications that return dynamic content (HTML, WML) into a region of a Portal page. Graphically, we could view a page with portlets as follows: Additionally, we must emphasize that the portlets are not isolated from the rest of the components in the pages, but can also share information and respond to events that occur in other components or portlets. WSRP specification The WSRP specification allows exposing portlets as Web services. For this purpose, clients access portlets through an interface (*. wsdl) and get graphic content associated. Optionally, the portlet might be able to interact directly with the user through events ocurring on them. This way of invoking offers the following advantages: The portals that share a portlet centralize their support in a single point. The portlet integration with the portal is simple and requires no programming. The use of portlets, hosted on different sites, helps to reduce the load on servers. WebCenter portlets Portlets can be built in different ways, and the applications developed with Oracle WebCenter can consume any of these types of portlets. JSF Portlets: This type of portlet is based on a JSF application, which is used to create a portlet using a JSF Portlet Bridge. Web Clipping: Using this tool, we can build portlets declaratively using only a browser. These portlets show content from other sites. OmniPortlet: These portlets can retrieve information from different types of data sources (XML, CSV, database, and so on) to expose different ways of presenting things, such as tables, forms, charts, and so on. Content Presenter: This allows you to drop content from UCM on the page and display this content in any way you like or using a template. Ensemble: This is a way to "mashup" or produce portlets or "pagelets" of information that can be displayed on the page. Programmatic Portlets: Obviously, in addition to the previous technologies that facilitate the construction of portlets, it is also possible to build in a programmatic way. When we build in this way, we reach a high degree of personalization and control. However, we need specialized Java knowledge in order to program in this way. As we can see, there are several ways in which we can build a portlet; however, in order to use the rich components that the ADF Faces framework offers, we will focus on JSF Portlets. Developing a portlet using ADF The portlet that we will build will have a chart, which shows the status of the company's requests. To do this, we must create a model layer that represents our business logic and exposes this information in a page. Therefore, we are going to do the following steps: Create an ADF application. Develop business components. Create a chart page. Generate a portlet using the page. Deploy the portlet. In this example, we use a page for the construction of a portlet; however, ADF also offers the ability to create portlets based on a flow of pages through the use of ADF TaskFlows. You can find more information on the following link: http://download.oracle.com/docs/cd/E15523_01/web.1111/b31974/taskflows.htm#BABDJEDD Creating an ADF application To create the application, do the following steps: Go to JDeveloper. In the menu, choose the option File | New to start the wizard for creating applications. In the window displayed, choose the Application category and choose the Fusion Web Application ADF option and press the OK button. Next, enter the following properties for creating the application: Name: ParacasPortlet Directory: c:ParacasPortlet Application Package Prefix : com.paracasportlet Click Finish to create the application. Developing business components Before starting this activity, make sure you have created a connection to the database. In the project Palette, right-click on Project Model, and choose New. On the next page, select the category Business Tier | ADF Business Components and choose Business Components from Tables. Next, press the OK button. In the following page, you configure the connection to the database. At this point, select the connection db_paracas and press the OK button. In order to build a page with a chart, we need to create a read-only view. For this reason, don't change anything, just press the Next button. (Move the mouse over the image to enlarge.) In this next step, we can create updateable views. But, we don't need this type of component. So, don't change anything. Click the Next button. Now, we need to allow the creation of read-only views. We will use this kind of component in our page; therefore select the table REQUEST, as shown next and press Next. Our next step will allow the creation of an application module. This component is necessary to display the read-only view in the whole application. Keep this screen with the suggested values and click the Finish button. Check the Application Navigator. You must have your components arranged in the same way as shown in the following screenshot: Our query must determine the number of requests for status. Therefore, it will be necessary to make some changes in the created component. To start, double-click on the view RequestView, select the Query category, and click on the Edit SQL Query option as shown in the following screenshot: In the window shown, modify the SQL as shown next and click the OK button. SELECT Request.STATUS, COUNT(*) COUNT_STATUSFROM REQUEST RequestGROUP BY Request.STATUS We only use the attributes Status and CountStatus. For this reason, choose the Attributes category, select the attributes that are not used, and press Delete selected attribute(s) as shown in the following screenshot: Save all changes and verify that the view is similar to that shown next:
Read more
  • 0
  • 0
  • 1415
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 £15.99/month. Cancel anytime
article-image-netbeans-platform-69-working-window-system
Packt
17 Aug 2010
12 min read
Save for later

NetBeans Platform 6.9: Working with Window System

Packt
17 Aug 2010
12 min read
(For more resources on NetBeans, see here.) Window System Large desktop applications need to provide many different views for visualizing data. These views have to be managed and shown and the NetBeans Platform handles these requirements for you out of the box via its docking framework. While it once might have been sufficient for a docking framework to provide static fixed window layouts, today the user expects far more flexibility. Windows should be able to be opened, movable, and, generally, customizable at runtime. The user tends to assume that the positions of views are modifiable and that they persist across restarts of the application. Not only that, but applications are assumed to be so fiexible that views should be detachable from the application's main window, enabling them to be displayed on multiple monitors at the same time. While once the simple fact of the availability of menus and toolbars was sufficient, today a far more dynamic handling is needed so that window content can be adapted dynamically. Connected to these expectations of flexibility, plugins are increasingly becoming a standard technology, with the user assuming their windows to be pluggable, too. In short, the requirements for window management have become quite complex and can only be met by means of an external docking framework, otherwise all these various concerns would need to be coded (and debugged, tested, and maintained) by hand. The NetBeans Platform provides all of these features via its docking framework, known as the NetBeans Window System. It also provides an API to let you programmatically access the window system. Together, the window system and its API fulfill all the requirements described above, letting you concentrate on your domain knowledge and business logic rather than on the work of creating a custom window management facility for each of your applications. This part of the article teaches you the following: How to define views How to position views in the main window Rest is covered in the second part of this article series. Creating a window The NetBeans Window System simplifies window management by letting you use a default component for displaying windows. The default component, that is, the superclass of all windows, is the TopComponent class, which is derived from the standard JComponent class. It defines many methods for controlling a window and handles notification of main window system events. The WindowManager is the central class controlling all the windows in the application. Though you can implement this class yourself, this is seldom done as normally the default WindowManager is sufficient. Similarly, you typically use the standard TopComponent class, rather than creating your own top-level Swing components. In contrast to the TopComponent class, the default WindowManager cannot manage your own top-level Swing components, so these cannot take advantage of the Window System API. Now let's create a TopComponent and let it be an editor for working with tasks. This is done easily by using the New Window wizard. In the Projects window, right-click the TaskEditor module project node and choose New | Window. On the first page of the wizard select Editor for Window Position and Open on Application Start. Click Next. In the next page of the wizard, type TaskEditor in Class Name Prefix. This prefix is used for all the generated files. It is possible to specify an icon that will be displayed in the tab of the new window, but let's skip that for the moment. Click Finish and all the files are generated into your module source structure. Next, open the newly created TaskEditorTopComponent and drag the TaskEditorPanel from the Palette, which is where you put it at the end of the last chapter, onto the form. The size of the component automatically adjusts to the required size of he panel. Position the panel with the preferred spacing to the left and top and activate the automatic resizing of the panel in horizontal and vertical direction. The form should now look similar to the following screenshot: Start the application. You now see a tab containing the new TaskEditor Window, which holds your form. Examining the generated files You have used a wizard to create a new TopComponent. However, the wizard did more than that. Let's take a look at all the files that have been created and at all the files that have been modified, as well as how these files work together. The only Java class that was generated is the TopComponent that will contain the TaskEditor, shown as follows: @ConvertAsProperties(dtd = "-//com.netbeansrcp.taskeditor//TaskEditor// EN", autostore = false) public final class TaskEditorTopComponent extends TopComponent { private static TaskEditorTopComponent instance; /** path to the icon used by the component and its open action */ // static final String ICON_PATH = "SET/PATH/TO/ICON/HERE"; private static final String PREFERRED_ID = "TaskEditorTopComponent"; public TaskEditorTopComponent() { initComponents(); setName(NbBundle.getMessage(TaskEditorTopComponent.class, "CTL_TaskEditorTopComponent")); setToolTipText(NbBundle.getMessage(TaskEditorTopComponent.class, "HINT_TaskEditorTopComponent")); // setIcon(ImageUtilities.loadImage(ICON_PATH, true)); } /**This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() { javax.swing.GroupLayout layout = new javax.swing. GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout. Alignment.LEADING).addGap(0, 555, Short.MAX_VALUE)); layout.setVerticalGroup(layout.createParallelGroup( javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 442, Short.MAX_VALUE) ); }// </editor-fold> // Variables declaration - do not modify // End of variables declaration /** * Gets default instance. Do not use directly: reserved for *.settings files only, * i.e. deserialization routines; otherwise you could get a non-deserialized instance. * To obtain the singleton instance, use {@link #findInstance}. */ public static synchronized TaskEditorTopComponent getDefault() { if (instance == null) { instance = new TaskEditorTopComponent(); } return instance; } /** * Obtain the TaskEditorTopComponent instance. Never call { @link #getDefault} directly! */ public static synchronized TaskEditorTopComponent findInstance() { TopComponent win = WindowManager.getDefault().findTopComponent (PREFERRED_ID); if (win == null) { Logger.getLogger(TaskEditorTopComponent.class.getName()). warning("Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); return getDefault(); } if (win instanceof TaskEditorTopComponent) { return (TaskEditorTopComponent) win; } Logger.getLogger(TaskEditorTopComponent.class.getName()). warning("There seem to be multiple components with the '" + PREFERRED_ID + "' ID. That is a potential source of errors and unexpected behavior."); return getDefault(); } @Override public int getPersistenceType() { return TopComponent.PERSISTENCE_ALWAYS; } @Override public void componentOpened() { // TODO add custom code on component opening } @Override public void componentClosed() { // TODO add custom code on component closing } void writeProperties(java.util.Properties p) { // better to version settings since initial version as advocated at // http://wiki.apidesign.org/wiki/PropertyFiles p.setProperty("version", "1.0"); // TODO store your settings } Object readProperties(java.util.Properties p) { if (instance == null) { instance = this; } instance.readPropertiesImpl(p); return instance; } private void readPropertiesImpl(java.util.Properties p) { String version = p.getProperty("version"); // TODO read your settings according to their version } @Override protected String preferredID() { return PREFERRED_ID; } } As expected, the class TaskEditorTopComponent extends the TopComponent class. Let's look at it more closely: For efficient resource usage, the generated TopComponent is implemented as a singleton. A private constructor prohibits its incorrect usage from outside by disallowing direct instantiation of the class. The static attribute instance holds the only instance in existence. The static method getDefault creates and returns this instance if necessary on demand. Typically, getDefault should never be called directly. Instead of this, you should use findInstance, which delegates to getDefault if necessary. findInstance tries to retrieve the instance using the Window Manager and the ID of the TopComponent before falling back to the singleton instance. This ensures the correct usage of persistent information. The constructor creates the component tree for the TaskEditorTopComponent by calling the method init Components(). This method contains only code generated via the NetBeans "Matisse" Form Builder and is read-only in the NetBeans Java editor. You can change the code in this method using the Form Builder's Property Sheet, as will be shown later. The static property PreferredID holds the TopComponent ID used for identification of the TopComponent. As indicated by its name, the ID can be changed by the Window System, if name clashes occur. The ID is used throughout all the configuration files. The methods componentOpened() and componentClosed() are part of the lifecycle of the TopComponent. You learn about the method getPersistenceType() later, in the section about the persistence of TopComponents. What does the Java code do and not do? The Java code only defines the visual aspects of the TaskEditorTopComponent and manages the singleton instance of this component. In no way does the code describe how and where the instance is shown. That's the task of the two XML files, described below. Two small XML files are created by the wizard. The first is the TopComponent's settings file: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE settings PUBLIC "-//NetBeans//DTD Session settings 1.0//EN" "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd"> <settings version="1.0"> <module name="com.netbeansrcp.taskeditor" spec="1.0"/> <instanceof class="org.openide.windows.TopComponent"/> <instanceof class="com.netbeansrcp.taskeditor. TaskEditorTopComponent"/> <instance class="com.netbeansrcp.taskeditor.TaskEditorTopComponent" method="getDefault"/> </settings> The settings file describes the persistent instance of the TopComponent. As you can see, the preceding configuration describes that the TopComponent belongs to the module TaskEditor in the specification version "1.0" and that it is an instance of the types TopComponent and TaskEditorTopComponent. Also described is that the instance that is created is done so using the method call TaskEditorTopComponent.getDefault(). <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE tc-ref PUBLIC "-//NetBeans//DTD Top Component in Mode Properties 2.0//EN" "http://www.netbeans.org/dtds/tc-ref2_0.dtd"> <tc-ref version="2.0" > <module name="com.netbeansrcp.taskeditor" spec="1.0"/> <tc-id id="TaskEditorTopComponent"/> <state opened="true"/> </tc-ref> The WSTCREF (window system creation file) describes the position of the TopComponent within the main window. This becomes clearer with the following file. The other important information in the WSTCREF file is the opened state at application start. Typically, you do not have to change these two configuration files by hand. This is not true for the following file, the layer.xml, which you often need to change manually, to register new folders and files in the filesystem. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http:// www.netbeans.org/dtds/filesystem-1_2.dtd"> <filesystem> <folder name="Actions"> <folder name="Window"> <file name="com-netbeansrcp-taskeditor.TaskEditorAction.instance"> <attr name="component" methodvalue="com.netbeansrcp.taskeditor. TaskEditorTopComponent.findInstance"/> <attr name="displayName" bundlevalue="com.netbeansrcp.taskeditor. Bundle#CTL_TaskEditorAction"/> <attr name="instanceCreate" methodvalue="org.openide.windows. TopComponent.openAction"/> </file> </folder> </folder> <folder name="Menu"> <folder name="Window"> <file name="TaskEditorAction.shadow"> <attr name="originalFile" stringvalue="Actions/Window/com netbeansrcp-taskeditor-TaskEditorAction.instance"/> </file> </folder> </folder> <folder name="Windows2"> <folder name="Components"> <file name="TaskEditorTopComponent.settings" url="TaskEditorTopComponentSettings.xml"/> </folder> <folder name="Modes"> <folder name="editor"> <file name="TaskEditorTopComponent.wstcref" url="TaskEditorTopComponentWstcref.xml"/> </folder> </folder> </folder> </filesystem> The layer.xml is integrated into the central registry (also known as the SystemFileSystem) using the via a registration entry in the module's manifest file. The SystemFileSystem is a virtual filesystem for user settings. Each module can supply a layer file for merging configuration data from the module into the SystemFileSystem. The Window System API and the Actions API reserve a number of folders in the central registry for holding its configuration data. These folders enable specific subfolders and files relating to window system registration to be added to the filesystem. Let's have a look at the folder Windows2. Windows2 contains a folder named Components, which contains a virtual file with the name of the TopComponent and the extension .settings. This .settings file redirects to the real settings file. It is used to make the configuration known to the Window System. In addition, the Windows2 folder contains a folder named Modes, which contains a folder named editor. Modes represent the possible positions at which TopComponents can be shown in the application. The editor folder contains a .wstcref file for our TopComponent, which refers to the real WSTCREF file. This registers the TopComponent in the mode editor, so it shows up where typically editor windows are opened, which is the central part of the main window. Next , take a look at the folder Actions. It contains a folder named Window which contains a file declaring the action opening the TaskEditorTopComponent. The name is typically following Java class naming conventions with dots replaced by dashes and ending in .instance. The declaration of the virtual file itself consists of three critical parts. The attribute component describes how to create the component (methodvalue declares which method to call). The attribute displayName describes the default action name as shown in the example, in menu items. A possible declaration is the bundle value which describes the bundle and key to use to retrieve the display name. The attribute instanceCreate uses a static method call to create a real action to use. The folder Menu describes the application main menu. The folder Window contains a .shadow file. The attribute originalFile uses the full path in the SystemFileSytem to delegate to the original action declaration. As described above, .shadow files are used as symbolic links to real-defined virtual files. This declaration adds the action to the real menu bar of the application. As a result, important parts of the Window System API are not called programmatically, but are simply used declaratively. Declarative aspects include configuration and the positioning of windows, as well as the construction of the menu. In addition, you discovered that the wizard for creating TopComponents always creates singleton views. If you would like to change that, you need to adapt the code created by the wizard. For the time being, it is sufficient to use the singleton approach, particularly as it is more resource-friendly.
Read more
  • 0
  • 0
  • 1139

article-image-netbeans-platform-69-advanced-aspects-window-system
Packt
17 Aug 2010
5 min read
Save for later

NetBeans Platform 6.9: Advanced Aspects of Window System

Packt
17 Aug 2010
5 min read
(For more resources on NetBeans, see here.) Creating custom modes You can get quite far with the standard modes provided by the NetBeans Platform. Still, sometimes you may need to provide a custom mode, to provide a new position for the TopComponents within the application. A custom mode is created declaratively in XML files, rather than programmatically in Java code. In the following example, you create two new modes that are positioned side by side in the lower part of the application using a specific location relative to each other. Create a new module named CustomModes, with Code Name Base com.netbeansrcp.custommodes, within the existing WindowSystemExamples application. Right-click the module project and choose New | Other to open the New File dialog. Then choose Other | Empty File, as shown in the following screenshot: Type mode1.wsmode as the new filename and file extension, as shown in the following screenshot. Click Finish. Define the content of the new mode1.wsmode as follows: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mode PUBLIC "-//NetBeans//DTD Mode Properties 2.3//EN" "http://www.netbeans.org/dtds/mode-properties2_3.dtd"> <mode version="2.3"> <name unique="mode1" /> <kind type="view" /> <state type="joined" /> <constraints> <path orientation="vertical" number="20" weight="0.2"/> <path orientation="horizontal" number="20" weight="0.5"/> </constraints> </mode> Create another file to define the second mode and name it mode2.wsmode. Add this content to the new file: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mode PUBLIC "-//NetBeans//DTD Mode Properties 2.3//EN" "http://www.netbeans.org/dtds/mode-properties2_3.dtd"> <mode version="2.3"> <name unique="mode2" /> <kind type="view" /> <state type="joined" /> <constraints> <path orientation="vertical" number="20" weight="0.2"/> <path orientation="horizontal" number="40" weight="0.5"/> </constraints> </mode> Via the two wsmode files described above, you have defined two custom modes. The first mode has the unique name mode1, with the second named mode2. Both are created for normal TopComponents (view instead of editor) that are integrated into the main window, rather than being undocked by default (joined instead of separated). The constraints elements in the files are comparable to GridBagLayout, with a relative horizontal and vertical position, as well as a relative horizontal and vertical weight. You place mode1 in position 20/20 with a weighting of 0,5/0,2, while mode2 is placed in position 20/40 with the same weighting. If all the other defined modes have TopComponents opened within them, the TopComponents in the two new modes should lie side by side, right above the status bar, taking up 20% of the available vertical space, with the horizontal space shared between them. Let us now create two new TopComponents and register them in the layer.xml file so that they will be displayed in your new modes. Do this by using the New Window wizard twice in the CustomModes module, first creating a window called Class Name Prefix Red and then a window with Class Name Prefix Blue. What should I set the window position to? In the wizard, in both cases, it does not matter what you set to be the window position, as you are going to change that setting manually afterwards. Let both of them open automatically when the application starts. In the Design mode of both TopComponents, add a JPanel to each of the TopComponents. Change the background property of the panel in the RedTopComponent to red and in the BlueTopComponent to blue. Edit the layer.xml of CustomModes module, registering the two .wsmode files and ensuring that the two new TopComponents open in the new modes: <folder name="Windows2"> <folder name="Components"> <file name="BlueTopComponent.settings" url="BlueTopComponentSettings.xml"/> <file name="RedTopComponent.settings" url="RedTopComponentSettings.xml"/> </folder> <folder name="Modes"> <file name="mode1.wsmode" url="mode1.wsmode"/> <file name="mode2.wsmode" url="mode2.wsmode"/> <folder name="mode1"> <file name="RedTopComponent.wstcref" url="RedTopComponentWstcref.xml"/> </folder> <folder name="mode2"> <file name="BlueTopComponent.wstcref" url="BlueTopComponentWstcref.xml"/> </folder> </folder> </folder> As before, perform a Clean and Build on the application project node and then start the application again. It should look as shown in the following screenshot: In the summary, you defined two new modes in XML files and registered them in the module's layer.xml file. To confirm that the modes work correctly, you use the layer.xml file to register two new TopComponents so that they open by default into the new modes. As a result, you now know how to extend the default layout of a NetBeans Platform application with new modes.
Read more
  • 0
  • 0
  • 1022

article-image-netbeans-platform-69-working-actions
Packt
10 Aug 2010
4 min read
Save for later

NetBeans Platform 6.9: Working with Actions

Packt
10 Aug 2010
4 min read
(For more resources on NetBeans, see here.) In Swing, an Action object provides an ActionListener for Action event handling, together with additional features, such as tool tips, icons, and the Action's activated state. One aim of Swing Actions is that they should be reusable, that is, can be invoked from a menu item as well as a related toolbar button and keyboard shortcut. The NetBeans Platform provides an Action framework enabling you to organize Actions declaratively. In many cases, you can simply reuse your existing Actions exactly as they were before you used the NetBeans Platform, once you have declared them. For more complex scenarios, you can make use of specific NetBeans Platform Action classes that offer the advantages of additional features, such as more complex displays in toolbars and support for context-sensitive help. Preparing to work with global actions Before you begin working with global Actions, let's make some changes to our application. It should be possible for the TaskEditorTopComponent to open for a specific task. You should therefore be able to pass a task into the TaskEditorTopComponent. Rather than the TaskEditorPanel creating a new task in its constructor, the task needs to be passed into it and made available to the TaskEditorTopComponent. On the other hand, it may make sense for a TaskEditorTopComponent to create a new task, rather than providing an existing task, which can then be made available for editing. Therefore, the TaskEditorTopComponent should provide two constructors. If a task is passed into the TaskEditorTopComponent, the TaskEditorTopComponent and the TaskEditorPanel are initialized. If no task is passed in, a new task is created and is made available for editing. Furthermore, it is currently only possible to edit a single task at a time. It would make sense to be able to work on several tasks at the same time in different editors. At the same time, you should make sure that the task is only opened once by the same editor. The TaskEditorTopComponent should therefore provide a method for creating new or finding existing editors. In addition, it would be useful if TaskEditorPanels were automatically closed for deleted tasks. Remove the logic for creating new tasks from the constructor of the TaskEditorPanel, along with the instance variable for storing the TaskManager, which is now redundant: public TaskEditorPanel() { initComponents(); this.pcs = new PropertyChangeSupport(this); } Introduce a new method to update a task: public void updateTask(Task task) { Task oldTask = this.task; this.task = task; this.pcs.firePropertyChange(PROP_TASK, oldTask, this.task); this.updateForm(); } Let us now turn to the TaskEditorTopComponent, which currently cannot be instantiated either with or without a task being provided. You now need to be able to pass a task for initializing the TaskEditorPanel. The new default constructor creates a new task with the support of a chained constructor, and passes this to the former constructor for the remaining initialization of the editor. In addition, it should now be able to return several instances of the TaskEditorTopComponent that are each responsible for a specific task. Hence, the class should be extended by a static method for creating new or finding existing instances. These instances are stored in a Map<Task, TaskEditorTopComponent> which is populated by the former constructor with newly created instances. The method checks whether the map for the given task already stores a responsible instance, and creates a new one if necessary. Additionally, this method registers a Listener on the TaskManager to close the relevant editor for deleting a task. As an instance is now responsible for a particular task this should be able to be queried, so we introduce another appropriate method. Consequently, the changes to the TaskEditorTopComponent looks as follows: private static Map<Task, TaskEditorTopComponent> tcByTask = new HashMap<Task, TaskEditorTopComponent>();public static TaskEditorTopComponent findInstance(Task task) { TaskEditorTopComponent tc = tcByTask.get(task); if (null == tc) { tc = new TaskEditorTopComponent(task); } if (null == taskMgr) { taskMgr = Lookup.getDefault().lookup(TaskManager.class); taskMgr.addPropertyChangeListener(newListenForRemovedNodes()); } return tc;}private class ListenForRemovedNodes implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent arg0) { if (TaskManager.PROP_TASKLIST_REMOVE.equals (arg0.getPropertyName())) { Task task = (Task) arg0.getNewValue(); TaskEditorTopComponent tc = tcByTask.get(task); if (null != tc) { tc.close(); tcByTask.remove(task); } } }}private TaskEditorTopComponent() { this(Lookup.getDefault().lookup(TaskManager.class)); }private TaskEditorTopComponent(TaskManager taskMgr) { this((taskMgr != null) ? taskMgr.createTask() : null); }private TaskEditorTopComponent(Task task) { initComponents();// ... ((TaskEditorPanel) this.jPanel1).updateTask(task); this.ic.add(((TaskEditorPanel) this.jPanel1).task); this.associateLookup(new AbstractLookup(this.ic)); tcByTask.put(task, this); }public String getTaskId() { Task task = ((TaskEditorPanel) this.jPanel1).task; return (null != task) ? task.getId() : ""; } With that our preparations are complete and you can turn to the following discussion on Actions.
Read more
  • 0
  • 0
  • 2164

article-image-using-spring-jmx-within-java-applications
Packt
04 Aug 2010
6 min read
Save for later

Using Spring JMX within Java Applications

Packt
04 Aug 2010
6 min read
(For more resources on Java, see here.) Yet for all its powerful capabilities, JMX is greatly underutilized and few developers seem to take advantage of its power. I attribute this underutilization to two factors: the scope of the Java universe as well as JMX's complex development model. As a deep and wide universe composed of a seemingly infinite number of tools, frameworks, design patterns and a never ending stream of new thoughts and ideas, I believe that JMX rarely finds itself on the list of the next technologies a developer plans to explore. While other shiny objects steal the Java community spotlight, the benefits of JMX patiently wait to be discovered and seem to largely be the playing field of only seasoned Java veterans who have had the time or industry longevity to have already encountered it. In regard to its complex development model, JMX itself has an extremely low level, clumsy, and obtrusive API and that has directly hindered its adoption. While this complex development model is a fact of JMX life, the Spring framework, as with numerous other aspects of Java development, offers excellent JMX support that greatly simplifies and radically reduces the learning curve and time investment required to incorporate JXM into your application. Spring's JMX support transforms JMX from an obscure API into what could become a central component of your application's architecture. While all of this sounds great, a tangible example of how easy it is to incorporate Spring JMX (and therefore JMX itself) into your application will make things more concrete. The following code and configuration sample presents a classic example of the benefits of JMX and is a piece of functionality which has proven its usefulness dozens upon dozens of times within my career: the ability to dynamically change an application's Log4j log level at runtime. Example 1: package com.spiegssoftware.common.util.management.logging; import org.apache.log4j.Category; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.springframework.jmx.export.annotation.ManagedOperation; /** * MBean exposing Log4j management operations. * <p> * This code is based on an example provided from http://uri.jteam.nl/?p=4 . */ public class Log4jJmxService { /** Logger for this class. */ private final Logger logger = Logger.getLogger(Log4jJmxService.class); @ManagedOperation(description = "Set this Logger to the DEBUG level") public boolean activateDebug(final String category) { return adjustLogLevel(category, Level.DEBUG); } @ManagedOperation(description = "Set this Logger to the INFO level") public boolean activateInfo(final String category) { return adjustLogLevel(category, Level.INFO); } @ManagedOperation(description = "Set this Logger to the WARN level") public boolean activateWarn(final String category) { return adjustLogLevel(category, Level.WARN); } @ManagedOperation(description = "Set this Logger to the ERROR level") public boolean activateError(final String category) { return adjustLogLevel(category, Level.ERROR); } @ManagedOperation(description = "Set this Logger to the FATAL level") public boolean activateFatal(final String category) { return adjustLogLevel(category, Level.FATAL); } protected boolean adjustLogLevel(final String category, final Level level) { boolean result = false; Category cat = LogManager.exists(category); if (cat == null) { logger.error("Logger '" + category + "' does not exist"); } else { logger.info("Activating " + level + " for category: " + category); cat.setLevel(level); result = true; } return result; } }   Example 2: <?xml version="1.0" encoding="UTF-8"?> <beans xsi_schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <bean id="log4jJmxService" class="com.spiegssoftware.common.util.management.logging.Log4jJmxService" /> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <util:map id="beans"> <entry key="com.spiegssoftware.common.util.management.logging:name=Log4jJmxService" value="log4jJmxService"/> </util:map> </property> </bean> </beans>   Based on code publicly available from Dev Thoughts, the class in Example 1 was decorated with Spring JMX annotations and the necessary Spring configuration was created in Example 2. To incorporate this functionality into your application, you will need to include this class (along with Spring JMX's dependencies) and the configuration into your project, rebuild, and deploy. Before starting your application server, you may need to enable its support for JMX; see the documentation for your specific application server for details. After your application server has started with JMX support enabled, any JMX console, such as the jConsole tool that ships with all recent JDK's, can be used to connect to the JVM the application is running within and the application's logging level can be adjusted without requiring a restart. The details of how to use jConsole are best left to its documentation, but for the impatient, jConsole can by be launched by opening a command window and issuing a "jconsole" command just as you would issue a "java -version". From there, select which JVM you wish to connect to; most likely you will want to connect to a local process. After selecting the MBeans tab, use the left hand navigation and find the Log4jJmxService under the key name you registered it under within your Spring configuration file; in Example 2 we chose to use a value of "log4jJmxService". After selecting the Log4jJmxService from the jConsole tree navigation and drilling down, you are presented with a screen that represents all of the public methods available on the Log4jJmxService. Simply clicking the invoke button next to each available public method results in the specified method on the Log4jJmxService being invoked just as if the bean's method had been invoked through traditional application user input; the application is unaware and indifferent as to the source of the invocation request and the normal execution flow takes place. You now have the ability to dynamically change the log level of your application at runtime. This JMX stuff is great, hu? With your toe now in the JMX waters, you're undoubtedly thinking of the numerous ways JMX can be incorporated within your applications: to inspect or alter application configuration, to access statistical data held within an application memory, or to manage an application by invoking application logic – all at runtime! JMX's uses are limited only by your creativity to incorporate it. JMX is so powerful and exposing your Spring based components through Spring JMX is so easy and convenient that it's likely you'll quickly find yourself wanting to expose every Spring bean throughout your entire application. While the two configuration strategies provided by Spring JMX (annotating classes or configuring beans in XML) are suitable for configuring a relatively low number of beans, when applied on a large scale each strategy has the disadvantage that it becomes tedious, verbose, and is the epitome of boilerplate; few would dispute that very quickly either your code or configuration becomes cluttered with JMX metadata. Having previously fallen into this advantageous trap of wanting to expose all Spring beans within an application multiple times before myself, it was time to take a step back and determine if this could accomplished in a better way.
Read more
  • 0
  • 0
  • 3084
article-image-yui-28-menus
Packt
26 Jul 2010
9 min read
Save for later

YUI 2.8: Menus

Packt
26 Jul 2010
9 min read
(For more resources on YUI, see here.) Common navigation structures All but the most limited of websites must have a mechanism by which visitors can navigate around the pages of the site from the home page. In order to meet accessibility guidelines, several methods of navigation will usually be available, including at least a navigation menu and a site map. There have been many different implementation styles that have been popular over the years. Before anyone really worried about accessibility or standards compliance, a common way of designing a navigation menu was to use a series of images that linked to other pages of the site, and there was also the popular frame-based navigation structure. While these methods saved the designer a lot of time, effort, and any real skill, they led to hugely increased page load times and a legacy of bad coding practice. Thankfully, those days have long since passed, and with the continued development of CSS, it's now possible to design an effective navigation structure based on semantic HTML and styled with CSS. Designing a navigation menu that is effective, robust, and presented effectively can still pose a challenge, and troubleshooting the compatibility of a menu between different browsers can be a very time-consuming process. This is where the YUI steps in. Instant menus—just add water (or a Menu Control) The Menu Control is used to add one of several different menus to your website, saving you the chore of adding this almost essential feature yourself. It's another control that takes a complex, difficult, or time-consuming task, and one which is an almost inherent requirement of any website, and packages it up into a convenient and easy-to-use module. The three different types of menu you can create are: A standard navigation menu An application-style menu bar A right-click context menu The navigation menu can be implemented as either a vertical or horizontal menu and generates a clean and attractive interface, which your visitors can use to navigate to different areas of your site. The navigation model of any site is key to whether using the site is easy and enjoyable; nothing turns off visitors more than a poorly designed or inconsistent navigation structure. Another type of menu that the Menu Control is able to create is an application-style menu bar, which stretches across the screen horizontally, building on the current trend in the online world to blur the distinction between the browser and the desktop. As well as taking care of navigation for you, it can also be used to add right-click context (pop-up) menus to any part of your web application, which again can give a web application a definite desktop feel to it. The Menu Control is very flexible and can be built from existing HTML markup using a clean and logical list structure, or it can be generated entirely through JavaScript and built at runtime. Each of the different menu types is also given a default appearance with the sam skin so there is very little that is required to generate the attractive and highly functional menus. We'll be looking at implementing each of the different types of menu ourselves in just a moment. Before we do this, let's take a quick look at the classes that go together to make the Menu Control. The Menu classes This component is made up of a small family of different types of menu. There is a range of different classes that work together to bring the functionality of the different types of menu to you. The three main classes behind the Menu family are: YAHOO.widget.Menu YAHOO.widget.ContextMenu YAHOO.widget.MenuBar Menu is a subclass of Overlay, part of the Container family, and the other two are subclasses of Menu. Just as each TabView is made of several Tabs, each kind of Menu has a different class for its items: YAHOO.widget.MenuItem YAHOO.widget.ContextMenuItem YAHOO.widget.MenuBarItem ContextMenuItem is simply an alias for MenuItem created just for the sake of symmetry. All types of menu items can have a Menu as a submenu; neither ContextMenu nor MenuBar can be nested in a menu item, they are only good at the outermost level. All the menus are coordinated by YAHOO.widget.MenuManager, which listens to events at the document body level and dispatches them to the corresponding menus or menu items using the technique of Event Delegation to the limit; after all, the document body is the furthest out an event can bubble. Like the other members of the Container family, the constructor for Menu and its subclasses take two arguments; first a reference to existing markup or, if built via code, the id we want the Menu to have once rendered. The second argument takes the configuration options, if any, and accepts any configuration attribute as would be expected. However, in Menu, it can also take a couple of properties: itemData and lazyLoad, while MenuItem can also take value. We will see what they can be used for shortly. Menus can be built from existing markup or from code or any combination of both. The required markup for a Menu might seem a little complicated at first but it is intended to work in older clients or for users without JavaScript enabled. This, we know, is called Progressive Enhancement and Menu supports it very well; the CSS style sheet for Menu works whether JavaScript is active or not and by using the correct class names the Menus will look just the same for all our visitors regardless of the capabilities of their browsers. The markup will usually consist of a series of unordered lists <ul>, each of their list items <li> containing an anchor element <a> that leads to the next non-JavaScript enhanced page and optionally followed by a further nested unordered list. Menus will also read <select> elements creating a MenuItem for each <option>. When building a Menu from code, we may create and add each individual MenuItem to the Menu or we may use the itemData configuration option we've just mentioned, which takes an object literal with the description of the whole Menu at once. This is particularly handy for ContextMenus as they hardly make any sense without JavaScript enabled. Just as with any container, Menus have to be rendered. ContextMenus are usually rendered into document.body, as they have no place in the normal flow of the page. If the menu structure is too complex and takes too long to render, the lazyLoad option tells the Menu to render just the first level of items, those that would be visible initially, postponing rendering the rest until needed. Menu subclasses The ContextMenu is a specialized version of the control that provides a menu hidden from view until the element that it is associated with (the trigger element) is clicked with the right mouse button (except in Opera on Windows and OS X which requires the left-click + Ctrl key combination). The trigger element is defined using the trigger configuration attribute; this is the only configuration attribute natively defined by the ContextMenu class, all others are inherited. The MenuBar is similar to the standard Menu, but is horizontal instead of vertical. It can behave like an application-style menu bar, where the top-level menu items must be clicked in order for them to expand, or it can behave more like a web menu where the menu items expand on a simple mouse over and have submenu indicators. This is controlled with the autosubmenudisplay Boolean configuration attribute. The MenuItem class Each menu type has a subclass representing the individual menu items that form choices within the menu. They will be created automatically when a Menu is built from existing markup or by setting the itemData configuration option to a menu description. You will only create individual MenuItems when extending the functionality of an existing menu. MenuItems have several interesting configuration attributes: checked: Shows a checkmark to the left of the label, useful for items that toggle in between two states. classname: Added to the existing if any further styling is required. It can read this from existing markup. disabled: This item cannot be selected and will be grayed out. When built from markup, it can read this attribute from an tag. keylistener: The key combination (Shift, Control, or Alt + character) that will trigger this item. It can be read from markup. onclick: The method to be called when this item is clicked. text: A string to be shown in the label. url, target: The destination page for this item when not handled via code. selected: The item shows highlighted. submenu: A nested instance of Menu, an object description of a nested Menu, or a reference to the markup that would produce it. value: A value associated with this item. MenuItem subclasses The two subclasses YAHOO.widget.ContextMenuItem and YAHOO.widget. MenuBarItem both extend the MenuItem class, providing a constructor and some basic properties and methods for programmatically working with individual ContextMenu or MenuBar menu items. As a matter of fact ConextMenuItem is simply an alias for MenuItem. MenuBarItem has different defaults than MenuItem to suit the different layout of the MenuBar. Creating a Basic Navigation Menu Let's put together a basic navigation menu. Our menu will be built from underlying HTML rather than from script. We'll be enhancing the example of the image portfolio by first providing a landing, home page that will welcome us. We'll later add a context menu to the image portfolio itself so that instead of adding an extra button (like Rate!) for each option, we'll handle them via menus. Once complete, our landing page should appear like this:
Read more
  • 0
  • 0
  • 1016

article-image-yui-28-rich-text-editor
Packt
16 Jul 2010
9 min read
Save for later

YUI 2.8: Rich Text Editor

Packt
16 Jul 2010
9 min read
(For more resources on YUI, see here.) Long gone are the days when we struggled to highlight a word in an e-mail message for lack of underlining or boldfacing. The rich graphic environment that the web provides has extended to anything we do on it; plain text is no longer fashionable. YUI includes a Rich Text Editor (RTE) component in two varieties, the basic YA-HOO.widget.SimpleEditor and the full YAHOO.widget.Editor. Both editors are very simple to include in a web page and they enable our visitors to enter richly formatted documents which we can easily read and use in our applications. Beyond that, the RTE is highly customizable and allows us to tailor the editor we show the user in any way we want. In this article we’ll see: What each of the two editors offers How to create either of them Ways to retrieve the text entered How to add toolbar commands   The Two Editors Nothing comes for free, features take bandwidth so the RTE component has two versions, SimpleEditor which provides the basic editing functionality and Editor which is a subclass of SimpleEditor and adds several features at a cost of close to 40% more size plus several more dependencies which we might have already loaded and might not add to the total. A look at their toolbars can help us to see the differences: The above is the standard toolbar of SimpleEditor. The toolbar allows selection of fonts, sizes and styles, select the color both for the text and the background, create lists and insert links and pictures. The full editor adds to the top toolbar sub and superscript, remove formatting, show source, undo and redo and to the bottom toolbar, text alignment, &ltHn> paragraph styles and indenting commands. The full editor requires, beyond the common dependencies for both, Button and Menu so that the regular HTML &ltselect> boxes can be replaced by a fancier one: Finally, while in the SimpleEditor, when we insert an image or a link, RTE will simply call window.prompt() to show a standard input box asking for the URL for the image or the link destination, the full editor can show a more elaborate dialog box such as the following for the Insert Image command: A simple e-mail editor It is high time we did some coding, however I hope nobody gets frustrated at how little we’ll do because, even though the RTE is quite a complex component and does wonderful things, there is amazingly little we have to do to get one up and running. This is what our page will look like: This is the HTML for the example: <form method="get" action="#" id="form1"> <div class="fieldset"><label for="to">To:</label> <input type="text" name="to" id="to"/></div> <div class="fieldset"><label for="from">From:</label> <input type="text" name="from" id="from" value="me" /></div> <div class="fieldset"><label for="subject">Subject:</label> <input type="text" name="subject" id="subject"/></div> <textarea id="msgBody" name="msgBody" rows="20" cols="75"> Lorem ipsum dolor sit amet, and so on </textarea> <input type="submit" value=" Send Message " /></form> This simple code, assisted by a little CSS would produce something pretty much like the image above, except for the editing toolbar. This is by design, RTE uses Progressive Enhancement to turn the &lttextarea> into a fully featured editing window so, if you don’t have JavaScript enabled, you’ll still be able to get your text edited, though it will be plain text. The form should have its method set to “post”, since the body of the message might be quite long and exceed the browser limit for a “get” request, but using “get” in this demo will allow us to see in the location bar of the browser what would actually get transmitted to the server. Our page will require the following dependencies: yahoo-dom-event.js, element-min.js and simpleeditor-min.js along its CSS file, simpleeditor.css. In a <script> tag right before the closing </body> we will have: YAHOO.util.Event.onDOMReady(function () { var myEditor = new YAHOO.widget.SimpleEditor('msgBody', { height: '300px', width: '740px', handleSubmit: true }); myEditor.get('toolbar').titlebar = false; myEditor.render();}); This is all the code we need turn that &lttextarea> into an RTE; we simply create an instance of SimpleEditor giving the id of the &lttextarea> and a series of options. In this case we set the size of the editor and tell it that it should take care of submitting the data on the RTE along the rest of the form. What the RTE does when this option is true is to set a listener for the form submission and dump the contents of the editor window back into the &lttextarea> so it gets submitted along the rest of the form. The RTE normally shows a title bar over the toolbar; we don't want this in our application and we eliminate it simply by setting the titlebar property in the toolbar configuration attribute to false. Alternatively, we could have set it to any HTML string we wanted shown on that area Finally, we simply render the editor. That is all we need to do; the RTE will take care of all editing chores and when the form is about to be submitted, it will take care of sending its data along with the rest. Filtering the data The RTE will not send the data unfiltered, it will process the HTML in its editing area to make sure it is clean, safe, and compliant. Why would we expect our data to contain anything invalid? If all text was written from within the RTE, there would be no problem at all as the RTE won't generate anything wrong, but that is not always the case. Plenty of text will be cut from somewhere else and pasted into the RTE, and that text brings with it plenty of existing markup. To clean up the text, the RTE will consider the idiosyncrasies of a few user agents and the settings of a couple of configuration attributes. The filterWord configuration attribute will make sure that the extra markup introduced by text pasted into the editor from MS Word does not get through. The markup configuration attribute has four possible settings: semantic: This is the default setting; it will favor semantic tags in contrast to styling tags, for example, it will change &ltb> into &ltstrong>, &ltti&g into &ltem> and &ltfont> into &ltspan style="font: …. css: It will favor CSS style attributes, for example, changing &ltb> into &ltspan style="font-weight:bold">. default: It does the minimum amount of changes required for safety and compliance. xhtml: Among other changes, it makes sure all tags are closed such as &ltbr />, &ltimg />, and &ltinput />.   The default setting, which is not the default, offers the least filtering that will be done in all cases; it will make sure tags have their matching closing tags, extra whitespace is stripped off, and the tags and attributes are in lower case. It will also drop several tags that don't fit in an HTML fragment, such as &lthtml> or &ltbody>, that are unsafe, such as &ltscript> or &ltiframe>, or would involve actions, such as &ltform> or form input elements. The list of invalid tags is stored in property .invalidHTML and can be freely changed. More validation We can further validate what the RTE sends in the form; instead of letting the RTE handle the data submission automatically, we can handle it ourselves by simply changing the previous code to this: YAHOO.util.Event.onDOMReady(function () { var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event; var myEditor = new YAHOO.widget.SimpleEditor('msgBody', { height: '300px', width: '740px' }); myEditor.get('toolbar').titlebar = false; myEditor.render(); Event.on('form1', 'submit', function (ev) { var html = myEditor.getEditorHTML(); html = myEditor.cleanHTML(html); if (html.search(/<strong/gi) > -1) { alert("Don't shout at me!"); Event.stopEvent(ev); } this.msgBody.innerHTML = html; });}); We have dropped the handleSubmit configuration attribute when creating the SimpleEditor instance as we want to handle it ourselves. We listen to the submit event for the form and in the listener we read the actual rich text from the RTE via .getEditorHTML(). We may or may not want to clean it; in this example, we do so by calling .cleanHTML(). In fact, if we call .cleanHTML() with no arguments we will get the cleaned-up rich text; we don't need to call .getEditorHTML() first. Then we can do any validation that we want on that string and any of the other values. We use the Event utility .stopEvent() method to prevent the form from submitting if an error is found, but if everything checks fine, we save the HTML we recovered from the RTE into the &lttextarea>, just as if we had the handleSubmit configuration attribute set, except that now we actually control what goes there. In the case of text in boldface, it would seem easy to filter it out by simply adding this line: myEditor.invalidHTML.strong = true; However, this erases the tag and all the content in between, probably not what we wanted. Likewise, we could have set .invalidHTML.em to true to drop italics, but other elements are not so easy. RTE replaces a &ltu> (long deprecated) by &ltspan style="text-decoration:underline;"> which is impossible to drop in this way. Besides, these replacements depend on the setting of the markup configuration attribute. This example has also served us to see how data can be read from the RTE and cleaned if desired. Data can also be sent to the RTE by using .setEditorHTML(), but not before the editorContentLoaded event is fired, as the RTE would not be ready to receive it. In the example, we wanted to manipulate the editor contents, so we read it and saved it back into the &lttextarea> in separate steps; otherwise, we could have used the .saveHTML() method to send the data back to the &lttextarea> directly. In fact, this is what the RTE itself does when we set handleSubmit to true.
Read more
  • 0
  • 0
  • 2497

article-image-build-your-own-application-access-twitter-using-java-and-netbeans-part-4
Packt
28 Jun 2010
7 min read
Save for later

Build your own Application to access Twitter using Java and NetBeans: Part 4

Packt
28 Jun 2010
7 min read
In the 3rd part of this article series, we learnt about: Added a Tabbed Pane component to your SwingAndTweet application, to show your own timeline on one tab and your friend’s timeline on another tab Used a JScrollPane component to add vertical and horizontal scrollbars to your friends’ timeline list Used the getFriendsTimeline() method from the Twitter4J API to get the 20 most recent tweets from your friend’s timeline Applied font styles to your JLabel components via the Font class Added a black border to separate each individual tweet by using the BorderFactory and Color classes Added the date and time of creation of each individual tweet by using the getCreatedAt() method from the twitter4j.Status interface, along with the Date class. All those things, we learnt in the third part of the article series were a big improvement for our Twitter client, but wouldn’t it be cool if you could click on the URL links from your friends’ time line and then a web browser window would open automatically to show you the related webpage? Well, after reading this part of the article series, you’ll be able to integrate this functionality into your own Twitter client among other things. Here are the links to the earlier articles of this article series: Read Build your own Application to access Twitter using Java and NetBeans: Part 1 Read Build your own Application to access Twitter using Java and NetBeans: Part 2 Read Build your own Application to access Twitter using Java and NetBeans: Part 3 Using a JEditorPane component Till now, we’ve been working with JPanel objects to show your Twitter information inside the JTabbedPane component. But as you can see from your friends’ tweets, the URL links that show up aren’t clickable. And how can we make them clickable? Well, fortunately for us there’s a Swing component called JEditorPane that will let us use HTML markup, so the URL hyperlinks will show up as if you were on a web page. Cool, huh? Now let’s start with the dirty job… Open your NetBeans IDE along with your SwingAndTweet project, and make sure you’re in the Source View. Scroll up to the import declarations section and type import javax.swing.JEditorPane; right below the last import declaration, so your code looks as shown below: Now scroll down to the last line of code, JLabel statusUser;, and type JEditorPane statusPane; just below that line, as shown in the following screenshot: The next step is to add statusPane to the JTabbedPane1 component in your application. Scroll through the code until you locate the //code for the Friends timelineline and the try-block code below that line; then type the following code block just above the for statement: String paneContent = new String();statusPane = new JEditorPane();statusPane.setContentType("text/html");statusPane.setEditable(false); The following screenshot shows how your code must look like after inserting the above block of code (the red square indicates the lines you must add): Now scroll down through the code inside the for statement and type paneContent = paneContent + statusUser.getText() + "<br>" + statusText.getText() + "<hr>"; right after the jPanel1.add(individualStatus); line, as shown below: Then add the following two lines of code after the closing brace of the try block: statusPane.setText(paneContent);jTabbedPane1.add("Friends - Enhanced", statusPane); The following screenshot shows how your code must look like after the insertion: Run your application and log into your Twitter account. A new tab will appear in your Twitter client, and if you click on it you’ll see your friends’ latest tweets, as in the following screenshot: If you take a good look at the screen, you’ll notice the new tab you added with the JEditorPane component doesn’t show a vertical scroll bar so you can scroll up and down to see the complete list. That’s pretty easy to fix: First add the import javax.swing.JScrollPane; line to the import declarations section and then replace the jTabbedPane1.add("Friends - Enhanced", statusPane); line you added on step 9 with the following lines: JScrollPane editorScrollPane = new JScrollPane(statusPane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, // vertical bar policy JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); // horizontal bar policyjTabbedPane1.add("Friends - Enhanced", editorScrollPane); Your code should now look like this: Run your Twitter application again and this time you’ll see the vertical scrollbar: Let’s stop for a while to review our progress so far. In the first step above the exercise, you added an import declaration to tell the Java compiler that we need to use an object from the JEditorPane class. In step 3, you added a JEditorPane object called statusPane to your application. This object acts as a container for your friends’ tweets. And in case you’re wondering why we didn’t use a regular JPanel object, just remember that we want to make the URL links in your friends’ tweets clickable, so when you click on one of them, a web browser window will pop up to show you the web page associated to that hyperlink. Now let’s get back to our exercise. In step 4, you added four lines to your application’s code. The first line: String paneContent = new String(); creates a String variable called paneContent to store the username and text of each individual tweet from your friends’ timeline. The next three lines: statusPane = new JEditorPane();statusPane.setContentType("text/html");statusPane.setEditable(false); create a JEditorPane object called statusPane, set its content type to text/html so we can include HTML markup and make the statusPane non-editable, so nothing gets messed up when showing your friends’ timeline. Now that we have the statusPane ready to roll, we need to fill it up with the information related to each individual tweet from your friends. That’s why we need the paneContent variable. In step 6, you inserted the following line: paneContent = paneContent + statusUser.getText() + "<br>" + statusText.getText() + "<hr>"; inside the for block to add the username and the text of each individual tweet to the paneContent variable. The <br> HTML tag inserts a line break so the username appears in one line and the text of each tweet appears in another line. The <hr> HTML tag inserts a horizontal line to separate one tweet from the other. Once the for loop ends, we need to add the information from the paneContent variable to the JEditorPane object called statusPane. That’s why in step 7, you added the following line: statusPane.setText(paneContent); and then the jTabbedPane1.add("Friends - Enhanced", statusPane); line creates a new tab in the jTabbedPane1 component and adds the statusPane component to it, so you can see the friends timeline with HTML markup. In step 10, you learned how to create a JScrollPane object called editorScrollPane to add scrollbars to your statusPane component and integrate them into the jTabbedPane1 container. In this example, the JScrollPane constructor requires arguments: the statusPane component, the vertical scrollbar policy and the horizontal scrollbar policy. There are three options you can choose for your vertical and horizontal scrollbars: show them as needed, never show them or always show them. In this specific case, we need the vertical scrollbar to show up as needed, in case the list of your friends’ tweets doesn’t fit the screen, so we use the JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED policy. And since we don’t need the horizontal bar to show up because the statusPane component can adjust its horizontal size to fit your application’s window, we use the JScrollPane.HORIZONTAL_SCROLLBAR_NEVER policy. The last line of code from step 10 adds the editorScrollPane component to the jTabbedPane1 container instead of adding the statusPane component directly, because now the JEditorPane component is contained within the JScrollPane component. Now let’s see how to convert the URL links to real hyperlinks.
Read more
  • 0
  • 0
  • 1762
article-image-easily-writing-sql-queries-spring-python
Packt
24 May 2010
9 min read
Save for later

Easily Writing SQL Queries with Spring Python

Packt
24 May 2010
9 min read
(For more resources on Spring, see here.) Many of our applications contain dynamic data that needs to be pulled from and stored within a relational database. Even though key/value based data stores exist, a huge majority of data stores in production are housed in a SQL-based relational database. Given this de facto requirement, it improves developer efficiency if we can focus on the SQL queries themselves, and not spend lots of time writing plumbing code and making every query fault tolerant. The classic SQL issue SQL is a long existing standard that shares a common paradigm for writing queries with many modern programming languages (including Python). The resulting effect is that coding queries by hand is laborious. Let's explore this dilemma by writing a simple SQL query using Python's database API. SQL is a long existing standard that shares a common paradigm for writing queries with many modern programming languages (including Python). The resulting effect is that coding queries by hand is laborious. Let's explore this dilemma by writing a simple SQL query using Python's database API. DROP TABLE IF EXISTS article;CREATE TABLE article ( id serial PRIMARY KEY, title VARCHAR(11), wiki_text VARCHAR(10000));INSERT INTO article(id, title, wiki_textVALUES(1, 'Spring Python Book', 'Welcome to the [http://springpythonbook.com Spring Python] book, where you can learn more about [[Spring Python]].');INSERT INTO article(id, title, wiki_textVALUES(2, 'Spring Python', ''''Spring Python''' takes the concepts of Spring and applies them to world of [http://python.org Python].'); Now, let's write a SQL statement that counts the number of wiki articles in the system using the database's shell. SELECT COUNT(*) FROM ARTICLE Now let's write some Python code that will run the same query on an sqlite3 database using Python's official database API (http://www.python.org/dev/peps/pep-0249). import sqlite3db = sqlite3.connect("/path/to/sqlite3db")cursor = db.cursor()results = Nonetry: try: cursor.execute("SELECT COUNT(*) FROM ARTICLE") results = cursor.fetchall() except Exception, e: print "execute: Trapped %s" % efinally: try: cursor.close() except Exception, e: print "close: Trapped %s, and throwing away" % ereturn results[0][0] That is a considerable block of code to execute such a simple query. Let's examine it in closer detail. First, we connect to the database. For sqlite3, all we needed was a path. Other database engines usually require a username and a password. Next, we create a cursor in which to hold our result set. Then we execute the query. To protect ourselves from any exceptions, we need to wrap this with some exception handlers. After completing the query, we fetch the results. After pulling the results from the result set into a variable, we close the cursor. Finally, we can return our response. Python bundles up the results into an array of tuples. Since we only need one row, and the first column, we do a double index lookup. What is all this code trying to find in the database? The key statement is in a single line. cursor.execute("SELECT COUNT(*) FROM ARTICLE") What if we were writing a script? This would be a lot of work to find one piece of information. Granted, a script that exits quickly could probably skip some of the error handling as well as closing the cursor. But it is still is quite a bit of boiler plate to just get a cursor for running a query. But what if this is part of a long running application? We need to close the cursors after every query to avoid leaking database resources. Large applications also have a lot of different queries we need to maintain. Coding this pattern over and over can sap a development team of its energy. Parameterizing the code This boiler plate block of code is a recurring pattern. Do you think we could parameterize it and make it reusable? We've already identified that the key piece of the SQL statement. Let's try and rewrite it as a function doing just that. import sqlite3def query(sql_statement): db = sqlite3.connect("/path/to/sqlite3db") cursor = db.cursor() results = None try: try: cursor.execute(sql_statement) results = cursor.fetchall() except Exception, e: print "execute: Trapped %s" % efinally: try: cursor.close() except Exception, e: print "close: Trapped %s, and throwing away" % ereturn results[0][0] Our first step nicely parameterizes the SQL statement, but that is not enough. The return statement is hard coded to return the first entry of the first row. For counting articles, what we have written its fine. But this isn't flexible enough for other queries. We need the ability to plug in our own results handler. import sqlite3def query(sql_statement, row_handler): db = sqlite3.connect("/path/to/sqlite3db") cursor = db.cursor() results = None try: try: cursor.execute(sql_statement) results = cursor.fetchall() except Exception, e: print "execute: Trapped %s" % e finally: try: cursor.close() except Exception, e: print "close: Trapped %s, and throwing away" % e return row_handler(results) We can now code a custom handler. def count_handler(results): return results[0][0]query("select COUNT(*) from ARTICLES", count_handler) With this custom results handler, we can now invoke our query function, and feed it both the query and the handler. The only thing left is to handle creating a connection to the database. It is left as an exercise for the reader to wrap the sqlite3 connection code with a factory solution. What we have coded here is essentially the core functionality of DatabaseTemplate. This method of taking an algorithm and parameterizing it for reuse is known as the template pattern. There are some extra checks done to protect the query from SQL injection attacks. Replacing multiple lines of query code with one line of Spring Python Spring Python has a convenient utility class called DatabaseTemplate that greatly simplifies this problem. Let's replace the two lines of import and connect code from the earlier example with some Spring Python setup code. from springpython.database.factory import Sqlite3ConnectionFactoryfrom springpython.database.core import DatabaseTemplateconn_factory = Sqlite3ConnectionFactory("/path/to/sqlite3db")dt = DatabaseTemplate(conn_factory) At first glance, we appear to be taking a step back. We just replaced two lines of earlier code with four lines. However, the next block should improve things significantly. Let's replace the earlier coded query with a call using our instance of return dt.query_for_object("SELECT COUNT(*) FROM ARTICLE") Now we have managed to reduce a complex 14-line block of code into one line of Spring Python code. This makes our Python code appear as simple as the original SQL statement we typed in the database's shell. And it also reduces the noise. The Spring triangle—Portable Service Abstractions From this diagram earlier , as an illustration of the key principles behind Spring Python is being made. The DatabaseTemplate represents a Portable Service Abstraction because: It is portable because it uses Python's standardized API, not tying us to any database vendor. Instead, in our example, we injected in an instance of Sqlite3ConnectionFactory It provides the useful service of easily accessing information stored in a relational database, but letting us focus on the query, not the plumbing code It offers a nice abstraction over Python's low level database API with reduced code noise. This allows us to avoid the cost and risk of writing code to manage cursors and exception handling DatabaseTemplate handles exceptions by catching and holding them, then properly closing the cursor. It then raises it wrapped inside a Spring Python DataAccessException. This way, database resources are properly disposed of without losing the exception stack trace. Using DatabaseTemplate to retrieve objects Our first example showed how we can easily reduce our code volume. But it was really only for a simple case. A really useful operation would be to execute a query, and transform the results into a list of objects. First, let's define a simple object we want to populate with the information retrieved from the database. As shown on the Spring triangle diagram, using simple objects is a core facet to the 'Spring way'. class Article(object): def __init__(self, id=None, title=None, wiki_text=None): self.id = id self.title = title self.wiki_text = wiki_text If we wanted to code this using Python's standard API, our code would be relatively verbose like this: cursor = db.cursor()results = []try: try: cursor.execute("SELECT id, title, wiki_text FROM ARTICLE") temp = cursor.fetchall() for row in temp: results.append( Article(id=temp[0], title=temp[1], wiki_text=temp[2])) except Exception, e: print "execute: Trapped %s" % efinally: try: cursor.close() except Exception, e: print "close: Trapped %s, and throwing away" % ereturn results This isn't that different from the earlier example. The key difference is that instead of assigning fetchall directly to results, we instead iterate over it, generating a list of Article objects. Instead, let's use DatabaseTemplate to cut down on the volume of code return dt.query("SELECT id, title, wiki_text FROM ARTICLE", ArticleMapper()) We aren't done yet. We have to code ArticleMapper, the object class used to iterate over our result set. from springpython.database.core import RowMapperclass ArticleMapper(RowMapper): def map_row(self, row, metadata=None): return Article(id=row[0], title=row[1], wiki_text=row[2]) RowMapper defines a single method: map_row. This method is called for each row of data, and includes not only the information, but also the metadata provided by the database. ArticleMapper can be re-used for every query that performs the same mapping This is slightly different from the parameterized example shown earlier where we defined a row-handling function. Here we define a class that contains the map_row function. But the concept is the same: inject a row-handler to convert the data.
Read more
  • 0
  • 0
  • 3675

article-image-scaling-your-application-across-nodes-spring-pythons-remoting
Packt
24 May 2010
5 min read
Save for later

Scaling your Application Across Nodes with Spring Python's Remoting

Packt
24 May 2010
5 min read
(For more resources on Spring, see here.) With the explosion of the Internet into e-commerce in recent years, companies are under pressure to support lots of simultaneous customers. With users wanting richer interfaces that perform more business functions, this constantly leads to a need for more computing power than ever before, regardless of being web-based or thick-client. Seeing the slowdown of growth in total CPU horsepower, people are looking to multi-core CPUs, 64-bit chips, or at adding more servers to their enterprise in order to meet their growing needs. Developers face the challenge of designing applications in the simple environment of a desktop scaled back for cost savings. Then they must be able to deploy into multi-core, multi-server environments in order to meet their companies business demands. Different technologies have been developed in order to support this. Different protocols have been drafted to help communicate between nodes. The debate rages on whether talking across the network should be visible in the API or abstracted away. Different technologies to support remotely connecting client process with server processes is under constant development. Introduction to Pyro (Python Remote Objects) Pyro is an open source project (pyro.sourceforge.net) that provides an object oriented form of RPC. As stated on the project's site, it resembles Java's Remote Method Invocation (RMI). It is less similar to CORBA (http://www.corba.org), a technology-neutral wire protocol used to link multiple processes together, because it doesn't require an interface definition language, nor is oriented towards linking different languages together. Pyro supports Python-to-Python communications. Thanks to the power of Jython, it is easy to link Java-to-Python, and vice versa. Python Remote Objects is not to be confused with the Python Robotics open source project (also named Pyro). Pyro is very easy to use out of the box with existing Python applications. The ability to publish services isn't hard to add to existing applications. Pyro uses its own protocol for RPC communication. Fundamentally, a Pyro-based application involves launching a Pyro daemon thread and then registering your server component with this thread. From that point on, the thread along with your server code is in stand-by mode, waiting to process client calls. The next step involves creating a Pyro client proxy that is configured to find the daemon thread, and then forward client calls to the server. From a high level perspective, this is very similar to what Java RMI and CORBA offer. However, thanks to the dynamic nature of Python, the configuration steps are much easier, and there are no requirements to extend any classes or implement any interfaces.. As simple as it is to use Pyro, there is still the requirement to write some minimal code to instantiate your objects and then register them. You must also code up the clients, making them aware of Pyro as well. Since the intent of this article is to dive into using Spring Python, we will skip writing a pure Pyro application. Instead, let's see how to use Spring Python's out-of-the-box Pyro-based components, eliminating the need to write any Pyro glue code. This lets us delegate everything to our IoC container so that it can do all the integration steps by itself. This reduces the cost of making our application distributed to zero. Converting a simple application into a distributed one on the same machine For this example, let's develop a simple service that processes some data and produces a response. Then, we'll convert it to a distributed service. First, let's create a simple service. For this example, let's create one that returns us an array of strings representing the Happy Birthday song with someone's name embedded in it. class Service(object): def happy_birthday(self, name): results = [] for i in range(4): if i == 2: results.append("Happy Birthday Dear %s!" % name) else: results.append("Happy Birthday to you!") return results Our service isn't too elaborate. Instead of printing the data directly to screen, it collects it together and returns it to the caller. This allows us the caller to print it, test it, store it, or do whatever it wants with the result. In the following screen text, we see a simple client taking the results and printing them a little formatting inside the Python shell. As we can see, we have defined a simple service, and can call it directly. In our case, we are simply joining the list together with a newline character, and printing it to the screen. Fetching the service from an IoC container from springpython.config import *from simple_service import *class HappyBirthdayContext(PythonConfig): def __init__(self): PythonConfig.__init__(self) @Object def service(self): return Service() Creating a client to call the service Now let's write a client script that will create an instance of this IoC container, fetch the service, and use it. from springpython.context import *from simple_service_ctx import *if __name__ == "__main__": ctx = ApplicationContext(HappyBirthdayContext()) s = ctx.get_object("service") print "n".join(s.happy_birthday("Greg")) Running this client script neatly creates an instance of our IoC container, fetches the service, and calls it with the same arguments shown earlier.
Read more
  • 0
  • 0
  • 1987