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

How-To Tutorials

7019 Articles
article-image-building-your-first-application-papervision3d-part-2
Packt
26 Oct 2009
10 min read
Save for later

Building your First Application with Papervision3D: Part 2

Packt
26 Oct 2009
10 min read
This article covers: Basics of a 3D composition in Papervision3D Building your first application Basics of a 3D scene in Papervision3D Before we're ready to program some code for Papervision3D, we need to know a little bit more about 3D in Papervision3D. The following section is meant to give you a better understanding of the code that you will write. Each of these concepts relates directly to classes that are used by Papervision3D. All the object types that will be described are active elements in a 3D scene. Let's have a visualized look at all these objects: Scene The scene is the entire composition of 3D objects in a 3D space. Think of it as your stage in Flash with three axes—x, y, and z. Each object that you want to be visible should be added to the scene. If you don't add objects to the scene, they will not appear on your screen. Camera You can think of this as a real camera, which is somewhere in 3D space recording activity inside the scene. The camera defines the point of view from which you are viewing the scene. Because a camera in 3D is not a visible object, it is not part of our scene and you don't need to add it. Like a real camera you can, for example, zoom and focus. Cameras in 3D space can usually do more than real cameras. For example, in 3D you can, exclude objects to be rendered, when they are too close or too far away from the camera. The camera is able to ignore objects that are not in a certain, defined range. This is done for performance reasons. Objects that are too far away, or even behind the camera, don't need to be recorded, saving a lot of calculation for objects that are in a position where the camera simply can't see them. Viewport A viewport is a container sprite on your stage, and shows the output of what the camera sees. In the illustrative object overview, this has been compared with the lens of the camera. The lens is our window onto the 3D scene. We can make this window small and see just a small part of the scene, or make it big and see a lot. This works exactly as a real window in a wall—making it bigger will not affect how the world outside looks, but will have an effect on how much we can see from this world. The following illustration contains the visualization of a relatively big viewport on the left, along with a relatively small and wide viewport on the right. The thick dark outlines represent the viewports. In the right image, you can clearly see how the window to the 3D world works and how changing its size will not stretch the view of the 3D object in the scene. It only affects how much we see of it. 3D Objects A shape in 3D space is called a 3D object, or DisplayObject3D in Papervision3D. You can think of 3D objects as more advanced display objects just like sprites and movie clips. The difference is that a DisplayObject3D has a third axis, so we can place it anywhere in 3D space and rotate over each of the three axes. Material A material is the texture that is printed on an object. When an object doesn't have a material applied, it will be invisible. There are a variety of materials available for you to use. For example, a very simple material is a color; a more advanced example of a material could be a live streaming video. Render engine A render engine is like a rolling camera. As long as you trigger the render engine, it will output information of the scene recorded by the camera to the viewport. When you stop triggering the render engine, your viewport will not show any new image from the scene and shows the last rendered image. Rendering is actually the most intensive task for your computer, as it needs to recalculate each object that is placed inside your scene and output this to the viewport. Left-handed Cartesian coordinate system Flash, as well as Papervision3D, make use of the Cartesian coordinate system. In Flash, a regular visual object can be positioned on the stage by entering an x and y value. The object will be positioned according to these values and relative to the upper left corner of the stage. A higher x value moves the object to the right, whereas a higher y value moves it downward. The coordinate system in Papervision3D works essentially the same, except for the following two differences: Flash uses Cartesian coordinates on two axes, whereas Papervision3D makes use of them on three axes The y-axis in Flash is inversed compared to the y-axis in Papervision3D In Papervision3D, we want to position objects not only on the x and y axes, but also on an extra axis, in order to position objects in depth. This third axis is called the z-axis. In Flash, objects are positioned relative to the upper left corner, which is the 0, 0 coordinate. This is called the center point, or the origin. Predictably, the position of the origin in 3D space is located at 0, 0, 0. The three axes and the origin point are illustrated by the following image: The plus and minus signs illustrate whether the coordinate values are increasing or decreasing in the direction away from the origin. Notice that the y-axis is inverted compared to 2D coordinates in Flash. When you want to position an object lower on your screen in Flash, you need to give it a higher y position. This is how 2D coordinates on a computer screen work. The above figure shows the y-axis as according to the coordinate system as it is used by Papervision3D. Creating a basic class for Papervision3D In the previous sections you've learned what a basic scene is made of, and how you can create your own classes. Now that we have gone through all this theory, it's now time that we get our hands dirty by writing some code. Start your favorite authoring tool. The first application we'll build is a 3D scene containing a sphere that is rotating over its y-axis. A sphere is basically a "3D ball" and is built into Papervision3D as one of the default primitives. The basic document class First, have a look at the document class that defines the basic structure for the rotating sphere application. package { import flash.display.Sprite; import org.papervision3d.cameras.Camera3D; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.render.BasicRenderEngine; import org.papervision3d.scenes.Scene3D; import org.papervision3d.view.Viewport3D; public class FirstApplication extends Sprite { private var scene:Scene3D; private var viewport:Viewport3D; private var camera:Camera3D; private var renderEngine:BasicRenderEngine; private var sphere:Sphere; public function FirstApplication() { scene = new Scene3D(); camera = new Camera3D(); sphere = new Sphere(); scene.addChild(sphere); viewport = new Viewport3D(); addChild(viewport); renderEngine = new BasicRenderEngine(); renderEngine.renderScene(scene,camera,viewport); } }} Let's have a look at this in detail. While we do that, you can create a new project in Flash, Flex Builder, or Flash Builder with a document class called FirstApplication. If you're using Flex Builder or Flash Builder, don't forget to configure the path to the Papervision3D source. We know that we need to have a scene, a camera, a viewport, a 3D object with a material, and a render engine, in order to create a rendered output on the screen. Remember that the document class needs to extend Sprite. To make all these available inside the class, we need to import them first. import flash.display.Sprite;import org.papervision3d.cameras.Camera3D;import org.papervision3d.objects.primitives.Sphere;import org.papervision3d.render.BasicRenderEngine;import org.papervision3d.scenes.Scene3D;import org.papervision3d.view.Viewport3D; Now that the imports are set, we define the properties of the class. private var scene:Scene3D;private var viewport:Viewport3D;private var camera:Camera3D;private var renderEngine:BasicRenderEngine;private var sphere:Sphere; We only define the properties and their class types here, without assigning any value. We'll do that inside the constructor. Let's first create the constructor. public function FirstApplication(){} We start by creating a scene inside the constructor. A scene is needed as the holder for our 3D objects. It's very easy to create one. scene = new Scene3D(); All you have to do is create an instance of the Scene3D class, which doesn't take any parameters. Next, we add a Camera3D, which is just as easy as the creation of a new Scene. camera = new Camera3D(); A camera can take parameters when you create a new instance; however, the default values will do for now. The scene and camera are useless as long as there are no 3D objects in the scene to film. For this example a sphere will be used, which is one of the built-in 3D objects. sphere = new Sphere(); A camera, as well as a sphere, takes parameters when you instantiate it. However, when you do not pass any parameters, a default sphere will be created. Now that we have created our sphere, we need to add it to the scene, otherwise it will not be seen by the camera. This works in exactly the same way as adding regular display objects to the display list in Flash, by using the addChild() method. scene.addChild(sphere); That looks familiar, right? Now that we have a scene, a camera, and a sphere, we need to set up the window to the 3D scene, so we can see what is going on in there. We need to define our viewport and add it to the stage to make it visible. viewport = new Viewport3D();addChild(viewport); While creating a new viewport, you can pass optional parameters to it, but the default parameters are again fine for now. We are now just one step away from publishing our application. First we need to get our camera rolling. This is done by defining the render engine, which will render the current view of the camera to the viewport. renderEngine = new BasicRenderEngine();renderEngine.renderScene(scene,camera,viewport); Now that the render engine is defined and an instruction to render the scene is added, we are ready to publish this project. This should result in the following image of triangles that together form something like a circle: Case sensitivityPay attention to upper cases and lower cases in the examples. Not following these conventions might result in an error. A good example of this is the definition and instantiation of a sphere as we did in our FirstApplication.var sphere:Sphere = new Sphere();Here we see that we have defined a class property called sphere (lowercase) of a Sphere (uppercase) type, containing an instance of Sphere (uppercase). Giving a variable/property the same name as the defined object type will often cause problems. The result of these few lines of code may not look very impressive yet, but in fact this is the beginning of something that will soon become exciting and could never be accomplished this easy by regular Flash use. When you have managed to compile this application that shows the triangles, you've made it through the hardest part of this example. Let's continue building your first application and see how we can add the illusion of 3D to our object, which still looks very 2D.
Read more
  • 0
  • 0
  • 2094

article-image-pentaho-reporting-building-interactive-reports-html
Packt
26 Oct 2009
1 min read
Save for later

Pentaho Reporting: Building Interactive Reports in HTML

Packt
26 Oct 2009
1 min read
Interactive HTML report properties All reporting elements share a common set of HTML-related properties that may be used to create a dynamic report. Below is a list of properties and their uses: HTML Properties class This property sets the class attribute of the current HTML entity to the specified value. name This property sets the name attribute of the current HTML entity to the specified value. title This property sets the title attribute of the current HTML entity to the specified value. xml-id This property allows the naming of the current HTML entity, setting the id attribute, making it possible to reference in outside scripts. append-body This property allows the placement of raw HTML within the body of the HTML document, prior to the rendering of the current element. append-body-footer This property allows the placement of raw HTML within the body of the HTML document, after the rendering of the current element. append-header Defined only at the master report level, this property allows the inclusion of raw HTML within the header of the HTML document generated. This location is traditionally used to load additional CSS files, as well as external JavaScript files.
Read more
  • 0
  • 0
  • 3033

article-image-article-personalize-your-pbx-using-freepbx-features
Packt
26 Oct 2009
4 min read
Save for later

Personalize Your Own PBX Using FreePBX Features

Packt
26 Oct 2009
4 min read
Let's get started. CallerID Lookup Sources Caller ID lookup sources supplement the caller ID name information that is sent by most telephone companies. A caller ID lookup source contains a list of phone numbers matched with names. When FreePBX receives a call, it can query a lookup source with the number of the caller. If the caller is on the lookup source's list, a name is returned that will be sent along with the call wherever the call gets routed to. The name will be visible on a phone's caller ID display (if the phone supports caller ID), and is also visible in the FreePBX call detail records. In order to set up a caller ID lookup source, click on the CallerID Lookup Sources link under the Inbound Call Control section of the navigation menu on the left side of the FreePBX interface as shown in the following screenshot: The Add Source screen has three common configuration options: Source Description Source type Cache results Source Description is used to identify this lookup source when it is being selected as a caller ID lookup source during the configuration of an inbound route. Source type is used to select the method that this source will use to obtain caller ID name information. FreePBX allows a lookup source to use one of the following methods: ENUM: FreePBX will use whichever ENUM servers are configured in /etc/asterisk/enum.conf to return caller ID name information. By default, this file contains the e164.arpa and e164.org zones for lookups. All ENUM servers in the enum.conf file will be queried. HTTP: FreePBX will query a web service for caller ID name information using the HTTP protocol. A lookup source that uses HTTP to query for information can use services such as Google Phonebook or online versions of the white/yellow pages to return caller ID names. When HTTP is selected as the source type, six additional options will appear for configuration. These options are discussed in the HTTP source type section. MySQL: FreePBX will connect to a MySQL database to query for caller ID name information. Usually, this will be a database belonging to a Customer Relationship Management (CRM) software package in which all customer information is stored. When MySQL is selected as the Source type, five additional options will appear for configuration. These options are discussed later in the MySQL source type section. SugarCRM: As of FreePBX version 2.5.1, this option is not yet implemented. In the future, this Source type option will allow FreePBX to connect to the database used by the SugarCRM software package to query for caller ID name information. If the Cache results checkbox is selected, then when a lookup source returns results they will be cached in the local AstDB database for quicker retrieval the next time the same number is looked up. Note that values cached in the AstDB will persist past a restart of Asterisk and a reboot of the PBX. Once a caller ID name has been cached, FreePBX will always return that name even if the name in the lookup source changes. Caching must be disabled for a new caller ID name to be returned from the lookup source. Once all configuration options have been filled out, click on the Submit Changes button followed by the orange-colored Apply Configuration Changes bar to make the new lookup source available to inbound routes. Now that we have an available lookup source, we can configure an inbound route to use this source to set caller ID information. Click on the Inbound Routes link under the Inbound Call Control section of the navigation menu on the left side of the FreePBX interface as shown in the following screenshot: Click the name of the inbound route that will use the new lookup source in the menu on the right side of the page (in this example, DID 5551234567) as shown in the following screenshot: Scroll down the page to the CID Lookup Source section. Select the name of the new lookup source from the Source drop-down menu: Click on the Submit button at the bottom of the page, followed by the orange-colored Apply Configuration Changes bar at the top of the page. Calls that are routing using this inbound route will now query our new lookup source for caller ID name information.
Read more
  • 0
  • 0
  • 4580

article-image-jboss-tools-palette
Packt
26 Oct 2009
4 min read
Save for later

JBoss Tools Palette

Packt
26 Oct 2009
4 min read
By default, JBoss Tools Palette is available in the Web Development perspective that can be displayed from the Window menu by selecting the Open Perspective | Other option. In the following screenshot, you can see the default look of this palette: Let's dissect this palette to see how it makes our life easier! JBoss Tools Palette Toolbar Note that on the top right corner of the palette, we have a toolbar made of three buttons (as shown in the following screenshot). They are (from left to right): Palette Editor Show/Hide Import Each of these buttons accomplishes different tasks for offering a high level of flexibility and customizability. Next, we will focus our attention on each one of these buttons. Palette Editor Clicking on the Palette Editor icon will display the Palette Editor window (as shown in the following screenshot), which contains groups and subgroups of tags that are currently supported. Also, from this window you can create new groups, subgroups, icons, and of course, tags—as you will see in a few moments. As you can see, this window contains two panels: one for listing groups of tag libraries (left side) and another that displays details about the selected tag and allows us to modify the default values (extreme right). Modifying a tag is a very simple operation that can be done like this: Select from the left panel the tag that you want to modify (for example, the <div> tag from the HTML | Block subgroup, as shown in the previous screenshot). In the right panel, click on the row from the value column that corresponds to the property that you want to modify (the name column). Make the desirable modification(s) and click the OK button for confirming it (them). Creating a set of icons The Icons node from the left panel allows you to create sets of icons and import new icons for your tags. To start, you have to right-click on this node and select the Create | Create Set option from the contextual menu (as shown in the following screenshot). This action will open the Add Icon Set window where you have to specify a name for this new set. Once you're done with the naming, click on the Finish button (as shown in the following screenshot). For example, we have created a set named eHTMLi: Importing an icon You can import a new icon in any set of icons by right-clicking on the corresponding set and selecting the Create | Import Icon option from the contextual menu (as shown in the following screenshot): This action will open the Add Icon window, where you have to specify a name and a path for your icon, and then click on the Finish button (as shown in the following screenshot). Note that the image of the icon should be in GIF format. Creating a group of tag libraries As you can see, the JBoss Tools Palette has a consistent default set of groups of tag libraries, like HTML, JSF, JSTL, Struts, XHTML, etc. If these groups are insufficient, then you can create new ones by right-clicking on the Palette node and selecting the Create | Create Group option from the contextual menu (as shown in the following screenshot). This action will open the Create Group window, where you have to specify a name for the new group, and then click on Finish. For example, we have created a group named mygroup: Note that you can delete (only groups created by the user) or edit groups (any group) by selecting the Delete or Edit options from the contextual menu that appears when you right-click on the chosen group. Creating a tag library Now that we have created a group, it's time to create a library (or a subgroup). To do this, you have to right-click on the new group and select the Create Group option from the contextual menu (as shown in the following screenshot). This action will open the Add Palette Group window, where you have to specify a name and an icon for this library, and then click on the Finish button (as shown in the following screenshot). As an example, we have created a library named eHTML with an icon that we had imported in the Importing an icon section discussed earlier in this article: Note that you can delete a tag library (only tag libraries created by the user) by selecting the Delete option from the contextual menu that appears when you right-click on the chosen library.
Read more
  • 0
  • 0
  • 1669

article-image-development-windows-mobile-applications-part-2
Packt
26 Oct 2009
3 min read
Save for later

Development of Windows Mobile Applications (Part 2)

Packt
26 Oct 2009
3 min read
Now let us see how to deploy it on Windows Mobile Device. For deploying the application on device you need to have ActiveSync installed. There are two ways in which application can be deployed on to the device.  First option is to connect the device to the Development machine via USB. ActiveSync will automatically detect it and you can click on on the top bar. And this time select option "Windows Mobile 6 Professional Device". But then this approach is useful when you want to test/deploy and use the application yourself. What if you want to distribute it to others? In that case you need to create an installation program for your Windows mobile application. The installation file in the Windows Mobile world is distributed in the form of a CAB file. So once we have done with application we should opt for option 2 of creating a CAB file (A CAB file is a library of compressed files stored as a single file). Creating CAB File Creating a CAB file itself is a new project. To create CAB project right click on the solution and select the option New Project as shown below. Clicking on New Project option will open Add New Project Wizard. Select option Setup and Deployment under Other Project Types on the left hand menu. Then select option Smart Device CAB Project on right hand side as shown below. We have named this project as MyFirstAppCAB. Click OK and MyFirstAppCAB project is created under the solution a shown. Now to add files to the CAB, right click on the Application Folder under File System on Target Machine and select option Add-> Project Output as shown. On selecting Project Output option, the following screen will popup. Depending upon the requirement, select what all needs to be compressed in CAB file. For this example we require only output, hence will select option Primary output. Now right click on CAB project MyFirstAppCAB and select option Build. CAB file with name MyFirstAppCAB will be created at the location MyFirstAppMyFirstAppCABDebug. Now let us see how we can deploy this CAB file on emulator and run the application. Click on Tools on the top bar and select option Device Emulator Manager. This will open a Device Emulator Manager as shown below. Select option Windows Mobile 6 Classic Emulator. Right click and select option Connect. Windows Mobile Emulator will start and on Device Emulator Manager you can see to left of Windows Mobile 6 Classic Emulator as shown below. Next step is to Cradle. If you are attempting to cradle for the 1st time, then you need to setup ActiveSync. To setup ActiveSync, double click on on right bottom on Task bar. Microsoft ActiveSync will open up, select option File -> Connection Settings as shown in figure below. Connection Settings window will be open up as shown below. Check the option Allow Connections to one of the followings and then from the drop down select the option DMA. After connecting Windows Mobile 6 Classic Emulator using Device Emulator Manager, again right click and select option Cradle as shown below. Cradle will start ActiveSync and make Emulator work as device connected using ActiveSync. On successful connection you can see on the left of option Windows Mobile 6 Classic Emulator on Device Emulator Manager as shown.
Read more
  • 0
  • 0
  • 2593

article-image-adding-interactive-course-material-moodle-19-part-2
Packt
26 Oct 2009
1 min read
Save for later

Adding Interactive Course Material in Moodle 1.9: Part 2

Packt
26 Oct 2009
1 min read
Adding the First Question Page Immediately after you save your lesson settings, Moodle presents you with the following page: At this point, it is time to create the first question page or import question pages from another system. Let's take a look at each of your options. Importing Questions If you choose to Import questions, you can import questions created by Moodle and other online learning systems. Some of the formats that you can import are: GIFT and Moodle XML These are Moodle's proprietary formats. GIFT is text only, and XML can include graphics and special characters. Aiken This format is for multiple choice questions. Missing Word This format is for missing word multiple choice questions. Blackboard If you're converting from Blackboard to Moodle, you can export questions from Blackboard and import them into Moodle. WebCT This format supports multiple choice questions, and short answers questions from WebCT. Course Test Manager If you're converting from Course Test Manager to Moodle, you can export questions from Course Test Manager, and import them into Moodle. Embedded Answers (Cloze) This format is a multiple question, multiple-choice question with embedded answers.  
Read more
  • 0
  • 0
  • 1574
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-storing-planning-data-ibm-cognos-d-cube-part-2
Packt
26 Oct 2009
6 min read
Save for later

Storing Planning Data in IBM Cognos: D-Cube (Part 2)

Packt
26 Oct 2009
6 min read
Exporting data from the D-Cube You can export data from the D-Cube into an ASCII file or to the clipboard. The export function gives you the ability to format how you want to export the data. You can set the delimiter, insert headers, and arrange the order of the dimensions. You can also suppress zero values in calculated data so that the export function will not include records that have zero or null data. To export from a D-Cube: Open the D-Cube. Click on D-Cube|Export. The Export function displays four tabs: Export Header/Footer Zeros Show Det/Tot Export Observe the following: Under the Export to option, select whether you want to export to a file or to the clipboard. If you are exporting to a file, enter the path and name of the file that you want to export the data to. Alternatively, you can click on the Browse button to save the file. Click the Select button to open the item selection box, and then select the dimension items containing the data you want to export. If you have a saved selection containing the data that you want to export, then you can load that selection into the dimension selection box. Groups Select how you want the dimensions to be displayed as columns: Single Column: Export each dimension as a single column. Multiple Column: Select one dimension whose items you want displayed as separate columns, and set the rest of the dimensions as single columns. The last dimension marked as [data] under the Dimension Order box contains the items that will used as multiple columns (see the following example): Single vs. multiple columns The following table illustrates a single column file with each dimension laid out as a separate column:       The following table illustrates a multiple column file. In this example, the items in the Month dimension are displayed as separate columns.       Format In the Format section, select the following: Separator: Set the delimiter to be used in the export file. Select Comma, Tab, Semicolon, or Aligned Columns. Column Headings Normal: This is the default setting. If you export in multiple columns, this option will include columns headers on each page but not for the rows and page dimensions. If you export in single columns, this no column headers will be included. At Top: This option includes the D-List names and column headers at the top of first page only. Above Each Page: This option includes the D-List names and column headers at the top of each page of the export. None: This option does not include column headers at all. Mode Append: Add the data to any previously-exported data in the same file. Overwrite: Overwrite any previously-exported data in the same file. Data formats: Apply Regional Settings: Select this checkbox to use the regional settings in your operating system as the format for the export. Pipes as Spaces: Select this checkbox to replace any pipe symbols (|) with spaces. Plain Number Format: Select this option to remove any numeric formats that you applied in the D-List. The values are exported in as many decimal places as is necessary in is basic format. All commas, currency signs, and percentages will be removed. Negative values enclosed in parenthesis will be prefixed with a minus sign. Non-numeric formats (Text, Date, and D-List) will be retained. Text Qualifier: Choose whether you want the data exported with a single quote or double quote text qualifier. Dimension Order: Determine the order of the dimensions that will be exported as columns. Move the dimensions up or down using the arrows, or click the Dimension Order button to arrange the dimensions according to their order in the D-Cube. Header/Footer This option lets you enter a title and/or footer to the export file. Enter the title or footnote by typing directly into the text box. Zeros This option suppresses any record with zero values. This option is independent of the zero suppression in force when you are viewing the D-Cube. You can suppress zeros in rows or columns by highlighting the dimension labeled R or C respectively. You can deselect a selected dimension by pressing Ctrl and clicking on the highlighted item. To suppress zeros in pages, select Suppress Zero Pages. If you want to suppress zeros in all of the dimensions, highlight all of the dimensions and then select Suppress Zero Pages. Show Det/Tot This option lets you choose whether you want to export only detail items or calculated items by highlighting the dimensions containing the detail or subtotal items, as appropriate. There are separate selection boxes for detail and total items. Breakback Breakback is a powerful feature in IBM Cognos Planning. With Breakback, you can enter data into calculated cells and change the variables that make up the formula according to rules that you specify. Breakback is commonly used to propagate changes to a total across its detail items, in proportion to the value of the detail items. Suppose you have five products, showing a total of 1500 units:   Product Current Product A 100 Product B 200 Product C 300 Product D 400 Product E 500 TOTAL 1500 With Breakback, if you enter 3000 in the total, the detail products will change as follows:   Product With Breakback Product A 200 Product B 400 Product C 600 Product D 800 Product E 1000 TOTAL 3000 Breakback distributes the changes in the TOTAL to Products A to E in proportion to their original share of the total. Breakback works on addition, subtraction, multiplication, and division. It can handle multiple calculations across multiple dimensions and hierarchies. Breakback on hierarchies You can apply Breakback on a grand total consisting of multiple subtotals across various hierarchical levels. In a simple hierarchy where you have only one subtotal, Breakback distributes the value across its children proportionately. In a multi-level hierarchy, Breakback cascades the changes one level at a time down through the hierarchy. If you enter a value into a total, Breakback will distribute the value proportionately to the subtotals immediately below the total, then to the subtotal the next level down, and so on.
Read more
  • 0
  • 0
  • 2370

article-image-making-world-wide-web-easier-place-talk-about
Packt
24 Oct 2009
4 min read
Save for later

Making the World Wide Web an Easier Place to Talk About

Packt
24 Oct 2009
4 min read
Why do we still use WWW? If you were tasked with finding the letter that, when spoken out loud repeatedly, was more awkward than any other, you would come up with W. Every other letter in the English alphabet is pronounced with a single syllable, yet the W is unique in requiring an impressive three syllables to utter. The irony is that ‘World Wide Web’ can be said in just three syllables, yet the abbreviation WWW encounters a tongue-twisting nine! How can any abbreviation take three times the effort to speak than the very words it is an abbreviation of!? ‘duh-bull-you duh-bull-you duh-bull-you’. WWW is such a prolific term these days and so hard to verbalize that this abbreviation has been abbreviated further with phrases such as “dub dub dub” or “all the Ws”. Almost every website we use starts with WWW and mankind is desperately seeking a way to make the oration of these website addresses an easier and less embarrassing task. The answer, you may be surprised to hear, is shockingly easy. Don’t uses Ws! Websites don’t need them, we don’t like to speak them, the internet will run equally well without them. Addressing the Internet A website address is made up of 3 key parts. Take for example www.google.co.uk The google part combined with the .co.uk part makes up the domain name. When Google purchased this address they purchased google.co.uk, NOT www.google.co.uk. The .co.uk part typically indicates the country, or type of domain (known as Top Level Domain or TLD). The www part is a sub-domain that can indicate anything at all, or can even be omitted. When a domain such as google.co.uk is purchased, it is the web developer who decides what sub-domains should be used, and by following convention and perceived wisdom they will normally opt for WWW. Why do they do this when they could use anything or even nothing at all? Why not w.google.co.uk, or web.google.co.uk or why not just google.co.uk? Many web developers have seen the light and make sure that their websites work even when a sub-domain is omitted. Try browsing to google.co.uk for example, or yahoo.com, or digg.com, they all work just as well as the same domain names with the Ws. Forward Slash is killing me So now that we have freed up the sub-domain, let’s make some good use of it. How many times have you heard an advert for a website followed by ‘forward slash deals’ or similar. Forward slash gives us another three syllable description of a single punctuation character. Easy to type, annoying to verbalize. It sounds either crude or demonic, and the alternative ‘stroke’ is equally cringe-worthy. We like dots. DotCom is easy to say – and with sub-domains we can ditch the slashes and strokes in favor of the smallest punctuation mark with the shortest name. Try these examples... www.google.co.uk/adwords contains 22 syllables yet adwords.google.co.uk just 10. That’s less than half the time to read, less than half to remember and 17% less to type. If your website sells cars and bikes, you might currently use www.123autos.com/cars and www.123autos.com/bikes. How much more succinct it would be to use 123autos.com for the main website, cars.123autos.com for the car pages, and bikes.123autos.com for the bike pages? Drop ‘em This campaign is to encourage people to forget that WWW ever existed. Don’t type it, don’t speak it, and complain to every website that still requires it. Drop the forward slashes, strokes and hyphens too. The only punctuation that should be used is the mighty yet humble dot and to achieve this, sub-domains are your loyal friends. Let’s make the World Wide Web an easier place to talk about.
Read more
  • 0
  • 0
  • 1358

article-image-service-versioning-soa
Packt
24 Oct 2009
15 min read
Save for later

Service Versioning in SOA

Packt
24 Oct 2009
15 min read
Making a Change For the next few months, the Center of Excellence paid off. Projects were identifying services early in the lifecycle. Those same projects were successfully identifying other potential consumers of these new services. Implementation technologies were being chosen correctly and interface design was being properly done. Most importantly, everyone felt the SOA effort was on track. By this time, it had been almost two years since Spencer's team developed the Customer Information Service for the auto insurance and home insurance divisions. While these two groups were very happy with the results, no additional teams had leveraged it. Outside of the annuity project, this wasn't a case of projects going in another direction; it was more due to lack of opportunities. That was about to change. Spencer was eating lunch in the cafeteria when Ramesh walked up. "Mind if I join you, Spencer?" "Hey Ramesh, it's been quite some time. Go ahead and pull up a chair." "Do you remember two years ago you tried to convince Ryan to use your Customer Information Service?" "I sure do. I didn't want to show my face in the annuity area for about a month after that. He really wasn't very receptive to the idea." "Well, I have some good news and some better news for you. The good news is that about six months later, Ryan decided to leave Advasco. The better news is that we've now got a major initiative to revamp a number of our systems in the annuity department. I'd like to take advantage of the Customer Information Service as part of that effort." "That's good news Ramesh. I didn't harbor any resentment towards Ryan, but I'm certainly happy about having another potential consumer for the Customer Information Service. I'll put you in touch with the service manager for it." "Thanks Spencer. That would be great. We're just getting started on our architecture, so the timing is perfect." "Let me know if you run into any problems. I'm still part of the SOA Center of Excellence, so it's still my job to make sure it goes well!" Spencer put Ramesh in contact with the service manager for the Customer Information Service, Maria. Maria had recently transferred over after her work on the account maintenance effort, and now had responsibilities for the Customer Information Service. In the meeting with Ramesh, she brought her technical lead, Craig, with her "Spencer told me that you're interested in utilizing the Customer Information Service in some of the new systems you're building in the annuity department, Ramesh." "That's right. We're rewriting a number of our systems, and based on what I remembered from Spencer two years ago, I thought we might be able to leverage the service." "Great, I'd be happy to help you out. This is Craig, the technical lead who covers the Customer Information Service. He's here if you've got any technical questions. Have you had a chance to review the information available in the service repository?" "I have. I reviewed the service interface, and while it certainly looks like there's enough there to warrant using the service rather than building our own, there's also a number of additional things that we'll need." Craig responded, "What kind of changes are you looking for? Are there new operations that you need that are specific to the annuity area?" Ramesh said, "There are two or three operations that we'd like to see, but most of the changes are actually in the message schemas for the existing operations. There are some additional attributes that we need, and some of the relationships between the attributes are different in our representation." For the rest of the meeting, Ramesh and Craig went through the changes that Ramesh wanted to be made to accommodate his needs. In the end, it was clear that some changes to the existing schemas would have to be made. Maria asked, "We're going to need to go back and look over these changes, along with the integration approach for your existing database. What does the schedule for your efforts currently look like?" Ramesh replied, "We're still in the initial stages of planning, which is why I wanted to make sure I talked to you now. Right now, the project sponsors would like to have something within six months, but they also know that nine months is far more likely. Since I have some flexibility in my schedule, why don't you take a week to look into the effort required for the changes, and let's work out the schedule then. Does that work for you?" "That works for me. We'll get back to you next week with what we think it will take to implement the changes." On the way back to their desks, Craig commented to Maria, "You know, while I don't have any concerns about getting the work done for Ramesh, I do have some concerns on how these changes are going to impact our existing consumers. Some of these changes are going to break the existing interfaces." Maria said, "That is a concern. I know that there aren't any resources available to do any work on the home insurance side of things. Any suggestions on how we should handle this?" Craig said, "Well, we definitely should make the existing consumers aware that a change is going to be made and at least get a clear idea of what the impact will be. If you can take care of that, I can take this to Spencer and the SOA Center of Excellence, and see what suggestions they have." "That sounds like a good plan to me," replied Maria Over the next week, the Customer Information Service team did the analysis required to estimate how long the changes would take to implement. Maria used the communication features of the service registry/repository to push out a message to the existing consumers about the pending change, and as she suspected, the biggest problem was going to be the home insurance system. Due to other priorities, the earliest they could even begin to make changes to their consumer would be nine months from now, potentially three months after the service needed to go live. Craig met with Spencer and explained the problem to him. Spencer agreed to facilitate a decision-making session to explore the different options. Representatives from all of the existing consumers were there, along with Ramesh, Craig, and Maria Spencer started the meeting, "I'm sure all of you saw the notification from Maria last week that some changes are necessary to the Customer Information Service in order to support its usage by the annuity department. The problem that we face is that these changes will break the existing consumers of the service, and not all of you can make the changes to your systems in the currently proposed timeframe. Let's start out by listing all possible options, regardless of whether we all presently agree or disagree on their viability." Craig started out, "Well, if we're listing all possible options, the first one is toupdate the service, and then get whatever push we need from management to get resources allocated to the consuming systems so they can make the changes in the time required." Maria replied, "Come on Craig, you know that we can't just pull resources off projects that easily." Jason, from the auto insurance department, added, "Aren't all of these changes a result of the annuity department? Why can't they just include modifying our applications within their project scope? They already have resources allocated to their project." Paul, from the home insurance department, replied, "Do you really want some developers that have never seen your application before mucking around in your code? I know I don't." Spencer said, "Let's remember, we're listing all options, regardless of whether we all know that the option won't fly. We want to make sure we've explored all of the options. I'm going to just leave this as one option, since we still wind up with the same result, regardless of where the resources come from. I'll capture the concerns about the option." Paul, from the home insurance group, added, "Okay, here's another option. Why don't we leave the existing service in place, and simply have the annuity project write a new service that just they use. Then, none of us using the existing service would be impacted." Craig replied, "That's true, but isn't that going against everything we're trying to do with SOA? I thought we were trying to avoid redundant implementations of the same capability." Spencer replied, "Duly noted, Craig. Just as with the last option, let's keep it on the board, and I'll make sure that your concerns are captured. Paul, that option actually triggered another one in my mind. In addition to having Maria's team write the new service for the annuity system, why couldn't they also keep the existing version of the service available in production for the rest of you? You can then migrate as your schedules allow." Paul and Jason both replied in tandem, "That would work for us!" Maria jumped into the conversation, "While I'm sure it would, that sets a very dangerous precedent for my team. How many versions of the service are we going to have to maintain? While it's a little bit better when all the implementations are owned by one team, we still have multiple implementations." Jason then asked, "Isn't there a way to make the new service backwards compatible with the messages associated with the old service? That way, Maria's teamwould only have one implementation, but we could each continue to use our existing interface." Spencer replied, "That's a very good question Jason. While we all agree that the service interface needs to change to support the annuity department's requirements, I don't know that any of us have thought about whether we can easily transform messages associated with the previous version to messages that will work with the new version, and vice versa. Craig, you're the one most familiar with the new proposed schemas. Do you think we could leverage XSLT to apply transformations for backward compatibility?" "Yes, I think it's possible. The only concern I have is what impact this will have on the service implementation. Working with XSLT within Java code isn't the easiest thing to do, and as we make future modifications, that's just going to get uglier and uglier." Spencer said, "There's another option for that. A year ago, we put some XML appliances in place for security purposes. I know they have XSLT capabilities and they're already in the request path." Craig replied, "Of all the options, I think that one would work out the best. I really don't like the idea of maintaining multiple versions of the service, and having to maintain all of that XSLT code within the service is only slightly better. Allowing the annuity group to write their own goes against everything we're trying to do with SOA." Spencer said, "Well, we know where Craig stands. Are there any other options that we should look into? No? Well, what does everyone think?" Paul was first, "We know that we're not going to find resources to make the changes in all of the consumers at the same time, so that option is out. Likewise, it doesn't make sense for Ramesh's team to write their own service given our SOA goals, so that one is out, too. As for whether Maria's team maintains two versions of the service or utilizes some transformations somewhere, it really doesn't matter to me. From my perspective, both options give me the freedom to migrate at the time that works best for me." Jason immediately added, "I agree with everything Paul just said." Ramesh then offered his opinion, "Well, I certainly know that I don't want to give up any of my developers to work on Jason's and Paul's systems. We need every developer we can get right now. As for writing our own service, we've already been down that path two years ago, and now we're obviously changing the system again. If we had migrated to the service earlier, it would be one less thing that we had to touch as part of these changes. As long as Maria's team delivers my service on time for my projects, it doesn't matter to me what Maria's team chooses to do on their side." Spencer replied, "Well Maria, it looks like everyone else thinks that we need a solution that will allow all of the consumers to continue to use their existing interfaces or the new one, but the details of how that happens is completely up to you and Craig." Maria said, "Let's not jump to conclusions yet. If I'm going to maintain multiple versions, I need some kind of guarantee that the existing consumers will eventually migrate to the new version. If my team allows continuous use of the old interface for 12 months from the time the new interface goes live, would that be an adequate time to complete whatever modifications are necessary?" Jason and Paul thought about it and decided that this was reasonable. For the past three years, they'd averaged an update every nine months. Maria said, "I'll make sure to remind you, early and often, that the old version and its associated interfaces are going to be decommissioned. In the meantime, I'd like to first get the new version built. I'm going to need to keep both versions around initially just to compare messages. Ramesh's team can begin using the new service, and…" As she was talking, she stopped mid-sentence. Spencer said, "Is there a problem, Maria?" She replied, "Well, I was just thinking, how are we going to avoid having two URL's out there? The existing consumers are using a URL that points to the XML appliances, right? We want to apply transformations to that path. What URL will Ramesh's team use? We don't want to try to apply transformations to their requests." Spencer said, "Fortunately, I don't think we'll need to do that. We'll need to talk to the team that operates the appliances to be sure, but I'm pretty sure that the appliances can apply processing based upon incoming attributes on the message. As long as we can determine which requests came from which consumer based on the message content, we should be able to control when transformations happen, while having all the existing consumers using a single URL. We'll obviously need multiple URLs behind the intermediary, but that will be hidden from the consumers." Maria replied, "Okay, that eases some of my fears. Just make sure you find out quickly whether the appliances can handle it or not. Until we find out, can we set up a simple routing rule so that requests from the annuity group go to the new service, while the old ones stay where they are? That way, Ramesh can use the new service as soon as it is available, and Craig and his team can start working on the transformations for backward compatibility. I'd like to wait and see how that work goes before deciding whether to leave both versions out there for 12 months or to leverage the intermediary. We've never used that functionality before, and I don't want to take a chance on impacting Ramesh's schedule in case we run into difficulty. By keeping both services available in production at first, we can eliminate any dependency between the decommissioning of the old service and Ramesh's schedule." Craig added, "From my point of view, that shouldn't be a problem. I can treat the new version as if it were a completely new service, as long as the intermediary shields the consumers from that change. I will need to check how we can manage both versions at the source code level, though." Maria responded, "Good points, Craig. Taking all of this into account, I think this approach poses the least risk overall." Spencer said, "Then we're all in agreement, right? Maria's team will build a new version of the service according to the new interface, and the old interface will be available for 12 months from the time the new service is deployed. Initially, both versions will be available in production, but Maria can decommission the old service before 12 months are up, so long as the new version can be made backwards compatible via XSLT transformations. Maria will notify all consumers prior to decommissioning the old service, since regression testing will be required to ensure that backward compatibility has been maintained. She will also notify all consumers as we get closer to the 12 month cutoff when the older interface will no longer be supported." Everyone in the meeting agreed with this approach, and the teams went off and made it happen. Craig's team investigated the best way to apply the transformations, testing them using the latest Java libraries, as well as the XML appliances that Advasco had recently installed. They found that the XML appliances performed very well, and kept the programming model of the service very clean. While the Java libraries performed satisfactorily, the resulting programming model was not as clean as the team desired. With the use of the routing rules in the appliance, they were able to remove the older version of the service from production, while still supporting the older messages for the full 12 months as promised.
Read more
  • 0
  • 0
  • 1453

article-image-data-profiling-ibm-information-analyzer
Packt
24 Oct 2009
3 min read
Save for later

Data Profiling with IBM Information Analyzer

Packt
24 Oct 2009
3 min read
Information Analyzer is a client-server software. A data profiling user (metadata analyst) works on its GUI client, so to make it easier to show you how I solve the problem I’ll use a lot of screenshots. Our example data is an Oracle table that has two columns and three rows (In real life, they can typically be more than 50 and a few millions, respectively). When you start the Information Analyzer client, called Information Server Console, you’ll be shown its start-up screen; and then, its log-in window. When your log-in is successful, the console main window will show up. Assuming the Oracle table that we’d like to profile is new; we must identify it to the Analyzer, which technically means importing its metadata. Make sure you have connected the Oracle database to the Information Analyzer server before you import the metadata of its tables. Expand Metadata Management from the HOME drop-down menu. Then, click Import Metadata. Our example Oracle data (table) is in the CLROPER database (hosted in DDOM02), so select CLROPER and then click Identify Next Level. It might take a while, particularly for a database that has many tables and many columns; so just wait. On the completion message screen, click OK to close the screen. All tables in CLROPER database will be identified (listed) including our example table named SPACE1. We’ll next identify the columns of our SPACE1 table; so select SPACE1 and then click Identify Next Level. The result shows that Analyzer has correctly identified the two columns of the table. Now, import metadata of all columns of the table by selecting the table and then clicking Import. Click OK to continue. Wait for completion. Click OK on the successful completion screen. We’re now done with the metadata of the data; we’re now ready to start our profiling task. In Information Analyzer (as in most other software of these days) we group our profiling works into projects. Here, I just use an existing project (DJONI_TEST), so select Open Project from the drop-down arrow on the right of NO PROJECT SELECTED. You’ll be shown the list of existing projects. Select your project, and click Open. Our previous (existing) profiling works are shown. Next, open click Project Properties from the OVERVIEW drop-down menu. Go to the Data Sources tab. Our SPACE1 table is not in the list yet, as we haven’t identified it specifically in our project (we did in the previous steps at the server-wide level); so we need to add it into our project, click Add. Expand the SPACE1 table to see its columns. Select all of the columns as we want to profile all of them, and then click OK. When completed, click Save All, and then close the Project Properties window. Now, we’re ready to profile our SPACE1 data, to analyze its columns. On the main toolbar select Investigate | Column analysis.
Read more
  • 0
  • 0
  • 4559
article-image-working-simple-associations-using-cakephp
Packt
24 Oct 2009
5 min read
Save for later

Working with Simple Associations using CakePHP

Packt
24 Oct 2009
5 min read
Database relationship is hard to maintain even for a mid-sized PHP/MySQL application, particularly, when multiple levels of relationships are involved because complicated SQL queries are needed. CakePHP offers a simple yet powerful feature called 'object relational mapping' or ORM to handle database relationships with ease.In CakePHP, relations between the database tables are defined through association—a way to represent the database table relationship inside CakePHP. Once the associations are defined in models according to the table relationships, we are ready to use its wonderful functionalities. Using CakePHP's ORM, we can save, retrieve, and delete related data into and from different database tables with simplicity, in a better way—no need to write complex SQL queries with multiple JOINs anymore! In this article by Ahsanul Bari and Anupom Syam, we will have a deep look at various types of associations and their uses. In particular, the purpose of this article is to learn: How to figure out association types from database table relations How to define different types of associations in CakePHP models How to utilize the association for fetching related model data How to relate associated data while saving There are basically 3 types of relationship that can take place between database tables: one-to-one one-to-many many-to-many The first two of them are simple as they don't require any additional table to relate the tables in relationship. In this article, we will first see how to define associations in models for one-to-one and one-to-many relations. Then we will look at how to retrieve and delete related data from, and save data into, database tables using model associations for these simple associations. Defining One-To-Many Relationship in Models To see how to define a one-to-many relationship in models, we will think of a situation where we need to store information about some authors and their books and the relation between authors and books is one-to-many. This means an author can have multiple books but a book belongs to only one author (which is rather absurd, as in real life scenario a book can also have multiple authors). We are now going to define associations in models for this one-to-many relation, so that our models recognize their relations and can deal with them accordingly. Time for Action: Defining One-To-Many Relation Create a new database and put a fresh copy of CakePHP inside the web root. Name the database whatever you like but rename the cake folder to relationship. Configure the database in the new Cake installation. Execute the following SQL statements in the database to create a table named authors, CREATE TABLE `authors` ( `id` int( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , `name` varchar( 127 ) NOT NULL , `email` varchar( 127 ) NOT NULL , `website` varchar( 127 ) NOT NULL ); Create a books table in our database by executing the following SQL commands: CREATE TABLE `books` ( `id` int( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , `isbn` varchar( 13 ) NOT NULL , `title` varchar( 64 ) NOT NULL , `description` text NOT NULL , `author_id` int( 11 ) NOT NULL ) Create the Author model using the following code (/app/models/authors.php): <?php class Author extends AppModel{ var $name = 'Author'; var $hasMany = 'Book';} ?> Use the following code to create the Book model (/app/models/books.php): <?phpclass Book extends AppModel{ var $name = 'Book'; var $belongsTo = 'Author';}?> Create a controller for the Author model with the following code: (/app/controllers/authors_controller.php): <?phpclass AuthorsController extends AppController { var $name = 'Authors'; var $scaffold;}?>   Use the following code to create a controller for the Book model (/app/controllers/books_controller.php): <?php class BooksController extends AppController { var $name = 'Books'; var $scaffold; } ?> Now, go to the following URLs and add some test data: http://localhost/relationship/authors/ and http://localhost/relationship/books/ What Just Happened? We have created two tables: authors and books for storing author and book information. A foreign-key named author_id is added to the books table to establish the one-to-many relation between authors and books. Through this foreign-key, an author is related to multiple books, as well as, a book is related to one single author. By Cake convention, the name of a foreign-key should be underscored, singular name of target model, suffixed with _id. Once the database tables are created and relations are established between them, we can define associations in models. In both of the model classes, Author and Book, we defined associations to represent the one-to-many relationship between the corresponding two tables. CakePHP provides two types of association: hasMany and belongsTo to define one-to-many relations in models. These associations are very appropriately named: As an author 'has many' books, Author model should have hasMany association to represent its relation with the Book model. As a book 'belongs to' one author, Book model should have belongsTo association to denote its relation with the Author model. In the Author model, an association attribute $hasMany is defined with the value Book to inform the model that every author can be related to many books. We also added a $belongsTo attribute in the Book model and set its value to Author to let the Book model know that every book is related to only one author. After defining the associations, two controllers were created for both of these models with scaffolding to see how the associations are working.
Read more
  • 0
  • 0
  • 7846

article-image-installation-and-basic-features-enterprisedb
Packt
24 Oct 2009
3 min read
Save for later

Installation and basic features of EnterpriseDB

Packt
24 Oct 2009
3 min read
Installing the EnterpriseDB Download PostgrePlus Advanced Server 8.3 ( pgplus-advsvr-windows-83012b.exe (120MB) ) from the following site: http://www.enterprisedb.com/products/download.do. After downloading the program double click the executable file. You may need to choose a language from a list of languages. Here English has been chosen. The welcome window gets displayed as shown. Click Next. Choose the option you need. Read notes on this page to make the choice. Here, the Oracle compatibility has been chosen. Click Next or, choose a different location by browsing. Here the default location is accepted. Click Next. The window that shows up displays all the various features that are available. Pick and choose the features. Here all features are chosen. Click Next. The next window shows the links from where the JDBC drivers for connecting to Oracle and MySQL are available displayed. Click Next . In the window that shows up you need to choose the password for the Operating System UserID and Password. Read the cautionary remarks on this page. Choose Next. At this point your anti-virus program may require you to permit to run the program. McAfee is the anti-virus program on this computer. In the window that gets displayed you may need to choose the administrator's log in credentials. You may also Browse and select the Data Destination Directory. Herein the default is accepted. Click Next. In the windows that gets displayed you may choose the type of environment for which the server will be used as well as the work load for which you may be using the server. The dynamic tuning options available are: Server Utilization Development: This is a development machine and many other applications will be running on it. Stress testing should not be performed with this configuration. EnterpriseDB will use a minimal amount of memory. Mixed: Several applications will be running on this machine. Choose this option for web/application servers. Dedicated: This machine is dedicated to run EnterpriseDB and will use available memory to optimize performance. The Workload Profile Transaction Processing: The running application is a transaction intensive applications. General Purpose: The database will be used for transaction processing as well as complex queries and reporting. Reporting: The database will be used for reporting applications. For this tutorial, the Mixed option for Server Utilization and General Purpose for Workload Profile were chosen. Click on Next. The Summary page gets displayed showing all the options chosen. Click on the Install button. The window with a progress bar gets displayed. You may get a warning from the anti-virus program on your computer to allow the file to be executed. Click OK to allow install.
Read more
  • 0
  • 0
  • 2302

article-image-custom-data-readers-ext-js
Packt
24 Oct 2009
9 min read
Save for later

Custom Data Readers in Ext JS

Packt
24 Oct 2009
9 min read
When writing Chapter 12, "It's All about the Data," of Learning Ext JS, I switched things up a bit and switched the server-side processes to utilizing Adobe's ColdFusion application server, instead of the PHP we had been using in the rest of the book. There were a few reasons we decided to do this. To show that Ext JS can work with any server-side technology. ColdFusion 8 includes Ext JS 1.1 for it's new Ajax form components. Adobe uses a custom format for the serialized JSON return of query data, making it perfect for our example needs. I'm a ColdFusion programmer. Some time ago, before writing Chapter 12, I had begun to use a Custom Data Reader that I had found on the Ext JS forums. Another Ext user and ColdFusion programmer, John Wilson, had written the custom reader to consume Adobe's custom JSON return for queries. First, let me show you why Adobe's format differs from the generally expected serialized JSON return of a query. Here's an example of a typical query response. { 'results': 2, 'rows': [ { 'id': 1, 'firstname': 'Bill', occupation: 'Gardener' }, // a row object { 'id': 2, 'firstname': 'Ben' , occupation: 'Horticulturalist' } // another row object ] } And here's an example of how ColdFusion returns a query response.     {        "COLUMNS":["INTPROPERTIESID","STRDEVELOPMENT","INTADDRESSID", "STRSTREET","STRSTREET2", "STRCITY","CHSTATEID","INTZIP"],        "DATA":[            [2,"Abbey Road",6,"456 Abbey Road","Villa 5","New York","NY",12345],            [6,"Splash",39,"566 aroundthe bend dr",null,"Nashville","TN",37221]        ]    } You can see, when examining the two formats that they are very divergent. The typical format returns an array of row objects of the query's results, whereas ColdFusion's format is an array (DATA) of arrays (each row of the query result), with each row array only containing the data. The ColdFusion format has extracted the column names into it's own array (COLUMNS), as opposed to the name/value pairing found in the object notation of the typical return. It's actually very smart, on Adobe's part, to return the data in this fashion, as it would ultimately mean smaller data sets returned from a remote call, especially with large recordsets. John's CFJsonReader, a custom data reader and an extended component of Ext's base DataReader, was able to translate ColdFusion's data returns by properly parsing the JSON return into Records of an Ext Store. It worked fairly well, with a few minor exceptions. it didn't handle the column aliasing you could do with any other Ext JS data reader (name:'development',mapping:'STRDEVELOPMENT') it didn't allow data type association with a value, as other Ext JS data readers (INTZIP is of type 'int', STRDEVELOPMENT is of type 'string', etc) So, it worked, but ultimately was limited. When I was writing Chapter 13, "Code for Reuse: Extending Ext JS", I really dove into extending existing Ext JS components. This helped me gain a better understanding of what John had done, when writing CFJsonReader. But, after really reviewing the code, I saw there was a better way of handling ColdFusion's JSON return. What it basically came down to was that John was extending Ext's base DataReader object, and then hand parsing almost the entire return. Looking at the above examples, you'll notice that Adobe's implementation is an array of arrays, rather than an array of objects. Ext JS already comes with an ArrayReader object, so I knew that by writing a custom data reader that extended it I would be able to get the desired results. Half an hour later, I had "built a better mousetrap" and we now have a Custom Data Reader for properly parsing ColdFusion's JSON return, without the previous limitations. /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license * ******************************************* * Steve 'Cutter' Blades (CutterBl) no.junkATcutterscrossingDOTcom * http://blog.cutterscrossing.com * * Inspired by the CFJsonReader, originally writtin by John Wilson (Daemach) * http://extjs.com/forum/showthread.php?t=21408&highlight=cfjsonreader * * This Custom Data Reader will take the JSON return of a ColdFusion * Query object, rather returned straight up, or via the ColdFusion * QueryForGrid() method. * * The CFQueryReader constructor takes two arguments * @meta : object containing single key/value pair for the 'id' of each record * @recordType : field mapping object * * The recordType object allows you to alias the returned ColdFusion column * name (which is always passed in upper case) to any 'name' you wish, as * well as assign a data type, which your ExtJS app will attempt to cast * whenever the value is referenced. * * ColdFusion's JSON return, for a ColdFusion Query object, will appear in the * following format: * * {"COLUMNS":["INTVENDORTYPEID","STRVENDORTYPE","INTEXPENSECATEGORIESID", * "STREXPENSECATEGORIES"],"DATA" :[[2,"Carpet Cleaning",1,"Cleaining"], * [1,"Cleaning Service",1,"Cleaining"]]} * * The ColdFusion JSON return on any query that is first passed through * ColdFusion's QueryForGrid() method will return the object in the * following format: * * {"TOTALROWCOUNT":3, "QUERY":{"COLUMNS":["MYIDFIELD","DATA1","DATA2"], * "DATA":[[1,"Bob","Smith"],[6,"Jim","Brown"]]}} * * The Ext.data.CFQueryReader is designed to accomodate either format * automatically. You would create your reader instance in much the same * way as the CFJsonReader was created: * * var myDataModel = [ * {name: 'myIdField', mapping: 'MYIDFIELD'}, * {name: 'data1', mapping: 'DATA1'}, * {name: 'data2', mapping: 'DATA2'} * ]; * * var myCFReader = new Ext.data.CFJsonReader({id:'myIdField'},myDataModel); * * Notice that the 'id' value mirrors the alias 'name' of the record's field. */ Ext.data.CFQueryReader = function(meta, recordType){ this.meta = meta || {}; Ext.data.CFQueryReader.superclass.constructor.call(this, meta, recordType || meta.fields); }; Ext.extend(Ext.data.CFQueryReader, Ext.data.ArrayReader, { read : function(response){ var json = response.responseText; var o = eval("("+json+")"); if(!o) { throw {message: "JsonReader.read: Json object not found"}; } if(o.TOTALROWCOUNT){ this.totalRowCount = o.TOTALROWCOUNT; } return this.readRecords(((o.QUERY)? o.QUERY : o)); }, readRecords : function(o){ var sid = this.meta ? this.meta.id : null; var recordType = this.recordType, fields = recordType.prototype.fields; var records = []; var root = o.DATA; // give sid an integer value that equates to it's mapping sid = fields.indexOfKey(sid); // re-assign the mappings to line up with the column position // in the returned json response for(var a = 0; a < o.COLUMNS.length; a++){ for(var b = 0; b < fields.length; b++){ if(fields.items[b].mapping == o.COLUMNS[a]){ fields.items[b].mapping = a; } } } for(var i = 0; i < root.length; i++){ var n = root[i]; var values = {}; var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null); for(var j = 0, jlen = fields.length; j < jlen; j++){ var f = fields.items[j]; var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j; var v = n[k] !== undefined ? n[k] : f.defaultValue; v = f.convert(v, n); values[f.name] = v; } var record = new recordType(values, id); record.json = n; records[records.length] = record; } if(!this.totalRowCount){ this.totalRowCount = records.length; } return { records : records, totalRecords : this.totalRowCount }; } }); So, this changes our examples for Chapter 12 just a little bit. First of all, we'll need to have the CFQueryReader included, in place of the CFJsonReader. You can change the script tags in the samples for Examples 3 and 4. ... <script language="javascript" type="text/javascript" src="/scripts/custom-ext/CFQueryReader.js"></script> ... Next, we'll change the scripts for these two examples. We'll remove our configuration references for CFJsonReader, and replace them with the updated configuration for the CFQueryReader. /* * Chapter 12 Example 3 * Data Store from custom reader * * Revised: SGB (Cutter): 12.17.08 * Replaced CFJsonReader with CFQueryReader */ // Save all processing until the // DOM is completely loaded Ext.onReady(function(){ var ourStore = new Ext.data.Store({ url:'Chapter12Example.cfc', baseParams:{ method: 'getFileInfoByPath', returnFormat: 'JSON', queryFormat: 'column', startPath: '/images/' }, reader: new Ext.data.CFQueryReader({ id: 'NAME', // This is supposed to match the 'mapping' fields:[ {name:'file_name',mapping:'NAME'}, {name:'file_size',mapping:'SIZE'}, {name:'type',mapping:'TYPE'}, {name:'lastmod',mapping:'DATELASTMODIFIED'}, {name:'file_attributes',mapping:'ATTRIBUTES'}, {name:'mode',mapping:'MODE'}, {name:'directory',mapping:'DIRECTORY'} ] }), fields: recordModel, listeners:{ beforeload:{ fn: function(store, options){ if (options.startPath && (options.startPath.length > 0)){ store.baseParams.startPath = options.startPath; } }, scope:this }, load: { fn: function(store,records,options){ console.log(records); } }, scope:this } }); ourStore.load(); }); /* * Chapter 12 Example 4 * Data Store from custom reader - Filtering * * Revised: SGB (Cutter): 12.17.08 * Replaced CFJsonReader with CFQueryReader */ // Simple function/object to 'clone' objects cloneConfig = function (config) { for (i in config) { if (typeof config[i] == 'object') { this[i] = new cloneConfig(config[i]); } else this[i] = config[i]; } } // Save all processing until the // DOM is completely loaded Ext.onReady(function(){ var initialBaseParams = { method: 'getDirectoryContents', returnFormat: 'JSON', queryFormat: 'column', startPath: '/testdocs/' }; var ourStore = new Ext.data.Store({ url:'Chapter12Example.cfc', baseParams: new cloneConfig(initialBaseParams), reader: new Ext.data.CFQueryReader({ id: 'NAME', // This is supposed to match the 'mapping' fields:[ {name:'file_name',mapping:'NAME'}, {name:'file_size',mapping:'SIZE'}, {name:'type',mapping:'TYPE'}, {name:'lastmod',mapping:'DATELASTMODIFIED'}, {name:'file_attributes',mapping:'ATTRIBUTES'}, {name:'mode',mapping:'MODE'}, {name:'directory',mapping:'DIRECTORY'} ] }), listeners:{ beforeload:{ fn: function(store, options){ for(var i in options){ if(options[i].length > 0){ store.baseParams[i] = options[i]; } } }, scope:this }, load: { fn: function(store, records, options){ console.log(records); }, scope: this }, update: { fn: function(store, record, operation){ switch (operation){ case Ext.record.EDIT: // Do something with the edited record break; case Ext.record.REJECT: // Do something with the rejected record break; case Ext.record.COMMIT: // Do something with the committed record break; } }, scope:this } } }); ourStore.load({recurse:true}); filterStoreByType = function (type){ ourStore.load({dirFilter:type}); } filterStoreByFileType = function (fileType){ ourStore.load({fileFilter:fileType}); } clearFilters = function (){ ourStore.baseParams = new cloneConfig(initialBaseParams); ourStore.load(); } }); Summary These very basic changes have no overall effect on our examples. They function exactly as they did before. The new Custom Data Reader loads the data, returned from ColdFusion, exactly as it should. Now, we can also work with these data stores in the same manor as we would with any other data store set up through Ext JS, having the ability to alias columns, define field data types, and more.
Read more
  • 0
  • 0
  • 5023
article-image-development-login-management-module-and-comment-management-module
Packt
24 Oct 2009
12 min read
Save for later

Development of Login Management Module and Comment Management Module

Packt
24 Oct 2009
12 min read
Lets get started right away. Developing the Login Management Module Even though Login and session handling are separate functionalities from User management, they depend on the same table—user. Also, the functionalities are more alike than different. Hence, instead of creating a new Controller, we will be using the UserController itself as the Controller for the Login module. Keeping this point in mind, let us look at the steps involved in developing the Login Management, which are: Creating the Login page Implementing the Authentication Method Setting up the Session Applying Authorization Leaving aside the first step, all other steps mainly focus on the Controller. Here we go. Creating the Login Page We need a login page with text boxes for user name and password in which users can put their credentials and submit to the login authenticator (fancy name for the action method that will contain the logic to authenticate the user). That's what we are going to create now. The convention for any website is to show the login page when the user enters the URL without any specific page in mind. RoR also follows this convention. For example, if you enter the URL as http://localhost:3000/user, it displays the list of users. The reason is that the index action method of the UserController class calls the list method whenever the aforementioned URL is used. From this, we can understand two things—first, the default action method is index, and second, the first page to be shown is changeable if we change the index method. What we need is to show the login page whenever a user enters the URLhttp://localhost:3000/user. So let's change the index method. Open theuser_controller.rb file from the app/views/user folder and remove all the statements from the body of the index method so that it looks like as follows: def indexend Next, let us create an index.rhtml file, which will be shown when the index method is called. This file will be the login page. In the app/views/user folder, create an index.rhtml file. It will be as follows <%= form_tag :action=> 'authenticate'%><table ><tr align="center" class="tablebody"><td>User name:</td><td><%= text_field("user", "user_name",:size=>"15" ) %></td></tr><tr align="center" class="tablebody"><td>Password:</td><td><%= password_field("user","password",:size=>"17" ) %></td></tr><tr align="center" class="tablebody"><td></td><td><input type="submit" value=" LOGIN " /></td></tr></table> It uses two new form helpers—text_field and password_field. The text_field creates a text field with the name passed as the parameter, and the password_field creates a password field again with the name passed as the parameter. We have passed the authenticate method as the action parameter so that the form is submitted to the authenticate method. That completes the login page creation. Next, we will work on the authenticate method. Implementing the Authenticate method Implementing the Authenticate method Authenticating a user essentially means checking whether the user name and password given by the user corresponds to the one in database or not. In our case, the user gives us the user name and password through the login page. What we will be doing is checking whether the user is in database and does the password that we got corresponds to the password stored in the database for the user? Here, we will be working on two levels: Model Controller We can put the data access part in the action method that being the Controller itself. But it will create problems in the future if we want to add something extra to the user name/password checking code. That's why we are going to put (or delegate) the data access part into Model. Model We will be modifying the User class by adding a method that will check whether the user name and password provided by the user is correct or not. The name of the method is login. It is as follows: def self.login(name,password)find(:first,:conditions => ["user_name = ? and password =?",name, password])end It is defined as a singleton method of the User class by using the self keyword. The singleton methods are special class-level methods. The conditions parameter of the find method takes an array of condition and the corresponding values. The find method generates an SQL statement from the passed parameters. Here, the find method finds the first record that matches the provided user_name and password. Now, let us create the method that the Controller will call to check the validity of the user. Let us name it check_login. The definition is as follows: def check_loginUser.login(self.user_name, self.password)end This function calls the login method. Now if you observe closely, check_login calls the login function. One more point to remember—if a method 'test' returns a value and you call 'test' from another method 'test1,' then you don't need to say 'return test' from within 'test1'.The value returned from 'test' will be returned by 'test1' implicitly. That completes the changes to be done at the Model level. Now let us see the changes at the Controller-level. Controller In the Controller for User—UserController—add a new method named authenticate. The method will first create a User object based on the user name and password. Then it will invoke check_login on the newly created User object. If check_login is successful, that is, it does not return nil, then the user is redirected to the list view of Tales. Otherwise, the user is redirected to the login page itself. Here is what the method will look like: def authenticate@user = User.new(params[:user])valid_user = @user.check_loginif logged_in_userflash[:note]="Welcome "+logged_in_user.nameredirect_to(:controller=>'tale',:action => "list")elseflash[:notice] = "Invalid User/Password"redirect_to :action=> "index"endend The redirect_to method accepts two parameters—the name of the Controller and the method within the Controller. If the user is valid, then the list method of TaleController is called, or in other words, the user is redirected to the list of tales. Next, let us make it more robust by checking for the get method. If a user directly types a URL to an action, then the get method is received by the method. If any user does that, we want him/her to be redirected to the login page. To do this, we wrap up the user validation logic in an if/else block. The code will be the following: def authenticate if request.get?render :action=> 'index'else@user = User.new(params[:user])valid_user = @user.check_loginif valid_userflash[:note]="Welcome "+valid_user.user_nameredirect_to(:controller=>'tale',:action => 'list')else flash[:notice] = "Invalid User/Password"redirect_to :action=> 'index'endendend The get? method returns true if the URL has the GET method else it returns false. That completes the login authentication part. Next, let us set up the session. In Ruby, any method that returns a Boolean value—true or false—is suffixed with a question mark (?). The get method of the request object returns a boolean value. So it is suffixed with a question mark (?). Setting up the Session Once a user is authenticated, the next step is to set up the session to track the user. Session, by definition, is the conversation between the user and the server from the moment the user logs in to the moment the user logs out. A conversation is a pair of requests by the user and the response from the server. In RoR, the session can be tracked either by using cookies or the session object. The session is an object provided by RoR. The session object can hold objects where as cookies cannot. Therefore, we will be using the session object. The session object is a hash like structure, which can hold the key and the corresponding value. Setting up a session is as easy as providing a key to the session object and assigning it a value. The following code illustrates this aspect: def authenticateif request.get?render :action=> 'index'else@user = User.new(params[:user])valid_user = @user.check_loginif valid_usersession[:user_id]=valid_user.idflash[:note]="Welcome "+valid_user.user_nameredirect_to(:controller=>'tale',:action => 'list')elseflash[:notice] = "Invalid User/Password"redirect_to :action=> 'index'endendend That completes setting up the session part. That brings us to the last step—applying authorization. Applying Authorization Until now, we have authenticated the user and set up a session for him/her. However, we still haven't ensured that only the authenticated users can access the different functionalities of TaleWiki. This is where authorization comes into the picture. Authorization has two levels—coarse grained and fine grained. Coarse grained authorization looks at the whole picture whereas the fine grained authorization looks at the individual 'pixels' of the picture. Ensuring that only the authenticated users can get into TaleWiki is a part of coarse grained authorization while checking the privileges for each functionality comes under the fine grained authorization. In this article, we will be working with the coarse grained authorization. The best place to apply the coarse grained authorization is the Controller as it is the central point of data exchange. Just like other aspects, RoR provides a functionality to easily apply any kind of logic on the Controller as a whole in the form of filters. To jog your memory, a filter contains a set of statements that need to be executed before, after (or before and after) the methods within the Controllers are executed. Our problem is to check whether the user is authenticated or not, before any method in a Controller is executed. The solution to our problem is using a 'before filter'. But we have to apply authorization to all the Controllers. Hence, the filter should be callable from any of the Controller. If you look at the definition of a Controller, you can find such a place. Each Controller is inherited from the ApplicationController. Anything placed in ApplicationController will be callable from other Controllers. In other words, any method placed in ApplicationController becomes global to all the Controllers within your application. So, we will place the method containing the filter logic in ApplicationController. To check whether a user is authentic or not, the simplest way is to check whether a session exists for that person or not. If it exists, then we can continue with the normal execution. Let us name it check_authentic_user. The implementation will be as follows: def check_authentic_userunless session[:user_id]flash[:notice] = "Please log in"redirect_to(:controller => "user", :action =>"index")endend It checks for the user_id key in a session. If it is not present, the user is redirected to the login page. Place the code in the application.rb file as a method of ApplicationController. Next, let us use it as a filter. First, we will tell UserController to apply the filter for all the action methods except index and authenticate methods. Add the following statement to the UserController. It should be the first statement after the starting of the Controller class. class UserController < ApplicationControllerbefore_filter :check_authentic_user, :except =>[ :index, :authenticate ]::end Similarly, we will place the filter in other Controllers as well. However, in their case, there are no exceptions. So TaleController will have: class TaleController < ApplicationControllerbefore_filter :check_authentic_user::end GenreController and RoleController will be the same as TaleController. Thus, we have completed the 'applying authorization' part for the time being. Now, let's tie up one loose end—the problem of adding a new tale. Tying Up the Loose Ends When we developed the User management, the Tale management was affected as the tales table has a many-to-one relationship with the users table. Now we can solve the problem created by the foreign key reference. First, open the user.rb file and add the following statement indicating that it is at the 'one' end of the relationship: has_many :tale After addition of the statement, the class will look like the following: class User < ActiveRecord::Basevalidates_presence_of :user_name, :password, :first_name,:last_name, :age, :email, :country validates_uniqueness_of :user_namevalidates_numericality_of :age validates_format_of :email, :with => /A([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})Z/ibelongs_to :rolehas_many :taledef check_loginUser.login(self.name, self.password)enddef self.login(name,password)find(:first,:conditions => ["user_name = ? and password=?",name, password])endend Next, add the following statement to the tale.rb file: belongs_to :user The file will look like as follows: class Tale < ActiveRecord::Basevalidates_presence_of :title, :body_text, :sourcebelongs_to:genrebelongs_to :userend Next, open the tale_controller.rb file. In the create method, we need to add the user's id to the tale's user id reference so that the referential integrity can be satisfied. For that, we will get the current user's id from the session and set it as the value of the user_id attribute of the tale object. The create method will look like as follows, after doing the changes: def create @tale = Tale.new(params[:tale])@tale.genre_id=params[:genre]@tale.user_id=session[:user_id]@tale.status="new" if @tale.saveflash[:notice] = 'Tale was successfully created.'redirect_to :action => 'list'elserender :action => 'new'endend That's it. The 'loose ends' related to the User management are tied up. Now let us move onto the Comment Management module.
Read more
  • 0
  • 0
  • 1544

article-image-vbnet-application-sql-anywhere-10-database-part-1
Packt
24 Oct 2009
4 min read
Save for later

VB.NET Application with SQL Anywhere 10 database: Part 1

Packt
24 Oct 2009
4 min read
SQL Anywhere 10 SQL Anywhere 10 is the latest version of Sybase's feature rich SQL Anywhere database technology. It is highly scalable from the small foot-print UltraLite database all the way to its enterprise server with gigabytes of data. It is a comprehensive database package with built-in support for a wide range of applications, including session based synchronization; data exchange with both relational and non-relational data bases; secure store and forward messaging; messaging with FTP and email; and asynchronous access to mobile web services. You may download an evaluation version of the software and take it for a test drive. Sybase Central is a graphical database management interface to the database and its various supporting applications. The integration features are used in this article to create a Windows application retrieving data from the SQL Anywhere 10’s demonstration database, a database which is a part of the default installation of the developer edition. Overview of SQL Anywhere 10 From Sybase Central you can connect to the demo database quite easily by clicking on the Connections menu item and choosing Connect with SQL Anywhere 10. Figure 1 shows the SQL Anywhere management interface, Sybase Central. Using this interface you may also create an ODBC DSN by following the trail; Tools --> SQL Anywhere 10 --> open ODBC Administrator. Figure 1   It is very easy to connect to the database using the ODBC driver which is provided with the default installation of this product. The Figure 2 shows the User DSN installed with the default installation in the ODBC Data Source Administrator window. Figure 2 The Username is DBA and the Password is sql (case sensitive) for the demo database, demo.db. Please refer to the article, "Migrating from Oracle 10G XE to SQL Anywhere 10" which describes connecting to the demo database in detail. Figure 3 shows the demo database and its objects. Figure 3 VB.NET Windows Application We will create an ASP.NET 2.0 Windows application called SqlAny. We will create forms which display retrieved data from a table on the database as well as from a stored procedure after accepting a parameter passed to the stored procedure interactively. The Figure 4 shows the details of the project in the Solution Explorer as well as the Object Browser. Figure 4 Accessing SQL Anywhere Explorer SQL Anywhere Explorer is a component of SQL Anywhere that lets you connect to SQL Anywhere and UltraLite  databases from Visual Studio .NET. From the View menu of Visual Studio, you can access the SQL Anywhere Explorer as shown in Figure 5 - SQL Anywhere 10 is integrated with Visual Studio (both 1.1 and 2.0 versions). Figure 5   Alternatively, you can access SQL Anywhere Explorer from the Tools menu item as shown in Figure 6. In this case the Sybase Central management interface opens in a separate window. Interactive SQL is another of SQL Anywhere 10's tools for working with SQL queries on this database. Figure 6   When you click on SQL Anywhere Explorer from the View menu, you will be lead to the following window shown in Figure 7 which allows you to establish a data connection. Figure 7 Click on the drop-down, Add Connection, which opens the window shown in Figure 8 where you will be given a choice of two connections that you may connect to, SQL Anywhere or UltraLite. These are both databases. Both can run on mobile devices, but UltraLite has a smaller footprint. Figure 8 By choosing to connect to SQL Anywhere you invoke the authentication window for making the connection, as shown in Figure 9. The Username is DBA and the Password is sql. After entering these values you can get to the ODBC DSN mentioned earlier, from the drop-down. You may also test the connectivity which you see as being a success, for the entered values of Username, Password, and ODBC DSN. Figure 9   Visual Studio makes a data connection as shown in Figure 10. The nodes for Tables, Views, and Procedures are all expanded in this figure showing all the objects that can be accessed on this database. Since we logged in as DBA, all permissions are in place. Figure 10 Before the connection is made, SQL Anywhere starts up as shown in Figure 11. This message console gets minimized and stays up in the system tray of the desktop. This can be restored and closed by activating the icon in the tray.   Figure 11
Read more
  • 0
  • 0
  • 8913
Modal Close icon
Modal Close icon