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

7018 Articles
article-image-geolocation-and-accelerometer-apis
Packt
25 Jan 2012
13 min read
Save for later

Geolocation and Accelerometer APIs

Packt
25 Jan 2012
13 min read
(For more resources on iOS, see here.) The iOS family makes use of many onboard sensors including the three-axis accelerometer, digital compass, camera, microphone, and global positioning system (GPS). Their inclusion has created a world of opportunity for developers, and has resulted in a slew of innovative, creative, and fun apps that have contributed to the overwhelming success of the App Store. Determining your current location The iOS family of devices are location-aware, allowing your approximate geographic position to be determined. How this is achieved depends on the hardware present in the device. For example, the original iPhone, all models of the iPod touch, and Wi-Fi-only iPads use Wi-Fi network triangulation to provide location information. The remaining devices can more accurately calculate their position using an on-board GPS chip or cell-phone tower triangulation. The AIR SDK provides a layer of abstraction that allows you to extract location information in a hardware-independent manner, meaning you can access the information on any iOS device using the same code. This recipe will take you through the steps required to determine your current location. Getting ready An FLA has been provided as a starting point for this recipe. From Flash Professional, open chapter9recipe1recipe.fla from the code bundle which can be downloaded from http://www.packtpub.com/support.   How to do it... Perform the following steps to listen for and display geolocation data: Create a document class and name it Main. Import the following classes and add a member variable of type Geolocation: package { import flash.display.MovieClip; import flash.events.GeolocationEvent; import flash.sensors.Geolocation; public class Main extends MovieClip { private var geo:Geolocation; public function Main() { // constructor code } }}   Within the class' constructor, instantiate a Geolocation object and listen for updates from it: public function Main() { if(Geolocation.isSupported) { geo = new Geolocation(); geo.setRequestedUpdateInterval(1000); geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated); }} Now, write an event handler that will obtain the updated geolocation data and populate the dynamic text fields with it: private function geoUpdated(e:GeolocationEvent):void { latitudeField.text = e.latitude.toString(); longitudeField.text = e.longitude.toString(); altitudeField.text = e.altitude.toString(); hAccuracyField.text = e.horizontalAccuracy.toString(); vAccuracyField.text = e.verticalAccuracy.toString(); timestampField.text = e.timestamp.toString();} Save the class file as Main.as within the same folder as the FLA. Move back to the FLA and save it too. Publish and test the app on your device. When launched for the first time, a native iOS dialog will appear. Tap the OK button to grant your app access to the device's location data.     Devices running iOS 4 and above will remember your choice, while devices running older versions of iOS will prompt you each time the app is launched.   The location data will be shown on screen and periodically updated. Take your device on the move and you will see changes in the data as your geographical location changes. How it works... AIR provides the Geolocation class in the flash.sensors package, allowing the location data to be retrieved from your device. To access the data, create a Geolocation instance and listen for it dispatching GeolocationEvent.UPDATE events. We did this within our document class' constructor, using the geo member variable to hold a reference to the object: geo = new Geolocation();geo.setRequestedUpdateInterval(1000);geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated); The frequency with which location data is retrieved can be set by calling the Geolocation. setRequestedUpdateInterval() method. You can see this in the earlier code where we requested an update interval of 1000 milliseconds. This only acts as a hint to the device, meaning the actual time between updates may be greater or smaller than your request. Omitting this call will result in the device using a default update interval. The default interval can be anything ranging from milliseconds to seconds depending on the device's hardware capabilities. Each UPDATE event dispatches a GeolocationEvent object, which contains properties describing your current location. Our geoUpdated() method handles this event by outputting several of the properties to the dynamic text fields sitting on the stage: private function geoUpdated(e:GeolocationEvent):void { latitudeField.text = e.latitude.toString(); longitudeField.text = e.longitude.toString(); altitudeField.text = e.altitude.toString(); hAccuracyField.text = e.horizontalAccuracy.toString(); vAccuracyField.text = e.verticalAccuracy.toString(); timestampField.text = e.timestamp.toString();} The following information was output: Latitude and longitude Altitude Horizontal and vertical accuracy Timestamp The latitude and longitude positions are used to identify your geographical location. Your altitude is also obtained and is measured in meters. As you move with the device, these values will update to reflect your new location. The accuracy of the location data is also shown and depends on the hardware capabilities of the device. Both the horizontal and vertical accuracy are measured in meters. Finally, a timestamp is associated with every GeolocationEvent object that is dispatched, allowing you to determine the actual time interval between each. The timestamp specifies the milliseconds that have passed since the app was launched. Some older devices that do not include a GPS unit only dispatch UPDATE events occasionally. Initially, one or two UPDATE events are dispatched, with additional events only being dispatched when location information changes noticeably. Also note the use of the static Geolocation.isSupported property within the constructor. Although this will currently return true for all iOS devices, it cannot be guaranteed for future devices. Checking for geolocation support is also advisable when writing cross-platform code. For more information, perform a search for flash.sensors.Geolocation and flash. events.GeolocationEvent within Adobe Community Help. There's more... The amount of information made available and the accuracy of that information depends on the capabilities of the device. Accuracy The accuracy of the location data depends on the method employed by the device to calculate your position. Typically, iOS devices with an on-board GPS chip will have a benefit over those that rely on Wi-Fi triangulation. For example, running this recipe's app on an iPhone 4, which contains a GPS unit, results in a horizontal accuracy of around 10 meters. The same app running on a third-generation iPod touch and relying on a Wi-Fi network, reports a horizontal accuracy of around 100 meters. Quite a difference! Altitude support The current altitude can only be obtained from GPS-enabled devices. On devices without a GPS unit, the GeolocationEvent.verticalAccuracy property will return -1 and GeolocationEvent.altitude will return 0. A vertical accuracy of -1 indicates that altitude cannot be detected. You should be aware of, and code for these restrictions when developing apps that provide location-based services. Do not make assumptions about a device's capabilities. If your application relies on the presence of GPS hardware, then it is possible to state this within your application descriptor file. Doing so will prevent users without the necessary hardware from downloading your app from the App Store. Mapping your location The most obvious use for the retrieval of geolocation data is mapping. Typically, an app will obtain a geographic location and display a map of its surrounding area. There are several ways to achieve this, but launching and passing location data to the device's native maps application is possibly the easiest solution. If you would prefer an ActionScript solution, then there is the UMap ActionScript 3.0 API, which integrates with map data from a wide range of providers including Bing, Google, and Yahoo!. You can sign up and download the API from www.umapper.com. Calculating distance between geolocations When the geographic coordinates of two separate locations are known, it is possible to determine the distance between them. AIR does not provide an API for this but an AS3 solution can be found on the Adobe Developer Connection website at: http://cookbooks.adobe.com/index.cfm?event=showdetails&postId=5701. The UMap ActionScript 3.0 API can also be used to calculate distances. Refer to www.umapper.com. Geocoding Mapping providers, such as Google and Yahoo!, provide geocoding and reverse-geocoding web services. Geocoding is the process of finding the latitude and longitude of an address, whereas reverse-geocoding converts a latitude-longitude pair into a readable address. You can make HTTP requests from your AIR for iOS application to any of these services. As an example, take a look at the Yahoo! PlaceFinder web service at http://developer.yahoo.com/geo/placefinder. Alternatively, the UMap ActionScript 3.0 API integrates with many of these services to provide geocoding functionality directly within your Flash projects. Refer to the uMapper website. Gyroscope support Another popular sensor is the gyroscope, which is found in more recent iOS devices. While the AIR SDK does not directly support gyroscope access, Adobe has made available a native extension for AIR 3.0, which provides a Gyroscope ActionScript class. A download link and usage examples can be found on the Adobe Developer Connection site at www.adobe.com/devnet/air/native-extensions-for-air/extensions/gyroscope.html. Determining your speed and heading The availability of an on-board GPS unit makes it possible to determine your speed and heading. In this recipe, we will write a simple app that uses the Geolocation class to obtain and use this information. In addition, we will add compass functionality by utilizing the user's current heading. Getting ready You will need a GPS-enabled iOS device. The iPhone has featured an on-board GPS unit since the release of the 3G. GPS hardware can also be found in all cellular network-enabled iPads. From Flash Professional, open chapter9recipe2recipe.fla from the code bundle. Sitting on the stage are three dynamic text fields. The first two (speed1Field and speed2Field) will be used to display the current speed in meters per second and miles per hour respectively. We will write the device's current heading into the third—headingField. Also, a movie clip named compass has been positioned near the bottom of the stage and represents a compass with north, south, east, and west clearly marked on it. We will update the rotation of this clip in response to heading changes to ensure that it always points towards true north. How to do it... To obtain the device's speed and heading, carry out the following steps: Create a document class and name it Main. Add the necessary import statements, a constant, and a member variable of type Geolocation: package { import flash.display.MovieClip; import flash.events.GeolocationEvent; import flash.sensors.Geolocation; public class Main extends MovieClip { private const CONVERSION_FACTOR:Number = 2.237; private var geo:Geolocation; public function Main() { // constructor code } }} Within the constructor, instantiate a Geolocation object and listen for updates: public function Main() { if(Geolocation.isSupported) { geo = new Geolocation(); geo.setRequestedUpdateInterval(50); geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated); }} We will need an event listener for the Geolocation object's UPDATE event. This is where we will obtain and display the current speed and heading, and also update the compass movie clip to ensure it points towards true north. Add the following method: private function geoUpdated(e:GeolocationEvent):void { var metersPerSecond:Number = e.speed; var milesPerHour:uint = getMilesPerHour(metersPerSecond); speed1Field.text = String(metersPerSecond); speed2Field.text = String(milesPerHour); var heading:Number = e.heading; compass.rotation = 360 - heading; headingField.text = String(heading);} Finally, add this support method to convert meters per second to miles per hour: private function getMilesPerHour(metersPerSecond:Number):uint { return metersPerSecond * CONVERSION_FACTOR;} Save the class file as Main.as. Move back to the FLA and save it too. Compile the FLA and deploy the IPA to your device. Launch the app. When prompted, grant your app access to the GPS unit. Hold the device in front of you and start turning on the spot. The heading (degrees) field will update to show the direction you are facing. The compass movie clip will also update, showing you where true north is in relation to your current heading. Take your device outside and start walking, or better still, start running. On average every 50 milliseconds you will see the top two text fields update and show your current speed, measured in both meters per second and miles per hour. How it works... In this recipe, we created a Geolocation object and listened for it dispatching UPDATE events. An update interval of 50 milliseconds was specified in an attempt to receive the speed and heading information frequently. Both the speed and heading information are obtained from the GeolocationEvent object, which is dispatched on each UPDATE event. The event is captured and handled by our geoUpdated() handler, which displays the speed and heading information from the accelerometer. The current speed is measured in meters per second and is obtained by querying the GeolocationEvent.speed property. Our handler also converts the speed to miles per hour before displaying each value within the appropriate text field. The following code does this: var metersPerSecond:Number = e.speed;var milesPerHour:uint = getMilesPerHour(metersPerSecond);speed1Field.text = String(metersPerSecond);speed2Field.text = String(milesPerHour); The heading, which represents the direction of movement (with respect to true north) in degrees, is retrieved from the GeolocationEvent.heading property. The value is used to set the rotation property of the compass movie clip and is also written to the headingField text field: var heading:Number = e.heading;compass.rotation = 360 - heading;headingField.text = String(heading); The remaining method is getMilesPerHour() and is used within geoUpdated() to convert the current speed from meters per second into miles per hour. Notice the use of the CONVERSION_FACTOR constant that was declared within your document class: private function getMilesPerHour(metersPerSecond:Number):uint { return metersPerSecond * CONVERSION_FACTOR;} Although the speed and heading obtained from the GPS unit will suffice for most applications, the accuracy can vary across devices. Your surroundings can also have an affect; moving through streets with tall buildings or under tree coverage can impair the readings. You can find more information regarding flash.sensors.Geolocation and flash.events.GeolocationEvent within Adobe Community Help. There's more... The following information provides some additional detail. Determining support Your current speed and heading can only be determined by devices that possess a GPS receiver. Although you can install this recipe's app on any iOS device, you won't receive valid readings from any model of iPod touch, the original iPhone, or W-Fi-only iPads. Instead the GeolocationEvent.speed property will return -1 and GeolocationEvent.heading will return NaN. If your application relies on the presence of GPS hardware, then it is possible to state this within the application descriptor file. Doing so will prevent users without the necessary hardware from downloading your app from the App Store. Simulating the GPS receiver During the development lifecycle it is not feasible to continually test your app in a live environment. Instead you will probably want to record live data from your device and re-use it during testing. There are various apps available that will log data from the sensors on your device. One such app is xSensor, which can be downloaded from iTunes or the App Store and is free. Its data sensor log is limited to 5KB but this restriction can be lifted by purchasing xSensor Pro. Preventing screen idle Many of this article's apps don't require you to touch the screen that often. Therefore you will be likely to experience the backlight dimming or the screen locking while testing them. This can be inconvenient and can be prevented by disabling screen locking.  
Read more
  • 0
  • 0
  • 11012

article-image-article-debugging-with-opengles-in-ios5
Packt
25 Jan 2012
14 min read
Save for later

Debugging with OpenGL ES in iOS 5

Packt
25 Jan 2012
14 min read
(For more resources on Debugging with OpenGL ES in iOS 5, see here.) The Open Graphics Library (OpenGL) can be simply defned as a software interface to the graphics hardware. It is a 3D graphics and modeling library that is highly portable and extremely fast. Using the OpenGL graphics API, you can create some brilliant graphics that are capable of representing 2D and 3D data.   The OpenGL library is a multi-purpose, open-source graphics library that supports applications for 2D and 3D digital content creation, mechanical and architectural design, virtual prototyping, fight simulation, and video games, and allows application developers to confgure a 3D graphics pipeline, and submit data to it. An object is defned by connected vertices. The vertices of the object are then transformed, lit, and assembled into primitives, and rasterized to create a 2D image that can be directly sent to the underlying graphics hardware to render the drawing, which is deemed to be typically very fast, due to the hardware being dedicated to processing graphics commands. We have some fantastic stuff to cover in this article, so let's get started. Understanding the new workfow feature within Xcode In this section, we will be taking a look at the improvements that have been made to the Xcode 4 development environment, and how this can enable us to debug OpenGL ES applications much easier, compared to the previous versions of Xcode. We will look at how we can use the frame capture feature of the debugger to capture all frame objects that are included within an OpenGL ES application. This tool enables you to list all the frame objects that are currently used by your application at a given point of time. We will familiarize ourselves with the new OpenGL ES debugger within Xcode, to enable us to track down specifc issues relating to OpenGL ES within the code. Creating a simple project to debug an OpenGL ES application Before we can proceed, we frst need to create our OpenGLESExample project. Launch Xcode from the /Developer/Applications folder. Select the OpenGL Game template from the Project template dialog box. Then, click on the Next button to proceed to the next step in the wizard. This will allow you to enter in the Product Name and your Company Identifer. Enter in OpenGLESExample for the Product Name, and ensure that you have selected iPhone from the Device Family dropdown box. Next, click on the Next button to proceed to the fnal step in the wizard. Choose the folder location where you would like to save your project. Then, click on the Create button to save your project at the location specifed. Once your project has been created, you will be presented with the Xcode development interface, along with the project fles that the template created for you within the Project Navigator window. Now that we have our project created, we need to confgure our project to enable us to debug the state of the objects. Detecting OpenGL ES state information and objects To enable us to detect and monitor the state of the objects within our application, we need to enable this feature through the Edit Scheme… section of our project, as shown in the following screenshot: From the Edit Scheme section, as shown in the following screenshot, select the Run OpenGLESExampleDebug action, then click on the Options tab, and then select the OpenGL ES Enable frame capture checkbox. For this feature to work, you must run the application on an iOS device, and the device must be running iOS 5.0 or later. This feature will not work within the iOS simulator. You will need to ensure that after you have attached your device, you will then need to restart Xcode for this option to become available. When you have confgured your project correctly, click on the OK button to accept the changes made, and close the dialog box. Next, build and run your OpenGL ES application. When you run your application, you will see two three-dimensional and colored box cubes. When you run your application on the iOS device, you will notice that the frame capture appears within the Xcode 4 debug bar, as shown in the following screenshot: When using the OpenGL ES features of Xcode 4.2, these debugging features enable you to do the following: Inspect OpenGL ES state information. Introspect OpenGL ES objects such as view textures and shaders. Step through draw calls and watch changes with each call. Step through the state calls that proceed each draw call to see exactly how the image is constructed. The following screenshot displays the captured frame of our sample application. The debug navigator contains a list of every draw call and state call associated with that particular frame. The buffers that are associated with the frame are shown within the editor pane, and the state information is shown in the debug windowpane. The default view when the OpenGL ES frame capture is launched is displayed in the Auto view. This view displays the color portion, which is the Renderbuffer #1, as well as its grayscale equivalent of the image, that being Renderbuffer #2. You can also toggle the visibility between each of the channels for red, green and blue, as well as the alpha channels, and then use the Range scroll to adjust the color range. This can be done easily by selecting each of the cog buttons, shown in the previous screenshot. You also have the ability to step through each of the draw calls in the debug navigator, or by using the double arrows and slider in the debug bar. When using the draw call arrows or sliders, you can have Xcode select the stepped-to draw call from the debug navigator. This can be achieved by Control + clicking below the captured frame, and choosing the Reveal in Debug Navigator from the shortcut menu. You can also use the shortcut menu to toggle between the standard view of drawing the image, as well as showing the wireframe view of the object, by selecting the Show Wireframe option from the pop-up menu, as shown in the previous screenshot. When using the wireframe view of an object, it highlights the element that is being drawn by the selected draw call. To turn off the wireframe feature and have the image return back to the normal state, select the Hide Wireframe option from the pop-up menu, as shown in the following screenshot: Now that you have a reasonable understanding of debugging through an OpenGL ES application and its draw calls, let's take a look at how we can view the textures associated with an OpenGL ES application. View textures When referring to textures in OpenGL ES 2.0, this is basically an image that can be sampled by the graphics engine pipeline, and is used to map a colored image onto a mapping surface. To view objects that have been captured by the frame capture button, follow these simple steps: Open the Assistant Editor to see the objects associated with the captured frame. In this view, you can choose to see all of the objects, only bound objects, or the stack. This can be accessed from the View | Assistant Editor | Show Assistant Editor menu, as shown in the following screenshot: Open a secondary assistant editor pane, so that you can see both the objects and the stack frame at the same time. This can be accessed from the View | Assistant Editor | Add Assistant Editor menu shown previously, or by clicking on the + symbol, as shown in the following screenshot: To see details about any object contained within the OpenGL ES assistant editor, double-click on the object, or choose the item from the pop-up list, as shown in the next screenshot. It is worth mentioning that, from within this view, you have the ability to change the orientation of any object that has been captured and has been rendered to the view. To change the orientation, locate the Orientation options shown at the bottom- right hand of the screen. Objects can be changed to appear in one or more views as needed, and these are as follows: Rotate clockwise Rotate counter-clockwise Flip orientation vertically Flip orientation horizontally For example, if you want to see information about the vertex array object (VAO), you would double-click on it to see it in more detail, as shown in the following screenshot. This displays all the X, Y, and Z-axes required to construct each of our objects. Next, we will take a look into how shaders are constructed. Shaders There are two types of shaders that you can write for OpenGL ES; these are Vertex shaders and Fragment shaders. These two shaders make up what is known as the Programmable portion of the OpenGL ES 2.0 programmable pipeline, and are written in a C-like language syntax, called the OpenGL ES Shading Language (GLSL). The following screenshot outlines the OpenGL ES 2.0 programmable pipeline, and combines a version of the OpenGL Shading Language for programming Vertex Shader and Fragment Shader that has been adapted for embedded platforms for iOS devices: Shaders are not new, these have been used in a variety of games that use OpenGL. Such games that come to mind are: Doom 3 and Quake 4, or several fight simulators, such as Microsoft's Flight Simulator X. Once thing to note about shaders, is that they are not compiled when your application is built. The source code of the shader gets stored within your application bundle as a text fle, or defned within your code as a string literal, that is, vertShaderPathname = [[NSBundlemainBundle] pathForResource:@"Shader" ofType:@"vsh"];   Before you can use your shaders, your application has to load and compile each of them. This is done to preserve device independence. Let's take for example, if Apple decided to change to a different GPU manufacturer, for future releases of its iPhone, the compiled shaders may not work on the new GPU. Having your application deferring the compilation to runtime will avoid this problem, and any latest versions of the GPU will be fully supported without a need for you to rebuild your application. The following table explains the differences between the two shaders. Shader type Description Vertex shaders These are programs that get called once-per-vertex in your scene. An example to explain this better would be - if you were rendering a simple scene with a single square, with one vertex at each corner, this would be called four times. Its job is to perform some calculations such as lighting, geometry transforms, moving, scaling and rotating of objects, to simulate realism. Fragment shaders These are programs that get called once-per-pixel in your scene. So, if you're rendering that same simple scene with a single square, it will be called once for each pixel that the square covers. Fragment shaders can also perform lighting calculations, and so on, but their most important job is to set the final color for the pixel. Next, we will start by examining the implementation of the vertex shader that the OpenGL template created for us. You will notice that these shaders are code fles that have been implemented using C-Syntax like instructions. Lets, start by examining each section of the vertex shader fle, by following these simple steps: Open the Shader.vsh vertex shader fle located within the OpenGLESExample folder of the Project Navigator window, and examine the following code snippet. attribute vec4 position; attribute vec3 normal; varyinglowp vec4 colorVarying; uniform mat4 modelViewProjectionMatrix; uniform mat3 normalMatrix; void main(){   vec3eyeNormal = normalize(normalMatrix * normal);   vec3lightPosition = vec3(0.0, 0.0, 1.0);   vec4diffuseColor = vec4(0.4, 0.4, 1.0, 1.0);   floatnDotVP = max(0.0, dot(eyeNormal, normalize(lightPosition)));   colorVarying = diffuseColor * nDotVP;   gl_Position = modelViewProjectionMatrix * position; } Next, we will take a look at what this piece of code is doing and explain what is actually going on. So let's start. The attribute keyword declares that this shader is going to be passed in an input variable called position. This will be used to indicate the position of the vertex. You will notice that the position variable has been declared of type vec4, which means that each vertex contains four foating-point values. The second attribute input variable that is declared with the variable name normal, has been declared of type vec3, which means that the vertex con- tains three foating-point values that are used for the rotational aspect around the x, y, and z axes. The third attribute input variable that is declared with the variable name diffuseColor, defnes the color to be used for the vertex. We declare an- other variable called colorVarying. You will notice that it doesn't contain the attribute keyword. This is because it is an output variable that will be passed to the fragment shader. The varying keyword tells us the value for a particular vertex. This basically means that you can specify a different color for each vertex, and it will make all the values in-between a neat gradient that you will see in the fnal output. We have declared this as vec4, because colors are comprised of four compo- nent values. Finally, we declare two uniform keyword variables called modelViewProjectionMatrix and normalMatrix. The model, view, and projection matrices are three separate matrices. Model maps from an object's local coordinate space into world space, view from world space to camera space, and projection from camera to screen. When all three are used, you can then use the one result to map all the way from object space to screen space, enabling you to work out what you need to pass on to the next stage of a programmable pipeline from the incoming vertex positions. The normal matrix vectors are used to determine how much light is received at the specifed vertex or surface. Uniforms are a second form of data that al- low you to pass from your application code to the shaders. Uniform types are available to both vertex and fragment shaders, which, unlike attributes, are only available to the vertex shader. The value of a uniform cannot be changed by the shaders, and will have the same value every time a shader runs for a given trip through the pipeline. Uniforms can also contain any kind of data that you want to pass along for use in your shader. Next, we assign the value from the color per-vertex attribute to the varying variable colorVarying. This value will then be available in the fragment shader in interpolated form. Finally, we modify the gl_Position output variable, using the foating point translate variable to move the vertex along the X, Y, and Z-axes, based on the value of the translate uniform. Next, we will take a look at the fragment shader that the OpenGL ES tem- plate created for us. Open the Shader.fsh fragment shader fle located within the OpenGLESExample folder of the Project Navigator window, and examine the following code snippet. varyinglowp vec4 colorVarying; void main(){   gl_FragColor = colorVarying; } We will now take a look at this code snippet, and explain what is actually going on here. You will notice that within the fragment shader, the declaration of the varying type variable colorVarying, as highlighted in the code, has the same name as it did in the vertex shader. This is very important; if these names were different, OpenGL ES won't realize it's the same variable, and your program will produce unexpected results. The type is also very important, and it has to be the same data type as it was declared within the vertex shader. This is a GLSL keyword that is used to specify the precision of the number of bytes used to represent a number. From a programming point of view, the more bytes that are used to represent a number, the fewer problems you will be likely to have with the rounding of foating point calculations. GLSL allows the user to precision modifers any time a variable is declared, and it must be declared within this fle. Failure to declare it within the fragment shader, will result in your shader failing to compile. The lowp keyword is going to give you the best performance with the least accuracy during interpolation. This is the better option when dealing with colors, where small rounding errors don't matter. Should you fnd the need to increase the precision, it is better to use the mediump or highp, if the lack of precision causes you problems within your application. For more information on the OpenGL ES Shading Language (GLSL) or the Precision modifers, refer to the following documentation located at: http://www.khronos.org/registry/ gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf.
Read more
  • 0
  • 0
  • 7109

article-image-forcecom-data-management
Packt
25 Jan 2012
12 min read
Save for later

Force.com: Data Management

Packt
25 Jan 2012
12 min read
The basics of data operations When we migrate an application on Force.com there is a very important process of migrating the data from the existing legacy system or spread sheets and loading it into the cloud application. To improve the data quality we also need to ensure that there are no duplicate entries and the data is clean. Some basic data management operations are: Data export: Data can be exported for making periodic backups or downloading the entire data It can also be used to get the template to insert data Inserting data: We can insert data into existing standard and custom objects Also helpful in migrating users Updating data: Updating is needed to run a de-dupe check to remove duplicates It is also used for enhancing or cleaning the data Deleting data: The deleting operation is used to free up legacy data and to remove erroneous data We will be seeing the above operations using the data loader, using a wizard as well as using the command line interface. The importance of record IDs Why do we have a separate section on record IDs? Once we master them, most of the data operations are a piece of cake. The record ID is a unique identifier of the record. The record ID is similar to the primary ID and foreign ID in a database table. The SalesForce.com record IDs are not just a bunch of random numbers, but have valuable information that can help us. 18-character and 15-character IDs Force.com uses two types of IDs, 18 character and 15 character. When we use the ID from the URL it is 15 characters, however, when we use the data loader we get 18 character IDs. The 15-character ID is case sensitive while the 18 character ID is not case sensitive. The 18-character ID is used to migrate data from legacy systems or spread sheets, which do not recognize case-sensitive IDs. Both the IDs work with Force.com and point to the same record. However, if we are to migrate data from the spread sheets or export data to the spread sheets we have to use 18-character IDs. The 15-character ID is case sensitive while the 18 character ID is not case sensitive. The 18-character ID is used to migrate data from legacy systems or spread sheets, which do not recognize case-sensitive IDs. Both the IDs work with Force.com and point to the same record. However, if we are to migrate data from the spread sheets or export data to the spread sheets we have to use 18-character IDs. URL of the record detail page. By running a report on the object. Data loader (API access). URL and report will always return the 15-characters IDs while the data loader and the API return the 18-digit IDs. API, however, accepts both 15-character and 18-character IDs. Let us now explore what we can gain from IDs in the URL. Exploring the URL format The easiest way to study the record ID is by understanding the standard Salesforce objects. These IDs are fixed and are of similar format across the Salesforce organization. Every custom object will have a fixed pattern, but will be unique to individual organizations only. For the purpose of an example we will study the record ID of opportunity object. Open an opportunity record as shown in the following screenshot:   If you do not find it on display, please select the Standard Sales Application using the drop-down in the corner. The record ID is seen in the address bar of the browser as shown in the following screenshot: Let's observe the format of the URL of the address bar carefully, the URL is made up of different things as shown in the following screenshot: Let us now look at the different parts of the URL separated in the preceding screenshot. Instance name: The first part of the URL is the instance name. Salesforce. com provides multiple instances of the server and the instance name varies depending on the time and location from where you create the ID. For the full list of instance name and their status please visit: Packt Publishing Salesforce Server URL: This is common for all the record IDs. Record ID: The third part of the URL as seen in the preceding screenshot is the Record ID. The format of the URL is common for all the detail page of the records across the organizations. We will be looking at the Record ID in greater detail in the next section. Record ID One important thing to note is that the record ID is case sensitive. So 00690000003zRfq is not equal to 00690000003ZRFQ. There will be a unique ID per organization, no two records will have same ID. When we migrate data from one organization to another, the IDs are changed. The record ID is further split into two parts as shown in the following screenshot: The first three characters of the record ID are the object identifiers, which help us to identify the object of the record. The remaining characters are the unique record ID to identify the record. All the objects thus have three-character encoded ID, which is a prefix to their record ID elsewhere. The following table shows us the three character prefixes and the objects that they are associated with: Prefix Object Name 001 Account 003 Contact 005 User 006 Opportunity 00e Profiles 00Q Leads 00T Tasks 00U Event 015 Document 01t Product 500 Case 701 Campaign 800 Contract These prefixes for the standard objects are the same across all the Salesforce organization. Now let's play with the URL a bit. If we wish to land on the list view of the standard object, all we need to do is append a /o against the object prefix example for the account object we need to add 001/o. The final Salesforce URL in this case would be: The instance name for every organization would be different. The name depends on the geography and the time when the Salesforce account is created, please check the instance name for your organization. This will open the list view or the tab view of the object, similarly we can change the prefix to any standard object. When we change the /o to /e it automatically opens the edit page for a new account: This will open the edit page for the account directly. If we add the /e in front of a record ID, we get the update detail page for the record, for example, let's say we want to open the opportunity edit page from our original example. Now if we add the /e in front of the URL, we get: This will automatically open this opportunity for editing; try adding a /e against any record URL and we will get the edit page for the record with all the existing data pre-filled with information. Only when we create the full-copy sandbox of the production organization, do we get similar IDs in productions and sandbox only once. Record ID in the field is not editable even in code. We cannot include it in DML for insert, but will be useful for DML of update and delete. Summarizing record IDs Let us recap some important points we learned about record IDs: A record ID is similar to the primary or foreign key in a database Record IDs are case sensitive on Salesforce.com 18-character IDs are case insensitive Every record ID has a prefix of the first three characters Every record in multiple and similar organizations have a different ID IDs are similar only for the first time when we have a full-copy sandbox of a production organization 18-character IDs are used for migrating data from a legacy system that is case insensitive Relationships — dependents first In the general library system we have the following relationships: The object Customer is related to Media through a junction object CustomerMedia. Media object is a master for books and video objects as shown in the following diagram: As shown in the following diagram, Fine has a Lookup on Cards: Finally, as shown in the following diagram Card has a Master-Detail lookup on Customer: When we load data we have to load in the order of dependencies, for example, in the library system the CustomerMedia object is dependent on the Customer and Media object. Hence, to load the CustomerMedia object we use the following sequence: Load Customer object data. Load Media object data. Finally load the CustomerMedia object data. The rule of thumb when uploading the data is that every dependent object data should be loaded first. As the CustomerMedia object had two dependencies, one on Customer object and another on Media object, we loaded both the data first. Modifying system fields When we are loading legacy data into the Salesforce system during migration it is sometimes essential to change the Created Date, Last Modified Date, Last Modified by, and Created by entries. Normally, when we load data into the system, the dates are not modifiable and are system time stamped. However, Salesforce.com provides us with the facility for modifying the auditing fields only once during the first insert. This is helpful in migrating legacy data and preserving history. To enable this feature we need to contact Salesforce customer support. Features of modifiable system fields Some of the important features of modifiable system fields are as follows: The fields can be modified only once in the lifetime of the initial insert They are accessible through API, that is, the data loader All custom objects can have modifiable system fields, however, not all standard objects can have modifiable fields Account, opportunity, contact, lead, case, task, and event can have modifiable fields The fields are read-only for existing records Connecting to Salesforce server through API When loading the data from a data loader, we make an API call for the Salesforce server. The login credentials are passed through the user name + password + security token. We can bypass the use of a security token if we white list the IP address from where the data is loaded. The CRUD commands The full-form of CRUD is create, read, update, and delete. Force.com provides us with all the four operations using API. Records can be inserted, updated, deleted, and extracted from the server. The inserting process requires data to be inserted without the ID because Force.com generates the record ID when the record is created. Data is uploaded in the form of a .CSV file created using a spread sheet. We can map individual fields to Force.com, however, to save time we can initially extract a single record with the selected fields and use it as a template to quickly map other fields. The update process is the same as insert, but since we are updating an existing record we need an ID or an External ID during update. The delete command only needs the ID of the record to be deleted. Apart from the general CRUD commands Force.com provides a special upsert command. Upsert is the combination of insert and update in a single call. Upsert uses ID or external ID to match records with existing records, if no match is found or the ID is missing, it inserts the record. The upsert command is helpful in avoiding duplicates based on ID or external ID. If more than one record is matched, an error is reported. External IDs When we are migrating data from other systems, it is useful to have a foreign key to link data between the two systems. The external ID helps to create that link. A custom field can be marked as an external ID to identify the primary keys of the legacy system (outside Salesforce). Text, number, e-mail, and auto-number fields can be flagged as external IDs. Fields marked as external IDs are indexed and are available on all the objects that can have custom fields. These fields are searchable and appear in search queries. An object can have three external ID fields. The external ID fields become the cross reference fields to the legacy systems. For example, if the following data is in the legacy spread sheets for the library system we can migrate it to Salesforce by mapping the Media Number to the Media Number External ID field. The original data from the legacy system are given as follows: Media Number Media Name Media Type 1 Harry Potter and the chamber of secret Book 2 Freakonomics Book 3 Mission Impossible Video We can map this legacy data to the Force.com fields using the following mapping: Media_Number__c (External ID) Name Media_Type__c 1 Harry Potter and the chamber of secret Book 2 Freakonomics Book 3 Mission Impossible Video When we are migrating data from the legacy system into the Salesforce.com system, we can upsert the data using the external ID field. This way there is no need to know the Salesforce record ID. We can prevent duplication of data using the external ID. We can also load data into related objects without using ID. Ideally, when loading data into related objects, we need to include the Salesforce ID as the field name in the data files, but we can also include an external ID and perform an upsert. Using the external ID while loading relationship is only permitted during the upsert operation. Exercise – migrating data from legacy system The general library wishes to migrate the media information from the spread sheets they are using. They have a field called Media Number in the spread sheet, which is a unique identifier (primary key) in the current system. Migrate the data into the media object and avoid duplicates. To migrate the data from the spread sheet, we first need to create an external ID field in the media object. Create a Media_number field on the media object in Salesforce, the field can be a text field. Check the flag for external ID to flag it. While loading the data use the Media_ number fleld and use upsert to load the data.
Read more
  • 0
  • 0
  • 2703

article-image-net-generics-40-container-patterns-and-best-practices
Packt
24 Jan 2012
6 min read
Save for later

.NET Generics 4.0: Container Patterns and Best Practices

Packt
24 Jan 2012
6 min read
(For more resources on .NET, see here.) Generic container patterns There are several generic containers such as List<T>, Dictionary<Tkey,Tvalue>, and so on. Now, let's take a look at some of the patterns involving these generic containers that show up more often in code. How these are organized Each pattern discussed in this article has a few sections. First is the title. This is written against the pattern sequence number. For example, the title for Pattern 1 is One-to-one mapping. The Pattern interface section denotes the interface implementation of the pattern. So anything that conforms to that interface is a concrete implementation of that pattern. For example, Dictionary<TKey,TValue> is a concrete implementation of IDictionary<TKey,TValue>. The Example usages section shows some implementations where TKey and TValue are replaced with real data types such as string or int. The last section, as the name suggests, showcases some ideas where this pattern can be used. Pattern 1: One-to-one mapping One-to-one mapping maps one element to another. Pattern interface The following is an interface implementation of this pattern: IDictionary<TKey,Tvalue> Some concrete implementations Some concrete implementations of this pattern are as follows: Dictionary<TKey,TValue> SortedDictionary<TKey,TValue> SortedList<TKey,TValue> Example usages The following are examples where TKey and TValue are replaced with real data types such as string or int: Dictionary<string,int> SortedDictionary<int,string> SortedList<string,string> Dictionary<string,IClass> Some situations where this pattern can be used One-to-one mapping can be used in the following situations: Mapping some class objects with a string ID Converting an enum to a string General conversion between types Find and replace algorithms where the find and replace strings become key and value pairs Implementing a state machine where each state has a description, which becomes the key, and the concrete implementation of the IState interface becomes the value of a structure such as Dictionary<string,IState> Pattern 2: One-to-many unique value mapping One-to-many unique value mapping maps one element to a set of unique values. Pattern interface The following is an interface implementation of this pattern: IDictionary<TKey,ISet<Tvalue>> Some concrete implementations Some concrete implementations of this pattern are as follows: Dictionary<TKey,HashSet<TValue>> SortedDictionary<TKey,HashSet<TValue>> SortedList<TKey,SortedSet<TValue>> Dictionary<TKey,SortedSet<TValue>> Example usages The following are examples where TKey and TValue are replaced with real data types such as string or int: Dictionary<int,HashSet<string>> SortedDictionary<string,HashSet<int>> Dictionary<string,SortedSet<int>> Some situations where this pattern can be used One-to-many unique value mapping can be used in the following situations: Mapping all the anagrams of a given word Creating spell check where all spelling mistakes can be pre-calculated and stored as unique values Pattern 3: One-to-many value mapping One-to-many value mapping maps an element to a list of values. This might contain duplicates. Pattern interface The following are the interface implementations of this pattern: IDictionary<TKey,ICollection<Tvalue>> IDictionary<TKey,Ilist<TValue>> Some concrete implementations Some concrete implementations of this pattern are as follows: Dictionary<TKey,List<TValue>> SortedDictionary<TKey,Queue<TValue>> SortedList<TKey,Stack<TValue>> Dictionary<TKey,LinkedList<TValue>> Example usages The following are examples where TKey and TValue are replaced with real data types such as string or int: Dictionary<string,List<DateTime>> SortedDictionary<string,Queue<int>> SortedList<int,Stack<float>> Dictionary<string,LinkedList<int>> Some situations where this pattern can be used One-to-many value mapping can be used in the following situations: Mapping all the grades obtained by a student. The ID of the student can be the key and the grades obtained in each subject (which may be duplicate) can be stored as the values in a list. Tracking all the followers of a Twitter account. The user ID for the account will be the key and all follower IDs can be stored as values in a list. Scheduling all the appointments for a patient whose user ID will serve as the key. Pattern 4: Many-to-many mapping Many-to-many mapping maps many elements of a group to many elements in other groups. Both can have duplicate entries. Pattern interface The following are the interface implementations of this pattern: IEnumerable<Tuple<T1,T2,..,ISet<Tresult>>> IEnumerable<Tuple<T1,T2,..,ICollection<Tresult>>> Some concrete implementations A concrete implementation of this pattern is as follows: IList<Tuple<T1,T2,T3,HashSet<TResult>>> Example usages The following are examples where TKey and TValue are replaced with real data types such as string or int: List<Tuple<string,int,int,int>> List<Tuple<string,int,int,int,HashSet<float>>> Some situations where this pattern can be used Many-to-many mapping can be used in the following situations: If many independent values can be mapped to a set of values, then these patterns should be used. ISet<T> implementations don't allow duplicates while ICollection<T> implementations, such as IList<T>, do. Imagine a company wants to give a pay hike to its employees based on certain conditions. In this situation, the parameters for conditions can be the independent variable of the Tuples, and IDs of employees eligible for the hike can be stored in an ISet<T> implementation. For concurrency support, replace non-concurrent implementations with their concurrent cousins. For example, replace Dictionary<TKey,TValue> with ConcurrentDictionary<TKey,TValue>.
Read more
  • 0
  • 0
  • 2411

article-image-axure-rp-6-prototyping-essentials-advanced-interactions
Packt
23 Jan 2012
20 min read
Save for later

Axure RP 6 Prototyping Essentials: Advanced Interactions

Packt
23 Jan 2012
20 min read
Conditions When you incorporate conditional logic into your prototype, you save yourself from a great deal of overhead work. If you don't use conditional logic, you are limiting your ability to simulate conditional interactions in your prototype. Let's face it, we use logic all the time, even if the results are not always logical. Moreover, in computer science and interaction design, we must use conditional logic in order to accommodate variable situations and exceptions. Yet, there seems to be a general reluctance to deal with direct use of logic when it comes to using software. A good example is the so-called "advanced" search feature that most search engines, including Google, offer, and the reason why Google's concept of a single search field and no operators has become the standard search interface. If-Then-Else Programming languages employ a variety of syntaxes for creating and evaluating conditional statements. Axure simplifies things by using the very common syntax of If-Then-Else, which essentially looks like this: If A is true, then do X Else, if B is true then do Z This kind of decision-making is very natural to UX designers because we use a similar logical approach to model task and interaction flows. When we create a conditional interaction, we reflect the flow's logic in the prototype. As it is best to learn by doing, let's dive into a quick example: Sandbox files for learning and experimentingSometimes, the most effective way to figure things out is by experimentation. In the course of prototyping, you will find yourself wondering how some Axure feature works, or wanting to explore a new interaction. This is where the sandboxing technique can help: Create a blank new Axure file on your desktop, work through your explorations on this file, and then apply your learning to the project file. In the sandbox file, you don't have to worry about "breaking" any of your previous work and can focus instead just on the mechanics of the feature you are trying to figure out. The technique will also keep your production file clean and free of experimentation wireframes. Guided example—conditions and dynamic panels Axure makes it very easy to apply conditional logic to the prototype, as you will see in this example. We will use a "sandbox" file to explore the feature, and later, apply the learning to the Alexandria project file. Step 1: Defining the interaction Our goal is to change the state of a dynamic panel based on the user input. This is one of the most commonly used conditional logic interactions in the construction of Axure prototypes, and a great illustration of If-Then-Else. In this simple example, the user selects a shape from a list of shapes and the appropriate shape appears. The first step is to define the desired interaction. Now that the conditional logic is involved, this is also the opportunity to spell out the logic: When: When the user changes the selection Where: A droplist widget What: Change the state of a dynamic panel to show the corresponding shape Conditions: On entry, the droplist should show the option "Select from List", and no shape should be visible If the user selects the value "Rectangle" from the droplist, show the rectangle state If the user selects the value "Triangle" from the droplist, show the triangle state If the user selects the value "Circle" from the droplist, show the circle state If the user selects the value "Select from List" from the droplist, hide the shapes The logic here is simple, because each selection in the droplist has a corresponding dynamic panel state. We instruct Axure to evaluate which option is selected in the droplist, and then change the state of the dynamic panel accordingly. Notice that the first sentence in the condition section specifies the entry state, in this case, the option "Select from List". This is the default state when the screen loads. When you plan interactions that involve conditions, always make sure you account for a default state of that UI control. Step 2: Constructing Wireframe We will start by preparing a sandbox environment. Create a blank directory on your desktop and label it Axure Sandbox. In this directory, create another directory named HTML—this is where we will generate the prototype. Finally, create a new Axure file named Sandbox and save it in the Axure Sandbox directory. The following screenshot illustrates the steps: From the Widgets pane, drag over a Droplist (A) and a Dynamic Panel (B) widget. Label them Droplist and Shape States, for example. Double-click on the droplist and in the Edit Droplist dialog (C), add the values specified in Step 1, and check Select from List to make it the default value (D). In the dynamic panel, create the states for the three corresponding shapes. This does not need to be an elaborate production. Make sure you label each of the states (E). Right-click on the dynamic panel and select Set Hidden from the context menu. Step 3: Setting the first condition When you start with interactions, having the flow pre-planned helps in creating the wireframe and in adding the interaction. Now that we covered both in Steps 1 and 2, we will set the conditional logic for the interaction. The following screenshot visualizes the process: With the droplist selected in the wireframe (A), switch to the Interactions tab in the Widgets Properties pane, and double-click on the OnChange event (B) to launch Case Editor (C). Before clicking on Case 1, we want to create the conditions. Click on the Add Condition link (D) to launch the Condition Builder window (E). We will discuss Condition Builder later in this article, but for now, notice that the first condition row already appears in the builder. It is contextual to the selected widget, and is structured like an equation: "The selected option of droplist equals the value Select from List." (F). As this is one of the conditions that needs to be evaluated, each time the user changes a value in the droplist, we can use it as is. Click on OK to close the builder. Back in Case Editor (C), notice that the condition has been added to Case 1, and Axure presents it in a human-friendly way: If selected option of Droplist equals "Select from List" (G). Step 4: Adding the first interaction Now we need to instruct Axure about the action to apply when the condition we have just set is met. In this case, when the value of the droplist is "Select from List", the shapes should be hidden. As the shapes are in the dynamic panel widget, we want to hide that widget. Now, you might ask yourself, "Why hide the dynamic panel, if it is already hidden?" When you create conditions, always make sure to account for ALL the possible cases that are applicable to the interaction. It is true that in the wireframe, the dynamic panel is hidden by default. However, once the user selects a shape from the list, the dynamic panel will become visible, until the user changes the value in the droplist to "Select from List". Moreover, this is where the Set Hidden action will come into play. The following screenshot illustrates the process: (Move the mouse over the image to enlarge.) First, label Case 1 to something meaningful such as Hide Shapes (A). Select the Hide Panel(s) action (B), and click to check the dynamic panel widget which is listed in the column Step 4: Configure actions (C). The complete interaction is now listed under the column Step 3: Organize actions (D). Step 5: Completing the interaction At this point, the interaction includes a single case, which handles a single condition. In order to complete the interaction, we need to capture additional cases when the user picks various shapes from the droplist. The following screenshot illustrates the first half of the process involved in adding the condition and interaction for the first shape: As we are evaluating the selected option of the same widget, the only variable to check is the value of that option. This means we can use a shortcut: Right-click on the initial case created in Step 4 (A), select Copy Case (B), and then Paste Case(s) (C) from the context menu. Double-click on the new case (D) to open Case Editor (E), where the first things to do are to rename the case (F), and delete the Hide action (G). Using one case as the starting point for another case is common. The following screenshot illustrates the process of tweaking the duplicate case to fit a new condition: In Case Editor, click on the Edit Condition link (A). All there is to do next is to change the copied selection from Select from List to Rectangle (B), and close the Condition Builder. Back in Case Editor, set the action to Set Panel state(s) to State(s) in the column Step 2: Add actions (C) and in the column Step 4: Configure actions (D), check the dynamic panel widget (D) and select the Rectangle shape (E). We need to add a Show Panel(s) action (F), because the dynamic panel widget is hidden by default and it is also hidden when the selected option in the droplist is Select from List. With this action set up, close Case Editor. The interaction for OnChange case (G) now covers a couple of the conditions we specified in Step 1 of this example. Add the additional cases using the process outlined in Step 5. The following screenshot illustrates what the complete conditional interaction should look like: When the user changes an item in the droplist, the OnChange action (A) evaluates the selection and: If the user selects the value Rectangle from the droplist, show the rectangle state (C) If the user selects the value Triangle from the droplist, show the triangle state (D) If the user selects the value Circle from the droplist, show the circle state (E) If the user selects the value Select from List from the droplist, hide the shapes (B) It is time to generate the HTML prototype. Make sure you set the path for the prototype to Axure Sandbox | HTML, as discussed in Step 2 of this example. The following screenshot (A through E) illustrates the generated result: The Condition Builder Let's take a closer look at a simple conditional statement from the previous example. The interaction for the Rectangle case is composed of the following two sentences: If the selected option in the Droplist equals Rectangle Set Shape State state to Rectangle The first sentence is the condition being evaluated, and the second sentence is the action that takes place if the condition is met. Now let's focus on the condition sentence itself, and the flexibility afforded by the modular nature of Axure's Condition Builder. Although the condition is composed of five droplists, what we are looking at is an equation in which we compare the first two droplists to the last two droplists. The following screenshot illustrates how the segments are assembled in the builder: selected option of is one of eleven choices in the first droplist (A). The selection made here affects the other droplists. Note that, although in our example, we have the interaction tied to the OnChange event of the droplist widget, the condition we add does not have to be limited to evaluating the selected value of the droplist. We could add other conditions to the OnChange event that evaluate any of the other choices in the builder's droplist A. Our choice in the first droplist narrows the options in the second droplist (B) to droplists and listbox widgets. Our example wireframe has only a single droplist widget and so Droplist (Droplist) is the only item listed. The third droplist (C) is where we set the comparison choices in droplists A and B to the choices in droplist D and E. In addition to the option Equals, there are nine more options to construct the equation. In the forth droplist (D), we specify what type of value the comparison will evaluate. In our example, we want to know what is the selected option in the wireframe droplist. However, we could also set a comparison to the selected option of some other droplist and create contextual droplists (example is coming below). The last droplist (E) is contextual to the selection we make in D. In our example, as we opted to look at the value of the Droplist widget (B), Axure lists the values we entered for that widget. Finally, the Description section (F) is automatically generated by Axure to reiterate the condition in plain English. Guided example—multiple conditions We often have to evaluate multiple conditions before we can determine which action to take. For example, simulating validation of the required form or authentication fields, or simulating a contextual rendering of an application screen, based on the user login, status, and other parameters. The Condition Builder is a significant time saver because with relatively few wireframes, mostly variations within dynamic panel states, it is possible to create multiple conditions and simulate sophisticated interactions. For this example, we will continue with our Alexandria project. Specifically, we will complete the login validation. Step 1: Defining the interaction Our goal is to evaluate if the user entered their username and password, prompt the user if there is missing or wrong information, or log them into the site. This interaction is broadly used, although interaction patterns vary slightly. Successful sign-in is a critical task because in most sites and applications, it affords access to the most valuable features. It is sometimes tempting to postpone, or skip altogether, simulating the sign-in task, because it is so trivial. However, without an explicit directive from UX about this flow, we are leaving the interpretation and design to the developers. In this example, we will use Condition Builder to compose several conditions and the first step is to define the desired interaction. As with all interactions that involve conditional logic, this is the opportunity to spell out the logic: When: When the user clicks on the Login button. Where: The Login rectangle widget. What: Validate the username and password. Alert the user if there is an issue, or log the user in. Conditions: On entry, the User Name and Password fields are empty, and the Login button is disabled. The Login button is enabled only if both fields are populated. When the user clicks on the Login button. If the provided credentials are ok, change the screen to reflect a successful sign-in screen. If any or both credentials are wrong, notify the user and disable the Login button. If the user re-typed the credentials, reactivate the Login button. The logic here is a bit more involved compared to the previous example, as we need to account for the interaction in multiple widgets: The form fields, the Login button, and the notification to the user in the case of an authentication problem. The following screenshot illustrates the current state of our Login wireframe and its interaction The Subscribe/Login master is a dynamic panel with two states Login Form is on the second state, and the only widget with an interaction is the Login button (A) The interaction is triggered by an OnClick event which does not evaluate the User Name and Password fields Step 2: Constructing Wireframe We will start by iterating on top of what we have already created, and make adjustments to the wireframe according to the new requirements. The following screenshot illustrates the tweaks: It appears that in the current wireframe, there is no room for providing the user with feedback if there is an issue with the username or password. Right above the Login button, we will add a dynamic panel which will contain the error message (see the preceding screenshot, A) and set it to Hidden (B). Remember that dynamic panel widgets are the only widget type or which you can control the visibility properties: The height of the current pop up needs to be adjusted to accommodate the added alert, and remember to increase the height of the dynamic panel that holds the state. The Login button (C) needs to have a disabled state. There are at least a couple of ways to approach the constructions. Converting the widget into a dynamic panel and adding a disabled state: Use the styling feature to add a Disabled Style (D) in the Set Disabled Style editor (E) and use actions to switch between normal and disabled styles. In this example, I will use the second option because it involves maintaining a single wireframe widget and the use of styles which is faster to update. With the wireframe updated to support the interaction, we can now move on to the next step. Step 3: Interaction tweaks A quick review of the flow of interactions, as it pertains to the wireframes, is illustrated in the following screenshot: The entire interaction is triggered when the user clicks on the Log In button (A), a widget in the Before Login state of the dynamic panel DP Subscribe Actions Bar (B), which is part of the master M Subscribe/Login (C). The dynamic panel Login Form (D), which is also in the master (C), will become visible. The alert dynamic panel (E) will be hidden. This is taken care of by setting the default visibility of this dynamic panel to hidden: The Login button (F) needs to be set to show its disabled state. As setting the Login button to a disabled style cannot be defaulted on the widget, it needs to be set in an action. The process is illustrated in the following screenshot: The logical place to have this action is on the button that triggers the interaction, the Log In button (see the preceding screenshot, A). Moreover, as we already created an initial interaction for this widget, we only need to add the action Disable Widget(s) (C and D) to that interaction. Generate the prototype and test this out. The styling of the Login button will be set to the visual style you assigned to the widget, and although the widget has an OnClick interaction associated with it, it will be disabled. Step 4: Evaluating multiple conditions Now that we have established an initial state for the Login button in the prompt, we can move on to composing the interaction that will enable the button and allow the user to continue with the login process. As the Login button should only be enabled if there is some content in the User Name and Password fields, let's assign each of the fields with an interaction to evaluate just that. In this example, our evaluation will not be too elaborate. For the User Name field, we will check whether the user typed in at least four characters. We will not look for a specific value. For the Password field, we will check whether the user typed in at least six characters. We will not look for a specific value. Both fields are Text Field widgets. This type of widget can support the following events: OnClick OnFocus OnKeyUp OnLostFocus How can we decide which event should trigger the interaction? The truth is that you can accomplish the requirements in several ways, and in some cases, it is a matter of trial and error. In this example, I will use the OnKeyUp event to trigger the actions that evaluate the fields, because this event provides an instant feedback to the user if the entered credentials satisfy the requirements. The following screenshot illustrates the conditions and interactions for the User Name field (A): We need to create two cases for the OnKeyUp event (B): The first case (C) will evaluate whether the length of the input in this field is at least four characters, and whether the length of the input in the Password field is at least six characters, because the user must provide both pieces of information. If the length of the input for both meets the conditions, the Login button (E) will be enabled. The second case (D) will keep the Login button disabled, if the length of input to the User Name field is less than four characters. We don't need to evaluate the length of the Password field, because the User Name must meet the condition and so it does not matter if the Password field is ok. This example was a demonstration of the frequent need to evaluate multiple conditions in a single case. Let's take a closer look at the Condition Builder: Satisfy all or any The preceding screenshot illustrates the Condition Builder's Satisfy droplist (A) which has only two values, all and any. By default, it is set to all, which is fine if you have a single condition in the builder. With two or more conditions, it becomes critical that this droplist is set to the correct value. To help you make sense of the condition, the droplist is set as part of a sentence. Always read the entire sentence when you consider which option to set: Satisfy all of the following: Satisfy any of the following: In our example, the conditional logic controls whether or not to activate the Login button, and because BOTH fields must be validated, the selection of "all" in the Satisfy droplist is the correct one. Step 5: Final conditional touches Complete the interactions and conditions for the Password field and generate the prototype to test whether the login control works as intended. The companion Axure file for this article includes the complete interaction, but I encourage you to try to figure it out yourself, because, as with anything else, we learn best by first-hand experimentation. It will also be nice to demonstrate the alert field, which notifies the user when either of the fields is invalid. This condition is tested when the user clicks on the active Login button. In this example, we will only evaluate the Password field and the process is illustrated in the following screenshot: We have already created an interaction for the Login button (A). The initial version did not evaluate anything, and the two cases for the OnClick event were to be triggered manually. The revised interactions for the Successful and Failed Login cases will evaluate a specific password value. While the conditional logic in the User Name and Password fields evaluates the length of the input, here, we want to simulate the system response if the value of the Password field (C) is incorrect. For convenience, set the password to something that is easy to remember and type, because as long as the conditional simulation of the login is in place, you will have to go through the flow each time you generate the prototype. This can become very tedious. Our password will be simply be 123456. If needed, however, you can use Axure to simulate very rigorous password verification conditions. The alert line (D) appears only if the password is incorrect. The Successful Login case will test if the password equals to 123456 and will change the state of the entire dynamic panel.
Read more
  • 0
  • 0
  • 4438

article-image-oracle-jdeveloper-11gr2-application-modules
Packt
20 Jan 2012
12 min read
Save for later

Oracle JDeveloper 11gR2: Application Modules

Packt
20 Jan 2012
12 min read
(For more resources on JDeveloper, see here.) Creating and using generic extension interfaces In this recipe, we will go over how to expose any of that common functionality as a generic extension interface. By doing so, this generic interface becomes available to all derived business components, which in turn can be exposed to its own client interface and make it available to the ViewController layer through the bindings layer. How to do it… Open the shared components workspace in JDeveloper Create an interface called ExtApplicationModule as follows: public interface ExtApplicationModule { // return some user authority level, based on // the user's name public int getUserAuthorityLevel();} Locate and open the custom application module framework extension class ExtApplicationModuleImpl. Modify it so that it implements the ExtApplicationModule interface. Then, add the following method to it: public int getUserAuthorityLevel() { // return some user authority level, based on the user's name return ("anonymous".equalsIgnoreCase(this.getUserPrincipalName()))? AUTHORITY_LEVEL_MINIMAL : AUTHORITY_LEVEL_NORMAL;} Rebuild the SharedComponents workspace and deploy it as an ADF Library JAR. Now, open the HRComponents workspace Locate and open the HrComponentsAppModule application module defnition. Go to the Java section and click on the Edit application module client interface button (the pen icon in the Client Interface section). On the Edit Client Interface dialog, shuttle the getUserAuthorityLevel() interface from the Available to the Selected list. How it works… In steps 1 and 2, we have opened the SharedComponents workspace and created an interface called HrComponentsAppModule. This interface contains a single method called getUserAuthorityLevel(). Then, we updated the application module framework extension class HrComponentsAppModuleImpl so that it implements the HrComponentsAppModule interface (step 3). We also implemented the method getUserAuthorityLevel() required by the interface (step 4). For the sake of this recipe, this method returns a user authority level based on the authenticated user's name. We retrieve the authenticated user's name by calling getUserPrincipal().getName() on the SecurityContext, which we retrieve from the current ADF context (ADFContext.getCurrent().getSecurityContext()). If security is not enabled for the ADF application, the user's name defaults to anonymous. In this example, we return AUTHORITY_LEVEL_MINIMAL for anonymous users, and for all others we return AUTHORITY_LEVEL_NORMAL. We rebuilt and redeployed the SharedComponents workspace in step 5. In steps 6 through 9, we opened the HRComponents workspace and added the getUserAuthorityLevel() method to the HrComponentsAppModuleImpl client interface. By doing this, we exposed the getUserAuthorityLevel() generic extension interface to a derived application module, while keeping its implementation in the base framework extension class ExtApplicationModuleImpl. There's more… Note that the steps followed in this recipe to expose an application module framework extension class method to a derived class' client interface can be followed for other business components framework extension classes as well. Exposing a custom method as a web service Service-enabling an application module allows you, among others, to expose custom application module methods as web services. This is one way for service consumers to consume the service-enabled application module. The other possibilities are accessing the application module by another application module, and accessing it through a Service Component Architecture (SCA) composite. Service-enabling an application module allows access to the same application module both through web service clients and interactive web user interfaces. In this recipe, we will go over the steps involved in service-enabling an application module by exposing a custom application module method to its service interface. Getting ready The HRComponents workspace requires a database connection to the HR schema. How to do it… Open the HRComponents project in JDeveloper. Double-click on the HRComponentsAppModule application module in the Application Navigator to open its defnition. Go to the Service Interface section and click on the Enable support for Service Interface button (the green plus sign icon in the Service Interface section). This will start the Create Service Interface wizard. In the Service Interface page, accept the defaults and click Next. In the Service Custom Methods page, locate the adjustCommission() method and shuttle it from the Available list to the Selected list. Click on Finish. Observe that the adjustCommission() method is shown in the Service Interface Custom Methods section of the application module's Service Interface. The service interface fles were generated in the serviceinterface package under the application module and are shown in the Application Navigator. Double-click on the weblogic-ejb-jar.xml fle under the META-INF package in the Application Navigator to open it. In the Beans section, select the com.packt.jdeveloper. cookbook.hr.components.model.application.common. HrComponentsAppModuleService Bean bean and click on the Performance tab. For the Transaction timeout feld, enter 120. How it works… In steps 1 through 6, we have exposed the adjustCommission() custom application module method to the application module's service interface. This is a custom method that adjusts all the Sales department employees' commissions by the percentage specifed. As a result of exposing the adjustCommission() method to the application module service interface, JDeveloper generates the following fles: HrComponentsAppModuleService.java: Defnes the service interface HrComponentsAppModuleServiceImpl.java: The service implementation class HrComponentsAppModuleService.xsd: The service schema fle describing the input and output parameters of the service HrComponentsAppModuleService.wsdl: The Web Service Defnition Language (WSDL) fle, describing the web service ejb-jar.xml: The EJB deployment descriptor. It is located in the src/META-INF directory weblogic-ejb-jar.xml: The WebLogic-specifc EJB deployment descriptor, located in the src/META-INF directory In steps 7 and 8, we adjust the service Java Transaction API (JTA) transaction timeout to 120 seconds (the default is 30 seconds). This will avoid any exceptions related to transaction timeouts when invoking the service. This is an optional step added specifcally for this recipe, as the process of adjusting the commission for all sales employees might take longer than the default 30 seconds, causing the transaction to time out. To test the service using the JDeveloper integrated WebLogic application server, right-click on the HrComponentsAppModuleServiceImpl.java service implementation fle in the Application Navigator and select Run or Debug from the context menu. This will build and deploy the HrComponentsAppModuleService web service into the integrated WebLogic server. Once the deployment process is completed successfully, you can click on the service URL in the Log window to test the service. This will open a test window in JDeveloper and also enable the HTTP Analyzer. Otherwise, copy the target service URL from the Log window and paste it into your browser's address feld. This will bring up the service's endpoint page. On this page, select the adjustCommission method from the Operation drop down, specify the commissionPctAdjustment parameter amount and click on the Invoke button to execute the web service. Observe how the employees' commissions are adjusted in the EMPLOYEES table in the HR schema. There's more… For more information on service-enabling application modules consult chapter Integrating Service-Enabled Application Modules in the Fusion Developer's Guide for Oracle Application Development Framework which can be found at http://docs.oracle.com/cd/ E24382_01/web.1112/e16182/toc.htm. Accessing a service interface method from another application module In the recipe Exposing a custom method as a web service in this article, we went through the steps required to service-enable an application module and expose a custom application module method as a web service. We will continue in this recipe by explaining how to invoke the custom application module method, exposed as a web service, from another application module. Getting ready This recipe will call the adjustCommission() custom application module method that was exposed as a web service in the Exposing a custom method as a web service recipe in this article. It requires that the web service is deployed in WebLogic and that it is accessible. The recipe also requires that both the SharedComponents workspace and the HRComponents workspace are deployed as ADF Library JARs and that are added to the workspace used by this specifc recipe. Additionally, a database connection to the HR schema is required. How to do it… Ensure that you have built and deployed both the SharedComponents and HRComponents workspaces as ADF Library JARs. Create a File System connection in the Resource Palette to the directory path where the SharedComponents.jar and HRComponents.jar ADF Library JARs are located. Create a new Fusion Web Application (ADF) called HRComponentsCaller using the Create Fusion Web Application (ADF) wizard. Create a new application module called HRComponentsCallerAppModule using the Create Application Module wizard. In the Java page, check on the Generate Application Module Class checkbox to generate a custom application module implementation class. JDeveloper will ask you for a database connection during this step, so make sure that a new database connection to the HR schema is created. Expand the File System | ReUsableJARs connection in the Resource Palette and add both the SharedComponents and HRComponents libraries to the project. You do this by right-clicking on the jar fle and selecting Add to Project… from the context menu. Bring up the business components Project Properties dialog and go to the Libraries and Classpath section. Click on the Add Library… button and add the BC4J Service Client and JAX-WS Client extensions. Double-click on the HRComponentsCallerAppModuleImpl.java custom application module implementation fle in the Application Navigator to open it in the Java editor. Add the following method to it: public void adjustCommission( BigDecimal commissionPctAdjustment) { // get the service proxy HrComponentsAppModuleService service = (HrComponentsAppModuleService)ServiceFactory .getServiceProxy( HrComponentsAppModuleService.NAME); // call the adjustCommission() service service.adjustCommission(commissionPctAdjustment);} Expose adjustCommission() to the HRComponentsCallerAppModule client interface. Finally, in order to be able to test the HRComponentsCallerAppModule application module with the ADF Model Tester, locate the connections.xml fle in the Application Resources section of the Application Navigator under the Descriptors | ADF META-INF node, and add the following confguration to it: <Reference name="{/com/packt/jdeveloper/cookbook/hr/components/model/ application/common/}HrComponentsAppModuleService" className="oracle.jbo.client.svc.Service" ><Factory className="oracle.jbo.client.svc.ServiceFactory"/><RefAddresses><StringRefAddr addrType="serviceInterfaceName"><Contents>com.packt.jdeveloper.cookbook.hr.components.model. application.common.serviceinterface.HrComponentsAppModuleService </Contents></StringRefAddr><StringRefAddr addrType="serviceEndpointProvider"><Contents>ADFBC</Contents></StringRefAddr><StringRefAddr addrType="jndiName"><Contents>HrComponentsAppModuleServiceBean#com.packt.jdeveloper. cookbook.hr.components.model.application.common. serviceinterface.HrComponentsAppModuleService</Contents></StringRefAddr><StringRefAddr addrType="serviceSchemaName"><Contents>HrComponentsAppModuleService.xsd</Contents></StringRefAddr><StringRefAddr addrType="serviceSchemaLocation"><Contents>com/packt/jdeveloper/cookbook/hr/components/model/ application/common/serviceinterface/</Contents></StringRefAddr><StringRefAddr addrType="jndiFactoryInitial"><Contents>weblogic.jndi.WLInitialContextFactory</Contents></StringRefAddr><StringRefAddr addrType="jndiProviderURL"><Contents>t3://localhost:7101</Contents></StringRefAddr></RefAddresses></Reference> How it works… In steps 1 and 2, we have made sure that both the SharedComponents and HRComponents ADF Library JARs are deployed and that a fle system connection was created, in order that both of these libraries get added to a newly created project (in step 5). Then, in steps 3 and 4, we create a new Fusion web application based on ADF, and an application module called HRComponentsCallerAppModule. It is from this application module that we intend to call the adjustCommission() custom application module method, exposed as a web service by the HrComponentsAppModule service-enabled application module in the HRComponents library JAR. For this reason, in step 4, we have generated a custom application module implementation class. We proceed by adding the necessary libraries to the new project in steps 5 and 6. Specifcally, the following libraries were added: SharedComponents.jar, HRComponents.jar, BC4J Service Client, and JAX-WS Client. In steps 7 through 9, we create a custom application module method called adjustCommission(), in which we write the necessary glue code to call our web service. In it, we frst retrieve the web service proxy, as a HrComponentsAppModuleService interface, by calling ServiceFactory.getServiceProxy() and specifying the name of the web service, which is indicated by the constant HrComponentsAppModuleService.NAME in the service interface. Then we call the web service through the retrieved interface. In the last step, we have provided the necessary confguration in the connections.xml so that we will be able to call the web service from an RMI client (the ADF Model Tester). This fle is used by the web service client to locate the web service. For the most part, the Reference information that was added to it was generated automatically by JDeveloper in the Exposing a custom method as a Web service recipe, so it was copied from there. The extra confguration information that had to be added is the necessary JNDI context properties jndiFactoryInitial and jndiProviderURL that are needed to resolve the web service on the deployed server. You should change these appropriately for your deployment. Note that these parameters are the same as the initial context parameters used to lookup the service when running in a managed environment. To test calling the web service, ensure that you have frst deployed it and that it is running. You can then use the ADF Model Tester, select the adjustCommission method and execute it. There's more… For additional information related to such topics as securing the ADF web service, enabling support for binary attachments, deploying to WebLogic, and more, refer to the Integrating Service-Enabled Application Modules section in the Fusion Developer's Guide for Oracle Application Development Framework which can be found at http://docs.oracle.com/ cd/E24382_01/web.1112/e16182/toc.htm.
Read more
  • 0
  • 0
  • 2775
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-working-dashboards-dynamics-crm
Packt
19 Jan 2012
5 min read
Save for later

Working with Dashboards in Dynamics CRM

Packt
19 Jan 2012
5 min read
(For more resources on Microsoft Dynamics CRM, see here.) Editing a user dashboard After creating a user dashboard or getting access to another user dashboard, you may still need to adjust the layout and settings of the dashboard. Getting ready Navigate to the Dashboards section in the Dynamics CRM 2011 Workplace area. How to do it... Carry out the following steps in order to complete this recipe: Select the Dashboards link from the Workplace area. Select one of your user dashboards, as shown in the following screenshot: From the Dashboards menu in the Dynamics CRM 2011 ribbon, click on the Edit button, as highlighted in the following screenshot: The dashboard editor screen will open, and the dashboard is now in Edit mode, as shown in the following screenshot: In order to edit the components on the dashboard, select a component by clicking on it with the mouse, and then click on the Edit Component ribbon button, as shown in the following screenshot: There's more... Dynamics CRM has a robust security system that combines roles-based security and user permissions. These security settings allow the administrator to control access to data and functionality in the Dynamics CRM system. Security roles for editing user dashboards In order for a Dynamics CRM user to edit user dashboards, they must have a security role that grants the Write privilege for the User Dashboard entity . If a user's security role does not have this privilege, then they will not see the Edit button on the dashboard ribbon: Editing a system dashboard The system dashboards are intended to be viewed by all users of Dynamics CRM. These dashboards are created and managed by users with the System Customizer or System Administrator security roles (by default these roles have the Write privilege for the System Forms entity). Edits made to these dashboards are seen by all users. Getting ready Editing a System dashboard requires you to first navigate to the Customization section in the Dynamics CRM 2011 Settings area. How to do it... Carry out the following steps in order to complete this recipe: From the Customization section, click on the Customize the System link, as shown in the following screenshot: This will launch the solution editor dialog showing the Default Solution for Dynamics CRM 2011. Click on the Dashboards link located in the left-hand side navigation section, as shown in the following screenshot: A listing of system dashboards will be shown. Double-click on the Microsoft Dynamics CRM Overview dashboard record. This will launch the dashboard editor screen. In order to edit the components on the dashboard, select a component by clicking on it with the mouse, and then click on the Edit Component ribbon button, as shown in the following screenshot: There's more... Dynamics CRM has a robust security system that combines roles-based security and user permissions. These security settings allow the administrator to control access to data and functionality in the Dynamics CRM system. Security roles for editing system dashboards In order for a Dynamics CRM user to edit system dashboards, they must have a security role which grants the Write privilege for the System Form entity. If a user's security role does not have this privilege, then they will not be able to edit the dashboard when customizing the system. By default, the System Forms are only editable by users with the System Customizer or System Administrator security roles as they both have full privileges to the System Form entity. Deleting a user dashboard Creating new dashboards in Dynamics CRM is an excellent feature; however the on-going management of dashboards may require you to remove or delete some dashboards that are no longer needed. Deleting dashboards in Dynamics CRM cannot be undone; users should understand that deleting a dashboard is permanent. Getting ready Navigate to the Dashboards section in the Dynamics CRM 2011 Workplace area. How to do it... Carry out the following steps in order to complete this recipe: Select the Dashboards link from the Workplace area, as shown in the following screenshot: The user dashboards will be in the My Dashboards section of this list. Once you have selected a user dashboard, the Delete button in the Dashboards bar will be enabled. Click on the Delete button, as shown in the following screenshot: You will be prompted with a Confirm Deletion dialog . As the message in this dialog states, deleting a dashboard cannot be undone. If you want to continue and delete this dashboard from your system, click on the OK button. When the operation is finished, the screen will refresh and that dashboard will no longer be available. How it works... The layouts and settings used to generate user dashboards are stored as records in the Dynamics CRM database. Deleting the dashboard will remove this record from the CRM database and cannot be reversed. Deleting the dashboard will only remove the dashboard layout and settings, not the associated data.
Read more
  • 0
  • 0
  • 4616

article-image-article-setting-development-environment
Packt
13 Jan 2012
14 min read
Save for later

Setting Up a Development Environment

Packt
13 Jan 2012
14 min read
Selecting your virtual environment Prisoners serving life sentences (in Canada) have what is known as a faint hope clause where you have a glimmer of a chance of getting parole after 15 years. However, those waiting for Microsoft to provide us a version of Virtual PC that can run Virtual Hard Drives (VHDs) hosting 64-bit operating systems (such as Windows Server 2008 R2), have no such hope of ever seeing that piece of software. But miracles do happen, and I hope that the release of a 64-bit capable Virtual PC renders this section of the article obsolete. If this has in fact happened, go with it and proceed to the following section.   Getting ready Head into your computer's BIOS settings and enable the virtualization setting. The exact setting you are looking for varies widely, so please consult with your manufacturer's documentation. This setting seems universally defaulted to off, so I am very sure you will need to perform this action.   How to do it... Since you are still reading, however, it is safe to say that a miracle has not yet happened. Your first task is to select a suitable virtualization technology that can support a 64-bit guest operating system. The recipe here is to consider the choices in this order, with the outcome of your virtual environment being selected: Microsoft Virtualization: Hyper-V certainly has the ability to create and run Virtual Hard Disks (VHDs) with 64-bit operating systems. It's free—that is, you can install the Hyper-V role , but it requires the base operating system to be Windows Server 2008 R2. It can be brutal to get it running properly on something like a laptop (for example, because of driver issues). It won't be a good idea to get Windows 2008 Server running on a laptop, primarily because of driver issues. I recommend that if your laptop is running Windows 7, look at creating a dual boot, and a boot to VHD where this other boot option / partition is Windows Server 2008 R2. The main disadvantage is coming up with an (preferably licensed) installation of Windows Server 2008 R2 as the main computer operating system (or as a dual boot option). Or perhaps your company runs Hyper-V on their server farm and would be willing to host your development environment for you? Either way, if you have managed to get access to a Hyper-V server, you are good to go! VMware Workstation: Go to http://www.vmware.com and download my absolute favorite virtualization technology—VMware Workstation—fully featured, powerful, and can run on Windows 7. I have used it for years and love it. You must of course pay for a license, but please believe me, it is a worthwhile investment. You can sign up for a 30 day trial to explore the benefits. Note that you only need one copy of VMware Workstation to create a virtual machine. Once you have created it, you can run it anywhere using the freely available VMware Player. Oracle Virtual Box: Go to http://www.virtualbox.org/ and download this free software that will run on Windows 7 and create and host 64-bit guest operating systems. The reason that this is at the bottom of the list is that I personally do not have experience using this software. However, I have colleagues who have used it and have had no problems with it. Give this a try and see if it works as equally well as a paid version of VMware. With your selected virtualization technology in hand, head to the next section to install and configure Windows Server 2008 R2, which is the base operating system required for an installation of SharePoint Server 2010. Installing and configuring Windows Server 2008 R2 SharePoint 2010 requires the Windows Server 2008 R2 operating system in order to run. In this recipe, we will confi gure the components of Windows Server 2008 necessary in order to get ready to install SQL Server 2008 and SharePoint 2010. Getting ready Download Windows Server 2008 R2 from your MSDN subscription, or type in windows server 2008 R2 trial download into your favorite search engine to download the 180-day trial from the Microsoft site. This article does not cover actually installing the base operating system. The specific instructions to do so will be dependent upon the virtualization software. Generally, it will be provided as an ISO image (the file extension will be .iso). ISO means a compressed disk image, and all virtualization software that I am aware of will let you mount (attach) an ISO image to the virtual machine as a CD Drive. This means that when you elect to create a new virtual machine, you will normally be prompted for the ISO image, and the installation of the operating system should proceed in a familiar and relatively automated fashion. So for this recipe, ready means that you have your virtualization software up and running, the Windows Server 2008 R2 base operating system is installed, and you are able to log in as the Administrator (and that you are effectively logging in for the first time). How to do it... Log in as the Administrator. You will be prompted to change the password the first time—I suggest choosing a very commonly used Microsoft password—Password1. However, feel free to select a password of your choice, but use it consistently throughout. The Initial configuration tasks screen will come up automatically. On this screen: Activate windows using your 180 day trial key or using your MSDN key. Select Provide computer name and domain. Change the computer name to a simpler one of your choice. In my case, I named the machine OPENHIGHWAY. Leave the Member of option as Workgroup. The computer will require a reboot. In the Update this server section, choose Download and install updates. Click on the Change settings link and select the option Never check for updates and click OK. Click the Check for updates link. The important updates will be selected. Click on Install Updates. Now is a good time for a coffee break! You will need to reboot the server when the updates complete. In the Customize this server section, click on Add Features. Select the Desktop Experience, Windows, PowerShell, Integrated, Scripting, and Environment options. Choose Add Required Features when prompted to do so. Reboot the server when prompted to do so. If the Initial configuration tasks screen appears now, or in the future, you may now select the checkbox for Do not show this window at logon. We will continue configuration from the Server Manager, which should be displayed on your screen. If not, launch the Server Manager using the icon on the taskbar. We return to Server Manager to continue the confi guration: OPTIONAL: Click on Configure Remote Desktop if you have a preference for accessing your virtual machine using Remote Desktop (RDP) instead of using the virtual machine's console software. In the Security Information section, click Go to Windows Firewall. Click on the Windows Firewall Properties link. From the dialog, go to each of the tabs, namely, Domain Profi le, Private Profi le, and Public Profi le and set the Firewall State to Off on each tab and click OK. Click on the Server Manager node, and from the main screen, click on the Configure IE ESC link. Set both options to Off and click OK. From the Server Manager, expand the Configuration node and then expand Local Users and Groups node, and then click on the Users folder. Right-click on the Administrator account and select Properties. Select the option for Password never expires and click OK. From the Server Manager, click the Roles node . Click the Add Roles link. Now, click on the Introductory screen and select the checkbox for Active Directory Domain Services. Click Next, again click on Next, and then click Install. After completion, click the Close this wizard and launch the Active Directory Domain Services Installation Wizard (dcpromo.exe) link. Now, carry out the following steps: From the new wizard that pops up, from the welcome screen, select the checkbox Use advanced mode installation, click Next, and again click on Next on the Operating System Compatibility screen. Select the option Create a new domain in a new forest and click Next. Choose your domain (FQDN)! This is completely internal to your development server and does not have to be real. For article purposes, I am using theopenhighway.net, as shown in the following screenshot. Then click Next: From the Set Forest Functional Level drop-down, choose Windows Server 2008 R2 and click Next. Click Next on the Additional Domain Controller Option screen. Select Yes on the Static IP assignment screen. Click Yes on the Dns Delegation Warning screen. Click Next on the Location for Database, Log Files, and SYSVOL screen. On the Directory Services Restore Mode Administrator Password screen, enter the same password that you used for the Administrator account, in my case, Password1. Click Next. Click Next on the Summary screen. Click on the Reboot On Completion screen. Otherwise reboot the server after the installation completes You will now confi gure a user account that will run the application pools for the SharePoint web applications in IIS. From the Server Manager, expand the Roles node. Keep expanding the Active Directory Domain Services until you see the Users folder. Click on the Users folder. Now carry out the following: Right-click on the Users folder and select New | User Enter SP_AppPool in the full name field and also enter SP_AppPool in the user logon field and click Next. Enter the password as Password1 (or the same as you had selected for the Administrator account). Deselect the option for User must change password at next logon and select the option for Password never expires. Click Next and then click Finish. A loopback check is a security feature to mitigate against reflection attacks, introduced in Windows Server 2003 SP1. You will likely encounter connection issues with your local websites and it is therefore universally recommended that you disable the loopback check on a development server. This is done from the registry editor: Click the Start menu button, choose Run…, enter Regedit, and click OK to bring up the registry editor. Navigate to HKEY_LOCAL_MACHINE | SYSTEM | CurrentControlSet | Control | Lsa Right-click the Lsa node and select New | DWORD (32-bit) Value In the place of New Value #1 type DisableLoopbackCheck. Right-click DisableLoopbackCheck, select Modify, change the value to 1, and click OK Congratulations! You have successfully confi gured Windows Server 2008 R2. There's more... The Windows Shutdown Event Tracker is simply annoying on a development machine. To turn this feature off, click the Start button, select Run…, enter gpedit.msc, and click OK. Scroll down, right-click on Display Shutdown Event Tracker, and select Edit. Select the Disabled option and click OK, as shown in the following screenshot: Installing and configuring SQL Server 2008 R2 SharePoint 2010 requires Microsoft SQL Server as a fundamental component of the overall SharePoint architecture. The content that you plan to manage in SharePoint, including web content and documents, literally is stored within and served from SQL Server databases. The SharePoint 2010 architecture itself relies on information stored in SQL Server databases, such as confi guration and the many service applications. In this recipe, we will install and configure the components of SQL Server 2008 necessary to install SharePoint 2010. Getting ready I do not recommend SQL Server Express for your development environment, although this is a possible, free, and valid choice for the installation of SharePoint 2010. In my personal experience, I have valued the full power and fl exibility of the full version of SQL Server as well as not having to live with the constraints and limitations of SQL Express. Besides, there is another little reason too! The Enterprise edition of SQL Server is either readily available with your MSDN subscription or downloadable as a trial from the Microsoft site. Download SQL Server 2008 R2 Enterprise from your MSDN subscription, or type in sql server 2008 enterprise R2 trial download into your favorite search engine to download the 180-day trial from the Microsoft site. For SQL Server 2008 R2 Enterprise, if you have MSDN software, then you will be provided with an ISO image that you can attach to the virtual machine. If you download your SQL Server from the Microsoft site as a trial, extract the software (it is a self-extracting EXE) on your local machine, and then share the folder with your virtual machine. Finallly, run the Setup.exe fi le. How to do it... Here is your recipe for installing SQL Server 2008 R2 Enterprise. Carry out the following steps to complete this recipe: You will be presented with the SQL Server Installation Center; on the left side of the screen, select Installation, as shown in the following screenshot: For the choices presented on the Installation screen, select New installation or add features to an existing installation. The Setup Support Rules (shown in the following screenshot) will run to identify any possible problems that might occur when installing SQL Server. All rules should pass. Click OK to continue: You will be presented with the SQL Server 2008 R2 Setup screen. On the fi rst screen, you can select an evaluation or use your product key (from, for example, MSDN) and then click Next. Accept the terms in the license, but do not check the Send feature usage data to Microsoft checkbox, and click Next. On the Setup Support Files screen, click Install. All tests will pass except for a warning that you can safely ignore (the one noting we are installing on a domain controller), and click Next, as shown in the following screenshot: On the Setup Role screen, select SQL Server Feature Installation and click Next. On the Feature Selection, as shown in the following screenshot, carry out the following tasks: In Instance Features, select Database Engine Services (and both SQL Server Replication and Full Text Search), Analysis Services, and Reporting Services In Shared Features, select Business Intelligence Development Studio, Management Tools Basic (and Management Tools Complete), and Microsoft Sync Framework Finally, click Next. On the Installation Rules screen, click Next On the Instance Confi guration screen, click Next. On the Disk Space Requirements screen, click Next On the Server Confi guration screen: Set the Startup Type for SQL Server Agent to be Automatic Click on the button Use the same account for all SQL Server services. Select the account NT AUTHORITYSYSTEM and click OK. Finally, click Next. On the Database Configuration Engine screen: Look for the Account Provisioning tab and click the Add Current User button under Specify SQL Server administrators. Finally, click Next On the Analysis Services Confi guration screen: Look for the Account Provisioning tab and click the Add Current User button under Specify which users have administrative permissions for Analysis Services. Finally, click Next. On the Reporting Services Configuration screen, select the option to Install but do not configure the report server. Now, click Next. On the Error Reporting Screen, click Next. On the Installation Confi guration Rules screen, click Next. On the Ready to Install screen, click Install. Your patience will be rewarded with the Complete screen! Finally, click Close. The Complete screen is shown in the following screenshot: You can close the SQL Server Installation Center. Confi gure SQL Server security for the SP_AppPool account: Click Start | All Programs | SQL Server 2008 R2 | SQL Server Management Studio. On Connect to server, type a period (.) in the Server Name field and click Connect. Expand the Security node. Right-click Logins and select New Login. Use the Search function and enter SP_AppPool in the box Enter object name to select. Click the check names button and then click OK. In my case, you see the properly formatted THEOPENHIGHWAYSP_AppPool in the login name text box. On the Server Roles tab, ensure that the dbcreator and securityadmin roles are selected (in addition to the already selected public role). Finally, click OK. Congratulations! You have successfully installed and confi gured SQL Server 2008 R2 Enterprise.
Read more
  • 0
  • 0
  • 3541

article-image-ext-js-4-working-grid-component
Packt
11 Jan 2012
10 min read
Save for later

Ext JS 4: Working with the Grid Component

Packt
11 Jan 2012
10 min read
(For more resources on JavaScript, see here.) Grid panel The grid panel is one of the most-used components when developing an application Ext JS 4 provides some great improvements related to this component. The Ext JS 4 Grid panel renders a different HTML than Ext JS 3 Grid did. Sencha calls this new feature Intelligent Rendering. Ext JS 3 used to create the whole structure, supporting all the features. But, what if someone just wanted to display a simple grid? All the other features not being rendered would just be wasted, because no one was using that structure. Ext JS 4 now renders only the features the grid uses, minimizing and boosting the performance. Before we examine the grid's new features and enhancements, let's take a look how to implement a simple grid in Ext JS 4: Ext.create('Ext.grid.Panel', { store: Ext.create('Ext.data.ArrayStore', { fields: [ {name: 'book'}, {name: 'author'} ], data: [['Ext JS 4: First Look','Loiane Groner']] }), columns: [ { text : 'Book', flex : 1, sortable : false, dataIndex: 'book' },{ text : 'Author', width : 100, sortable : true, dataIndex: 'author' }], height: 80, width: 300, title: 'Simple Grid', renderTo: Ext.getBody() }); As you can see in the preceding code, the two main parts of the grid are the store and the columns declarations. Note, as well, names of both store and model fields always have to match with the column's dataIndex (if you want to display the column in the grid). So far, nothing has changed. The way we used to declare a simple grid in Ext JS 3 is the same way we do for Ext JS 4. However, there are some changes related to plugins and the new features property. We are going to take a closer look at that in this section. Let's dive into the changes! Columns Ext JS 4 organizes all the column classes into a single package—the Ext.grid.column package. We will explain how to use each column type with an example. But first, we need to declare a Model and a Store to represent and load the data: Ext.define('Book', { extend: 'Ext.data.Model', fields: [ {name: 'book'}, {name: 'topic', type: 'string'}, {name: 'version', type: 'string'}, {name: 'released', type: 'boolean'}, {name: 'releasedDate', type: 'date'}, {name: 'value', type: 'number'} ] }); var store = Ext.create('Ext.data.ArrayStore', { model: 'Book', data: [ ['Ext JS 4: First Look','Ext JS','4',false,null,0], ['Learning Ext JS 3.2','Ext JS','3.2',tr ue,'2010/10/01',40.49], ['Ext JS 3.0 Cookbook','Ext JS','3',true,'2009/10/01',44.99], ['Learning Ext JS','Ext JS','2.x',true,'2008/11/01',35.99], ] }); Now, we need to declare a grid: Ext.create('Ext.grid.Panel', { store: store, width: 550, title: 'Ext JS Books', renderTo: 'grid-example', selModel: Ext.create('Ext.selection.CheckboxModel'), //1 columns: [ Ext.create('Ext.grid.RowNumberer'), //2 { text: 'Book',//3 flex: 1, dataIndex: 'book' },{ text: 'Category', //4 xtype:'templatecolumn', width: 100, tpl: '{topic} {version}' },{ text: 'Already Released?', //5 xtype: 'booleancolumn', width: 100, dataIndex: 'released', trueText: 'Yes', falseText: 'No' },{ text: 'Released Date', //6 xtype:'datecolumn', width: 100, dataIndex: 'releasedDate', format:'m-Y' },{ text: 'Price', //7 xtype:'numbercolumn', width: 80, dataIndex: 'value', renderer: Ext.util.Format.usMoney },{ xtype:'actioncolumn', //8 width:50, items: [{ icon: 'images/edit.png', tooltip: 'Edit', handler: function(grid, rowIndex, colIndex) { var rec = grid.getStore().getAt(rowIndex); Ext.MessageBox.alert('Edit',rec.get('book')); } },{ icon: 'images/delete.gif', tooltip: 'Delete', handler: function(grid, rowIndex, colIndex) { var rec = grid.getStore().getAt(rowIndex); Ext.MessageBox.alert('Delete',rec.get('book')); } }] }] }); The preceding code outputs the following grid: The first column is declared as selModel, which, in this example, is going to render a checkbox, so we can select some rows from the grid. To add this column into a grid, simply declare the selModel (also known as sm in Ext JS 3) as CheckBox selection model, as highlighted in the code (comment 1 in the code). The second column that we declared is the RowNumberer column. This column adds a row number automatically into the grid. In the third column (with text:'Book'), we did not specify a column type; this means the column will display the data itself as a string. In the fourth column, we declared a column with xtype as templatecolumn. This column will display the data from the store, specified by an XTemplate, as declared in the tpl property. In this example, we are saying we want to display the topic (name of the technology) and its version. The fifth column is declared as booleancolumn. This column displays a true or false value. But, if we do not want to display true or false in the grid, we can specify the values that we want to get displayed. In this example, we displayed the value as Yes (for true values) and No (for false values), as we declared in the trueText and falseText. The sixth column we declared as datecolumn, which is used to display dates. We can also declare a date format we want to be displayed. In this example, we want to display only the month and the year. The format follows the same rules for PHP date formats. The seventh column we declared as numbercolumn. This column is used to display numbers, such as a quantitative number, money, and so on. If we want to display the number in a particular format, we can use one of the Ext JS renderers to create a customized one. And the last column we declared is the actioncolumn. In this column, we can display icons that are going to execute an action, such as delete or edit. We declare the icons we want to display in the items property. topic: {name}{rows.length} Book topic: {name}{rows.length} Books Feature support In Ext JS 3, when we wanted to add a new functionality to a grid, we used to create a plugin or extend the GridPanel class. There was no default way to do it. Ext JS 4 introduces the Ext.grid.feature.Feature class that contains common methods and properties to create a plugin. Inside the Ext.grid.feature packages, we will find seven classes: AbstractSummary, Chunking, Feature, Grouping, GroupingSummary, RowBody, and Summary. A feature is very simple to use—we need to add the feature inside the feature declaration in the grid: features: [{ groupHeaderTpl: 'Publisher: {name}', ftype: 'groupingsummary' }] Let's take a look at how to use some of these native grid features. Ext.grid.feature.Grouping Grouping rows in Ext JS 4 has changed. Now, Grouping is a feature and can be applied to a grid through the features property. The following code displays a grid grouped by book topic: Ext.define('Book', { extend: 'Ext.data.Model', fields: ['name', 'topic'] }); var Books = Ext.create('Ext.data.Store', { model: 'Book', groupField: 'topic', data: [{ name: 'Learning Ext JS', topic: 'Ext JS' },{ name: 'Learning Ext JS 3.2', topic: 'Ext JS' },{ name: 'Ext JS 3.0 Cookbook', topic: 'Ext JS' },{ name: 'Expert PHP 5 Tools', topic: 'PHP' },{ name: 'NetBeans IDE 7 Cookbook', topic: 'Java' },{ name: 'iReport 3.7', topic: 'Java' },{ name: 'Python Multimedia', topic: 'Python' },{ name: 'NHibernate 3.0 Cookbook', topic: '.NET' },{ name: 'ASP.NET MVC 2 Cookbook', topic: '.NET' }] }); Ext.create('Ext.grid.Panel', { renderTo: Ext.getBody(), frame: true, store: Books, width: 350, height: 400, title: 'Books', features: [Ext.create('Ext.grid.feature.Grouping',{ groupHeaderTpl: 'topic: {name} ({rows.length} Book{[values.rows.length > 1 ? "s" : ""]})' })], columns: [{ text: 'Name', flex: 1, dataIndex: 'name' },{ text: 'Topic', flex: 1, dataIndex: 'topic' }] }); In the groupHeaderTpl attribute, we declared a template to be displayed in the grouping row. We are going to display one of the following customized strings, depending on the number of books belonging to the topic: The string comprises of the topic name ({name}) and the count of the book for the topic ({rows.length}). In Ext JS 3, we still had to declare a grouping field in the store; but, instead of a Grouping feature, we used to declare GroupingView, as follows: view: new Ext.grid.GroupingView({ forceFit:true, groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Books" : "Book"]})' }) If we execute the grouping grid, we will get the following output:   Ext.grid.feature.GroupingSummary The GroupingSummary feature also groups rows with a field in common, but it also adds a summary row at the bottom of each group. Let's change the preceding example to use the GroupingSummary feature: Ext.create('Ext.grid.Panel', { renderTo: Ext.getBody(), frame: true, store: Books, width: 350, height: 400, title: 'Books', features: [{ groupHeaderTpl: 'Topic: {name}', ftype: 'groupingsummary' }], columns: [{ text: 'Name', flex: 1, dataIndex: 'name', summaryType: 'count', summaryRenderer: function(value){ return Ext.String.format('{0} book{1}', value, value !== 1 ? 's' : ''); } },{ text: 'Topic', flex: 1, dataIndex: 'topic' }] }); We highlighted two pieces in the preceding code. The first line is the feature declaration: in the previous example (Grouping) we created the feature using the Ext.create declaration. But if we do not want to explicitly create the feature every time we declare, we can use the ftype property, which is groupingsummary in this example. The groupingsummary that we added to the grid's name column is in the second line of highlighted code. We declared a summaryType property and set its value as count. Declaring the summaryType as count means we want to display the number of books in that particular topic/category; it is going to count how many records we have for a particular category in the grid. It is very similar to the count of the PL/SQL language. Other summary types we can declare are: sum, min, max, average (these are self-explanatory). In this example, we want to customize the text that will be displayed in the summary, so we are going to use the summaryRenderer function. We need to pass a value argument to it, and the value is the count of the name column. Then, we are going to return a customized string that is going to display the count (token {0}) and the string book or books, depending on the count (if it is more than 1 we add s at the end of the string book). Ext.String.format is a function that allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each token must be unique and must increment in the format {0}, {1}, and so on. The preceding code will output the following grid: Ext.grid.feature.Summary The GroupingSummary feature adds a row at the bottom of each grouping. The Summary feature adds a row at the bottom of the grid to display summary information. The property configuration is very similar to that for GroupingSummary, because both classes are subclasses of AbstractSummary (a class that provides common properties and methods for summary features). Ext.create('Ext.grid.Panel', { renderTo: Ext.getBody(), frame: true, store: Books, width: 350, height: 300, title: 'Books', features: [{ ftype: 'summary' }], columns: [{ text: 'Name', flex: 1, dataIndex: 'name', summaryType: 'count', summaryRenderer: function(value){ return Ext.String.format('{0} book{1}', value, value !== 1 ? 's' : ''); } },{ text: 'Topic', flex: 1, dataIndex: 'topic' }] }); The only difference from the GroupingSummary feature is the feature declaration itself. The summayType and summaryRenderer properties work in a similar way. The preceding code will output the following grid: Ext.grid.feature.RowBody The rowbody feature adds a new tr->td->div in the bottom of the row that we can use to display additional information. Here is how to use it: Ext.create('Ext.grid.Panel', { renderTo: Ext.getBody(), frame: true, store: Books, width: 350, height: 300, title: 'Books', features: [{ ftype: 'rowbody', getAdditionalData: function(data, idx, record, orig) { return { rowBody: Ext.String.format( '->topic: {0}', data.topic) }; } }, { ftype: 'rowwrap' }], columns: [{ text: 'Name', flex: 1, dataIndex: 'name' }] });  In the preceding code, we are not only displaying the name of the book; we are using the rowbody to display the topic of the book as well. The first step is to declare the rowbody feature. One very important thing to be noted is that rowbody will be initially hidden, unless you override the getAdditionalData method. If we execute the preceding code, we will get the following output:
Read more
  • 0
  • 0
  • 6783

article-image-ext-js-4-working-tree-and-form-components
Packt
11 Jan 2012
6 min read
Save for later

Ext JS 4: Working with Tree and Form Components

Packt
11 Jan 2012
6 min read
Tree panel The tree component is much more simplified in Ext JS 4. Like grid, it is also a subclass of Ext.panel.Table. This means we can add most functionality of the grid in the tree as well. Let's start declaring a simple tree in Ext JS 3: new Ext.tree.TreePanel({ renderTo: 'tree-example', title: 'Simple Tree', width: 200, rootVisible: false, root: new Ext.tree.AsyncTreeNode({ expanded: true, children: [ { text: "Menu Option 1", leaf: true }, { text: "Menu Option 2", expanded: true, children: [ { text: "Sub Menu Option 2.1", leaf: true }, { text: "Sub Menu Option 2.2", leaf: true} ] }, { text: "Menu Option 3", leaf: true } ] }) }); Now, let's see how to declare the same tree in Ext JS: Ext.create('Ext.tree.Panel', { title: 'Simple Tree', width: 200, store: Ext.create('Ext.data.TreeStore', { root: { expanded: true, children: [ { text: "Menu Option 1", leaf: true }, { text: "Menu Option 2", expanded: true, children: [ { text: "Sub Menu Option 2.1", leaf: true }, { text: "Sub Menu Option 2.2", leaf: true} ] }, { text: "Menu Option 3", leaf: true } ] } }), rootVisible: false, renderTo: 'tree-example' }); In Ext JS 4, we also have the title, width, and div properties, where the tree is going to be rendered, and a config store. The store config is a new element for the tree. If we output both of the codes, we will have the same output, which is the following tree: If we take a look at the data package, we will see three files related to tree: NodeInterface, Tree, and TreeStore. NodeInterface applies a set of methods to the prototype of a record to decorate it with a Node API. The Tree class is used as a container of a series of nodes and TreeStore is a store implementation used by a Tree. The good thing about having TreeStore is that we can use its features, such as proxy and reader, as we do for any other Store in Ext JS 4. Drag-and-drop and sorting The drag-and-drop feature is very useful for rearranging the order of the nodes in the Tree class. Adding the drag-and-drop feature is very simple. We need to add the following code into the tree declaration: Ext.create('Ext.tree.Panel', { store: store, viewConfig: { plugins: { ptype: 'treeviewdragdrop' } }, //other properties }); And how do we handle drag-and-drop in store? We do it in the same way as we handled the edition plugin on the Grid, using a Writer: var store = Ext.create('Ext.data.TreeStore', { proxy: { type: 'ajax', api: { read : '../data/drag-drop.json', create : 'create.php' } }, writer: { type: 'json', writeAllFields: true, encode: false }, autoSync:true }); In the earlier versions of Ext JS 4, the autoSync config option does work. Another way of synchronizing the Store with the server is adding a listener to the Store instead of the autoSync config option, as follows: listeners: { move: function( node, oldParent, newParent, index, options ) { this.sync(); } } And, to add the sorting feature to the Tree class, we simply need to configure the sorters property in the TreeStore, as follows: Ext.create('Ext.data.TreeStore', { folderSort: true, sorters: [{ property: 'text', direction: 'ASC' }] }); Check tree To implement a check tree, we simply need to make a few changes in the data that we are going to apply to the Tree. We need to add a property called checked to each node, with a true or false value; true indicates the node is checked, and false, otherwise. For this example, we will use the following json code: [{ "text": "Cartesian", "cls": "folder", "expanded": true, "children": [{ "text": "Bar", "leaf": true, "checked": true },{ "text": "Column", "leaf": true, "checked": true },{ "text": "Line", "leaf": true, "checked": false }] },{ "text": "Gauge", "leaf": true, "checked": false },{ "text": "Pie", "leaf": true, "checked": true }] And as we can see, the code is the same as that for a simple tree: var store = Ext.create('Ext.data.TreeStore', { proxy: { type: 'ajax', url: 'data/check-nodes.json' }, sorters: [{ property: 'leaf', direction: 'ASC' }, { property: 'text', direction: 'ASC' }] }); Ext.create('Ext.tree.Panel', { store: store, rootVisible: false, useArrows: true, frame: true, title: 'Charts I have studied', renderTo: 'tree-example', width: 200, height: 250 }); The preceding code will output the following tree: Tree grid In Ext JS 3, the client JavaScript Component, Tree Grid, was an extension part of the ux package. In Ext JS 4, this Component is part of the native API but it is no longer an extension. To implement a Tree Grid, we are going to use the Tree Component as well; the only difference is that we are going to declare some columns inside the tree. This is the good part of Tree being a subclass of Ext.panel.Table, the same super class for Grid as well. First, we will declare a Model and a Store, to represent the data we are going to display in the Tree Grid. We will then load the Tree Grid: Ext.define('Book', { extend: 'Ext.data.Model', fields: [ {name: 'book', type: 'string'}, {name: 'pages', type: 'string'} ] }); var store = Ext.create('Ext.data.TreeStore', { model: 'Book', proxy: { type: 'ajax', url: 'data/treegrid.json' }, folderSort: true }); So far there is no news. We declared the variable store as any other used in a grid, except that this one is a TreeStore. The code to implement the Component Tree Grid is declared as follows: Ext.create('Ext.tree.Panel', { title: 'Books', width: 500, height: 300, renderTo: Ext.getBody(), collapsible: true, useArrows: true, rootVisible: false, store: store, multiSelect: true, singleExpand: true, columns: [{ xtype: 'treecolumn', text: 'Task', flex: 2, sortable: true, dataIndex: 'task' },{ text: 'Assigned To', flex: 1, dataIndex: 'user', sortable: true }] }); The most important line of code is highlighted—the columns declaration. The columns property is an array of Ext.grid.column.Column objects, as we declare in a grid. The only thing we have to pay attention to is the column type of the first column, that is, treecolumn; this way we know that we have to render the node into the Tree Grid. We also configured some other properties. collapsible is a Boolean property; if set to true it will allow us to collapse and expand the nodes of the tree. The useArrows is also a Boolean property, which indicates whether the arrow icon will be visible in the tree (expand/collapse icons). The property rootVisible indicates whether we want to display the root of the tree, which is a simple period (.). The property singleExpand indicates whether we want to expand a single node at a time and the multiSelect property indicates whether we want to select more than one node at once. The preceding code will output the following tree grid:
Read more
  • 0
  • 0
  • 5979
article-image-article-building-location-aware-web-applications-mongodb-php
Packt
06 Jan 2012
14 min read
Save for later

Building Location-aware Web Applications with MongoDB and PHP

Packt
06 Jan 2012
14 min read
(For more resources on PHP and MongoDB, see here.) A geolocation primer The term geolocation refers to the act of locating the geographic position of a person, a place, or any place of interest. The geographic position of the object is determined mainly by the latitude and longitude of the object, sometimes its height from sea level is also taken into account. In this section, we are going to learn about different techniques that location- based applications use to determine a user's location. You may skip this section if you are already familiar with them, or if you just cannot wait to get started coding! Methods to determine location There are several ways to locate the geographic position of a computing device. Let's briefly learn about the most effective ones among them: Global Positioning System (GPS ): Nowadays, tech savvy people carry GPS-enabled smartphones in their pockets. Devices like these act as GPS receivers; they constantly exchange information with GPS satellites orbiting the Earth and calculate their geographic position. This process is known as trilateration . This is perhaps the most accurate way to determine location, as of today. Cellphone tracking: Each cellphone has a Cell ID assigned to it that uniquely identifies it in a particular cellular network. In a process known as cellular triangulation , three base stations (cellphone towers) are used to correctly identify the latitude and longitude of the cellphone identified by the Cell ID. This method is more accurate in urban areas, where there are more cellphone towers close to each other, than in rural areas. IP address: Internet service providers are given blocks of IP addresses based on a country/city/region. When a user visits a website, the website could take a look at his IP address and consult an database that stores location data against IP addresses (it might be either an internal database or provided by a third-party service) to get the location of the user. Accuracy of this approach depends on the accuracy of the database itself. Also, if the user is behind a proxy server, the application will see the IP address of the proxy server, which could be located in a different region than the user. Wi-Fi MAC address tracking: A Wi-Fi access point has a MAC (Media Access Control) address assigned to it, which is globally unique. Some location-based services use this to identify the location of the Wi-Fi router, and therefore, the location of users on that Wi-Fi LAN. In principle, it works in the same way IP address-based geolocation does. Google has an API that gives location information (latitude, longitude, and so on) when provided with a MAC address. If you are curious to learn more about how geolocation works, How Stuff Works has a comprehensive article on it available at http://electronics.howstuffworks.com/ everyday-tech/location-tracking.htm. Detecting the location of a web page visitor When building a location-aware web application, the first part of the problem to be solved is to get the location of the user visiting the web page. We have covered geolocation techniques in the previous section, now it is time to see them in action. The W3C Geolocation API We are going to use the W3C Geolocation API for locating the visitors to our web page. The W3C Geolocation API provides a high-level interface for web developers to implement geolocation features in an application. The API takes care of detecting the location using one or more methods (GPS, Cell ID, IP address). The developers do not have to worry about what is going on under the hood; they only need to focus on the geographic information returned by the API! You can read the whole specification online at http://www.w3.org/TR/ geolocation-API/. Browsers that support geolocation The following table lists the browsers that support the W3C Geolocation API: Browser Version Google Chrome 5.0+ Mozilla Firefox 3.5+ Internet Explorer 9.0+ Safari 5.0+ Opera 10.6+ iPhone 3.1+ Android 2.0+ Blackberry 6.0+ Make sure you use one of these browsers when you try the practical examples in this article. Time for action – detecting location with W3C API In this section, we are going to build a web page that detects the location of a visitor using the Geolocation API. The API will detect the latitude and longitude of the user who loads the page in his browser. We are going use that information on a map, rendered dynamically using the Google Maps API: Fire up your text editor and create a new HTML file named location.html. Put the following code in it: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xml_lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel="stylesheet" href="styles.css"/> <style type="text/css" media="screen"> div#map { width:450px; height: 400px; } </style> <title>Locating your position</title> </head> <body> <div id="contentarea"> <div id="innercontentarea"> <h2>Locating your position</h2> <div id="map"></div> </div> </div> <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"> </script> <script type="text/javascript" src="geolocation.js"> </script> </body> </html> Create another file named geolocation.js and put the following JavaScript code in it: var mapContainer = document.getElementById('map');var map;function init() { //Google map settings (zoom level, map type etc.) var mapOptions = {zoom: 16, disableDefaultUI: true, mapTypeId: google.maps.MapTypeId.ROADMAP}; //map will be drawn inside the mapContainer map = new google.maps.Map(mapContainer, mapOptions); detectLocation();}function detectLocation(){ var options = { enableHighAccuracy: true, maximumAge: 1000, timeout: 30000}; //check if the browser supports geolocation if (window.navigator.geolocation) { //get current position window.navigator.geolocation.getCurrentPosition( drawLocationOnMap, handleGeoloacteError, options); } else { alert("Sorry, your browser doesn't seem to support geolocation :-("); }}//callback function of getCurrentPosition(), pinpoints location//on Google mapfunction drawLocationOnMap(position) { //get latitude/longitude from Position object var lat = position.coords.latitude; var lon = position.coords.longitude; var msg = "You are here: Latitude "+lat+", Longitude "+lon; //mark current location on Google map var pos = new google.maps.LatLng(lat, lon); var infoBox = new google.maps.InfoWindow({map: map, position:pos, content: msg}); map.setCenter(pos); return;}function handleGeoloacteError() { alert("Sorry, couldn't get your geolocation :-(");}window.onload = init; Load the location.html page in your browser. When the browser asks for permission to allow the page to access your location, click Yes/OK/Allow:   (Move the mouse over the image to enlarge.)   Once you allow the page to access your location, it renders a map that shows your current location on it, along with the geographic coordinates: What just happened? We built a web page and added JavaScript code that detects the latitude and longitude of the user who loads the page in his browser. The API needs the user's permission to get his geographic information. So when the page loads, it prompts the user to specify whether or not he will allow the page to get his location. If the user agrees, the JavaScript code executes and gets his geographic coordinates using the W3C Geolocation API. Then it renders a small map using the Google Maps API, and highlights the user's location on the map. The Geolocation object The Geolocation object implements the W3C Geolocation API. The JavaScript engine uses this object to obtain geographic information of the computer or phone on which the browser is running. Geolocation is a property of the Browser object (window.navigator), accessed as window.navigator.geolocation. In our example, we detect if the browser has geolocation capabilities by accessing this object, and notify the user if the browser fails the test: //check if the browser supports geolocationif (window.navigator.geolocation) { window.navigator.geolocation.getCurrentPosition( drawLocationOnMap, handleGeoloacteError, options);} else { alert("Sorry, your browser doesn't seem to support geolocation.");} The getCurrentPosition() method The location information is obtained invoking the getCurrentPosition() method on the Geolocation object.   getCurrentPostition(callbackOnSuccess, [callbackOnFailure, options])   The argument callbackOnSuccess is a reference to a callback function. It is executed when the getCurrentPosition() method successfully determines the geolocation. This is a mandatory argument. callbackOnFailure is an optional argument, a callback function for handling failure to get the geolocation. options represents the PositionOptions object, which specifies optional configuration parameters to the method. The PositionOptions object has the following properties: enableHighAccuracy : Tells the API to try its best to get the exact current position. It is set to false by default. When set to true, the API response tends to be slower. maximumAge : If API responses are cached, this setting specifies that the API will not use the cached responses older than maximumAge milliseconds. timeout : The timeout value in milliseconds to receive the API response. In our example, we used the drawLocationOnMap() method as a callbackOnSuccess function , which draws a map and pinpoints the location on it (we will walkthrough it shortly). The handleGeoloacteError() method notifies the user of any error while getting the position: window.navigator.geolocation.getCurrentPosition( drawLocationOnMap, handleGeoloacteError, options); Drawing the map using the Google Maps APTI The Google Maps APTIis a popular JavaScript API for drawing maps on a web page. This API has methods to highlight objects on the rendered map. We can access the API methods by adding the following script tag in the web page (as we did in the location.html file): <script type="text/javascript"src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> If you are on a GPS-enabled device, set the sensor parameter to true, as follows: When the script is loaded, we can initiate the map drawing by instantinating the google. maps.Map object . The Map object takes a DOM object as its first parameter; the map will be rendered inside this DOM. It also takes an optional JSON object that specifies configurations for the map (zoom level, map type, and so on): var mapContainer = document.getElementById('map');var mapOptions = {zoom: 16, disableDefaultUI: true, mapTypeId: google.maps.MapTypeId.ROADMAP};map = new google.maps.Map(mapContainer, mapOptions); Now, let's focus on the drawLocationOnMap() function in the geolocation.js file, which is the callback function of the getCurrentPosition() method . As we know, this method gets called when the W3C API successfully locates the position; it receives a Position object as its argument. This object holds all the geolocation data returned by the API. The Position object holds a reference to the Coordinates object (accessed by the property coords). The Coordinates object contains geographical coordinates such as latitude, longitude, altitude, and so on of the location: function drawLocationOnMap(position) { var lat = position.coords.latitude; var lon = position.coords.longitude; var msg = "You are here: Latitude "+lat+", Longitude "+lon; ……………………………………………………………………………………………………………………………………………………………} After we get the latitude and longitude values of the coordinate, we set it as the center of the map. We also display an information box with a message saying, You are here on the map! function drawLocationOnMap(position) { var lat = position.coords.latitude; var lon = position.coords.longitude; var msg = "You are here: Latitude "+lat+", Longitude "+lon; var pos = new google.maps.LatLng(lat, lon); var infoBox = new google.maps.InfoWindow({map: map, position:pos, content: msg}); map.setCenter(pos); return;} Get to know Google Maps API We are going to use the Google Maps API in the upcoming examples as well. You might consider familiarizing yourself with it by reading some of its online documentation at http://code.google.com/apis/maps/ documentation/javascript/basics.html. Geospatial indexing We can now turn our attention to the main topic of this article—geospatial indexing . A geospatial index is a special kind of index, designed specifically with location queries in mind, so you can perform queries like "Give me the closest n objects to my location". Geospatial indexing essentially turns your collection into a two-dimensional map. Each point of interest on that map (each document in the collection) is assigned a special value named geohash. Geohashing divides the coordinate system into hierarchical buckets of grids; the whole map gets divided into smaller quadrants. When you look for objects nearest to a point (x,y) on the map, MongoDB calculates the geohash of (x,y) and returns the points with the same geohash. I am not going to delve into much detail here on how it works, but if you are interested, I recommend you read MongoDB in Flatland (found at http://www.snailinaturtleneck.com/blog/2011/06/08/mongo-in-flatland/), an elaborate yet simple demonstration of how geospatial indexing works in MongoDB. Indexes are generally applied on fields to make field lookups faster. Time for action – creating geospatial indexes Let's see how we can build the geospatial index on a MongoDB collection: Launch the mongo interactive shell. Switch to a new database namespace called geolocation: $ ./mongodb/bin/mongoMongoDB shell version: 1.8.1connecting to: test> use geolocationswitched to db geolocation> Insert a few documents in a collection named ?map. Each document must contain an embedded document with two fields, latitude and longitude: > db.map.insert({coordinate: {latitude:23.2342987, longitude:90.20348}})> db.map.insert({coordinate: {latitude:23.3459835, longitude:90.92348}})> db.map.insert({coordinate: {latitude:23.6743521, longitude:90.30458}}) Create the geospatial index for the map collection by issuing the following command: >db.map.ensureIndex({coordinate: '2d'}) Enter the next command to check if the index was created: > db.system.indexes.find(){ "name" : "_id_", "ns" : "geolocation.map", "key" : { "_id" : 1 }, "v" : 0 }{ "_id" : ObjectId("4e46af48ffd7d5fd0a4d1e41"), "ns" : "geolocation.map", "key" : { "coordinate" : "2d" }, "name" : " coordinate _" } What just happened? We created a MongoDB collection named geocollection in a database named map. We manually inserted documents into the collection, each document contains some random latitude and longitude values in an embedded document named coordinate: > db.map.findOne(){ "_id" : ObjectId("4e46ae9bffd7d5fd0a4d1e3e"), "coordinate" : { "latitude" : 23.2342987, "longitude" : 90.20348 }} After that, we built the geospatial index on the latitude/longitude pairs by calling the ensureIndex() method on the collection: db.map.ensureIndex({coordinate: "2d"}) Next, we invoked the system.indexes.find() method that lists the indexes in the database. The index we created should be in that list: > db.system.indexes.find(){ "name" : "_id_", "ns" : "geolocation.map", "key" : { "_id" : 1 }, "v" : 0 }{ "_id" : ObjectId("4e46af48ffd7d5fd0a4d1e41"), "ns" : "geolocation.map", "key" : { "coordinate" : "2d" }, "name" : " coordinate _" } Geospatial indexing – Important things to know There are a few of things you must know about geospatial indexing: There can be only one geospatial index for a MongoDB collection. You cannot have more than one geospatial index for a collection. The index must be created for an embeded documendt or an array field of the document. If you build the index for an array field, the first two elements of the array will be considered as the (x,y) coordinate: >db.map.insert({coordinate: [23.3459835, 90.92348]})>db.map.ensureIndex({coordinate: "2d"}) Ordering is important when you are storing coordinates. If you store them in the order (y,x) rather than (x,y), you will have to query the collection with (y,x). Use arrays to store coordinates When storing coordinates in a geospatially indexed field, arrays are preferable to embedded objects. This is because an array preserves the order of items in it. No matter what programming language you are using to interact with MongoDB, this comes in very handy when you do queries.
Read more
  • 0
  • 0
  • 1777

article-image-article-tuning-jboss-as7-performance
Packt
02 Jan 2012
26 min read
Save for later

Tuning jBoss AS 7 Performance

Packt
02 Jan 2012
26 min read
Tuning JBoss AS 7 performance In this article by Francesco Marchioni, author of JBoss AS 7 Configuration, Administration and Deployment we'll discuss about JBoss AS 7 performance tuning. Performance tuning is a topic which concerns every application once it is rolled in production. The performance of the application server is influenced by a lot of factors; for this reason, a complete guide about tuning would require a book by itself (affectionate readers might remember that last year a book covering JBoss AS 5 performance tuning was authored by me and published by Packt Publishing available at: http://www.packtpub.com/jboss-5-performance-tuning/book. In this chapter, we will try to stress out the most important factors that influence the performance of the application server itself and of the applications deployed on it, with a special focus on the factors introduced by the new platform. So this article will basically cover the following topics: First, we will introduce in a nutshell the basics of performance tuning Then, we will show which are the key elements of the AS configuration which can influence its performance Definition of tuning The performance tuning process can be defined as an iterative process that you use to identify and eliminate bottlenecks until your application meets its performance objectives. You start by establishing a baseline. In this part of the process, you should decide what you are going to measure, and make sure you develop a performance test plan before you start. The test plan typically includes the desired performance, as well as the testing methodology which will be used. Then you collect data, analyse the results, and make configuration changes based on the analysis. After performance testing is completed, analysis of the test results helps determine the root causes of poor performance. This should be done before attempting any changes for performance. In order to measure performance it's essential to establish how performance will be measured. There are two important properties that you should measure for quantifying the performance of your applications: Response Time Throughput The response time is the time it takes for one user to perform an operation. For example, in an e-commerce site, after the customer puts items in a shopping cart and clicks the Buy button, the time it takes to process the order and for the checkout screen to appear is the response time for the checkout web page Throughput is the number of transactions that can occur in a given amount of time. The throughput is usually measured in Transactions Per Second (TPS). Although many architects and software engineer agree that about 70-80% of the application performance depends on how the application itself is coded—a poorly configured server environment can affect your user experience significantly, and eventually, on your application value. The amount of configuration elements, which can influence your server performance are quite a lot; however, some of them deserve a special attention: JVM tuning Application server resource pooling Logging Caching data Let's see each element in detail! The JBoss AS 7 runs within a Java Virtual Machine (JVM), hence it's natural that the AS can give a better performance with proper configuration of the JVM parameters. JVM tuning has evolved over the years and actually changed with each version of Java. Because the release 5.0 of the J2SE, the JVM is able to provide some default configuration ("Ergonomics") which is consistent with your environment. However, the smarter choice provided by Ergonomics is not always the optimal and without an explicit user setting, the performance can fall below your expectations. Basically, the JVM tuning process can be divided into the following steps: Choose a correct JVM heap size. This can be divided into setting an appropriate initial heap size (-Xms) and a maximum heap size (-Xmx). Choose a correct Garbage collector algorithm. Let's see both elements more in details. Choosing the correct JVM heap size Java objects are created in Heap; each heap is divided into three parts or generations for sake of garbage collection in Java. These are called as Young generation, Tenured or Old generation, and Permanent area of heap. JVM tuning has evolved over the years and actually changed with each version of Java. Because of the release 5.0 of the J2SE, the JVM is able to provide some default configuration ("Ergonomics") that is consistent with your environment. However, the smarter choice provided by Ergonomics is not always the optimal, and without an explicit user setting, the performance can fall below your expectations. New Generation is further divided into three parts known as Eden space, Survivor 1, and Survivor 2 space. When an object is first created in heap, it gets created in new generation inside Eden space, and after a subsequent minor Garbage collection if the object survives, it gets moved to Survivor 1, and then to Survivor 2 before the major garbage collection moved that object to Old or Tenured generation. Permanent generation of Heap or Perm Area of Heap is somewhat special, and it is used to store metadata related to the classes and methods in JVM; it also hosts String pool provided by JVM. In order to tune the JVM, you should choose a correct ratio between young generation (where objects are initially placed after instantiation) and the tenured generation (where old living generations are moved). For most applications, the correct ratio between the young generation and the tenured generation ranges between 1/3 and close to 1/2. The appropriate max heap size can be determined by testing your application with a peak load for a consistent time. Once you have determined the peak of memory demanded by your application, you can allow an extra 25-40 percent additional maximum heap size, depending on the nature of your application. As far as the initial heap size is concerned, a good rule of thumb is to set minimum heap size to be the same as the maximum heap size in order to avoid having the JVM allocate memory to expand the heap. This is particularly useful for production environment, while developers (who have limited resources) might choose a smaller initial heap size. Keep this suggested configuration as a reference for smaller environments and also for larger ones: java -Xmx1024m -Xms1024m -XX:MaxNewSize=448m -XX:NewSize=448m -XX:SurvivorRatio=6 java –Xmx2048m –Xms2048m -XX:MaxNewSize=896m -XX:NewSize=896m -XX:SurvivorRatio=6 The following table will help you to recap the meaning of the JVM flags: Property Description -Xmx Maximum heap size allowed for the JVM -Xms Initial heap size allowed for the JVM -XX:MaxNewSize Maximum size of new (young) generation -XX:NewSize Default size of new generation -XX:SurvivorRatio Ratio of Eden/Survivor space size Tuning the garbage collector Garbage collection is a mechanism provided by Java Virtual Machine to reclaim heap space from objects that are eligible for Garbage collection. An object becomes eligible for Garbage collection or GC if it is not reachable from any live threads or any static references. In other words, you can say that an object becomes eligible for Garbage collection if all its references are null. Choosing the correct Garbage collector algorithm is a key (but often overlooked) factor that plays an important role in reaching your service level requirements. There are several garbage collectors available as: Serial collector (-XX:+UseSerialGC): It performs garbage collector using a single thread that stops other JVM threads. This collector is fit for smaller applications; we don't advise using it for Enterprise applications. Parallel collector (-XX:+UseParallelGC): It performs minor collections in parallel and because J2SE 5.0 can also perform major collections in parallel (-XX:+UseParallelOldGC). This collector is fit for multiprocessor machines and applications requiring high throughput. It is also a suggested choice for applications that produce a fragmented Java heap, allocating large-size objects at different timelines. Concurrent collector (-XX:+UseConcMarkSweepGC): It performs most of its work concurrently using a single garbage collector thread that runs with the application threads simultaneously. It is fit for fast processor machines and applications with a strict service-level agreement. It can also be the best choice for applications using a large set of long-lived objects live HttpSessions. The new G1 collector One of the major enhancements in Java 7 is the new G1 ("Garbage first") low-latency garbage collector planned to replace CMS in the Hotspot JVM. It is a server-style collector, targeted at multiprocessor machines with large amounts of memory. The G1 collector is a departure from earlier collectors that had a physical separation between the young and old generations. With G1, even though it is generational, there is no physical separation between the two generations. This collector divides the entire space into regions and allows a set of regions to be collected, rather than split the space into an arbitrary young and old generation. The key features of the G1 collector are: G1 uses parallelism that is mostly used in hardware today. The main advantage of G1 is it is designed in such a way as to make use of all the available CPUs and utilize the processing power of all CPUs as well as increase the performance and speed up the garbage collection. The next feature that plays a key role in increasing the garbage collection is treating the young objects(newly created) and the old objects (that lived for some time) differently. G1 mainly focuses on young objects as they can be reclaimable when traversing the old objects. Heap compaction is done to eliminate fragmentation problems. In essence, because G1 compacts as it proceeds, it copies objects from one area of the heap to the other. Therefore, because of compaction, it will not encounter fragmentation issues that CMS might. There will always be areas of contiguous free space from which to allocate, allowing G1 to have consistent pauses over time. Compared to CMS, the G1 collector is also much easier to use, because it has a lesser number of switches and hence tuning VM is simpler. G1 is already present in JDK 7 as of now, and one can try it. To use G1, these two switches need to be passed: -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC Using large memory pages Another area of the JVM tuning that can yield substantial benefits to your application is the usage of large memory pages. Large memory pages is a feature of modern CPUs that allows memory-hungry applications to allocate memory in 2-4 MB chunks, instead of the standard 4 KB. Beginning with Java SE 5.0, there is a cross- platform flag for requesting large memory pages: -XX:+UseLargePages (On by default for Solaris, Off by default for Windows and Linux). The goal of large-page support is to optimize processor Translation Lookaside Buffer (TLB). A Translation Lookaside Buffer is a page translation cache that holds the most recently used virtual-to-physical address translation. TLB is a scarce system resource. A TLB miss can be costly as the processor must then read from the hierarchical page table, which may require multiple memory accesses. By using bigger page size, a single TLB entry can represent large memory ranges. There will be less pressure on TLB, and the memory-intensive applications may have better performance. Large memory pages are available with the 64-bit JVM. (Red Hat Enterprise Linux does let you allocate large pages on the 32-bit OS, but you get an illegal argument when starting the JVM). The Sun JVM, as well as OpenJDK, requires the following option, passed on the command line, to use large pages: -XX:+UseLargePages. Application server resource pools Application server pools are used because the very first release of any application server as means to set boundaries for the resources they contain. Resource pooling offers several benefits, such as: Improved performance: You can re-assign resource-intensive objects such as a database connection instead of creating and destroying a resource every time. Improved security: By granting a limited number of resources, you prevent plundering of server resources from applications that could eventually lead to an interruption of the AS services. JBoss AS 7 uses several resource pools to manage different kinds of services. The application server ships with a default configuration for all resource pools, which could be just good for simple applications. If you are planning to write mission- critical applications, however, you need to find the appropriate number of resources to be assigned to your pools. We will discuss in particular the following pool of resources, which ultimately play an important role in performance tuning: The database connection pool The EJB pool used by Stateless EJBs and MDBs The Web server pool of threads At the time of writing, the application server is not ready to produce performance metrics for the single subsystem that we have mentioned. Although it would be preferable to monitor the application server pools through management interfaces, you can still have a look inside the application server pools using some other tools or with a minimal sample application. That's what we will do in the next sections (if you want to check all the AS 7 latest updates that couldn't be added in this book, check the author's blog at: http://tinyurl.com/63tvagg). Tuning the database connection pool Establishing a JDBC connection with a DBMS can be quite slow. If your application requires database connections that are repeatedly opened and closed, this can become a significant performance issue. The connection pools in JBoss AS datasources offer an efficient solution to this problem. What is important to stress is that when a client closes a connection from a datasource, the connection is returned to the pool and becomes available for other clients; therefore, the connection itself is not closed. The cost of opening and closing pooled connections can be measured in terms of nanoseconds, so it's irrelevant in terms of performance. <datasource jndi-name="MySqlDS" pool-name="MySqlDS_Pool" enabled="true" jta="true" use-java-context="true" use-ccm="true"> <connection-url> jdbc:mysql://localhost:3306/MyDB </connection-url> <driver>mysql</driver> <pool> <min-pool-size>10</min-pool-size> <max-pool-size>30</max-pool-size> <prefill>true</prefill> </pool> <timeout> <blocking-timeout-millis>30000</blocking-timeout-millis> <idle-timeout-minutes>5</idle-timeout-minutes> </timeout> . . . .</datasource> Here, we configured an initial pool capacity of 10 connections, which can grow up to 30. As you can see from the following MySQL administration console, when you set the pre-fill element to true, the application server attempts to pre-fill the connection pool at the startup. This can produce a performance hit, especially if your connections are costly to acquire. If the application server is not able to serve any more connections because they are all in use, then it will wait up to the blocking-timeout-millis before throwing an exception to the client. At the same time, connections that have been idle for some minutes over th parameter idle-timeout-minutes are forced to return to the pool. Adjusting the pool size To determine the proper sizing, you need to monitor your connection usage. As we said, at the time of writing, the application server is not able to produce runtime metrics for the connection pool. However, there are some valid alternative as well: the first and most obvious is monitoring the database sessions. The following table shows some useful commands, which can be used to keep track of active database connections on different databases: Database Command / Table Oracle Query the V$SESSION view MySQL Use the command SHOW FULL PROCESSLIST Postgre-SQL Query the PG_STAT_ACTIVITY table Another option is using a tool such as P6Spy, which acts as a JDBC proxy driver (the author has blogged an article about it at: http://tinyurl.com/6dkmbxn). Once you have found the peak of connection used by your application, just set the maximum at least 25-30 percent higher. Don't be concerned about setting the maximum too high, because if you don't need that many connections, the pool will shrink automatically, provided that you have set idle-timeout-minutes. On the other hand, your server logs are still an invaluable help to check if your pool is running into trouble. For example, if you start seeing this exception in your server logs, there is a strong clue that you need to look at your connection pooling: 21:57:57,781 ERROR [stderr] (http-executor-threads - 7) Caused by: javax. resource.ResourceException: IJ000655: No managed connections available within configured blocking timeout (30000 [ms]) 21:57:57,782 ERROR [stderr] (http-executor-threads - 7) at org.jboss.jca.core. connectionmanager.pool.mcp.SemaphoreArrayListManagedConnectionPool.getC onnection(SemaphoreArrayListManagedConnectionPool.java:355) EJB connection pool The creation and destruction of beans can be an expensive operation, especially if they acquire external resources. To reduce this cost, the EJB container creates a pool of beans that, therefore, don't need to be re-initialized every time they are needed. The Stateless EJB pool and MDB pool are used to provide stateless business services to their client, acquiring beans from the pool when they are requested and releasing the bean to the pool as soon as they are finished. A typical EJB pool configuration looks like the following: <pools> <bean-instance-pools> <strict-max-pool name="slsb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/> <strict-max-pool name="mdb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/> </bean-instance-pools></pools> At the time of writing in AS 7, there is only support for strict-max-pool as a bean instance pool. A strict max pool allows you to configure a maximum upper limit for the pool. At runtime, when all the bean instances from the pool are in use and a new bean invocation request comes in, the pool blocks the request until the next bean instance is available or until a timeout (set in instance-acquisition-timeout) is reached. Monitoring the EJB pools will soon be available through the CLI, which will have a set of minimal operations to check the pool metrics. Setting up a self-made solution to monitor the current pool size is, however, not too complicated: you can use the handy EJB3 Interceptor API to monitor the EJB that have been taken from the pool and those that have been released (if you want to learn more about interceptors, you can check out this link from the JBoss EJB 3 documentation: http://tinyurl.com/cuev69q ). In the following Interceptor, we are simply setting a single EJB singleton field before contacting the EJB, and after that, it has completed its job and hence returned to the pool. package com.packtpub.chapter12;import javax.ejb.EJB;import javax.interceptor.AroundInvoke;import javax.interceptor.InvocationContext;public class EJBInterceptor { @EJB EJBCounter singleton; @AroundInvoke public Object defaultMethod(InvocationContext context) throws Exception{ // Take a bean from the pool singleton.getFromPool(); // Invoke the EJB method Object result = context.proceed(); // Return the bean to the pool singleton.returnToPool(); // Prints out the current pool size singleton.dumpPoolSize(); return result; }} The EJBCounter is a singleton EJB that merely contains the counter variable holding the EJB max-pool-size. package com.packtpub.chapter12;import javax.ejb.Singleton;@Singletonpublic class EJBCounter { private int count=20; public void getFromPool() { count--; } public void returnToPool() { count++; } public void dumpPoolSize() { System.out.println("Current pool size is "+count); }} You can further refine this approach by adding a @PostContruct annotation, which loads this variable from an external source such as the DB or a property file. Another viable option is launching a CLI script that collects the value from the max-pool-size attribute. Here's an example: [standalone@localhost:9999 /] /subsystem=ejb3/strict-max-bean-instance-pool=slsb-strict-max-pool:read-attribute(name="max-pool-size"){ "outcome" => "success", "result" => 20} The interceptors can then be applied to the stateless EJB that are used by your application either by means of a simple annotation or by declaring them into the ejb-jar.xml configuration file. For example, here's how to intercept all EJB invocations via the @Interceptors annotation: import javax.ejb.Stateless;import javax.interceptor.Interceptors;@StatelessInterceptors(EJBInterceptor.class)public class EJBTest {. . .} Turning off the EJB pool Although this might sound weird to you, there can be some scenarios where you don't want your EJB resources to be managed by a pool but created on demand. For example, if your EJB does not need a costly initialization (like acquiring external resources), it can be advantageous, in terms of performance, to avoid using the EJB 3 pool (the JBoss 5 Performance tuning book, for example, shows a case where a so-called heavy Stateful EJB can even outperform the Stateless counterpart. This is mostly due to the fact that handling the stateless pool is not a trivial task in terms of performance). Switching off the EJB pool just requires to comment/evict the bean-instance-pool- ref element that refers to the EJB pool: <stateless> <!-- <bean-instance-pool-ref pool-name="slsb-strict-max-pool"/> --></stateless> Of course, it is strongly recommended to run a consistent test bed to demonstrate that your application can benefit from such a change, which will take away any check on the number of EJB instances. Web server thread pool There are a large set of tuning aspects that ultimately influence the performance of the web server. One of the most important factors is tuning the HTTP Connector thread pool settings to more closely match the web request load you have. This is difficult to do, but it is very important to get right for best performance. <subsystem > <connector enable-lookups="false" enabled="true" executor="http-executor" max-connections="200" max-post-size="2048" max-save-post-size="4096" name="http" protocol="HTTP/1.1" proxy-name="proxy" proxy-port="8081" redirect-port="8443" scheme="http" secure="false" socket-binding="http" />. . .</subsystem> The amount of threads that are allowed by the web server are referenced through the executor attribute: <subsystem > <bounded-queue-thread-pool name="http-executor" blocking="true"> <core-threads count="10" per-cpu="20"/> <queue-length count="10" per-cpu="20"/> <max-threads count="10" per-cpu="20"/> <keepalive-time time="10" unit="seconds"/> </bounded-queue-thread-pool> </subsystem> Then, within the threads subsystem, you can define the number of threads that will be used by the pool, along with the other thread attributes: The most important Connector attributes are defined into the core-threads and max-threads. Setting these values too low means that you may not have enough threads to handle all of the requests, in which case, requests have to sit idle for some time without being handled until another request thread is freed up. Too low a value also means that JBoss Web server will be unable to take advantage of your server machine's hardware. On the other hand, be careful before increasing these thread counts blindly. By increasing the thread count too much, you will: Consume a good chunk of memory Your system will spend too much time context-switching You should, at first, investigate if instead it's a problem of individual requests taking too long. Are your threads returning to the pool? If, for example, database connections are not released, threads pile up waiting to obtain a database connection, thereby making it impossible to process additional requests. In such a scenario, simply adding more thread will make things even worse by introducing a greater stress on the CPU and on the garbage collector. You can discover this kind of problem by simply taking a thread dump in your application to find out where your web server threads are stuck. For example, in this picture, taken from the JConsole threads tab, you can see how an idle thread should look like, by looking at its stack trace: On the other hand, the following HTTP thread is busy doing input/output operations, which could mean, for example, the web server is acquiring data from an external resource. On the other hand, the following HTTP thread is busy doing input/output operations, which could mean, for example, the web server is acquiring data from an external resource. The above snapshots also give you a clue on how you can monitor the number of web server running threads. Just fill in the executor name (http-executor) in the lower textfield, and you will have a filtered list of all your web server threads. Logging tuning Logging is an essential activity of every applications. However, the default configuration is generally appropriate for development, but not for a production environment. The key elements that you need to consider when switching to production are: Choosing the appropriate handler to output your logs. Choose a log level that provides just the amount of information you need and nothing else. Choose an appropriate format for your logs As far as log handlers are concerned, in the default configuration, both console logging and file logging are enabled. While this can be fine for development, using console logging in production is an expensive process that causes lots of un-buffered I/O. While some applications may be fine with console logging, high- volume applications benefit from turning off console logging and just using the FILE handler. In order to remove console logging, you can simply comment out its handler: <root-logger> <level name="INFO"/> <handlers> <!-- <handler name="CONSOLE"/> --> <handler name="FILE"/> </handlers> </root-logger> The next step is choosing the correct logging verbosity. Obviously, the less you log, the less I/O will occur, and the better your overall application. The default configuration uses the "INFO" level for the root logger. You could consider raising this to a higher threshold such as "WARN" or (using a fine grained approach) changing the single logging categories: <logger category="org.hibernate"> <level name="WARN"/></logger> In this example, we have just raised the log level for org.hibernate package to "WARN", which will produce much more concise information from Hibernate. Finally, the pattern used by your logs can also influence the performance of your applications. For example, let's take the default pattern format, which is: <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/> Starting from this basic format, with as little as adding the flag %l you can greatly enhance the verbosity of your logs by printing the line number and the class that emitted the log: <pattern-formatter pattern="%l %d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/> And this is the console output once the server configuration has been reloaded: While this information can be quite useful in development, it will result in a huge burden when ported in production. (which prints out the caller class information) , %M (which outputs the method where logging was emitted), and %F (which outputs the filename where the logging request was issued).   Cache tuning Most performance issues in Enterprise applications arise from data access, hence caching data is one of the most important tuning techniques. The current release of the application server uses Infinispan as a distributed caching provider, and you can use it to cache anything you like. In particular, you can use it as a second-level caching provider by adding the following configuration in your persistence.xml file: code no 16 Second-level caching is intended for data that is read-mostly. It allows you to store the entity and query data in memory so that this data can be retrieved without the overhead of returning to the database. On the other hand, for applications with heavy use of write operations, caching may simply add overhead without providing any real benefit. In order to cache entities, you can use the @javax.persistence.Cacheable in conjunction with the shared-cache-mode element of persistence.xml. When you have enabled a selective cache of your entities, the @Cachable annotation will load entities into the Hibernate second-level cache. If you want to monitor the cache statistics, you can use the following property in your persistence.xml file, which will expose the cache statistics via JMX: <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode><properties> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_minimal_puts" value="true"/></properties> A very simple way to check the MBeans exposed by AS 7 is starting the JConsole application and choosing the Mbeans tab in the upper area of the application: The following table describes synthetically the meaning of the statistics provided by Infinispan's cache: Attribute Description Evictions Number of cache eviction operations RemoveMisses Number of cache removals where keys were not found ReadWriteRatio Read/writes ratio for the cache Hits Number of cache attribute hits NumberofEntries Number of entries currently in the cache StatisticsEnabled Enables or disables the gathering of statistics by this component TimeSinceReset Number of seconds since the cache statistics were last reset ElapsedTime Number of seconds since cache started Misses Number of cache attribute misses RemoveHits Number of cache removal hits AverageWriteTime Average number of milliseconds for a write operation in the cache Stores Number of cache attribute put operations/td> HitRatio Percentage hit/(hit+miss) ratio for the cache AverageReadTime Average number of milliseconds for a read operation on the cache Evicting data from the cache is also fundamental in order to save memory when cache entries are not needed anymore. You can configure the cache expiration policy, which determines when the data will be refreshed in the cache (for example, 1 hour, 2 hours, 1 day, and so on) according to the requirements for that entity. Configuring data eviction can be done either programmatically (see Infinispan documentation for examples about it: <property name="hibernate.cache.infinispan.entity.eviction.strategy" value= "LRU"/><property name="hibernate.cache.infinispan.entity.eviction.wake_up_interval" value= "2000"/><property name="hibernate.cache.infinispan.entity.eviction.max_entries" value= "5000"/><property name="hibernate.cache.infinispan.entity.expiration.lifespan" value= "60000"/><property name="hibernate.cache.infinispan.entity.expiration.max_idle" value= "30000"/> And here's a description for the properties that are contained in the configuration file: Property Description hibernate.cache.infinispan.entity.eviction. strategy The eviction strategy used by Infinispan. Can be either UNORDERED, FIFO, LIFO, NONE (check Infinispan docs for more details). hibernate.cache.infinispan.entity.eviction. wake_up_interval The time (ms) interval between each eviction thread runs. hibernate.cache.infinispan.entity.eviction. max_entries The maximum number of entries allowed in a cache (after that, eviction takes place). hibernate.cache.infinispan.entity.expiration. lifespan The time expiration (ms) of entities cached. hibernate.cache.infinispan.entity.expiration. max_idle The idle time expiration (ms) of entities.
Read more
  • 0
  • 0
  • 7685

article-image-gadgets-jira
Packt
29 Dec 2011
14 min read
Save for later

Gadgets in JIRA

Packt
29 Dec 2011
14 min read
(For more resources on JIRA, see here.) Writing JIRA 4 gadgets Gadgets are a big leap in JIRA's reporting features! The fact that JIRA is now an OpenSocial container lets its user add useful gadgets (both JIRA's own and third-party) into its dashboard. At the same time, gadgets written for JIRA can be added in other containers like iGoogle, Gmail, and so on! In this recipe, we will have a look at writing a very simple gadget, one that says 'Hello from JTricks'. By keeping the content simple, it will let us concentrate more on writing the gadget! Before we start writing the gadget, it is probably worth understanding the key components of a JIRA gadget: Gadget XML is the most important part of a JIRA Gadget. It holds the specification of the gadget and includes the following: Gadget Characteristics. It includes title, description, author's name, and so on Screenshot and a thumbnail image. Please note that the screenshot is not used within Atlassian containers such as JIRA or Confluence. We can optionally add it if we want them to be used in other OpenSocial containers Required features that the gadget container must provide for the gadget User preferences which will be configured by the gadget users The Gadget content created using HTML and JavaScript A screenshot and thumbnail image will be used during preview and while selecting the gadget from the container. An i18n property file used for internationalization in the gadget. Optional CSS and JavaScript file used to render the display in the Content section of the gadget. We will see each of them in the recipe. Getting ready Create a skeleton plugin using Atlassian Plugin SDK. How to do it... The following are the steps to write our first gadget, one that shows the greetings from JTricks! Modify the plugin descriptor with the gadget module and the resources required for our gadget: Add the Gadget module in the plugin descriptor: <gadget key="hello-gadget" name="Hello Gadget" location="hello-gadget.xml"> <description>Hello Gadget! </description></gadget> As you can see, this has a unique key and points to the location of the gadget XML! You can have as many gadget definitions as you want in your atlassian-plugin.xml file, but in our example, we stick with the preceding one. Include the thumbnail and screenshot images and downloadable resources in the plugin descriptor. More can be learned at http://confluence.atlassian.com/display/JIRADEV/Downloadable+Plugin+Resources. In our example, the resources are added on to the plugin descriptor as: <resource type="download" name="screenshot.png" location="/images/screenshot.png"/><resource type="download" name="thumbnail.png" location="/images/thumbnail.png"/> The location is relative to the src/main/resources folder in the plugin. As mentioned before, the screenshot is optional. Add the i18n properties file that will be used in the gadget also as a downloadable resource: <resource type="download" name="i18n/messages.xml" location="i18n/messages.xml"> <param name="content-type" value="text/xml; charset=UTF-8"/></resource> The atlassian-plugin.xml will now look like this: <atlassian-plugin key="com.jtricks.gadgets" name="Gadgets Plugin" plugins-version="2"> <plugin-info> <description>Gadgets Example</description> <version>2.0</version> <vendor name="JTricks" url="http://www.j-tricks.com/" /> </plugin-info> <gadget key="hello-gadget" name="Hello Gadget" location="hello-gadget.xml"> <description>Hello Gadget!</description> </gadget> <resource type="download" name="screenshot.png" location="/images/screenshot.png"/> <resource type="download" name="thumbnail.png" location="/images/thumbnail.png"/> <resource type="download" name="i18n/messages.xml" location="i18n/messages.xml"> <param name="content-type" value="text/xml; charset=UTF-8"/> </resource> </atlassian-plugin> Add the screenshot and thumbnail images under the src/main/resources/ images folder. The thumbnail image should be of the size 120 x 60 pixels. Add the i18n properties file under the src/main/resources/i18n folder. The name of the filer we defined in messages.xml. This file is an XML file wrapped within the messagebundle tag. Each property in the file is entered as an XML tag, as shown next: <msg name="gadget.title">Hello Gadget</msg> The msg tag has a name attribute, which is the property, and the corresponding Value is enclosed in the msg tag. We use three properties in our example and the entire file in our example looks like the following: <messagebundle> <msg name="gadget.title">Hello Gadget</msg> <msg name="gadget.title.url">http://www.j-tricks.com</msg> <msg name="gadget.description">Example Gadget from J-Tricks</msg></messagebundle> Write the Gadget XML. The Gadget XML has a Module element at the root of the XML. It has mainly three elements underneath – ModulePrefs, UserPref, and Content. We will write of each of them in this example. The entire set of attributes and elements and other details of the gadget specification can be read at http://confluence.atlassian. com/display/GADGETDEV/Creating+your+Gadget+XML+Specification. Write the ModulePrefs element. This element holds the information about the gadget. It also has two child elements – Require and Optional, that are used to define the required or optional features for the gadget. The following is how the ModulePrefs element looks in our example after it is populated with all the attributes: <ModulePrefs title="__MSG_gadget.title__" title_url="__MSG_gadget.title.url__" description="__MSG_gadget.description__" author="Jobin Kuruvilla" author_email=jobinkk@gmail.com screenshot='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "screenshot.png")' thumbnail='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "thumbnail.png")' height="150" ></ModulePrefs> As you can see, it holds information like title, title URL (to which the gadget title will link to), description, author name and email, height of the gadget, and URLs to screenshot and thumbnail images. Anything that starts with __MSG_ and ends with __ is a property that is referred from the i18n properties file. The height of the gadget is optional and 200, by default. The images are referenced using #staticResourceUrl where the first argument is the fully qualified gadget module key which is of the form ${atlassianplugin- key}:${module-key}. In our example, the plugin key is com. jtricks.gadgets and the module key is hello-gadget. Add the optional gadget directory feature inside ModulePrefs. This is currently supported only in JIRA: <Optional feature="gadget-directory"> <Param name="categories"> Other </Param></Optional> In the example, we add the category as Other! Other values supported for category are: JIRA, Confluence, FishEye, Crucible, Crowd, Clover, Bamboo, Admin, Charts, and External Content. You can add the gadget to more than one category by adding the categories within the Param element, each in a new line. Include Required features if there are any under the XML tag require. A full list of supported features can be found at http://confluence.atlassian.com/display/GADGETDEV/Including+Features+into+your+Gadget. Add the Locale element to point to the i18n properties file: <Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.jtricks.gadgets/i18n/messages.xml"/> Here the property __ATLASSIAN_BASE_URL__ will be automatically substituted with JIRA's configured base URL when the gadget is rendered. The path to the property file here is __ATLASSIAN_BASE_URL__/download/ resources/com.jtricks.gadgets, where com.jtricks.gadgets is the Atlassian plugin key. The path to the XML file /i18n/messages.xml is what is defined in the resource module earlier. Add User Preferences if required, using the UserPref element. We will omit the same in this example as the 'Hello Gadget' doesn't take any inputs from the user. Add the Content for the gadget. This is where the gadget is rendered using HTML and JavaScript. In our example, we just need to provide the static text 'Hello From JTricks' and it is fairly easy. The entire content is wrapped within the <![CDATA[ and ]]>, so that they won't be treated as XML tags. The following is how it looks in our example: <Content type="html" view="profile"> <![CDATA[ Hello From JTricks ]]></Content> Our gadget's XML is now ready and looks like the following block of code: <?xml version="1.0" encoding="UTF-8" ?><Module> <ModulePrefs title="__MSG_gadget.title__" title_url="__MSG_gadget.title.url__" description="__MSG_gadget.description__" author="Jobin Kuruvilla" author_email=jobinkk@gmail.com screenshot='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "screenshot.png")' thumbnail='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "thumbnail.png")' height="150" > <Optional feature="gadget-directory"> <Param name="categories"> Other </Param> </Optional> <Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.jtricks.gadgets/i18n/messages.xml"/> </ModulePrefs> <Content type="html" view="profile"> <![CDATA[ Hello From JTricks ]]> </Content></Module> Package the plugin, deploy it, and test it. How it works... Once the plugin is deployed, we need to add the gadget in the JIRA dashboard. The following is how it appears in the Add Gadget screen. Note the thumbnail is the one we have in the plugin and also note that it appears in the Other section: Once it is added, it appears as follows in the Dashboards section:   (Move the mouse over the image to enlarge.)   There's more... We can modify the look-and-feel of the gadgets by adding more HTML or gadget preferences! For example, <font color="red">Hello From JTricks</font> will make it appear in red. We can adjust the size of the gadget using the dynamic-height feature. We should add the following under the ModulePrefs element: <Require feature="dynamic-height"/> We should then invoke gadgets.window.adjustHeight(); whenever the content is reloaded. For example, we can do it in a window onload event, as shown next: <script type="text/javascript" charset="utf-8"> function resize() { gadgets.window.adjustHeight(); } window.onload=resize;</script> The gadget xml file, in this case, will look like this: <?xml version="1.0" encoding="UTF-8" ?><Module> <ModulePrefs title="__MSG_gadget.title__" title_url="__MSG_gadget.title.url__" description="__MSG_gadget.description__" author="Jobin Kuruvilla" author_email="jobinkk@gmail.com" screenshot='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "screenshot.png")' thumbnail='#staticResourceUrl("com.jtricks.gadgets:hello-gadget", "thumbnail.png")' height="150" > <Optional feature="gadget-directory"> <Param name="categories"> Other </Param> </Optional> <Require feature="dynamic-height"/> <Locale messages="__ATLASSIAN_BASE_URL__/download/resources/com.jtricks.gadgets/i18n/messages.xml"/> </ModulePrefs> <Content type="html" view="profile"> <![CDATA[ <script type="text/javascript" charset="utf-8"> function resize() { gadgets.window.adjustHeight(); } window.onload=resize; </script> Hello From JTricks ]]> </Content></Module> The gadget should now appear as follows: Note that the size is adjusted to just fit the text! Invoking REST services from gadgets In the previous recipe, we saw how to write a gadget with static content. In this recipe, we will have a look at creating a gadget with dynamic content or the data that is coming from the JIRA server. JIRA uses REST services to communicate between the gadgets and the server. In this recipe, we will use an existing REST service. Getting ready Create the Hello Gadget, as described in the previous recipe. How to do it... Let us consider a simple modification to the existing Hello Gadget to understand the basics of invoking REST services from gadgets. We will try to greet the current user by retrieving the user details from the server instead of displaying the static text: Hello From JTricks. JIRA ships with some inbuilt REST methods, one of which is to retrieve the details of the current user. The method can be reached in the URL: /rest/gadget/1.0/currentUser. We will use this method to retrieve the current user's full name and then display it in the gadget greeting. If the user's name is Jobin Kuruvilla, the gadget will display the message as Hello, Jobin Kuruvilla. As we are only changing the content of the gadget, the only modification is required in the gadget XML, which is hello-gadget.xml in our example. Only the Content element needs to be modified, which will now invoke the REST service and render the content. The following are the steps: Include the common Atlassian gadget resources: #requireResource("com.atlassian.jira.gadgets:common")#includeResources() #requireResource will bring in the JIRA gadget JavaScript framework into the gadget's context. #includeResources will write out the HTML tags for the resource in place. Check out http://confluence.atlassian.com/display/GADGETDEV/Using+Web+Resources+in+your+Gadget for more details. Construct a gadget object as follows: var gadget = AJS.Gadget The gadget object has four top-level options: baseUrl: An option to pass the base URL. It is a mandatory option, and we use __ATLASSIAN_BASE_URL__ here which will be rendered as JIRA's base URL. useOauth: An optional parameter. Used to configure the type of authentication which must be a URL. /rest/gadget/1.0/currentUser is commonly used. config: Another optional parameter. Only used if there are any configuration options for the gadget. view: Used to define the gadget's view. In our example, we don't use authentication or any configuration options. We will just go with the baseUrl and view options. The following is how the Gadget is created using JavaScript: <script type="text/javascript"> (function () { var gadget = AJS.Gadget({ baseUrl: "__ATLASSIAN_BASE_URL__", view: { ................ } }); })();</script> Populate the gadget view. The view object has the following properties: enableReload: Optional. Used to reload the gadget at regular intervals. onResizeReload: Optional. Used to reload the gadget when the browser is resized. onResizeAdjustHeight: Optional and used along with the dynamicheight feature. This will adjust the gadget height when the browser is resized. template: Created the actual view. args: An array of objects or function that returns an array of objects. It has two attributes. Key –used to access the data from within the template and ajaxOptions – set of request options used to connect to the server and retrieve data. In our example, we will use the template and args properties to render the view. First, let us see args because we use the data retrieved here in the template. args will look like the following: args: [{ key: "user", ajaxOptions: function() { return { url: "/rest/gadget/1.0/currentUser" }; } }] As you can see, we invoke the /rest/gadget/1.0/currentUser method and use the key user to refer the data we retrieved while rendering the view. ajaxOptions uses the jQuery Ajax Options, details of which can be found at http://api.jquery.com/jQuery.ajax#options. The key user will now hold the user details from the REST method, as follows: {"username":"jobinkk","fullName":"Jobin Kuruvilla","email":"jobinkk@gmail.com"} The template function will now use this args object (defined earlier) and its key, user to render the view as follows: template: function(args) { var gadget = this; var userDetails = AJS.$("<h1/>").text("Hello, "+args.user["fullName"]); gadget.getView().html(userDetails); } Here, args.user["fullName"] will retrieve the user's fullName from the REST output. Username or e-mail can be retrieved in a similar fashion. AJS.$ will construct the view as <h1>Hello, Jobin Kuruvilla</h1>, where Jobin Kuruvilla is the fullName retrieved. The entire Content section will look as shown in the following lines of code: <Content type="html" view="profile"> <![CDATA[ #requireResource("com.atlassian.jira.gadgets:common") #includeResources() <script type="text/javascript"> (function () { var gadget = AJS.Gadget({ baseUrl: "__ATLASSIAN_BASE_URL__", view: { template: function(args) { var gadget = this; var userDetails = AJS.$("<h1/>").text("Hello, "+args.user["fullName"]); gadget.getView().html(userDetails); }, args: [{ key: "user", ajaxOptions: function() { return { url: "/rest/gadget/1.0/currentUser" }; } }] } }); })(); </script> ]]> </Content> Package the gadget and deploy it. How it works... After the modification to the gadget XML, the gadget will now display the method as follows:
Read more
  • 0
  • 0
  • 21115
article-image-sencha-touch-catering-form-related-needs
Packt
28 Dec 2011
14 min read
Save for later

Sencha Touch: Catering Form Related Needs

Packt
28 Dec 2011
14 min read
(For more resources on Sencha Touch, see here.) Most of the useful applications not only present the data, but also accept inputs from their users. When we think of having a way to accept inputs from the user, send them to the server for further processing, and allow the user to modify them, we think of forms and the form fields. If our application requires users to enter some information, then we go about using the HTML form fields, such as <input>, <select>, and so on, and wrap inside a <form> element. Sencha Touch uses these tags and provides convenient JavaScript classes to work with the form and its fields. It provides field classes such as Url, Toggle, Select, Text, and so on. Each of these classes provides properties to initialize the field, handle the events, and utility methods to manipulate the behavior and the values of the field. On the other side, the form takes care of the rendering of the fields and also handles the data submission. Each field can be created by using the JSON notation (JavaScript Object Notation—http://www.json.org) or by creating an instance of the class. For example, a text field can either be constructed by using the following JSON notation: { xtype: 'textfield', name: 'text', label: 'My Text' } Alternatively, we can use the following class constructor: var txtField = new Ext.form.Text({ name: 'text', label: 'My Text' }); The first approach relies on xtype, which is a type assigned to each of the Sencha Touch components. It is used as shorthand for the class. The basic difference between the two is that the xtype approach is more for the lazy initialization and rendering. The object is created only when it is required. In any application, we would use a combination of these two approaches. In this article, we will go through all the form fields and understand how to make use of them and learn about their specific behaviors. In addition, we will see how to create a form using one or more form fields and handle the form validation and submission. Getting your form ready with FormPanel This recipe shows how to create a basic form using Sencha Touch and implement some of the behaviors such as submitting the form data, handling errors during the submission, and so on. Getting ready Make sure that you have set up your development environment How to do it... Carry out the following steps: Create a ch02 folder in the same folder where we had created the ch01 folder. Create and open a new file named ch02_01.js and paste the following code into it: Ext.setup({ onReady: function() { var form; //form and related fields config var formBase = { //enable vertical scrolling in case the form exceeds the page height scroll: 'vertical', url: 'http://localhost/test.php', items: [{//add a fieldset xtype: 'fieldset', title: 'Personal Info', instructions: 'Please enter the information above.', //apply the common settings to all the child items of the fieldset defaults: { required: true, //required field labelAlign: 'left', labelWidth: '40%' }, items: [ {//add a text field xtype: 'textfield', name : 'name', label: 'Name', useClearIcon: true,//shows the clear icon in the field when user types autoCapitalize : false }, {//add a password field xtype: 'passwordfield', name : 'password', label: 'Password', useClearIcon: false }, { xtype: 'passwordfield', name : 'reenter', label: 'Re-enter Password', useClearIcon: true }, {//add an email field xtype: 'emailfield', name : 'email', label: 'Email', placeHolder: 'you@sencha.com', useClearIcon: true }] } ], listeners : { //listener if the form is submitted, successfully submit : function(form, result){ console.log('success', Ext.toArray(arguments)); }, //listener if the form submission fails exception : function(form, result){ console.log('failure', Ext.toArray(arguments)); } }, //items docked to the bottom of the form dockedItems: [ { xtype: 'toolbar', dock: 'bottom', items: [ { text: 'Reset', handler: function() { form.reset(); //reset the fields } }, { text: 'Save', ui: 'confirm', handler: function() { //submit the form data to the url form.submit(); } } ] } ] }; if (Ext.is.Phone) { formBase.fullscreen = true; } else { //if desktop Ext.apply(formBase, { autoRender: true, floating: true, modal: true, centered: true, hideOnMaskTap: false, height: 385, width: 480 }); } //create form panel form = new Ext.form.FormPanel(formBase); form.show(); //render the form to the body } });   Include the following line in index.html: <script type="text/javascript" charset="utf-8" src="ch02/ch02_01.js"></script> Deploy and access it from the browser. You will see the following screen: How it works... The code creates a form panel, with a field set inside it. The field set has four fields specified as part of its child items. xtype mentioned for each field instructs the Sencha Touch component manager which class to use to instantiate them. form = new Ext.form.FormPanel(formBase) creates the form and the other field components using the config defined as part of the formBase. form.show() renders the form to the body and that is how it will appear on the screen. url contains the URL where the form data will be posted upon submission. The form can be submitted in the following two ways: By hitting Go, on the virtual keyboard or Enter on a field that ends up generating the action event. By clicking on the Save button, which internally calls the submit() method on the form object. form.reset() resets the status of the form and its fields to the original state. Therefore, if you had entered the values in the fields and clicked on the Reset button, all the fields would be cleared. form.submit() posts the form data to the specified url. The data is posted as an Ajax request using the POST method. Use of useClearIcon on the field instructs Sencha Touch whether it should show the clear icon in the field when the user starts entering a value in it. On clicking on this icon, the value in the field is cleared. There's more... In the preceding code, we saw how to construct a form panel, add fields to it, and handle events. We will see what other non-trivial things we may have to do in the project and how we can achieve these using Sencha Touch. Standard submit This is the old and traditional way for form data posting to the server url. If your application need is to use the standard form submit, rather than Ajax, then you will have to set standardSubmit to true on the form panel. This is set to false, by default. The following code snippet shows the usage of this property: var formBase = { scroll: 'vertical', standardSubmit: true, ... After this property is set to true on the FormPanel, form.submit() will load the complete page specified in url. Do not submit on field action As we saw earlier, the form data is automatically posted to the url if the action event (when the Go or Enter key is hit) occurs. In many applications, this default feature may not be desirable. In order to disable this feature, you will have to set submitOnAction to false on the form panel. Post-submission handling Say we posted our data to the url. Now, either the call may fail or it may succeed. In order to handle these specific conditions and act accordingly, we will have to pass additional config options to the form's submit() method. The following code shows the enhanced version of the submit call: form.submit({ success: function(form, result) { Ext.Msg.alert("INFO", "Form submitted!"); }, failure: function(form, result) { Ext.Msg.alert("INFO", "Form submission failed!"); } }); If the Ajax call (to post form data) fails, then the failure callback function is called, and in the case of success, the success callback function is called. This works only if the standardSubmit is set to false. Working with search In this and the subsequent recipes of the article, we will go over each of the form fields and understand how to work with them. This recipe describes the steps required to create and use a search form field. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe. How to do it... Carry out the following steps: Copy ch02_01.js to ch02_02.js. Open a new file named ch02_02.js and replace the definition of formBase with the following code: var formBase = { items: [{ xtype: 'searchfield', name: 'search', label: 'Search' }] }; Include ch02_02.js in place of ch02_01.js in index.html. Deploy and access the application in the browser. You will see a form panel with a search field. How it works... The search field can be constructed by using the Ext.form.Search class instance or by using the xtype—searchfield. The search form field implements HTML5 <input> with type="search". However, the implementation is very limited. For example, the HTML5 search field allows us to associate a data list to it which it can use during the search, whereas this feature is not present in Sencha Touch. Similarly, the W3 search field spec defines a pattern attribute to allow us to specify a regular expression against which a User Agent is meant to check the value, which is not supported yet in Sencha Touch. For more detail, you may refer to the W3 search field (http://www.w3.org/TR/html-markup/input.search.html) and the source code of the Ext.form.Search class. There's more... Often, in the application, for the search fields we do not use a label. Rather, we would like to show a text, such as Search inside the field that will disappear when the focus is on the field. Let's see how we can achieve this. Using a placeholder Placeholders are supported by most of the form fields in Sencha Touch by using the property placeHolder. The placeholder text appears in the field as long as there is no value entered in it and the field does not have the focus. The following code snippet shows the typical usage of it: { xtype: 'searchfield', name: 'search', label: 'Search', placeHolder: 'Search...' } Putting custom validation in the e-mail field This recipe describes how to make use of the e-mail form field provided by Sencha Touch and how to validate the value entered into it to find out whether the entered e-mail passes the validation rule or not. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe in this article. How to do it... Carry out the following steps: Copy ch02_01.js to ch02_03.js. Open a new file named ch02_03.js and replace the definition of formBase with the following code: var formBase = { items: [{ xtype: 'emailfield', name : 'email', label: 'Email', placeHolder: 'you@sencha.com', useClearIcon: true, listeners: { blur: function(thisTxt, eventObj) { var val = thisTxt.getValue(); //validate using the pattern if (val.search("[a-c]+@[a-z]+[.][a-z]+") == -1) Ext.Msg.alert("Error", "Invalid e-mail address!!"); else Ext.Msg.alert("Info", "Valid e-mail address!!"); } } }] }; Include ch02_03.js in place of ch02_02.js in index.html. Deploy and access the application in the browser. How it works... The e-mail field can be constructed by using the Ext.form.Email class instance or by using the xtype: emailfield. The e-mail form field implements HTML5 <input> with type="email". However, as with the search field, the implementation is very limited. For example, the HTML5 e-mail field allows us to specify a regular expression pattern which can be used to validate the value entered in the field. Working with dates using DatePicker This recipe describes how to make use of the date picker form field provided by Sencha Touch which allows the user to select a date. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe in this article. How to do it... Carry out the following steps: Copy ch02_01.js to ch02_04.js. Open a new file named ch02_04.js and replace the definition of formBase with the following code: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date' }] }; Include ch02_04.js in place of ch02_03.js in index.html. Deploy and access the application in the browser. How it works... The date picker field can be constructed by using the Ext.form.DatePicker class instance or by using xtype: datepickerfield. The date picker form field implements HTML <select>. When the user tries to select an entry, it shows the date picker with the month, day, and year slots for selection. After selection, when the user clicks on the Done button, the field is set with the selected value. There's more... Additionally, there are other things that can be done such as setting the date to the current date, or any particular date, or changing the order of appearance of a month, day, and year. Let's see what it takes to accomplish this. Setting the default date to the current date In order to set the default value to the current date, the value property must be set to the current date. The following code shows how to do it: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', value: new Date(), Setting the default date to a particular date The default date is 01/01/1970. Let's assume that you need to set the date to a different date, but not the current date. To do so, you will have to set the value using the year, month, and day properties, as follows: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', value: {year: 2011, month: 6, day: 11}, ... Changing the slot order By default, the slot order is month, day, and year. You can change it by setting the slotOrder property of the picker property of date picker, as shown in the following code: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', picker: {slotOrder: ['day', 'month', 'year']} }] }; Setting the picker date range By default, the date range shown by the picker is 1970 until the current year. For our application need, if we have to alter the year range, we can do so by setting the yearFrom nd yearTo properties of the picker property of the date picker, as follows: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', picker: {yearFrom: 2000, yearTo: 2010} }] }; Making a field hidden Often in an application, there would be a need to hide fields which are not needed in a particular context but are required and hence need to be shown in another. In this recipe, we will see how to make a field hidden and show it, conditionally. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe in this article. How to do it... Carry out the following steps: Edit ch02_04.js and modify the code as follows by adding the hidden property: var formBase = { items: [{ xtype: 'datepickerfield', id: 'datefield-id', name: 'date', hidden: true, label: 'Date'}] }; Deploy and access the application in the browser. How it works... When a field is marked as hidden, Sencha Touch uses the DOM's hide method on the element to hide that particular field. There's more... Let's see how we can programmatically show/hide a field. Showing/Hiding a field at runtime Each component in Sencha Touch supports two methods: show and hide. The show method shows the element and hide hides the element. In order to call these methods, we will have to first find the reference to the component, which can be achieved by either using the object reference or by using the Ext.getCmp() method. Given a component ID, the getCmp method returns us the component. The following code snippet demonstrates how to show an element: var cmp = Ext.getCmp('datefield-id'); cmp.show(); To hide an element, we will have to call cmp.hide();
Read more
  • 0
  • 0
  • 2993

article-image-unity-3-0-enter-third-dimension
Packt
28 Dec 2011
10 min read
Save for later

Unity 3-0 Enter the Third Dimension

Packt
28 Dec 2011
10 min read
(For more resources on Unity 3D, see here.) Getting to grips with 3D Let's take a look at the crucial elements of 3D worlds, and how Unity lets you develop games in three dimensions. Coordinates If you have worked with any 3D application before, you'll likely be familiar with the concept of the Z-axis. The Z-axis, in addition to the existing X for horizontal and Y for vertical, represents depth. In 3D applications, you'll see information on objects laid out in X, Y, Z format—this is known as the Cartesian coordinate method. Dimensions, rotational values, and positions in the 3D world can all be described in this way. As in other documentation of 3D, you'll see such information written with parenthesis, shown as follows: (3, 5, 3) This is mostly for neatness, and also due to the fact that in programming, these values must be written in this way. Regardless of their presentation, you can assume that any sets of three values separated by commas will be in X, Y, Z order. In the following image, a cube is shown at location (3,5,3) in the 3D world, meaning it is 3 units from 0 in the X-axis, 5 up in the Y-axis, and 3 forward in the Z-axis: Local space versus world space A crucial concept to begin looking at is the difference between local space and world space. In any 3D package, the world you will work in is technically infinite, and it can be difficult to keep track of the location of objects within it. In every 3D world, there is a point of origin, often referred to as the 'origin'> or 'world zero', as it is represented by the position (0,0,0). All world positions of objects in 3D are relative to world zero. However, to make things simpler, we also use local space (also known as object space) to define object positions in relation to one another. These relationships are known as parent-child relationships. In Unity, parent-child relationships can be established easily by dragging one object onto another in the Hierarchy. This causes the dragged object to become a child, and its coordinates from then on are read in terms relative to the parent object. For example, if the child object is exactly at the same world position as the parent object, its position is said to be (0,0,0), even if the parent position is not at world zero. Local space assumes that every object has its own zero point, which is the point from which its axes emerge. This is usually the center of the object, and by creating relationships between objects, we can compare their positions in relation to one another. Such relationships, known as parent-child relationships, mean that we can calculate distances from other objects using local space, with the parent object's position becoming the new zero point for any of its child objects. This is especially important to bear in mind when working on art assets in 3D modelling tools, as you should always ensure that your models are created at 0,0,0 in the package that you are using. This is to ensure that when imported into Unity, their axes are read correctly. We can illustrate this in 2D, as the same conventions will apply to 3D. In the following example: The first diagram (i) shows two objects in world space. A large cube exists at coordinates(3,3), and a smaller one at coordinates (6,7). In the second diagram (ii), the smaller cube has been made a child object of the larger cube. As such the smaller cube's coordinates are said to be (3,4), because its zero point is the world position of the parent. Vectors You'll also see 3D vectors described in Cartesian coordinates. Like their 2D counterparts, 3D vectors are simply lines drawn in the 3D world that have a direction and a length. Vectors can be moved in world space, but remain unchanged themselves. Vectors are useful in a game engine context, as they allow us to calculate distances, relative angles between objects, and the direction of objects. Cameras Cameras are essential in the 3D world, as they act as the viewport for the screen. Cameras can be placed at any point in the world, animated, or attached to characters or objects as part of a game scenario. Many cameras can exist in a particular scene, but it is assumed that a single main camera will always render what the player sees. This is why Unity gives you a Main Camera object whenever you create a new scene. Projection mode—3D versus 2D The Projection mode of a camera states whether it renders in 3D (Perspective) or 2D (Orthographic). Ordinarily, cameras are set to Perspective Projection mode, and as such have a pyramid shaped Field of View (FOV). A Perspective mode camera renders in 3D and is the default Projection mode for a camera in Unity. Cameras can also be set to Orthographic Projection mode in order to render in 2D—these have a rectangular field of view. This can be used on a main camera to create complete 2D games or simply used as a secondary camera used to render Heads Up Display (HUD) elements such as a map or health bar. In game engines, you'll notice that effects such as lighting, motion blurs, and other effects are applied to the camera to help with game simulation of a person's eye view of the world—you can even add a few cinematic effects that the human eye will never experience, such as lens flares when looking at the sun! Most modern 3D games utilize multiple cameras to show parts of the game world that the character camera is not currently looking at—like a 'cutaway' in cinematic terms. Unity does this with ease by allowing many cameras in a single scene, which can be scripted to act as the main camera at any point during runtime. Multiple cameras can also be used in a game to control the rendering of particular 2D and 3D elements separately as part of the optimization process. For example, objects may be grouped in layers, and cameras may be assigned to render objects in particular layers. This gives us more control over individual renders of certain elements in the game. Polygons, edges, vertices, and meshes In constructing 3D shapes, all objects are ultimately made up of interconnected 2D shapes known as polygons. On importing models from a modeling application, Unity converts all polygons to polygon triangles. By combining many linked polygons, 3D modeling applications allow us to build complex shapes, known as meshes. Polygon triangles (also referred to as faces) are in turn made up of three connected edges. The locations at which these edges meet are known as points or vertices. By knowing these locations, game engines are able to make calculations regarding the points of impact, known as collisions, when using complex collision detection with Mesh Colliders, such as in shooting games to detect the exact location at which a bullet has hit another object. In addition to building 3D shapes that are rendered visibly, mesh data can have many other uses. For example, it can be used to specify a shape for collision that is less detailed than a visible object, but roughly the same shape. This can help save performance as the physics engine needn't check a mesh in detail for collisions. This is seen in the following image from the Unity car tutorial, where the vehicle itself is more detailed than its collision mesh: In the second image, you can see that the amount of detail in the mesh used for the collider is far less than the visible mesh itself: In game projects, it is crucial for the developer to understand the importance of the polygon count. The polygon count is the total number of polygons, often in reference to models, but also in reference to props, or an entire game level (or in Unity terms, 'Scene'). The higher the number of polygons, the more work your computer must do to render the objects onscreen. This is why we've seen an increase in the level of detail from early 3D games to those of today. Simply compare the visual detail in a game such as id's Quake(1996) with the details seen in Epic's Gears Of War (2006) in just a decade. As a result of faster technology, game developers are now able to model 3D characters and worlds, for games that contain a much higher polygon count and resultant level of realism, and this trend will inevitably continue in the years to come. This said, as more platforms emerge such as mobile and online, games previously seen on dedicated consoles can now be played in a web browser thanks to Unity. As such, the hardware constraints are as important now as ever, as lower powered devices such as mobile phones and tablets are able to run 3D games. For this reason, when modeling any object to add to your game, you should consider polygonal detail, and where it is most required. Materials, textures, and shaders Materials are a common concept to all 3D applications, as they provide the means to set the visual appearance of a 3D model. From basic colors to reflective image-based surfaces, materials handle everything. Let's start with a simple color and the option of using one or more images—known as textures. In a single material, the material works with the shader, which is a script in charge of the style of rendering. For example, in a reflective shader, the material will render reflections of surrounding objects, but maintain its color or the look of the image applied as its texture. In Unity, the use of materials is easy. Any materials created in your 3D modeling package will be imported and recreated automatically by the engine and created as assets that are reusable. You can also create your own materials from scratch, assigning images as textures and selecting a shader from a large library that comes built-in. You may also write your own shader scripts or copy-paste those written by fellow developers in the Unity community, giving you more freedom for expansion beyond the included set. When creating textures for a game in a graphics package such as Photoshop or GIMP, you must be aware of the resolution. Larger textures will give you the chance to add more detail to your textured models, but be more intensive to render. Game textures imported into Unity will be scaled to a power of 2 resolution. For example: 64px x 64px 128px x 128px 256px x 256px 512px x 512px 1024px x 1024px Creating textures of these sizes with content that matches at the edges will mean that they can be tiled successfully by Unity. You may also use textures scaled to values that are not powers of two, but mostly these are used for GUI elements.  
Read more
  • 0
  • 0
  • 3446
Modal Close icon
Modal Close icon