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

How-To Tutorials

7019 Articles
article-image-cross-platform-development-build-once-deploy-anywhere
Packt
01 Oct 2013
19 min read
Save for later

Cross-platform Development - Build Once, Deploy Anywhere

Packt
01 Oct 2013
19 min read
(For more resources related to this topic, see here.) The demo application – how the projects work together Take a look at the following diagram to understand and familiarize yourself with the configuration pattern that all of your Libgdx applications will have in common: What you see here is a compact view of four projects. The demo project to the very left contains the shared code that is referenced (that is, added to the build path) by all the other platform-specific projects. The main class of the demo application is MyDemo.java. However, looking at it from a more technical view, the main class where an application gets started by the operating system, which will be referred to as Starter Classes from now on. Notice that Libgdx uses the term "Starter Class" to distinguish between these two types of main classes in order to avoid confusion. We will cover everything related to the topic of Starter Classes in a moment. While taking a closer look at all these directories in the preceding screenshot, you may have spotted that there are two assets folders: one in the demo-desktop project and another one in demo-android. This brings us to the question, where should you put all the application's assets? The demo-android project plays a special role in this case. In the preceding screenshot, you see a subfolder called data, which contains an image named libgdx.png, and it also appears in the demo-desktop project in the same place. Just remember to always put all of your assets into the assets folder under the demo-android project. The reason behind this is that the Android build process requires direct access to the application's assets folder. During its build process, a Java source file, R.java, will automatically be generated under the gen folder. It contains special information for Android about the available assets. It would be the usual way to access assets through Java code if you were explicitly writing an Android application. However, in Libgdx, you will want to stay platform-independent as much as possible and access any resource such as assets only through methods provided by Libgdx. You may wonder how the other platform-specific projects will be able to access the very same assets without having to maintain several copies per project. Needless to say that this would require you to keep all copies manually synchronized each time the assets change. Luckily, this problem has already been taken care of by the generator as follows: The demo-desktop project uses a linked resource, a feature by Eclipse, to add existing files or folders to other places in a workspace. You can check this out by right-clicking on the demo-desktop project then navigating to Properties | Resource | Linked Resources and clicking on the Linked Resources tab. The demo-html project requires another approach since Google Web Toolkit ( GWT ) has a different build process compared to the other projects. There is a special file GwtDefinition.gwt.xml that allows you to set the asset path by setting the configuration property gdx.assetpath, to the assets folder of the Android project. Notice that it is good practice to use relative paths such as ../demo-android/assets so that the reference does not get broken in case the workspace is moved from its original location. Take this advice as a precaution to protect you and maybe your fellow developers too from wasting precious time on something that can be easily avoided by using the right setup right from the beginning. The following is the code listing for GwtDefinition.gwt.xml from demo-html : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/ distro-source/core/src/gwt-module.dtd"> <module> <inherits name='com.badlogic.gdx.backends.gdx_backends_gwt' /> <inherits name='MyDemo' /> <entry-point class='com.packtpub.libgdx.demo.client.GwtLauncher' /> <set-configuration-property name="gdx.assetpath" value="../demo-android/assets" /> </module> Backends Libgdx makes use of several other libraries to interface the specifics of each platform in order to provide cross-platform support for your applications. Generally, a backend is what enables Libgdx to access the corresponding platform functionalities when one of the abstracted (platform-independent) Libgdx methods is called. For example, drawing an image to the upper-left corner of the screen, playing a sound file at a volume of 80 percent, or reading and writing from/to a file. Libgdx currently provides the following three backends: LWJGL (Lightweight Java Game Library) Android JavaScript/WebGL As already mentioned in Introduction to Libgdx and Project Setup , there will also be an iOS backend in the near future. LWJGL (Lightweight Java Game Library) LWJGL ( Lightweight Java Game Library ) is an open source Java library originally started by Caspian Rychlik-Prince to ease game development in terms of accessing the hardware resources on desktop systems. In Libgdx, it is used for the desktop backend to support all the major desktop operating systems, such as Windows, Linux, and Mac OS X. For more details, check out the official LWJGL website at http://www.lwjgl.org/. Android Google frequently releases and updates their official Android SDK. This represents the foundation for Libgdx to support Android in the form of a backend. There is an API Guide available which explains everything the Android SDK has to offer for Android developers. You can find it at http://developer.android.com/guide/components/index.html. WebGL WebGL support is one of the latest additions to the Libgdx framework. This backend uses the GWT to translate Java code into JavaScript and SoundManager2 ( SM2 ), among others, to add a combined support for HTML5, WebGL, and audio playback. Note that this backend requires a WebGL-capable web browser to run the application. You might want to check out the official website of GWT: https://developers.google.com/web-toolkit/. You might want to check out the official website of SM2: http://www.schillmania.com/projects/soundmanager2/. You might want to check out the official website of WebGL: http://www.khronos.org/webgl/. There is also a list of unresolved issues you might want to check out at https://github.com/libgdx/libgdx/blob/master/backends/gdx-backends-gwt/issues.txt. Modules Libgdx provides six core modules that allow you to access the various parts of the system your application will run on. What makes these modules so great for you as a developer is that they provide you with a single Application Programming Interface ( API ) to achieve the same effect on more than just one platform. This is extremely powerful because you can now focus on your own application and you do not have to bother with the specialties that each platform inevitably brings, including the nasty little bugs that may require tricky workarounds. This is all going to be transparently handled in a straightforward API which is categorized into logic modules and is globally available anywhere in your code, since every module is accessible as a static field in the Gdx class. Naturally, Libgdx does always allow you to create multiple code paths for per-platform decisions. For example, you could conditionally increase the level of detail in a game when run on the desktop platform, since desktops usually have a lot more computing power than mobile devices. The application module The application module can be accessed through Gdx.app. It gives you access to the logging facility, a method to shutdown gracefully, persist data, query the Android API version, query the platform type, and query the memory usage. Logging Libgdx employs its own logging facility. You can choose a log level to filter what should be printed to the platform's console. The default log level is LOG_INFO. You can use a settings file and/or change the log level dynamically at runtime using the following code line: Gdx.app.setLogLevel(Application.LOG_DEBUG); The available log levels are: LOG_NONE: This prints no logs. The logging is completely disabled. LOG_ERROR: This prints error logs only. LOG_INFO: This prints error and info logs. LOG_DEBUG: This prints error, info, and debug logs. To write an info, debug, or an error log to the console, use the following listings: Gdx.app.log("MyDemoTag", "This is an info log."); Gdx.app.debug("MyDemoTag", "This is a debug log."); Gdx.app.error("MyDemoTag", "This is an error log."); Shutting down gracefully You can tell Libgdx to shutdown the running application. The framework will then stop the execution in the correct order as soon as possible and completely de-allocate any memory that is still in use, freeing both Java and the native heap. Use the following listing to initiate a graceful shutdown of your application: Gdx.app.exit(); You should always do a graceful shutdown when you want to terminate your application. Otherwise, you will risk creating memory leaks, which is a really bad thing. On mobile devices, memory leaks will probably have the biggest negative impact due to their limited resources. Persisting data If you want to persist your data, you should use the Preferences class. It is merely a dictionary or a hash map data type which stores multiple key-value pairs in a file. Libgdx will create a new preferences file on the fly if it does not exist yet. You can have several preference files using unique names in order to split up data into categories. To get access to a preference file, you need to request a Preferences instance by its filename as follows: Preferences prefs = Gdx.app.getPreferences("settings.prefs"); To write a (new) value, you have to choose a key under which the value should be stored. If this key already exists in a preferences file, it will be overwritten. Do not forget to call flush() afterwards to persist the data, or else all the changes will be lost. prefs.putInteger("sound_volume", 100); // volume @ 100% prefs.flush(); Persisting data needs a lot more time than just modifying values in memory (without flushing). Therefore, it is always better to modify as many values as possible before a final flush() method is executed. To read back a certain value from a preferences file, you need to know the corresponding key. If this key does not exist, it will be set to the default value. You can optionally pass your own default value as the second argument (for example, in the following listing, 50 is for the default sound volume): int soundVolume = prefs.getInteger("sound_volume", 50); Querying the Android API Level On Android, you can query the Android API Level, which allows you to handle things differently for certain versions of the Android OS. Use the following listing to find out the version: Gdx.app.getVersion(); On platforms other than Android, the version returned is always 0. Querying the platform type You may want to write a platform-specific code where it is necessary to know the current platform type. The following example shows how it can be done: switch (Gdx.app.getType()) { case Desktop: // Code for Desktop application break; case Android: // Code for Android application break; case WebGL: // Code for WebGL application break; default: // Unhandled (new?) platform application break; } Querying memory usage You can query the system to find out its current memory footprint of your application. This may help you find excessive memory allocations that could lead to application crashes. The following functions return the amount of memory (in bytes) that is in use by the corresponding heap: long memUsageJavaHeap = Gdx.app.getJavaHeap(); long memUsageNativeHeap = Gdx.app.getNativeHeap(); Graphics module The graphics module can be accessed either through Gdx.getGraphics() or by using the shortcut variable Gdx.graphics. Querying delta time Query Libgdx for the time span between the current and the last frame in seconds by calling Gdx.graphics.getDeltaTime(). Querying display size Query the device's display size returned in pixels by calling Gdx.graphics.getWidth() and Gdx.graphics.getHeight(). Querying the FPS (frames per second) counter Query a built-in frame counter provided by Libgdx to find the average number of frames per second by calling Gdx.graphics.getFramesPerSecond(). Audio module The audio module can be accessed either through Gdx.getAudio() or by using the shortcut variable Gdx.audio. Sound playback To load sounds for playback, call Gdx.audio.newSound(). The supported file formats are WAV, MP3, and OGG. There is an upper limit of 1 MB for decoded audio data. Consider the sounds to be short effects like bullets or explosions so that the size limitation is not really an issue. Music streaming To stream music for playback, call Gdx.audio.newMusic(). The supported file formats are WAV, MP3, and OGG. Input module The input module can be accessed either through Gdx.getInput() or by using the shortcut variable Gdx.input. In order to receive and handle input properly, you should always implement the InputProcessor interface and set it as the global handler for input in Libgdx by calling Gdx.input.setInputProcessor(). Reading the keyboard/touch/mouse input Query the system for the last x or y coordinate in the screen coordinates where the screen origin is at the top-left corner by calling either Gdx.input.getX() or Gdx.input.getY(). To find out if the screen is touched either by a finger or by mouse, call Gdx.input.isTouched() To find out if the mouse button is pressed, call Gdx.input.isButtonPressed() To find out if the keyboard is pressed, call Gdx.input.isKeyPressed() Reading the accelerometer Query the accelerometer for its value on the x axis by calling Gdx.input.getAccelerometerX(). Replace the X in the method's name with Y or Z to query the other two axes. Be aware that there will be no accelerometer present on a desktop, so Libgdx always returns 0. Starting and canceling vibrator On Android, you can let the device vibrate by calling Gdx.input.vibrate(). A running vibration can be cancelled by calling Gdx.input.cancelVibrate(). Catching Android soft keys You might want to catch Android's soft keys to add an extra handling code for them. If you want to catch the back button, call Gdx.input.setCatchBackKey(true). If you want to catch the menu button, call Gdx.input.setCatchMenuKey(true). On a desktop where you have a mouse pointer, you can tell Libgdx to catch it so that you get a permanent mouse input without having the mouse ever leave the application window. To catch the mouse cursor, call Gdx.input.setCursorCatched(true). The files module The files module can be accessed either through Gdx.getFiles() or by using the shortcut variable Gdx.files. Getting an internal file handle You can get a file handle for an internal file by calling Gdx.files.internal(). An internal file is relative to the assets folder on the Android and WebGL platforms. On a desktop, it is relative to the root folder of the application. Getting an external file handle You can get a file handle for an external file by calling Gdx.files.external(). An external file is relative to the SD card on the Android platform. On a desktop, it is relative to the user's home folder. Note that this is not available for WebGL applications. The network module The network module can be accessed either through Gdx.getNet() or by using the shortcut variable Gdx.net. HTTP GET and HTTP POST You can make HTTP GET and POST requests by calling either Gdx.net.httpGet() or Gdx.net.httpPost(). Client/server sockets You can create client/server sockets by calling either Gdx.net.newClientSocket() or Gdx.net.newServerSocket(). Opening a URI in a web browser To open a Uniform Resource Identifier ( URI ) in the default web browser, call Gdx.net.openURI(URI). Libgdx's Application Life-Cycle and Interface The Application Life-Cycle in Libgdx is a well-defined set of distinct system states. The list of these states is pretty short: create, resize, render, pause, resume, and dispose. Libgdx defines an ApplicationListener interface that contains six methods, one for each system state. The following code listing is a copy that is directly taken from Libgdx's sources. For the sake of readability, all comments have been stripped. public interface ApplicationListener { public void create (); public void resize (int width, int height); public void render (); public void pause (); public void resume (); public void dispose (); } All you need to do is implement these methods in your main class of the shared game code project. Libgdx will then call each of these methods at the right time. The following diagram visualizes the Libgdx's Application Life-Cycle: Note that a full and dotted line basically has the same meaning in the preceding figure. They both connect two consecutive states and have a direction of flow indicated by a little arrowhead on one end of the line. A dotted line additionally denotes a system event. When an application starts, it will always begin with create(). This is where the initialization of the application should happen, such as loading assets into memory and creating an initial state of the game world. Subsequently, the next state that follows is resize(). This is the first opportunity for an application to adjust itself to the available display size (width and height) given in pixels. Next, Libgdx will handle system events. If no event has occurred in the meanwhile, it is assumed that the application is (still) running. The next state would be render(). This is where a game application will mainly do two things: Update the game world model Draw the scene on the screen using the updated game world model Afterwards, a decision is made upon which the platform type is detected by Libgdx. On a desktop or in a web browser, the displaying application window can be resized virtually at any time. Libgdx compares the last and current sizes on every cycle so that resize() is only called if the display size has changed. This makes sure that the running application is able to accommodate a changed display size. Now the cycle starts over by handling (new) system events once again. Another system event that can occur during runtime is the exit event. When it occurs, Libgdx will first change to the pause() state, which is a very good place to save any data that would be lost otherwise after the application has terminated. Subsequently, Libgdx changes to the dispose() state where an application should do its final clean-up to free all the resources that it is still using. This is also almost true for Android, except that pause() is an intermediate state that is not directly followed by a dispose() state at first. Be aware that this event may occur anytime during application runtime while the user has pressed the Home button or if there is an incoming phone call in the meanwhile. In fact, as long as the Android operating system does not need the occupied memory of the paused application, its state will not be changed to dispose(). Moreover, it is possible that a paused application might receive a resume system event, which in this case would change its state to resume(), and it would eventually arrive at the system event handler again. Starter Classes A Starter Class defines the entry point (starting point) of a Libgdx application. They are specifically written for a certain platform. Usually, these kinds of classes are very simple and mostly consist of not more than a few lines of code to set certain parameters that apply to the corresponding platform. Think of them as a kind of boot-up sequence for each platform. Once booting has finished, the Libgdx framework hands over control from the Starter Class (for example, the demo-desktop project) to your shared application code (for example, the demo project) by calling the different methods from the ApplicationListener interface that the MyDemo class implements. Remember that the MyDemo class is where the shared application code begins. We will now take a look at each of the Starter Classes that were generated during the project setup. Running the demo application on a desktop The Starter Class for the desktop application is called Main.java. The following listing is Main.java from demo-desktop : package com.packtpub.libgdx.demo; import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; public class Main { public static void main(String[] args) { LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration(); cfg.title = "demo"; cfg.useGL20 = false; cfg.width = 480; cfg.height = 320; new LwjglApplication(new MyDemo(), cfg); } } In the preceding code listing, you see the Main class, a plain Java class without the need to implement an interface or inherit from another class. Instead, a new instance of the LwjglApplication class is created. This class provides a couple of overloaded constructors to choose from. Here, we pass a new instance of the MyDemo class as the first argument to the constructor. Optionally, an instance of the LwjglApplicationConfiguration class can be passed as the second argument. The configuration class allows you to set every parameter that is configurable for a Libgdx desktop application. In this case, the window title is set to demo and the window's width and height is set to 480 by 320 pixels. This is all you need to write and configure a Starter Class for a desktop. Let us try to run the application now. To do this, right-click on the demo-desktop project in Project Explorer in Eclipse and then navigate to Run As | Java Application. Eclipse may ask you to select the Main class when you do this for the first time. Simply select the Main class and also check that the correct package name ( com.packtpub.libgdx.demo ) is displayed next to it. The desktop application should now be up and running on your computer. If you are working on Windows, you should see a window that looks as follows: Summary In this article, we learned about Libgdx and how all the projects of an application work together. We covered Libgdx's backends, modules, and Starter Classes. Additionally, we covered what the Application Life Cycle and corresponding interface are, and how they are meant to work. Resources for Article: Further resources on this subject: Panda3D Game Development: Scene Effects and Shaders [Article] Microsoft XNA 4.0 Game Development: Receiving Player Input [Article] Introduction to Game Development Using Unity 3D [Article]
Read more
  • 0
  • 0
  • 5715

article-image-how-create-openscenegraph-application
Packt
07 Apr 2011
11 min read
Save for later

How to Create an OpenSceneGraph Application

Packt
07 Apr 2011
11 min read
OpenSceneGraph 3.0: Beginner's Guide Constructing your own projects To build an executable program from your own source code, a platform-dependent solution or makefile is always required. At the beginning of this article, we are going to introduce another way to construct platform-independent projects with the CMake system, by which means, we are able to focus on interacting with the code and ignore the painstaking compiling and building process. Time for action – building applications with CMake Before constructing your own project with CMake scripts, it could be helpful to keep the headers and source files together in an empty directory first. The second step is to create a CMakeLists.txt file using any text editor, then and start writing some simple CMake build rules. The following code will implement a project with additional OSG headers and dependency libraries. Please enter them into the newly-created CMakeLists.txt file: cmake_minimum_required( VERSION 2.6 ) project( MyProject ) find_package( OpenThreads ) find_package( osg ) find_package( osgDB ) find_package( osgUtil ) find_package( osgViewer ) macro( config_project PROJNAME LIBNAME ) include_directories( ${${LIBNAME}_INCLUDE_DIR} ) target_link_libraries( ${PROJNAME} ${${LIBNAME}_LIBRARY} ) endmacro() add_executable( MyProject main.cpp ) config_project( MyProject OPENTHREADS ) config_project( MyProject OSG ) config_project( MyProject OSGDB ) config_project( MyProject OSGUTIL ) config_project( MyProject OSGVIEWER ) We have only added a main.cpp source file here, which is made up of the "Hello World" example and will be compiled to generate an executable file named MyProject. This small project depends on five major OSG components. All of these configurations can be modified to meet certain requirements and different user applications. Next, start cmake-gui and drag your CMakeLists.txt into the GUI. You may not be familiar with the CMake scripts to be executed, at present. However, the CMake wiki will be helpful for further understanding: http://www.cmake.org/Wiki/CMake. Create and build a Visual Studio solution or a makefile. The only point is that you have to ensure that your CMake software version is equal to or greater than 2.6, and make sure you have the OSG_ROOT environment variable set. Otherwise, the find_package() macro may not be able to find OSG installations correctly. The following image shows the unexpected errors encountered because OSG headers and libraries were not found in the path indicated by OSG_ROOT (or the variable was just missed): Note that, there is no INSTALL project in the Visual Studio solution, or any make install command to run at this time, because we don't write such CMake scripts for post-build installations. You could just run the executable file in the build directory directly. What just happened? CMake provides easy-to-read commands to automatically find dependencies for user projects. It will check preset directories and environment variables to see if there are any headers and libraries for the required package. The environment variable OSG_ROOT (OSG_DIR is OK, too) will facilitate in looking for OSG under Windows and UNIX, as CMake will first search for valid paths defined in it, and check if there are OSG prebuilt headers and libraries existing in these paths. Have a go hero – testing with different generators Just try a series of tests to generate your project, using Visual Studio, MinGW, and the UNIX gcc compiler. You will find that CMake is a convenient tool for building binary files from source code on different platforms. Maybe this is also a good start to learning programming in a multi-platform style. Using a root node Now we are going to write some code and build it with a self-created CMake script. We will again make a slight change to the frequently-used "Hello World" example. Time for action – improving the "Hello World" example The included headers, <osgDB/ReadFile> and <osgViewer/Viewer>, do not need to be modified. We only add a root variable that provides the runtime access to the Cessna model and assigns it to the setSceneData() method. In the main entry, record the Cessna model with a variable named root: osg::ref_ptr<osg::Node> root = osgDB::readNodeFile("cessna.osg"); osgViewer::Viewer viewer; viewer.setSceneData( root.get() ); return viewer.run(); Build and run it at once: You will see no difference between this example and the previous "Hello World". So what actually happened? What just happened? In this example, we introduced two new OSG classes: osg::ref_ptr<> and osg::Node. The osg::Node class represents the basic element of a scene graph. The variable root stands for the root node of a Cessna model, which is used as the scene data to be visualized. Meanwhile, an instance of the osg::ref_ptr<> class template is created to manage the node object. It is a smart pointer, which provides additional features for the purpose of efficient memory management. Understanding memory management In a typical programming scenario, the developer should create a pointer to the root node, which directly or indirectly manages all other child nodes of the scene graph. In that case, the application will traverse the scene graph and delete each node and its internal data carefully when they no longer need to be rendered. This process is tiresome and error-prone, debugging dozens of bad trees and wild pointers, because developers can never know how many other objects still keep a pointer to the one being deleted. However without writing the management code, data segments occupied by all scene nodes will never be deleted, which will lead to unexpected memory leaks. This is why memory management is important in OSG programming. A basic concept of memory management always involves two topics: Allocation: Providing the memory needed by an object, by allocating the required memory block. Deallocation: Recycling the allocated memory for reuse, when its data is no longer used. Some modern languages, such as C#, Java, and Visual Basic, use a garbage collector to free memory blocks that are unreachable from any program variables. That means to store the number of objects reaching a memory block, and deallocate the memory when the number decrements to zero. The standard C++ approach does not work in such a way, but we can mimic it by means of a smart pointer, which is defined as an object that acts like a pointer, but is much smarter in the management of memory. For example, the boost library provides the boost::shared_ptr<> class template to store pointers in order to dynamically allocated related objects. ref_ptr<> and Referenced classes Fortunately, OSG also provides a native smart pointer, osg::ref_ptr<>, for the purpose of automatic garbage collection and deallocation. To make it work properly, OSG also provides the osg::Referenced class to manage reference-counted memory blocks, which is used as the base class of any classes that may serve as the template argument. The osg::ref_ptr<> class template re-implements a number of C++ operators as well as member functions, and thus provides convenient methods to developers. Its main components are as follows: get(): This public method returns the managed pointer, for instance, the osg::Node* pointer if you are using osg::Node as the template argument. operator*(): This is actually a dereference operator, which returns l-value at the pointer address, for instance, the osg::Node& reference variable. operator->() and operator=(): These operators allow a user application to use osg::ref_ptr<> as a normal pointer. The former calls member functions of the managed object, and the latter replaces the current managed pointer with a new one. operator==(), operator!=(), and operator!(): These operators help to compare smart pointers, or check if a certain pointer is invalid. An osg::ref_ptr<> object with NULL value assigned or without any assignment is considered invalid. valid(): This public method returns true if the managed pointer is not NULL. The expression some_ptr.valid() equals to some_ptr!=NULL if some_ptr is defined as a smart pointer. release(): This public method is useful when returning the managed address from a function. The osg::Referenced class is the pure base class of all elements in a scene graph, such as nodes, geometries, rendering states, and any other allocatable scene objects. The osg::Node class actually inherits from osg::Referenced indirectly. This is the reason why we program as follows: osg::ref_ptr<osg::Node> root; The osg::Referenced class contains an integer number to handle the memory block allocated. The reference count is initialized to 0 in the class constructor, and will be increased by 1 if the osg::Referenced object is referred to by an osg::ref_ptr<> smart pointer. On the contrary, the number will be decreased by 1 if the object is removed from a certain smart pointer. The object itself will be automatically destroyed when no longer referenced by any smart pointers. The osg::Referenced class provides three main member methods: The public method ref() increases the referenced counting number by 1 The public method unref() decreases the referenced counting number by 1 The public method referenceCount() returns the value of the current referenced counting number, which is useful for code debugging These methods could also work for classes that are derived from osg::Referenced. Note that it is very rarely necessary to call ref() or unref() directly in user programs, which means that the reference count is managed manually and may conflict with what the osg::ref_ptr<> is going to do. Otherwise, OSG's internal garbage collecting system will get the wrong number of smart pointers in use and even crash when managing memory blocks in an improper way. Collecting garbage: why and how Here are some reasons for using smart pointers and the garbage collection system in programming: Fewer bugs: Using smart pointers means the automatic initialization and cleanup of pointers. No dangling pointers will be created because they are always reference-counted. Efficient management: Objects will be reclaimed as soon as they are no longer referenced, which gives more available memory to applications with limited resources. Easy to debug: We can easily obtain the referenced counting number and other information on objects, and then apply other optimizations and experiments. For instance, a scene graph tree is composed by a root node and multiple levels of child nodes. Assuming that all children are managed with osg::ref_ptr<>, user applications may only keep the pointer to the root node. As is illustrated by the following image, the operation of deleting the root node pointer will cause a cascading effect that will destroy the whole node hierarchy: Each node in the example scene graph is managed by its parent, and will automatically be unreferenced during the deletion of the parent node. This node, if no longer referenced by any other nodes, will be destroyed immediately, and all of its children will be freed up. The entire scene graph will finally be cleaned without worries after the last group node or leaf node is deleted. The process is really convenient and efficient, isn't it? Please make sure the OSG smart pointer can work for you, and use a class derived from osg::Referenced as the osg::ref_ptr<> template argument, and correctly assign newly-allocated objects to smart pointers. A smart pointer can be used either as a local variable, a global variable, or a class member variable, and will automatically decrease the referenced counting number when reassigned to another object or moved out of the smart pointer's declaration scope. It is strongly recommended that user applications always use smart pointers to manage their scenes, but there are still some issues that need special attention: osg::Referenced and its derivatives should be created from the heap only. They cannot be used as local variables because class destructors are declared protected internally for safety. For example: osg::ref_ptr<osg::Node> node = new osg::Node; // this is legal osg::Node node; // this is illegal! A regular C++ pointer is still workable temporarily. But user applications should remember to assign it to osg::ref_ptr<> or add it to a scene graph element (almost all OSG scene classes use smart pointers to manage child objects) in the end, as it is always the safest approach. osg::Node* tmpNode = new osg::Node; // this is OK ... osg::ref_ptr<osg::Node> node = tmpNode; // Good finish! Don't play with reference cycles, as the garbage collecting mechanism cannot handle it. A reference cycle means that an object refers to itself directly or indirectly, which leads to an incorrect calculation of the referenced counting number. The scene graph shown in the following image contains two kinds of reference cycles, which are both invalid. The node Child 1.1 directly adds itself as the child node and will form a dead cycle while traversing to its children, because it is the child of itself, too! The node Child 2.2, which also makes a reference cycle indirectly, will cause the same problem while running: Now let's have a better grasp of the basic concepts of memory management, through a very simple example.  
Read more
  • 0
  • 0
  • 5713

article-image-using-jquery-script-creating-dynamic-table-contents
Packt
21 Oct 2009
6 min read
Save for later

Using jQuery Script for Creating Dynamic Table of Contents

Packt
21 Oct 2009
6 min read
  A typical jQuery script uses a wide assortment of the methods that the library offers. Selectors, DOM manipulation, event handling, and so forth come into play as required by the task at hand. In order to make the best use of jQuery, we need to keep in mind the wide range of capabilities it provides. A Dynamic Table of Contents As an example of jQuery in action, we'll build a small script that will dynamically extract the headings from an HTML document and assemble them into a table of contents for that page. Our table of contents will be nestled on the top right corner of the page: We'll have it collapsed initially as shown above, but a click will expand it to full height: At the same time, we'll add a feature to the main body text. The introduction of the text on the page will not be initially loaded, but when the user clicks on the word Introduction, the introductory text will be inserted in place from another file: Before we reveal the script that performs these tasks, we should walk through the environment in which the script resides. Obtaining jQuery The official jQuery website (http://jquery.com/) is always the most up-to-date resource for code and news related to the library. To get started, we need a copy of jQuery, which can be downloaded right from the home page of the site. Several versions of jQuery may be available at any given moment; the latest uncompressed version will be most appropriate for us. No installation is required for jQuery. To use jQuery, we just need to reside it on our site in a public location. Since JavaScript is an interpreted language, there is no compilation or build phase to worry about. Whenever we need a page to have jQuery available, we will simply refer to the file's location from the HTML document. Setting Up the HTML Document There are three sections to most examples of jQuery usage— the HTML document itself, CSS files to style it, and JavaScript files to act on it. For this example, we'll use a page containing the text of a book: <?xml version="1.0" encoding="UTF-8" ?><!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"/>      <title>Doctor Dolittle</title>    <link rel="stylesheet" href="dolittle.css" type="text/css" />      <script src="jquery.js" type="text/javascript"></script>      <script src="dolittle.js" type="text/javascript"></script>  </head>  <body>    <div id="container">      <h1>Doctor Dolittle</h1>      <div class="author">by Hugh Lofting</div>      <div id="introduction">        <h2><a href="introduction.html">Introduction</a></h2>      </div>      <div id="content">        <h2>Puddleby</h2>        <p>ONCE upon a time, many years ago when our grandfathers           were little children--there was a doctor; and his name was           Dolittle-- John Dolittle, M.D.  &quot;M.D.&quot; means            that he was a proper doctor and knew a whole lot.       </p>           <!-- More text follows... -->      </div>    </div>  </body></html> The actual layout of files on the server does not matter. References from one file to another just need to be adjusted to match the organization we choose. In most examples in this book, we will use relative paths to reference files (../images/foo.png) rather than absolute paths (/images/foo.png).This will allow the code to run locally without the need for a web server. The stylesheet is loaded immediately after the standard <head> elements. Here are the portions of the stylesheet that affect our dynamic elements: /* -----------------------------------   Page Table of Contents-------------------------------------- */#page-contents {  position: absolute;  text-align: left;  top: 0;  right: 0;  width: 15em;  border: 1px solid #ccc;  border-top-width: 0;  border-right-width: 0;  background-color: #e3e3e3;}#page-contents h3 {  margin: 0;  padding: .25em .5em .25em 15px;  background: url(arrow-right.gif) no-repeat 0 2px;  font-size: 1.1em;  cursor: pointer;}#page-contents h3.arrow-down {  background-image: url(arrow-down.gif);}#page-contents a {  display: block;  font-size: 1em;  margin: .4em 0;  font-weight: normal;}#page-contents div {  padding: .25em .5em .5em;    display: none;  background-color: #efefef;}/* -----------------------------------   Introduction-------------------------------------- */.dedication {  margin: 1em;  text-align: center;  border: 1px solid #555;  padding: .5em;} After the stylesheet is referenced, the JavaScript files are included. It is important that the script tag for the jQuery library be placed before the tag for our custom scripts; otherwise, the jQuery framework will not be available when our code attempts to reference it.  
Read more
  • 0
  • 0
  • 5707

article-image-simple-slack-websocket-integrations-10-lines-python
Bradley Cicenas
09 Sep 2016
3 min read
Save for later

Simple Slack Websocket Integrations in <10 lines of Python

Bradley Cicenas
09 Sep 2016
3 min read
If you use Slack, you've probably added a handful of integrations for your team from the ever-growing App Directory, and maybe even had an idea for your own Slack app. While the Slack API is featureful and robust, writing your own integration can be exceptionally easy. Through the Slack RTM (Real Time Messaging) API, you can write our own basic integrations in just a few lines of Python using the SlackSocket library. Want an accessible introduction to Python that's comprehensive enough to give you the confidence you need to dive deeper? This week, follow our Python Fundamentals course inside Mapt. It's completely free - so what have you got to lose? Structure Our integration will be structured with the following basic components: Listener Integration/bot logic Response The listener watches for one or more pre-defined "trigger" words, while the response posts the result of our intended task. Basic Integration We'll start by setting up SlackSocket with our API token: fromslacksocketimportSlackSocket slack=SlackSocket('<slack-token>', event_filter=['message']) By default, SlackSocket will listen for all Slack events. There are a lot of different events sent via RTM, but we're only concerned with 'message' events for our integration, so we've set an event_filter for only this type. Using the SlackSocketevents() generator, we'll read each 'message' event that comes in and can act on various conditions: for e inslack.events(): ife.event['text'] =='!hello': slack.send_msg('it works!', channel_name=e.event['channel']) If our message text matches the string '!hello', we'll respond to the source channel of the event with a given message('it works!'). At this point, we've created a complete integration that can connect to Slack as a bot user(or regular user), follow messages, and respond accordingly. Let's build something a bit more useful, like a password generator for throwaway accounts. Expanding Functionality For this integration command, we'll write a simple function to generate a random alphanumeric string 15 characters long: import random import string defrandomstr(): chars=string.ascii_letters+string.digits return''.join(random.choice(chars) for _ inrange(15)) Now we're ready to provide our random string generator to the rest of the team using the same chat logic as before, responding to the source channel with our generated password: for e inslack.events(): e.event['text'].startswith('!random'): slack.send_msg(randomstr(), channel_name=e.event['channel']) Altogether: import random import string fromslacksocketimportSlackSocket slack=SlackSocket('<slack-token>', event_filter=['message']) defrandomstr(): chars=string.ascii_letters+string.digits return''.join(random.choice(chars) for _ inrange(15)) for e inslack.events(): ife.event['text'].startswith('!random'): slack.send_msg(randomstr(), channel_name=e.event['channel']) And the results:  A complete integration in 10 lines of Python. Not bad! Beyond simplicity, SlackSocket provides a great deal of flexibility for writing apps, bots, or integrations. In the case of massive Slack groups with several thousand users, messages are buffered locally to ensure that none are missed. Dropped websocket connections are automatically re-connected as well, making it an ideal base for chat client. The code for SlackSocket is available on GitHub, and as always, we welcome any contributions or feature requests! About the author Bradley Cicenas is a New York City-based infrastructure engineer with an affinity for microservices, systems design, data science, and stoops.
Read more
  • 0
  • 0
  • 5703

article-image-recursive-queries
Packt
06 Sep 2013
5 min read
Save for later

Recursive queries

Packt
06 Sep 2013
5 min read
(For more resources related to this topic, see here.) XML is one solution to dealing with hierarchical data, but it isn't the most natural for the relational database. Instead, you often wind up with nested categories, or filesystem-like folder hierarchies, or links back to older records. A popular way to structure a relational database for data with this shape is using a self reference, a link back to a parent object in the same table.For instance, you might want to model categories that can have subcategories with arbitrary nesting. A simple table might look like this: CREATE TABLE categories ( id SERIAL PRIMARY KEY, name VARCHAR, parent_id INTEGER REFERENCES categories); What makes this structure recursive is that self-referencing parent_id column, which refers to the table we're defining from within its own definition. We would treat categories with a NULL value for the parent_id column as top-level categories, categories that do not belong within any other category. To get a feel for the kind of data, let's put a few categories in there for an online retailer. Say we sell shirts and books, and books we further divide into fiction and non-fiction, and then we'll put programming books inside non-fiction. It might look like the following: INSERT INTO categories (id, name, parent_id) VALUES(1, 'Shirts', NULL),(2, 'Books', NULL),(3, 'Fiction', 2),(4, 'Non-fiction', 2),(5, 'Programming', 4); Usually you won't put these in manually like this, but you can see that Fiction and Non-fiction are children of the Books category because their parent_id values are 2, the value for Books is NULL and Programming has the parent_id value as 4, which is the id value for Non-fiction. Suppose you want to make navigation breadcrumbs given a category. You need to look up that category and then you need to look up the parent category. You need to do this until the parent category is NULL. In a procedural pseudocode, the process might look like the following: def write_breadcrumbs(category): row = getOne("SELECT * FROM categories WHERE id = ?", category) while row['parent_id'] != NULL: write(row['name']) write(' > ') row = getOne("SELECT * FROM categories WHERE id = ?", row['parent_id']) This kind of solution leads to the N+1 query problem. There's an action you want to take for a particular value, but to take that action you have to run an arbitrary number of separate queries. Recursive queries in PostgreSQL provide you with a way to have the database do the heavy lifting instead. Because we'll be using recursion in the SQL, let's first see what a recursion formulation would look like in our pseudocode: def write_breadcrumbs(category): row = getOne("SELECT * FROM categories WHERE id = ?", category) write(row['name']) if row['parent_id'] != NULL: write(' > ') write_breadcrumbs(row['parent_id']) It's debatable whether this is better code; it's shorter, and it has fewer bugs, but it also might expose the developer to the possibility of stack overflows. Recursive functions always have some similar structure though, some number of base cases that do not call the function recursively and some number of inductive cases that work by calling the same function again on slightly different data. The final destination will be one of the base cases. The inductive cases will peel off a small piece of the problem and solve it, then delegate the rest of the work to an invocation of the same function. PostgreSQL's recursive query support works with something called the common table expressions (CTEs). The idea is to make a named alias for a query. We won't delve into the details too much here, but all recursive queries will have the same basic structure: WITH RECURSIVE recursive-query-name AS ( SELECT <base-case> FROM table UNION ALL SELECT <inductive-case> FROM table JOIN <recursive-query-name> ON )SELECT * FROM <recursive-query-name>; For an example, let's get all the categories above Programming. The base case will be the Programming category itself. The inductive case will be to find the parent of a category we've already seen: WITH RECURSIVE programming_parents AS ( SELECT * FROM categories WHERE id = 5 UNION ALL SELECT categories.* FROM categories JOIN programming_parents ON programming_parents.parent_id = categories.id)SELECT * FROM programming_parents; This works as we'd hope. Output of a simple recursive query Without using this trick we'd have to do three separate queries to get this information, but with the trick it will always take one query no matter how deeply nested the categories are. We can also go in the other direction and build up something like a tree underneath a category by searching for categories that have a category we've already seen as a parent category. We can make the hierarchy more explicit by building up a path as we go: WITH RECURSIVE all_categories AS ( SELECT *, name as path FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.*, p.path || '/' || c.name FROM categories AS c JOIN all_categories p ON p.id = c.parent_id)SELECT * FROM all_categories; Finding all the categories with their path We can be more discriminating with the search by looking for a particular category as the starting value in the base case: WITH RECURSIVE all_categories AS ( SELECT *, name as path FROM categories WHERE id = 2 UNION ALL SELECT c.*, p.path || '/' || c.name FROM categories AS c JOIN all_categories p ON p.id = c.parent_id)SELECT * FROM all_categories; The category tree rooted at Books This is hugely useful with hierarchical data. Summary In this article we learned about recursive queries as one of the most important features of PostgreSQL. Resources for Article: Further resources on this subject: Introduction to PostgreSQL 9 [Article] PostgreSQL: Tips and Tricks [Article] Obtaining a binary backup [Article]
Read more
  • 0
  • 0
  • 5701

article-image-packt-book-review-challenge
Packt
27 Apr 2011
2 min read
Save for later

Packt book review challenge

Packt
27 Apr 2011
2 min read
Can I participate in this book review writing challenge? Yes you can as long as you have your own blog.   How do I sign up? That's easy. No registration formalities are required. There’s no need to fill in a form, or even to give us your email address. Once you have reviewed your Packt book on your blog simply tweet the review URL @ReviewPacktOS if you have reviewed an Open Source book or @ReviewPacktENT if you have reviewed an Enterprise title. If you’re unsure of the brand of your book, just tweet either account.   Are there any rules? There aren’t any rules as such, but there are a few guidelines which we suggest our reviewers follow: - Be honest! If it’s a good book, say so. If it’s a bad book, tell us - Reviews should be between 350 and 750 words - Include an introductory section, about 100-125 words long that indicates the nature and the title of the book clearly - On your first reference to the book, use the complete title, well punctuated, as it appears on the book cover - Add a link to the book's web page, preferably in the introductory or middle paragraph, allowing more of your readers to find the book easily - If available, you can include a link to a sample chapter, which can be found on the book's web page.   What do I win? The book reviews will be judged by our team of experts who will select a winner and a runner up. First prize: A year long subscription to PacktLib worth £150 absolutely free. Second prize: Any Packt print book of your choice.   Hurry!! The competition is open from the 1st of June till the 30th of June. All reviews must be posted within this period to be considered valid. Winners will be announced on the 6th of July.   For a list of our titles, please visit our homepage.  If you have any queries, feel free to contact us: reviewrequest@packtpub.com          
Read more
  • 0
  • 1
  • 5700
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 $19.99/month. Cancel anytime
article-image-reversing-android-applications
Packt
20 Mar 2014
8 min read
Save for later

Reversing Android Applications

Packt
20 Mar 2014
8 min read
(For more resources related to this topic, see here.) Android application teardown An Android application is an archive file of the data and resource files created while developing the application. The extension of an Android application is .apk, meaning application package, which includes the following files and folders in most cases: Classes.dex (file) AndroidManifest.xml (file) META-INF (folder) resources.arsc (file) res (folder) assets (folder) lib (folder) In order to verify this, we could simply unzip the application using any archive manager application, such as 7zip, WinRAR, or any preferred application. On Linux or Mac, we could simply use the unzip command in order to show the contents of the archive package, as shown in the following screenshot: Here, we have used the -l (list) flag in order to simply show the contents of the archive package instead of extracting it. We could also use the file command in order to see whether it is a valid archive package. An Android application consists of various components, which together create the working application. These components are Activities, Services, Broadcast Receivers, Content providers, and Shared Preferences. Before proceeding, let's have a quick walkthrough of what these different components are all about: Activities: These are the visual screens which a user could interact with. These may include buttons, images, TextView, or any other visual component. Services: These are the Android components which run in the background and carry out specific tasks specified by the developer. These tasks may include anything from downloading a file over HTTP to playing music in the background. Broadcast Receivers: These are the receivers in the Android application that listen to the incoming broadcast messages by the Android system, or by other applications present in the device. Once they receive a broadcast message, a particular action could be triggered depending on the predefined conditions. The conditions could range from receiving an SMS, an incoming phone call, a change in the power supply, and so on. Shared Preferences: These are used by an application in order to save small sets of data for the application. This data is stored inside a folder named shared_prefs. These small datasets may include name value pairs such as the user's score in a game and login credentials. Storing sensitive information in shared preferences is not recommended, as they may fall vulnerable to data stealing and leakage. Intents: These are the components which are used to bind two or more different Android components together. Intents could be used to perform a variety of tasks, such as starting an action, switching activities, and starting services. Content Providers: These are used to provide access to a structured set of data to be used by the application. An application can access and query its own data or the data stored in the phone using the Content Providers. Now that we know of the Android application internals and what an application is composed of, we can move on to reversing an Android application. That is getting the readable source code and other data sources when we just have the .apk file with us. Reversing an Android application As we discussed earlier, that Android applications are simply an archive file of data and resources. Even then, we can't simply unzip the archive package (.apk) and get the readable sources. For these scenarios, we have to rely on tools that will convert the byte code (as in classes.dex) into readable source code. One of the approaches to convert byte codes to readable files is using a tool called dex2jar. The .dex file is the converted Java bytecode to Dalvik bytecode, making it optimized and efficient for mobile platforms. This free tool simply converts the .dex file present in the Android application to a corresponding .jar file. Please follow the ensuing steps: Download the dex2jar tool from https://code.google.com/p/dex2jar/. Now we can use it to run against our application's .dex file and convert to .jar format. Now, all we need to do is go to the command prompt and navigate to the folder where dex2jar is located. Next, we need to run the d2j-dex2jar.bat file (on Windows) or the d2j-dex2jar.sh file (on Linux/Mac) and provide the application name and path as the argument. Here in the argument, we could simply use the .apk file, or we could even unzip the .apk file and then pass the classes.dex file instead, as shown in the following screenshot: As we can see in the preceding screenshot, dex2jar has successfully converted the .dex file of the application to a .jar file named helloworld-dex2jar.jar. Now, we can simply open this .jar file in any Java graphical viewer such as JD-GUI, which can be downloaded from its official website at http://jd.benow.ca/. Once we download and install JD-GUI, we could now go ahead and open it. It will look like the one shown in the following screenshot: Here, we could now open up the converted .jar file from the earlier step and see all the Java source code in JD-GUI. To open a .jar file, we could simply navigate to File | Open. In the right-hand side pane, we can see the Java sources and all the methods of the Android application. Note that the recompilation process will give you an approximate version of the original Java source code. This won't matter in most cases; however, in some cases, you might see that some of the code is missing from the converted .jar file. Also, if the application developer is using some protections against decompilations such as proguard and dex2jar, when we decompile the application using dex2jar or Apktool, we won't be seeing the exact source code; instead, we will see a bunch of different source files, which won't be the exact representation of the original source code. Using Apktool to reverse an Android application Another way of reversing an Android application is converting the .dex file to smali files. A smali is a file format whose syntax is similar to a language known as Jasmine. We won't be going in depth into the smali file format as of now. For more information, take a look at the online wiki at https://code.google.com/p/smali/wiki/ in order to get an in-depth understanding of smali. Once we have downloaded Apktool and configured it, we are all set to go further. The main advantage of Apktool over JD-GUI is that it is bidirectional. This means if you decompile an application and modify it, and then recompile it back using Apktool, it will recompile perfectly and will generate a new .apk file. However, dex2jar and JD-GUI won't be able to do this similar functionality, as it gives an approximate code and not the exact code. So, in order to decompile an application using Apktool, all we need to do is to pass in the .apk filename along with the Apktool binary. Once decompiled, Apktool will create a new folder with the application name in which all of the files will be stored. To decompile, we will simply go ahead and use apktool d [app-name].apk. Here, the -d flag stands for decompilation. In the following screenshot, we can see an app being decompiled using Apktool: Now, if we go inside the smali folder, we will see a bunch of different smali files, which will contain the code of the Java classes that were written while developing the application. Here, we can also open up a file, change the values, and use Apktool to build it back again. To build a modified application from smali, we will use the b (build) flag in Apktool. apktool d [decompiled folder name] [target-app-name].apk However, in order to decompile, modify, and recompile applications, I would personally recommend using another tool called Virtuous Ten Studio (VTS). This tool offers similar functionalities as Apktool, with the only difference that VTS presents it in a nice graphical interface, which is relatively easy to use. The only limitation for this tool is it runs natively only on the Windows environment. We could go ahead and download VTS from the official download link, http://www.virtuous-ten-studio.com/. The following is a screenshot of the application decompiling the same project: Summary In this article, we covered some of the methods and techniques that are used to reverse the Android applications. Resources for Article: Further resources on this subject: Android Native Application API [Article] Introducing an Android platform [Article] Animating Properties and Tweening Pages in Android 3-0 [Article]
Read more
  • 0
  • 0
  • 5700

article-image-data-driven-design
Packt
10 Jul 2013
21 min read
Save for later

Data-driven Design

Packt
10 Jul 2013
21 min read
(For more resources related to this topic, see here.) Loading XML files I have chosen to use XML files because they are so easy to parse. We are not going to write our own XML parser, rather we will use an open source library called TinyXML. TinyXML was written by Lee Thomason and is available under the zlib license from http://sourceforge.net/projects/tinyxml/. Once downloaded the only setup we need to do is to include a few of the files in our project: tinyxmlerror.cpp tinyxmlparser.cpp tinystr.cpp tinystr.h tinyxml.cpp tinyxml.h Also, at the top of tinyxml.h, add this line of code: #define TIXML_USE_STL By doing this we ensure that we are using the STL versions of the TinyXML functions. We can now go through a little of how an XML file is structured. It's actually fairly simple and we will only give a brief overview to help you get up to speed with how we will use it. Basic XML structure Here is a basic XML file: <?xml version="1.0" ?> <ROOT> <ELEMENT> </ELEMENT> </ROOT> The first line of the file defines the format of the XML file. The second line is our Root element; everything else is a child of this element. The third line is the first child of the root element. Now let's look at a slightly more complicated XML file: <?xml version="1.0" ?> <ROOT> <ELEMENTS> <ELEMENT>Hello,</ELEMENT> <ELEMENT> World!</ELEMENT> </ELEMENTS> </ROOT> As you can see we have now added children to the first child element. You can nest as many children as you like. But without a good structure, your XML file may become very hard to read. If we were to parse the above file, here are the steps we would take: Load the XML file. Get the root element, <ROOT>. Get the first child of the root element, <ELEMENTS>. For each child, <ELEMENT> of <ELEMENTS>, get the content. Close the file. Another useful XML feature is the use of attributes. Here is an example: <ROOT> <ELEMENTS> <ELEMENT text="Hello,"/> <ELEMENT text=" World!"/> </ELEMENTS> </ROOT> We have now stored the text we want in an attribute named text. When this file is parsed, we would now grab the text attribute for each element and store that instead of the content between the <ELEMENT></ELEMENT> tags. This is especially useful for us as we can use attributes to store lots of different values for our objects. So let's look at something closer to what we will use in our game: <?xml version="1.0" ?> <STATES> <!--The Menu State--> <MENU> <TEXTURES> <texture filename="button.png" ID="playbutton"/> <texture filename="exit.png" ID="exitbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton"/> </OBJECTS> </MENU> <!--The Play State--> <PLAY> </PLAY> <!-- The Game Over State --> <GAMEOVER> </GAMEOVER> </STATES> This is slightly more complex. We define each state in its own element and within this element we have objects and textures with various attributes. These attributes can be loaded in to create the state. With this knowledge of XML you can easily create your own file structures if what we cover within this book is not to your needs. Implementing Object Factories We are now armed with a little XML knowledge but before we move forward, we are going to take a look at Object Factories. An object factory is a class that is tasked with the creation of our objects. Essentially, we tell the factory the object we would like it to create and it goes ahead and creates a new instance of that object and then returns it. We can start by looking at a rudimentary implementation: GameObject* GameObjectFactory::createGameObject(ID id) { switch(id) { case "PLAYER": return new Player(); break; case "ENEMY": return new Enemy(); break; // lots more object types } } This function is very simple. We pass in an ID for the object and the factory uses a big switch statement to look it up and return the correct object. Not a terrible solution but also not a particularly good one, as the factory will need to know about each type it needs to create and maintaining the switch statement for many different objects would be extremely tedious. We want this factory not to care about which type we ask for. It shouldn't need to know all of the specific types we want it to create. Luckily this is something that we can definitely achieve. Using Distributed Factories Through the use of Distributed Factories we can make a generic object factory that will create any of our types. Distributed factories allow us to dynamically maintain the types of objects we want our factory to create, rather than hard code them into a function (like in the preceding simple example). The approach we will take is to have the factory contain std::map that maps a string (the type of our object) to a small class called Creator whose only purpose is the creation of a specific object. We will register a new type with the factory using a function that takes a string (the ID) and a Creator class and adds them to the factory's map. We are going to start with the base class for all the Creator types. Create GameObjectFactory.h and declare this class at the top of the file. #include <string> #include <map> #include "GameObject.h" class BaseCreator { public: virtual GameObject* createGameObject() const = 0; virtual ~BaseCreator() {} }; We can now go ahead and create the rest of our factory and then go through it piece by piece. class GameObjectFactory { public: bool registerType(std::string typeID, BaseCreator* pCreator) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); // if the type is already registered, do nothing if(it != m_creators.end()) { delete pCreator; return false; } m_creators[typeID] = pCreator; return true; } GameObject* create(std::string typeID) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); if(it == m_creators.end()) { std::cout << "could not find type: " << typeID << "n"; return NULL; } BaseCreator* pCreator = (*it).second; return pCreator->createGameObject(); } private: std::map<std::string, BaseCreator*> m_creators; }; This is quite a small class but it is actually very powerful. We will cover each part separately starting with std::map m_creators. std::map<std::string, BaseCreator*> m_creators; This map holds the important elements of our factory, the functions of the class essentially either add or remove from this map. This becomes apparent when we look at the registerType function: bool registerType(std::string typeID, BaseCreator* pCreator) This function takes the ID we want to associate the object type with (as a string), and the creator object for that class. The function then attempts to find the type using the std::mapfind function: std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); If the type is found then it is already registered. The function then deletes the passed in pointer and returns false: if(it != m_creators.end()) { delete pCreator; return false; } If the type is not already registered then it can be assigned to the map and then true is returned: m_creators[typeID] = pCreator; return true; } As you can see, the registerType function is actually very simple; it is just a way to add types to the map. The create function is very similar: GameObject* create(std::string typeID) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); if(it == m_creators.end()) { std::cout << "could not find type: " << typeID << "n"; return 0; } BaseCreator* pCreator = (*it).second; return pCreator->createGameObject(); } The function looks for the type in the same way as registerType does, but this time it checks whether the type was not found (as opposed to found). If the type is not found we return 0, and if the type is found then we use the Creator object for that type to return a new instance of it as a pointer to GameObject. It is worth noting that the GameObjectFactory class should probably be a singleton. We won't cover how to make it a singleton in this article. Try implementing it yourself or see how it is implemented in the source code download. Fitting the factory into the framework With our factory now in place, we can start altering our GameObject classes to use it. Our first step is to ensure that we have a Creator class for each of our objects. Here is one for Player: class PlayerCreator : public BaseCreator { GameObject* createGameObject() const { return new Player(); } }; This can be added to the bottom of the Player.h file. Any object we want the factory to create must have its own Creator implementation. Another addition we must make is to move LoaderParams from the constructor to their own function called load. This stops the need for us to pass the LoaderParams object to the factory itself. We will put the load function into the GameObject base class, as we want every object to have one. class GameObject { public: virtual void draw()=0; virtual void update()=0; virtual void clean()=0; // new load function virtual void load(const LoaderParams* pParams)=0; protected: GameObject() {} virtual ~GameObject() {} }; Each of our derived classes will now need to implement this load function. The SDLGameObject class will now look like this: SDLGameObject::SDLGameObject() : GameObject() { } voidSDLGameObject::load(const LoaderParams *pParams) { m_position = Vector2D(pParams->getX(),pParams->getY()); m_velocity = Vector2D(0,0); m_acceleration = Vector2D(0,0); m_width = pParams->getWidth(); m_height = pParams->getHeight(); m_textureID = pParams->getTextureID(); m_currentRow = 1; m_currentFrame = 1; m_numFrames = pParams->getNumFrames(); } Our objects that derive from SDLGameObject can use this load function as well; for example, here is the Player::load function: Player::Player() : SDLGameObject() { } void Player::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); } This may seem a bit pointless but it actually saves us having to pass through LoaderParams everywhere. Without it, we would need to pass LoaderParams through the factory's create function which would then in turn pass it through to the Creator object. We have eliminated the need for this by having a specific function that handles parsing our loading values. This will make more sense once we start parsing our states from a file. We have another issue which needs rectifying; we have two classes with extra parameters in their constructors (MenuButton and AnimatedGraphic). Both classes take an extra parameter as well as LoaderParams. To combat this we will add these values to LoaderParams and give them default values. LoaderParams(int x, int y, int width, int height, std::string textureID,int numFrames, int callbackID = 0, int animSpeed = 0) : m_x(x), m_y(y), m_width(width), m_height(height), m_textureID(textureID), m_numFrames(numFrames), m_callbackID(callbackID), m_animSpeed(animSpeed) { } In other words, if the parameter is not passed in, then the default values will be used (0 in both cases). Rather than passing in a function pointer as MenuButton did, we are using callbackID to decide which callback function to use within a state. We can now start using our factory and parsing our states from an XML file. Parsing states from an XML file The file we will be parsing is the following (test.xml in source code downloads): <?xml version="1.0" ?> <STATES> <MENU> <TEXTURES> <texture filename="assets/button.png" ID="playbutton"/> <texture filename="assets/exit.png" ID="exitbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton" numFrames="0" callbackID="2"/> </OBJECTS> </MENU> <PLAY> </PLAY> <GAMEOVER> </GAMEOVER> </STATES> We are going to create a new class that parses our states for us called StateParser. The StateParser class has no data members, it is to be used once in the onEnter function of a state and then discarded when it goes out of scope. Create a StateParser.h file and add the following code: #include <iostream> #include <vector> #include "tinyxml.h" class GameObject; class StateParser { public: bool parseState(const char* stateFile, std::string stateID, std::vector<GameObject*> *pObjects); private: void parseObjects(TiXmlElement* pStateRoot, std::vector<GameObject*> *pObjects); void parseTextures(TiXmlElement* pStateRoot, std::vector<std::string> *pTextureIDs); }; We have three functions here, one public and two private. The parseState function takes the filename of an XML file as a parameter, along with the current stateID value and a pointer to std::vector of GameObject* for that state. The StateParser.cpp file will define this function: bool StateParser::parseState(const char *stateFile, string stateID, vector<GameObject *> *pObjects, std::vector<std::string> *pTextureIDs) { // create the XML document TiXmlDocument xmlDoc; // load the state file if(!xmlDoc.LoadFile(stateFile)) { cerr << xmlDoc.ErrorDesc() << "n"; return false; } // get the root element TiXmlElement* pRoot = xmlDoc.RootElement(); // pre declare the states root node TiXmlElement* pStateRoot = 0; // get this states root node and assign it to pStateRoot for(TiXmlElement* e = pRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == stateID) { pStateRoot = e; } } // pre declare the texture root TiXmlElement* pTextureRoot = 0; // get the root of the texture elements for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("TEXTURES")) { pTextureRoot = e; } } // now parse the textures parseTextures(pTextureRoot, pTextureIDs); // pre declare the object root node TiXmlElement* pObjectRoot = 0; // get the root node and assign it to pObjectRoot for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("OBJECTS")) { pObjectRoot = e; } } // now parse the objects parseObjects(pObjectRoot, pObjects); return true; } There is a lot of code in this function so it is worth covering in some depth. We will note the corresponding part of the XML file, along with the code we use, to obtain it. The first part of the function attempts to load the XML file that is passed into the function: // create the XML document TiXmlDocument xmlDoc; // load the state file if(!xmlDoc.LoadFile(stateFile)) { cerr << xmlDoc.ErrorDesc() << "n"; return false; } It displays an error to let you know what happened if the XML loading fails. Next we must grab the root node of the XML file: // get the root element TiXmlElement* pRoot = xmlDoc.RootElement(); // <STATES> The rest of the nodes in the file are all children of this root node. We must now get the root node of the state we are currently parsing; let's say we are looking for MENU: // declare the states root node TiXmlElement* pStateRoot = 0; // get this states root node and assign it to pStateRoot for(TiXmlElement* e = pRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == stateID) { pStateRoot = e; } } This piece of code goes through each direct child of the root node and checks if its name is the same as stateID. Once it finds the correct node it assigns it to pStateRoot. We now have the root node of the state we want to parse. <MENU> // the states root node Now that we have a pointer to the root node of our state we can start to grab values from it. First we want to load the textures from the file so we look for the <TEXTURE> node using the children of the pStateRoot object we found before: // pre declare the texture root TiXmlElement* pTextureRoot = 0; // get the root of the texture elements for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("TEXTURES")) { pTextureRoot = e; } } Once the <TEXTURE> node is found, we can pass it into the private parseTextures function (which we will cover a little later). parseTextures(pTextureRoot, std::vector<std::string> *pTextureIDs); The function then moves onto searching for the <OBJECT> node and, once found, it passes it into the private parseObjects function. We also pass in the pObjects parameter: // pre declare the object root node TiXmlElement* pObjectRoot = 0; // get the root node and assign it to pObjectRoot for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("OBJECTS")) { pObjectRoot = e; } } parseObjects(pObjectRoot, pObjects); return true; } At this point our state has been parsed. We can now cover the two private functions, starting with parseTextures. void StateParser::parseTextures(TiXmlElement* pStateRoot, std::vector<std::string> *pTextureIDs) { for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { string filenameAttribute = e->Attribute("filename"); string idAttribute = e->Attribute("ID"); pTextureIDs->push_back(idAttribute); // push into list TheTextureManager::Instance()->load(filenameAttribute, idAttribute, TheGame::Instance()->getRenderer()); } } This function gets the filename and ID attributes from each of the texture values in this part of the XML: <TEXTURES> <texture filename="button.png" ID="playbutton"/> <texture filename="exit.png" ID="exitbutton"/> </TEXTURES> It then adds them to TextureManager. TheTextureManager::Instance()->load(filenameAttribute, idAttribute, TheGame::Instance()->getRenderer()); The parseObjects function is quite a bit more complicated. It creates objects using our GameObjectFactory function and reads from this part of the XML file: <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton" numFrames="0" callbackID="2"/> </OBJECTS> The parseObjects function is defined like so: void StateParser::parseObjects(TiXmlElement *pStateRoot, std::vector<GameObject *> *pObjects) { for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { int x, y, width, height, numFrames, callbackID, animSpeed; string textureID; e->Attribute("x", &x); e->Attribute("y", &y); e->Attribute("width",&width); e->Attribute("height", &height); e->Attribute("numFrames", &numFrames); e->Attribute("callbackID", &callbackID); e->Attribute("animSpeed", &animSpeed); textureID = e->Attribute("textureID"); GameObject* pGameObject = TheGameObjectFactory::Instance() ->create(e->Attribute("type")); pGameObject->load(new LoaderParams (x,y,width,height,textureID,numFrames,callbackID, animSpeed)); pObjects->push_back(pGameObject); } } First we get any values we need from the current node. Since XML files are pure text, we cannot simply grab ints or floats from the file. TinyXML has functions with which you can pass in the value you want to be set and the attribute name. For example: e->Attribute("x", &x); This sets the variable x to the value contained within attribute "x". Next comes the creation of a GameObject * class using the factory. GameObject* pGameObject = TheGameObjectFactory::Instance()->create(e->Attribute("type")); We pass in the value from the type attribute and use that to create the correct object from the factory. After this we must use the load function of GameObject to set our desired values using the values loaded from the XML file. pGameObject->load(new LoaderParams(x,y,width,height,textureID,numFrames,callbackID)); And finally we push pGameObject into the pObjects array, which is actually a pointer to the current state's object vector. pObjects->push_back(pGameObject); Loading the menu state from an XML file We now have most of our state loading code in place and can make use of this in the MenuState class. First we must do a little legwork and set up a new way of assigning the callbacks to our MenuButton objects, since this is not something we could pass in from an XML file. The approach we will take is to give any object that wants to make use of a callback an attribute named callbackID in the XML file. Other objects do not need this value and LoaderParams will use the default value of 0. The MenuButton class will make use of this value and pull it from its LoaderParams, like so: void MenuButton::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); m_callbackID = pParams->getCallbackID(); m_currentFrame = MOUSE_OUT; } The MenuButton class will also need two other functions, one to set the callback function and another to return its callback ID: void setCallback(void(*callback)()) { m_callback = callback;} int getCallbackID() { return m_callbackID; } Next we must create a function to set callbacks. Any state that uses objects with callbacks will need an implementation of this function. The most likely states to have callbacks are menu states, so we will rename our MenuState class to MainMenuState and make MenuState an abstract class that extends from GameState. The class will declare a function that sets the callbacks for any items that need it and it will also have a vector of the Callback objects as a member; this will be used within the setCallbacks function for each state. class MenuState : public GameState { protected: typedef void(*Callback)(); virtual void setCallbacks(const std::vector<Callback>& callbacks) = 0; std::vector<Callback> m_callbacks; }; The MainMenuState class (previously MenuState) will now derive from this MenuState class. #include "MenuState.h" #include "GameObject.h" class MainMenuState : public MenuState { public: virtual void update(); virtual void render(); virtual bool onEnter(); virtual bool onExit(); virtual std::string getStateID() const { return s_menuID; } private: virtual void setCallbacks(const std::vector<Callback>& callbacks); // call back functions for menu items static void s_menuToPlay(); static void s_exitFromMenu(); static const std::string s_menuID; std::vector<GameObject*> m_gameObjects; }; Because MainMenuState now derives from MenuState, it must of course declare and define the setCallbacks function. We are now ready to use our state parsing to load the MainMenuState class. Our onEnter function will now look like this: bool MainMenuState::onEnter() { // parse the state StateParser stateParser; stateParser.parseState("test.xml", s_menuID, &m_gameObjects, &m_textureIDList); m_callbacks.push_back(0); //pushback 0 callbackID start from 1 m_callbacks.push_back(s_menuToPlay); m_callbacks.push_back(s_exitFromMenu); // set the callbacks for menu items setCallbacks(m_callbacks); std::cout << "entering MenuStaten"; return true; } We create a state parser and then use it to parse the current state. We push any callbacks into the m_callbacks array inherited from MenuState. Now we need to define the setCallbacks function: void MainMenuState::setCallbacks(const std::vector<Callback>& callbacks) { // go through the game objects for(int i = 0; i < m_gameObjects.size(); i++) { // if they are of type MenuButton then assign a callback based on the id passed in from the file if(dynamic_cast<MenuButton*>(m_gameObjects[i])) { MenuButton* pButton = dynamic_cast<MenuButton*>(m_gameObjects[i]); pButton->setCallback(callbacks[pButton->getCallbackID()]); } } } We use dynamic_cast to check whether the object is a MenuButton type; if it is then we do the actual cast and then use the objects callbackID as the index into the callbacks vector and assign the correct function. While this method of assigning callbacks could be seen as not very extendable and could possibly be better implemented, it does have a redeeming feature; it allows us to keep our callbacks inside the state they will need to be called from. This means that we won't need a huge header file with all of the callbacks in. One last alteration we need is to add a list of texture IDs to each state so that we can clear all of the textures that were loaded for that state. Open up GameState.h and we will add a protected variable. protected: std::vector<std::string> m_textureIDList; We will pass this into the state parser in onEnter and then we can clear any used textures in the onExit function of each state, like so: // clear the texture manager for(int i = 0; i < m_textureIDList.size(); i++) { TheTextureManager::Instance()-> clearFromTextureMap(m_textureIDList[i]); } Before we start running the game we need to register our MenuButton type with the GameObjectFactory. Open up Game.cpp and in the Game::init function we can register the type. TheGameObjectFactory::Instance()->registerType("MenuButton", new MenuButtonCreator()); We can now run the game and see our fully data-driven MainMenuState.
Read more
  • 0
  • 0
  • 5698

article-image-service-chaining
Packt
29 Oct 2013
5 min read
Save for later

Service Chaining

Packt
29 Oct 2013
5 min read
(For more resources related to this topic, see here.) Service Chaining is one of the common and popular use case for an ESB to support. To cater for a request from the client, the ESB may have to call a chain of business services. This is different from the Scatter and Gather pattern we covered before. The Scatter and Gather EIP explains how to handle a scenario where the incoming request has to be handled by multiple recipients and each recipient will reply back to form an aggregated response. Service Chaining does more intelligent decision making and simply does which is very much similar to service orchestration. Getting ready Let's take TravelManagementService as an example. To cater a travel arrangement request from the client, TravelManagementService has to call AirLineReservationService with the provided dates for travelling. If that succeeds it will call RentACarService and finally it will call the HotelReservationService to reserve a hotel. How to do it... Setup AirLineReservationService, RentACarService, and HotelReservationService. We need to have these business services up and running, so WSO2 ESB can route messages to them. Download AirLineReservationService.aar, RentACarService.aar, and HotelReservationService.aar from SAMPLE-6 and copy those to wso2esb-4.8.0/samples/axis2Server/repository/services/. If there is no services directory create one. Start simple Axis2Server from wso2esb-4.8.0/samples/axis2Server. Validate whether the three business services are up and running by accessing their WSDLs. Start WSO2 ESB and login with username as admin and password also as admin. ESB will start on https://localhost:9443. Get synapse.xml from SAMPLE-6, copy the content of it, go to Main | Manage | Service Bus | Source View, and paste it there, overriding the existing and click on Update. The above will create a proxy service called TravelManagementService in the ESB, which will route messages to the three business services running in simple Axis2Server. Go to Main | Manage | Services | List. You should be able to see TravelManagementService listed there. Now let's test our set up with cURL. $ curl -d @request.xml -H "Content-Type: application/soap+xml action=arrangeMyTravel" http://localhost:8280/services/TravelManagementService You can get request.xml from SAMPLE-6. How it works... Let's have a deep look at the synapse configuration. The following explains key configuration elements: When the request hits inSequence we have to first call the AirLineReservationService and need to process its response. If the response is true, only we will proceed to call RentACarService. The same applies when we call HotelReservationService. <send receive="rentACarSeq"> <endpoint> <address uri="…/AirLineReservationService"/> </endpoint> </send> The receive attribute in the <send/> mediator will make sure the response from calling endpoint goes to the <sequence/> defined with that particular name. In this case, the response here will hit the rentACarSeq sequence. If we do not define this attribute then the response will directly go to the outSequence. The rentACarSeq sequence has to process the response from the AirLineReservationService. If it's false, then it will call the processResponse sequence to send a message back to the client, without further processing. If the response is true, then rentACarSeq will proceed to call RentACarService. <sequence name="rentACarSeq"> <log level="full"/> <switch source="//ns:reserveAirLineResponse/ns:return"> <case regex="false"> <sequence key="processResponse"/> </case> <default> <send receive="reserveHotelSeq"> <endpoint> <address uri="…/RentACarService"/> </endpoint> </send> </default> </switch> </sequence> The processResponse sequence sends the response back to the client. Here we are using the <payloadFactory/> mediator to craft the response. Also when you want to send a response back to the client using the <send/> mediator we need to set the RESPONSE property to true prior to that. <sequence name="processResponse"> <payloadFactory> <format> <res:arrangeMyTravel > <res:return>$1</res:return> </res:arrangeMyTravel> </format> <args> <arg evaluator="xml" expression="$ctx:businessServiceResponse"/> </args> </payloadFactory> <property name="RESPONSE" value="true" scope="default" type="STRING"/> <send/> </sequence> A better approach to send back the response to the client was introduced from WSO2 ESB 4.8.0 onwards. There you need to use the <respond> mediator instead of the above. Summary So now you have a brief idea about WSO2 ESB. We also learnt how to cater a request from the client using Service Chaining. We've also learnt that the ESB removes point-to-point dependencies in your system to build a highly scalable and loosely coupled solution. And lastly, we have learnt the difference between Scatter and Gather EIP and Service Chaining. Resources for Article: Further resources on this subject: BizTalk: The ESB Management Portal [Article] Aggregate Services in ServiceMix JBI ESB [Article] Getting Started with Mule [Article]
Read more
  • 0
  • 0
  • 5687

article-image-creating-and-warping-3d-text-away3d-36
Packt
11 Feb 2011
7 min read
Save for later

Creating and Warping 3D Text with Away3D 3.6

Packt
11 Feb 2011
7 min read
  Away3D 3.6 Essentials The external library, swfvector, is contained in the wumedia package. More information about the swfvector library can be found at http://code.google.com/p/swfvector/. This library was not developed as part of the Away3D engine, but has been integrated since version 2.4 and 3.4, to enable Away3D to provide a way to create and display text 3D objects within the scene. Embedding fonts Creating a text 3D object in Away3D requires a source SWF file with an embedded font. To accommodate this, we will create a very simple application using the Fonts class below. This class embeds a single true-type font called Vera Sans from the Vera.ttf file. When compiled, the resulting SWF file can then be referenced by our Away3D application, allowing the embedded font file to be accessed. When embedding fonts using the Flex 4 SDK, you may need to set the embedAsCFF property to false, like: [Embed(mimeType="application/x-font", source="Vera. ttf", fontName="Vera Sans", embedAsCFF=false)] This is due to the new way fonts can be embedded with the latest versions of the Flex SDK. You can find more information on the embedAsCFF property at http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf6320a-7fea.html. package { import flash.display.Sprite; public class Fonts extends Sprite { [Embed(mimeType="application/x-font", source="Vera.ttf", fontName="Vera Sans")] public var VeraSans:Class; } } The font used here is Bitstream Vera, which can be freely distributed, and can be obtained from http://www.gnome.org/fonts/. However, not all fonts can be freely redistributed, so be mindful of the copyright or license restrictions that may be imposed by a particular font. Displaying text in the scene Text 3D objects are represented by the TextField3D class, from the away3d.primitives package. Creating a text 3D object requires two steps: Extracting the fonts that were embedded inside a separate SWF file. Creating a new TextField3D object. Let's create an application called FontDemo that creates a 3D textfield and adds it to the scene. package { We import the TextField3D class, making it available within our application. import away3d.primitives.TextField3D; The VectorText class will be used to extract the fonts from the embedded SWF file. import wumedia.vector.VectorText; public class FontDemo extends Away3DTemplate { The Fonts.SWF file was created by compiling the Fonts class above. We want to embed this SWF file as raw data, so we specify the MIME type to be application/octet-stream. [Embed(source="Fonts.swf", mimeType="application/octet-stream")] protected var Fonts:Class; public function FontDemo() { super(); } protected override function initEngine():void { super.initEngine(); Before any TextField3D objects can be created we need to extract the fonts from the embedded SWF file. This is done by calling the static extractFonts() function in the VectorText class, and passing a new instance of the embedded SWF file. Because we specified the MIME type of the embedded file to be application/octet-stream, a new instance of the class is created as a ByteArray. VectorText.extractFont(new Fonts()); } protected override function initScene():void { super.initScene(); this.camera.z = 0; Here we create the new instance of the TextField3D class. The first parameter is the font name, which corresponds to the font name included in the embedded SWF file. The TextField3D constructor also takes an init object, whose parameters are listed in the next table. var text:TextField3D = new TextField3D("Vera Sans", { text: "Away3D Essentials", align: VectorText.CENTER, z: 300 } ); scene.addChild(text); } } } The following table shows you the init object parameters accepted by the TextField3D constructor. When the application is run, the scene will contain a single 3D object that has been created to spell out the words "Away3D Essentials" and formatted using the supplied font. At this point, the text 3D object can be transformed and interacted with, just like other 3D object. 3D Text materials You may be aware of applying bitmap materials to the surface of a 3D object according to their UV coordinates. The default UV coordinates defined by a TextField3D object generally do not allow bitmap materials to be applied in a useful manner. However, simple colored materials like WireframeMaterial, WireColorMaterial, and ColorMaterial can be applied to a TextField3D object. Extruding 3D text By default, a text 3D object has no depth (although it is visible from both sides). One of the extrusion classes called TextExtrusion can be used to create an additional 3D object that uses the shape of a text 3D object and extends it into a third dimension. When combined, the TextExtrusion and TextField3D objects can be used to create the appearance of a solid block of text. The FontExtrusionDemo class in the following code snippet gives an example of this process: package { import away3d.containers.ObjectContainer3D; import away3d.extrusions.TextExtrusion; import away3d.primitives.TextField3D; import flash.events.Event; import wumedia.vector.VectorText; public class FontExtrusionDemo extends Away3DTemplate { [Embed(source="Fonts.swf", mimeType="application/octet-stream")] protected var Fonts:Class; The TextField3D 3D object and the extrusion 3D object are both added as children of a ObjectContainer3D object, referenced by the container property. protected var container:ObjectContainer3D; The text property will reference the TextField3D object used to display the 3D text. protected var text:TextField3D; The extrusion property will reference the TextExtrusion object used to give the 3D text some depth. protected var extrusion:TextExtrusion; public function FontExtrusionDemo() { super(); } protected override function initEngine():void { super.initEngine(); this.camera.z = 0; VectorText.extractFont(new Fonts()); } protected override function initScene():void { super.initScene(); text = new TextField3D("Vera Sans", { text: "Away3D Essentials", align: VectorText.CENTER } ); The TextExtrusion constructor takes a reference to the TextField3D object (or any other Mesh object). It also accepts an init object, which we have used to specify the depth of the 3D text, and to make both sides of the extruded mesh visible. extrusion = new TextExtrusion(text, { depth: 10, bothsides:true } ); The ObjectContainer3D object is created, supplying the TextField3D and TextExtrusion 3D objects that were created above as children. The initial position of the ObjectContainer3D object is set to 300 units down the positive end of the Z-axis. container = new ObjectContainer3D(text, extrusion, { z: 300 } ); The container is then added as a child of the scene. scene.addChild(container); } protected override function onEnterFrame(event:Event):void { super.onEnterFrame(event); The container is slowly rotated around its Y-axis by modifying the rotationY property in every frame. In previous examples, we have simply incremented the rotation property, without any regard for when the value became larger than 360 degrees. After all, rotating a 3D object by 180 or 540 degrees has the same overall effect. But in this case, we do want to keep the value of the rotationY property between 0 and 360 so we can easily test to see if the rotation is within a given range. To do this, we use the mod (%) operator. container.rotationY = (container.rotationY + 1) % 360; Z-sorting issues can rise due to the fact that the TextExtrusion and TextField3D objects are so closely aligned. This issue results in parts of the TextField3D or TextExturude 3D objects showing through where it is obvious that they should be hidden. To solve this problem, we can use the procedure to force the sorting order of 3D objects. Here we are assigning a positive value to the TextField3D screenZOffset property to force it to be drawn behind the TextExturude object, when the container has been rotated between 90 and 270 degrees around the Y-axis. When the container is rotated like this, the TextField3D object is at the back of the scene. Otherwise, the TextField3D is drawn in front by assigning a negative value to the screenZOffset property. if (container.rotationY > 90 && container.rotationY < 270) text.screenZOffset = 10; else text.screenZOffset = -10; } } } The result of the FontExtrusionDemo application is shown in the following image:
Read more
  • 0
  • 0
  • 5686
article-image-ibm-lotus-domino-creating-action-buttons-and-adding-style-views
Packt
11 May 2011
7 min read
Save for later

IBM Lotus Domino: Creating Action Buttons and Adding Style to Views

Packt
11 May 2011
7 min read
IBM Lotus Domino: Classic Web Application Development Techniques A step-by-step guide for web application development and quick tips to enhance applications using Lotus Domino Provide view navigation buttons Simple views intended to provide information (for example, a table of values) or links to a limited number of documents can stand alone quite nicely, embedded on a page or a view template. But if more than a handful of documents display in the view, you should provide users a way to move forward and backward through the view. If you use the View Applet, enable the scroll bars; otherwise add some navigational buttons to the view templates to enable users to move around in it. Code next and previous navigation buttons If you set the line count for a view, only that number of rows is sent to the browser. You need to add Action buttons or hotspots on the view template to enable users to advance the view to the next set of documents or to return to the previous set of documents—essentially paging backward and forward through the view. Code a Next button with this formula: @DbCommand("Domino"; "ViewNextPage") Code a Previous button with this formula: @DbCommand("Domino"; "ViewPreviousPage") Code first and last buttons Buttons can be included on the view template to page to the first and last documents in the view. Code an @Formula in a First button's Click event to compute and open a relative URL. The link reopens the current view and positions it at the first document: @URLOpen("/"+@WebDbName+"/"+@Subset(@ViewTitle;-1) + "?OpenView&Start=1") For a Last button, add a Computed for Display field to the view template with this @Formula: @Elements(@DbColumn("":"NoCache"; "" ; @ViewTitle; 1)) The value for the field (vwRows in this example) is the current number of documents in the view. This information is used in the @Formula for the Last button's Click event: url := "/" + @WebDbName + "/" + @Subset(@ViewTitle;-1) ; @URLOpen(url + "?OpenView&Start=" + @Text(vwRows)) When Last is clicked, the view reopens, positioned at the last document. Please note that for very large views, the @Formula for field vwRows may fail because of limitations in the amount of data that can be returned by @DbColumn. Let users specify a line count As computer monitors today come in a wide range of sizes and resolutions, it may be difficult to determine the right number of documents to display in a view to accommodate all users. On some monitors the view may seem too short, on others too long. Here is a strategy you might adapt to your application, that enables users to specify how many lines to display. The solution relies on several components working together: Several Computed for display fields on the view template A button that sets the number of lines with JavaScript Previous and Next buttons that run JavaScript to page through the view The technique uses the Start and Count parameters, which can be used when you open a view with a URL. The Start parameter, used in a previous example, specifies the row or document within a view that should display at the top of the view window on a page. The Count parameter specifies how many rows or documents should display on the page. The Count parameter overrides the line count setting that you may have set on an embedded view element. Here are the Computed for display fields to be created on the view template. The Query_String_Decoded field (a CGI variable) must be named as such, but all the other field names in this list are arbitrary. Following each field name is the @Formula that computes its value: Query_String_Decoded: Query_String_Decoded vwParms: @Right(@LowerCase(Query_String_Decoded); "&") vwStart: @If(@Contains(vwParms; "start="); @Middle(vwParms; "start="; "&"); "1") vwCount: @If(@Contains(vwParms; "count="); @Middle(vwParms; "count="; "&"); "10") vwURL: "/" + @WebDbName + "/"+ @Subset(@ViewTitle;1) + "?OpenView" vwRows: @Elements(@DbColumn("":"NoCache"; ""; @ViewTitle; 1)) countFlag "n" newCount: "1" Add several buttons to the view template. Code JavaScript in each button's onClick event. You may want to code these scripts inline for testing, and then move them to a JavaScript library when you know they are working the way you want them to. The Set Rows button's onClick event is coded with JavaScript that receives a line count from the user. If the user-entered line count is not good, then the current line count is retained. A flag is set indicating that the line count may have been changed: var f = document.forms[0] ; var rows = parseInt(f.vwRows.value) ; var count = prompt("Number of Rows?","10") ; if ( isNaN(count) | count < 1 | count >= rows ) { count = f.vwCount.value ; } f.newCount.value = count ; f.countFlag.value = "y" ; The Previous button's onClick event is coded to page backward through the view using the user-entered line count: var f = document.forms[0] ; var URL = f.vwURL.value ; var ctFlag = f.countFlag.value ; var oCT = parseInt(f.vwCount.value) ; var nCT = parseInt(f.newCount.value) ; var oST = parseInt(f.vwStart.value) ; var count ; var start ; if ( ctFlag == "n" ) { count = oCT ; start = oST - oCT ; } else { count = nCT ; start = oST - nCT ; } if (start < 1 ) { start = 1 ; } location.href = URL + "&Start=" + start + "&Count=" + count ; The Next button pages forward through the view using the user-entered line count: var f = document.forms[0] ; var URL = f.vwURL.value ; var ctFlag = f.countFlag.value ; var oCT = parseInt(f.vwCount.value) ; var nCT = parseInt(f.newCount.value) ; var start = parseInt(f.vwStart.value) + oCT ; if ( ctFlag == "n" ) { location.href = URL + "&Start=" + start + "&Count=" + oCT ; } else { location.href = URL + "&Start=" + start + "&Count=" + nCT ; } Finally, if First and Last buttons are included with this scheme, they need to be recoded as well to work with a user-specified line count. The @formula in the First button's Click event now looks like this: count := @If(@IsAvailable(vwCount); vwCount; "10") ; parms := "?OpenView&Start=1&Count=" + count ; @URLOpen("/" + @WebDbName + "/" + @Subset(@ViewTitle;-1) + parms) ;7 The @formula in the Last button's Click event is also a little more complicated. Note that if the field vwRows is not available, then the Start value is set to 1,000. This is really more for debugging since the Start parameter should always be set to the value of vwRows: start := @If(@IsAvailable(vwRows); @Text(vwRows); "1000") ; count := @If(@IsAvailable(vwCount); vwCount; "10") ; parms := "?OpenView&Start=" + start + "&Count=" + count ; url := "/" + @WebDbName + "/" + @Subset(@ViewTitle;-1) ; @URLOpen(url + parms) ; Code expand and collapse buttons for categorized views Two other navigational buttons should be included on the view template for categorized views or views that include document hierarchies. These buttons expand all categories and collapse all categories respectively: The Expand All button's Click event contains this @Command: @Command([ViewExpandAll]) The Collapse All button's Click event contains this @Command: @Command([ViewCollapseAll]) Co-locate and define all Action buttons Action Bar buttons can be added to a view template as well as to a view. If Action buttons appear on both design elements, then Domino places all the buttons together on the same top row. In the following image, the first button is from the view template, and the last three are from the view itself: If it makes more sense for the buttons to be arranged in a different order, then take control of their placement by co-locating them all either on the view template or on the view. Create your own Action buttons As mentioned previously, Action Bar buttons are rendered in a table placed at the top of a form. But on typical Web pages, buttons and hotspots are located below a banner, or in a menu at the left or the right. Buttons along the top of a form look dated and may not comply with your organization's web development standards. You can replace the view template and view Action buttons with hotspot buttons placed elsewhere on the view template: Create a series of hotspots or hotspot buttons on the view template, perhaps below a banner. Code @formulas for the hotspots that are equivalent to the Action Bar button formulas. Define a CSS class for those hotspots, and code appropriate CSS rules. Delete or hide from the Web all standard Action Bar buttons on the view template and on the view.
Read more
  • 0
  • 0
  • 5684

article-image-openid-ultimate-sign
Packt
23 Oct 2009
13 min read
Save for later

OpenID: The Ultimate Sign On

Packt
23 Oct 2009
13 min read
Introduction How many times have you walked away from some Internet forum because you could not remember your login ID or password, and just did not want to go through the tedium of registering again? Or gone back to re-register yourself only to forget you password the next day? Remembering all those login IDs and passwords is indeed an onerous task and one more registration for a new site seems like one too many. We have all tried to get around these problems by jotting down passwords on pieces of paper or sticking notes to our terminal – all potentially dangerous practices that defeat the very purpose of keeping a digital identity secure. If you had the choice of a single user ID and password combination – essentially a single digital identity – imagine how easy it might become to sign up or sign in to new sites. Suppose you could also host your own digital identity or get it hosted by third party providers who you could change at will, or create different identity profiles for different classes of sites, or choose when your User ID with a particular site should expire; suppose you could do all this and more in a free, non-proprietary, open standards based, extensible, community-driven framework (whew!) with Open Source libraries and helpful tutorials to get you on board, you would say: “OpenID”. To borrow a quote from the OpenID website openid.net: “OpenID is an open, decentralized, free framework for user-centric digital identity.” The Concept The concept itself is not new (and there are proprietary authentication frameworks already in existence). We are all aware of reference checks or identity documents where a reliable agency is asked to vouch for your credentials. A Passport or a Driver's License is a familiar example. Web sites, especially those that transact business, have digital certificates provided by a reliable Certification Authority so that they can prove to you, the site visitor, they are indeed who they claim to be. From here, it does not require a great stretch of imagination to appreciate that an individual netizen can have his or her own digital identity based on similar principles. This is how you get the show on the road. First, you need to get yourself a personal identity based on OpenID from one of the numerous OpenID providers[1] or some sites that provide an OpenID with membership. This personal identity comes in the form a URL or URI (essentially a web address that starts with http:// or https://) that is unique to you. When you need to sign up or sign in to a web site that accepts OpenID logins (look for the words 'OpenID' or the OpenID logo), you submit your OpenID URL. The web site then redirects you to the site of your ID provider where you authenticate yourself with your password and optionally choose the details – such as full name, e-mail ID, or nickname, or when your login ID should expire for a particular site – that you want to share with the requesting site and allow the authentication request to go through. You are then returned to the requesting site. That is all there is to it. You are authenticated! The requesting site will usually ask you to associate a nickname with your OpenID. It should be possible to register with and sign in to different sites using different nicknames – one for each site – but the same OpenID. But you may not want to overdo this lest you get into trouble trying to recall the right nickname for a particular site. Just Enough Detail This is not a technical how-to. For serious technical details, you can follow the excellent links in the References section. This is a basic guide to get you started with OpenID, to show you how flexible it is, and to give pointers to its technical intricacies. By the end of this article you should be able to create your own personal digital identities based on OpenID (or discover if you already have one – you just might!), and be able to use them effectively. In the following sections, I have used some real web sites as examples. These are only for the purpose of illustration and in no way shows any preference or endorsement. Getting Your OpenID The simplest and most direct way to get your personal OpenID is to go to a third party provider. But before that, the smart thing to do would be find out if you already have one. For instance, if you blog at wordpress.com, then http://yourblogname.wordpress.com is an OpenID already available to you. There are other sites[1], too, that automatically provide you an OpenID with membership. Yahoo! gives you an OpenID if you have an account with them; but it is not automatic and you need to sign up for it at http://openid.yahoo.com. Your OpenID at Yahoo! will be of the form https://me.yahoo.com/your-nickname. To get your third party hosted OpenID we will choose Verisignlab's Personal Identity Provider (PIP) site -- http://pip.verisignlabs.com/ as an example. You are of course free to decide and choose your own provider(s). The sign up form is a simple no-fuss affair with the minimum number of fields. (If you are tired of hearing 'third party', the reason for using the term will get clearer further on. For the purpose of this article, you, the owner of the OpenID are the first party, the web site that wants you authenticated is the second party, the OpenID provider being the third.) After replying to the confirmation e-mail you are ready to take on the wide world with your OpenID. If you gave your ID as 'johndoe' then you will get an OpenID like: http://johndoe.pip.verisignlabs.com. You can come back to the PIP site and update your profile; some sites request information such as full name or e-mail ID but you are always in control whether you want to pass on this information back to them. If you choose to have just one OpenID, then this is about as much as you would ever do to sign on to any OpenID enabled site. You can also create multiple OpenID's for yourself – remember what we said earlier about having multiple ID's to suite different classes of sites. Testing Your OpenID Now that we have our OpenID we will test it and in the process also see how a typical OpenID-based authentication works in practice. Use the testing form[7] in the References section and enter your OpenID URL that you want tested. When you are redirected to your PIP's site (we are sticking to our Verisign example), enter your password and also choose what information you want passed back to the requesting site before clicking “Allow” to let the authentication go through. Important tip: Enter your password only on the PIP's site and nowhere else! Be aware that this particular testing page may not work with all OpenIDs; that may not necessarily mean that the OpenID itself has a problem. Step-by-Step: Use your WordPress or Verisign OpenID For this tutorial part, we will take the example of http://www.propeller.com (a voting site among other things) that accepts OpenID sign ups and sign ins. For an OpenID we will use the URL of your WordPress blog – http://yourblogname.wordpress.com. You could also use your OpenID URL (the one you got from the Verisign example) and follow through. On the Propeller site, go to the sign up page. Look for the prominent OpenID logo. Type in your OpenID URL and click on the 'Verify ...' button. You are taken to the site of your PIP where you need to authenticate yourself.   If you used your Verisign OpenID, enter your password, complete the details you want to pass back to the requesting site (remember, we are trying to sign up with Propeller) and allow the authentication to go through. You are now back with the Propeller site. Just hang in there a moment as we check the flow for a Wordpress OpenID.   For a WordPress OpenID, you will get a screen instead that asks you to deliberately sign in to your WordPress account. Once you are signed in, you will see a hyperlink that prompts you to continue with the authentication request from Propeller.     Follow this link to a form that asks your permission to pass back information to Propeller such your nickname and e-mail ID. You can change both these fields if you wish and allow the authentication to go through.   Now you should be back at the Propeller site with a successful OpenID verification. The site will ask you to associate a nickname with your OpenID and a working e-mail to complete your registration process. This step is no different from a normal sign up process. Check your e-mail, click on the link provided therein, get back to the Propeller site, and click another link to complete the registration process. You are automatically signed in to Propeller. Sign out for the moment so that we can see how an OpenID sign in works. Go to the sign in page at Propeller. You will see a normal sign in and an OpenID sign in. We will use the OpenID one (of course!). Type in your OpenID URL and click on the “Sign in...” button. Complete the formalities on your PIP site (for Verisign you will get a sign in page; for Wordpress you will need to sign in first unless you are already signed in) and let the authentication go through. This time you are back on the Propeller site all signed in and ready to go. Note that your nickname appears correctly because your OpenID is associated with it. That is all there is to it. Easier done than said. Try this a couple of times and I bet it will feel easier than the remote control of your home entertainment system! Your Custom OpenID URL If you want a personalized OpenID URL and do not like the one provided by your PIP you can always use delegation to get what you want. To make your blog or personal home page as your OpenID URL, insert the following in the head portion (the part that falls between <head> and </head> on an HTML page) of your blog or any page that you own. This will only work with pages that you completely own and have control over their source. There is a Wordpress plug-in that gives delegating capability to your Wordpress.com blog but we will not go into that here. The first URL is your OpenID server. The second URL is your OpenID URL – either the one you host yourself or the one provided by a third party. The requesting site discovers your OpenID and correctly authenticates you. With this approach you can switch providers transparently. At the risk of repeating: test your new personalized URL before you start using it. Note that the 'openid.server' URL may vary depending on the PIP. To get the name of your PIP's OpenID server, use the testing service[7] which reports the correct URL for your PIP to use with the “openid.server” part your delegation mark up. <link rel="openid.server" href="http://pip.verisignlabs.com/server" /><link rel="openid.delegate" href="http://johndoe.pip.verisignlabs.com/" /> Rolling Your Own If you are paranoid about entrusting the management of your digital identity to another web site and also have the technical smarts to match, there are ways you can become your own PIP[5][6]. If you are tech-savvy then you cannot fail to appreciate the elegance of the OpenID architecture and the way it lets control stay where it should – with you. Account Management – Lite? OpenID makes life easier for site visitors. But what about the site and the domain administrators? If administrators decide to go the OpenID way[3], it lightens their load by taking away a major part of the chore of membership administration and authentication. As a bonus, it also potentially opens up a site to the entire community of net users that have OpenID's or are getting one. Security and Reliability As the wisecrack goes – if you want complete security, you should unplug from the Internet. On a serious note, there are some precautions you have to take while using OpenID and they are no different from the precautions you would take for any item associated with your identity, say your Passport or your credit card. Remember to enter your password only on the Identity Provider's site and nowhere else. Be alert to phishing. This explains why WordPress asks you to log in explicitly rather than take you directly to their authentication page. Never use your e-mail ID handle as your OpenID name but use a different one. Using OpenID has its flip side, too. Getting your OpenID from a provider potentially lays open your browsing habits to tracking. You can get around this by being your own PIP, delegating from your own domain, or creating a PIP profile under an alias. There is the possibility that your OpenID provider goes out of service or worse, out of business. It is thus important to choose a reliable identity provider. There are sites that allow you to associate multiple OpenIDs with your account and perhaps this can be a way forward to popularize OpenID and to allay any fears of getting locked in with a single vendor and getting locked out of your identity in the process. Your Call There are many sites today that are not OpenID-ready. There are some sites that allow only OpenID sign ons. However, if you see the elegance of the OpenID mechanism and the convenience it provides both site administrators and members, you might agree that its time has come. Get an OpenID if you do not have one. Convince your friends to get theirs. And if you run an online community or are a member of one, throw your weight around to ensure that your site also provides an OpenID sign on. References http://wiki.openid.net/OpenIDServers is a list of ID providers. http://blogs.zdnet.com/digitalID/?p=78 makes a strong case for OpenID. Read it to get a good perspective on the subject. http://www.plaxo.com/api/openid_recipe is a soup-to-nuts tutorial on how to enable your site for OpenID authentication or migrate to OpenID from your current site-specific authentication scheme. Check out http://www.openidenabled.com/php-openid/ if you are looking for software libraries to OpenID-enable your site. http://www.intertwingly.net/blog/2007/01/03/OpenID-for-non-SuperUsers is a crisp if intermediate-level how-to that lets you try out new things in the OpenID space. http://siege.org/projects/phpMyID/ shows you how you can run your own (yes, your own) PIP server. http://www.openidenabled.com/resources/openid-test/checkup is a link that helps you test your OpenID. Once you get your OpenID, you can submit it to the form on this URL and get yourself authenticated to see if everything works fine. Does not seem to work with Wordpress and Yahoo! OpenIDs as of this writing. http://www.openid.net is the OpenID site.   Read another article by Gurudutt Talgery Podcasting with Linux Command Line Tools and Audacity  
Read more
  • 0
  • 0
  • 5681

article-image-setting-biztalk-server-environment
Packt
09 Apr 2012
18 min read
Save for later

Setting up a BizTalk Server Environment

Packt
09 Apr 2012
18 min read
Gathering requirements by asking the right questions Although, this is not an exact recipe, asking questions to obtain requirements for your BizTalk environment is important. Having a clear view and understanding of the requirements enables you to deploy the desired BizTalk environment that meets expectations of the customer. What are the right questions you may ask yourself? Well, there is quite a large area in general you basically need to cover with questions. These questions will be around the following topics: A BizTalk work load(s) that is functional Non-functional (high availability, scalability, and so on) Licensing (software) Hardware Virtualization Development, Test, Acceptance, and Production (DTAP) environment Tracking/Tracing Hosting Security Getting ready Organize the sessions, and/or the workshop(s) to discuss the BizTalk architecture (environment), functionality, and non-functional requirements, where you do a series of interviews with appropriate stakeholders. This way you will be able to retrieve the necessary requirements and information for a BizTalk environment. You will need to focus on business first and IT later. You will notice that each business will have a different set of requirements on integration of data and processes. Some of these are listed as follows: Business is able to have the access of information from anywhere any time Have the proper information to present to the proper people Have the necessary information available when needed Manage knowledge efficiently and be able to share it with the business Change the information when needed Automate the business process that is error-prone Automate the business process to reduce the processing time of orders, invoices, and so on Regarding the business requirements, BizTalk will have certain workloads, and with the business you determine if you want BizTalk to aid in automating processes, exchange of information with partners, maintaining business rules, visibility of psychical events, and/or integration with different systems. One important factor to reckon with bringing BizTalk into an organization is risk-associated with transitioning to its platform. This risk can be of a technical, operational, political, and financial nature. BizTalk solutions have to operate correctly, meet the business requirements, and be accepted by stakeholders within the organization and should not be too expensive. With IT, you focus more on the technical side of the BizTalk Environment such as, "What messages in size, format, and encoding are sent to the BizTalk system or what does it need to output?" You should consider security around it, when information going to or coming from trading partners is confidential. Encryption and decryption of data such as, "What processes that are automated need to interact with internal and external systems?" or "How are you going to monitor messages that are going in and out?" can come into play. Support needs to be set up properly to keep BizTalk and its solutions healthy. Solutions need to be developed and tested, preferably using different environments such as test and acceptance. For that, you will need an agreed deployment process with IT. These are factors to reckon with and need to be addressed when interviewing or talking to IT stakeholders within the organization. How to do it… Categorize your stakeholders into two categories—business and IT. Create a communication plan and list of questions related to areas mentioned earlier. With the list of questions you can assign each question to a person you think can answer it. This way you ask the right questions to the right people. The following table shows a sample of roles belonging to business and/or IT. It could be that you identify more roles depending on your situation: Category Role Business CEO, CIO, Security Officer, Business Analyst, Enterprise Architect, and Solution Architect. IT IT Manager, Enterprise Architect, Solution Architect, System/Application Architect, System Analyst, Developer, System Engineer, and DBA. Having the roles clear belonging to either business, IT, or both, you will then need to have a list of questions and assign these to the appropriate role. You can find an example list of questions associated to a particular role in the following table: Question Role Will BizTalk integrate with systems in the enterprise? Which consumers and host systems will it integrate with? Enterprise Architect, Solution Architect What are the applicable workloads? Enterprise Architect Is BizTalk going to be strategic for integration with internal/external systems? CEO, CIO, Enterprise Architect, and Business Analyst Number of messages a day/hour Enterprise Architect What are the candidate processes to automate with BizTalk? Business Analyst, Solution Architect What communication protocols are required? Enterprise Architect, Solution Architect Choice of Microsoft platform-Operating System, SQL Server Database Enterprise Architect, Security Officer, Solution Architect, System Engineer, and DBA Encryption algorithm for data Enterprise Architect, Security Officer, Solution Architect, and System Engineer Is Secure Socket Layer required for communication? Enterprise Architect, Security Oficer, Solution Architect, and System Engineer What kind of certificate store is there? Enterprise Architect, Security Officer, Solution Architect, and System Engineer Is the Support for BizTalk going to be outsourced CEO, IT Manager There's more… The best approach to gather the requirements is to view it as a project or a part of the project. You can use a methodology such as PRINCE2. PRINCE2 Projects in Controlled Environments (PRINCE) is a project management method. It covers the management, control, and organization of a project. PRINCE2 is the second major release of it. More information is available at http://www.prince2.com/. Microsoft BizTalk Server website The Microsoft BizTalk Server website provides a lot of information. Especially, the Production Information section provides detailed information on system requirements, roadmap, and the FAQs. The latter sections provide details on pricing, licensing, and so on. Go to http://www.microsoft.com/biztalk/en/us/default.aspx. Analyzing requirements and creating a design Analyzing requirements and creating a design for the BizTalk landscape is the next step forward before planning and installing. With the gathered requirements, you can make decisions on how to design a BizTalk environment(s). If BizTalk is used for the first time in an enterprise environment capacity, planning and server allocation is something to focus on. Once you gather requirements and ask questions, you will have a clear picture of where the platform will be hosted and whether it needs to be scaled up or out. If everything gets placed on one big server, it will introduce a serious single point of failure. You should try to avoid this scenario. Therefore, separating BizTalk from the SQL Server is the first thing you will do in your design, each on a separate hardware preferably. Depending on availability requirements, you will probably cluster the SQL Server. Besides that, you can choose to scale out BizTalk into a multiserver group, because of availability requirements and if the expected load cannot be handled by one BizTalk instance. You can opt for installing BizTalk and SQL separately first and then scale-out after performing benchmark tests. You can scale vertically (scaleup) by increasing the number of processors and the amount of memory each server uses, or you can scale horizontally (scaleout) by adding more servers to your BizTalk Server configuration. Other options you can consider during your design are as follows: Having multiple MessageBox databases Separate BizTalk databases These options are best visualized by the scale-out poster from Microsoft (http://www.microsoft.com/download/en/details.aspx?id=13103). Based on the requirements, you can consider isolating the BizTalk hosts to be able to manage BizTalk applications better and divide the load. By separating send, receive, and processing functionality in different hosts, you will benefit from better memory and thread management. If you expect a high load of large messages or orchestrations that would consume large amounts of resources, you should isolate send and/or receive adapters. Another consideration is to separate a host to handle tracking and relieve processing hosts from it. So far we have discussed scalability and design decisions you could consider. There are some other design considerations for a BizTalk environment such as security, tracking, fault tolerance, load balancing, choice of license, and support for virtualization (http:// support.microsoft.com/kb/842301). BizTalk security can be enhanced by deploying Secure Socket Layer (SSL), IPSec Tunneling, the Inter Security and Acceleration (ISA) server, and certificate services included with the Windows Server 2008. With the BizTalk Server, you can apply access control, implement least rights to limit access, and provide integrated security through Enterprise Single Sign-On (http://msdn.microsoft.com/en-us/library/aa577802%28v=bts.70%29.aspx). Furthermore, you can protect and secure applications and data by authenticating the sender of a message and authorizing the receiver of a message. Tracking messages in BizTalk messages can be useful to see what messages come in and out of the system, or for auditing, troubleshooting, or archiving purposes. Tracking of messages within BizTalk is a process by which parts of a message such as the body, properties, and metadata are stored in a database. These parts can be viewed by running queries from the Group Hub page in the BizTalk Server Administration console. It is important that you decide, or take up into the design, what needs to be tracked based on the requirements. There are some considerations to make regarding tracking. Tracking everything is not the smart thing to do, as each time a message is touched in BizTalk; a copy is made and stored. Focus on scope by tracking only on a specific port, which is better for performance and keeps the database uncluttered. For the latter, it is important that the data purge and archive job is configured properly. As mentioned earlier, it is worth considering a dedicated host for tracking. Fault tolerance and load balancing for BizTalk can be achieved through clustering, separating hosts as described earlier, implement a Storage Area Network (SAN) to house the BizTalk Server databases, cluster Enterprise Single Sign-On (SSO) Master Secret Server, and configuring the Internet Information Services (IIS) web server for isolated host instances and the BAM Portal web page to be highly available using Network Load Balancing (NLB) or other load balancing devices. The best way to implement this is to follow the steps in the Checklist: Providing High Availability with Fault Tolerance or Load Balancing document found on MSDN (http://msdn.microsoft.com/en-us/library/gg634479%28v=bts.70%29.aspx). Another important topic regarding your BizTalk environment is costs and based on requirements you will choose the Branch, Standard, or Enterprise Edition. The editions differ not only in price, but also in functionality. As with the Standard Edition, it is not possible to support scenarios for high availability, fault tolerance, and is limited on CPU and applications. The Branch Edition is even more limited and is designed for hub and spoke deployment scenarios including Radio Frequency Identification (RFID). With any version, you probably want to consider whether or not to virtualize. With virtualization in mind, licensing can be difficult. With the Standard Edition, you need a license for each virtual processor used by the virtual OS environment, regardless of whether the number of virtual processors is less than, or greater than, the number of physical processors on the server. With the Enterprise Edition, if you license all physical CPUs on the server you can run any number of instances in the physical or virtual OS environment. With both of these, a virtual processor is assumed to have the same number of cores as the physical processor. Using less than the number of cores available in the physical processor still counts as a full virtual processor (http://www.microsoft. com/biztalk/en/us/editions.aspx). Last, but not least, you need to consider how to support your BizTalk environment. It is worth considering the System Center Operation Manager to monitor your BizTalk environment using management packs for the SQL Server, Windows Server, and BizTalk Server 2010. The management pack for the BizTalk Server 2010 provides two views, one for the enterprise IT administrator and one for the BizTalk Server administrator. The first will be monitoring the state and health of the various enterprise deployments, the machines hosting the SQL Server databases, machines hosting the Enterprise SSO service, host instance machines, IIS, network services, and is interested in the overall health of the "physical deployment" of a BizTalk Server setup. The BizTalk Server Administrator will be monitoring the state and health of various BizTalk Server application artifacts, such as orchestrations, send ports, receive locations, and is interested in monitoring and tracking the BizTalk Server's health. If necessary, he/she can carry out corrective measures to keep applications running as expected. What you have read so far are considerations, which are useful while analyzing requirements and preparing your design. You need to take a considerable amount of time for analyzing requirements to be able to create a solid design for your BizTalk environment. There is a wealth of information provided by Microsoft in this book. It will be worth investing time now as you will lose a lot time and money if your applications do not perform or the system cripples under load while receiving the process. How to do it... To analyze the requirements, you will need to categorize them to certain topics mentioned in the Gathering requirements by asking the right questions recipe. You will then go over each requirement and decide how it can be met best. For each requirement, you will consider what the best option is and capture that in your design for the BizTalk setup. The BizTalk design will be a Word document, where you capture your design, considerations, and decisions. How it works... During analysis of each requirement, you will capture your considerations and decisions in a word document. Besides that, you will also describe the situation at the enterprise where the BizTalk environment will be deployed. You will find an example structure of a design document for a Development, Test, Acceptance, and Production (DTAP) environment, as follows, where you can place all the information: Introduction Purpose Current situation IT landscape Design Decisions Considerations/Issues Overview DTAP landscape Scope MS BizTalk and SQL Server editions SQL Database Server ICT Policy Operating systems Windows Server Backup Antivirus Windows update Security Settings Backup and Restore Backup procedure Restore procedure Development Development environment Development server Developer machine Test Test server Acceptance SQL Server clustering BizTalk group Acceptance server Production SQL Server clustering BizTalk group (load balancing) Production server Management and security Groups and accounts SCOM Single Sign-On Hosts In process hosts Isolated hosts Trusted and untrusted hosts Hosts configuration DTAP Resources Appendix A Redistributable CAB Files Design decisions are the important parts of your document. Here, you summarize all your design decisions and reference them to each corresponding chapter/section in the document, where a decision is described; you also note issues around your design. There's more... Analyzing requirements is an important task, which should not be taken lightly. Knowing architectural patterns, for instance, can help you choose the right technology and create the appropriate design. It can be that the BizTalk Server is not the right fit for the purpose. The following resources can aid you in analyzing the requirements: Architectural Patterns: Packt has published a book called Applied Architecture Patterns on Microsoft Platform that can aid you in analyzing the requirements by selecting the right technology. Wiki TechNet article: Refer to the Recommendations for Installing, Sizing, Deploying, and Maintaining a BizTalk Server Solution article at http://social.technet. microsoft.com/wiki/contents/articles/666.aspx. Microsoft BizTalk Server 2010 Operations Guide: Microsoft has created a BizTalk Server 2010 Operations Guide for anyone involved in the implementation and administration of a BizTalk solution, particularly IT professionals. You can find it online (http://msdn.microsoft.com/en-us/library/ gg634499%28v=bts.70%29.aspx) or you can download it from http://www. microsoft.com/downloads/en/details.aspx?FamilyID=4ef9eebb-b3f4-4534-b733-3eb2cb83d867&displaylang=en. Microsoft volume licensing brief: Licensing Microsoft Server Products in Virtual Environments is an interesting white paper from Microsoft. It describes licensing models under virtual environments for the server operating systems and server applications. It can help you understand how to use Microsoft server products with virtualization technologies, such as Microsoft Hyper-V technology, Microsoft Virtual Server 2005 R2, or third-party virtualization solutions that are provided by VMWare and Parallels. You can download from the URL: http://www.microsoft.com/ downloads/en/details.aspx?FamilyID=9ef7fc47-c531-40f1-a4e9-9859e593a1f1&displaylang=en. Microsoft poster scale-out configurations: Microsoft has published a poster (normal or interactive) that can be downloaded describing typical scenarios and commonly used options for scaling out the BizTalk Server 2010's physical configurations. This post clearly illustrates how to scale for achieving high availability through load balancing and fault tolerance. It also shows how to configure for high-throughput scenarios. A normal poster can be obtained from the URL: http://www.microsoft.com/ downloads/en/details.aspx?FamilyID=2b70cbfc-d158-45a6-8bbd-99782d6747dc. An interactive poster created in Silverlight can be obtained from the URL:http:// www.microsoft.com/downloads/en/details.aspx?FamilyID=7ef9ae69-9cc8-442a-8193-831a414dfc30. Installing and using the BizTalk Best Practices Analyzer The Best Practices Analyzer (BPA) examines a BizTalk Server 2010 deployment and generates a list of issues pertaining to best practice standards for BizTalk Server deployments. This tool is designed to assess the configuration of a BizTalk installation. The BPA performs configuration-level verification by gathering data from different information sources, such as Windows Management Instrumentation (WMI) classes, SQL Server databases, and registry entries and presents a report to the user. Under the hood, it uses the data to evaluate the deployment configuration. It does not modify any system settings and is not a self-tuning tool. The tool is there to deliver support in achieving the best suitable configuration and report issues or possible issues, that could potentially harm the BizTalk environment. Getting ready The latest version of the BPA tool (V1.2) can be obtained from the Microsoft download center (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=93d432fe-1370-4b6d-aaa8-a0c43c30f5ab&displaylang=en) and must be installed on the BizTalk machine. As a user, you need an account that has local administrative rights, that is a member of the BizTalk Server Administrators group, and a member of the SSO Administrators group to be able to run the BPA. You may need to explicitly set some WMI permissions before you can use the BPA in a distributed environment, where the SQL Server is not installed on the same computer as the BizTalk Server. This is because when the BPA tries to connect to a remote computer running the SQL Server, WMI may not have sufficient access to determine whether the SQL Server Agent is running. This may result in incorrect BPA evaluations. How to do it... To run the Best Practices Analyzer, perform one of the following: Start the BizTalk Server Best Practices Analyzer from the Start menu. Go to Start | Programs | Microsoft BizTalk Server Best Practices Analyzer. Open Windows Explorer and navigate to the Best Practices Analyzer installation directory (by default, c:Program FilesBizTalkBPA) and double-click on BizTalkBPA.exe. Open a command prompt, change to the installation directory, and then enter BizTalkBPACmd.exe. The following steps need to be performed to do the analysis: As soon as you start the BPA, it will check for updates. The user can decide whether or not to check for updates for newer versions of the configuration: (Move the mouse over the image to enlarge.) If a newer version is found, you are able to download the latest updates. The next step is to perform a scan by clicking on Start a scan: After starting the scan, starts data will be gathered from different information sources as described earlier. After the scan has been completed, the user can decide to view the report of the performed scan: You can click View a report of this Best Practices scan and the report will be generated. After generation of the report, several tabs will appear: Critical Issues All Issues Non-Default Settings Recent Changes Baseline Informational Items How it works... When the BPA is running, it gathers information and evaluates them to best practice rules from the Microsoft product group and support. A report is presented to the user providing information on issues, non-default settings, changes, and so on. The report enables you to take action and apply the necessary changes to resolve identified issues. The BPA can be run again to verify that it adheres to all the necessary best practices. This shows the value of the tool when assessing the deployed BizTalk environment before it is operational. When BizTalk becomes operational, the MessageBox Viewer (MBV) has more value. There's more... The BPA is very useful and gives you information that helps you to tune BizTalk and to keep it healthy. There are more tools that can help in sustaining a healthy environment overall. The Microsoft SQL Server 2008 R2 BPA is a diagnostic tool that provides information about a server and a Microsoft SQL Server 2008 or Microsoft SQL Server 2008 R2 instance installed on that server. The Microsoft SQL Server 2008 R2 Best Practices Analyzer can be downloaded from http://www.microsoft.com/download/en/details.aspx?id=15289. There are a couple of analyzers provided by Microsoft that do a good job helping you and the system engineer to put out a healthy, robust, and stable environment: Best Practices Analyzer: http://technet.microsoft.com/en-us/library/dd759260.aspx Microsoft Baseline Configuration Analyzer 2.0: http://www.microsoft.com/download/en/details.aspx?id=16475 Microsoft Baseline Security Analyzer 2.1.1: http://www.microsoft.com/download/en/details.aspx?id=19892
Read more
  • 0
  • 0
  • 5680
article-image-building-hello-world-application
Packt
29 Oct 2013
6 min read
Save for later

Building a Hello World application

Packt
29 Oct 2013
6 min read
(For more resources related to this topic, see here.) The Hello World application In the previous sections, we saw how to set up environments for development of Sencha Touch. Now let's start with the Hello World application. First of all, create a new folder in your web server and name it sencha-touch-start. Create a subfolder lib inside this folder. In this folder, we will store our Sencha Touch resources. Create two more subfolders inside the lib folder and name them js and css respectively. Copy the sencha-touch-all.js file from the SDK, which we had downloaded, to the lib/js folder. Copy the sencha-touch.css file from SDK to the lib/css folder. Now, create a new file in the sencha-touch-start folder, name it index.html, and add the following code snippet to it: <!DOCTYPE html><html><head><meta charset="utf-8"><title>Hello World</title><script src = "lib/js/sencha-touch-all.js" type="text/javascript"></script><link href="lib/css/sencha-touch.css" rel="stylesheet"type="text/css" /></head><body></body></html> Now create a new file in the sencha-touch-start folder, name it app.js, and add the following code snippet to it: Ext.application({name: 'Hello World',launch: function () {var panel = Ext.create('Ext.Panel', {fullscreen: true,html: 'Welcome to Sencha Touch'});Ext.Viewport.add(panel);}}); Add a link to the app.js file in the index.html page; we created the following link to sencha-touch-all.js and sencha-touch.css: <script src = "app.js" type="text/javascript"></script> Here in the code, Ext.application({..}) creates an instance of the Ext.Application class and initializes our application. The name property defines the name of our application. The launch property defines what an application should do when it starts. This property should always be set to a function inside which we will add our code to initialize the application. Here in this function, we are creating a panel with Ext.create and adding it to Ext.Viewport. Ext. Viewport is automatically created and initialized by the Sencha Touch framework. This is like a base container that holds other components of the application. At this point, your application folder structure should look like this: Now run the application in the browser and you should see the following screen: If your application does not work, please check your web server. It should be turned on, and the steps mentioned earlier should be repeated. Now we will go through some of the most important features and configurations of Sencha Touch. These are required to build real-time Sencha Touch applications. Introduction to layouts Layouts give a developer a number of options to arrange components inside the application. Sencha Touch offers the following four basic layouts: fit hbox vbox card hbox and vbox layouts arrange items horizontally and vertically, respectively. Let's modify our previous example by adding hbox and vbox layouts to it. Modify the code in the launch function of app.js as follows: var panel = Ext.create('Ext.Panel', {fullscreen: true,layout: 'hbox',items: [{xtype: 'panel',html: 'BOX1',flex: 1,style: {'background-color': 'blue'}},{xtype: 'panel',html: 'BOX2',flex: 1,style: {'background-color': 'red'}},{xtype: 'panel',html: 'BOX3',flex: 1,style: {'background-color': 'green'}}]});Ext.Viewport.add(panel); In the preceding code snippet, we specified the layout for a panel by setting the layout: 'hbox' property, and added three items to the panel. Another important configuration to note here is flex. The flex configuration is unique to the hbox and vbox layouts. It controls how much space the component will take up, proportionally, in the overall layout. Here, we have specified flex : 1 to all the child containers; that means the height of the main container will be divided equally in a 1:1:1 ratio among all the three containers. For example, if the height of the main container is 150 px, the height of each child container would be 50 px. Here, the height of the main container would be dependent on the browser width. So, it will automatically adjust itself. This is how Sencha Touch adaptive layout works; we will see this in detail in later sections. If you run the preceding code example in your browser, you should see the following screen: Also, we can change the layout to vbox by setting layout: 'vbox', and you should see the following screen: When we specify a fit layout, a single item will automatically expand to cover the whole space of the container. If we add more than one item and specify only the fit layout, only the first item would be visible at a time. The card layout arranges items in a stack of cards and only one item will be visible at a time, but we can switch between items using the setActiveItem function. We will see this in detail in a later section. Panel – a basic container We have already mentioned the word "panel" in previous examples. It's a basic container component of the Sencha Touch framework. It's basically used to hold items and arrange them in a proper layout by adding the layout configuration. Besides this, it is also used as overlays. Overlays are containers that float over your application. Overlay containers can be positioned relative to some other components. Create another folder in your web server, name it panel- demo, and copy all the files and folders from the sencha-touch-start folder of the previous example. Modify the title in the index.html file. <title>Panel Demo</title> Modify app.js as follows: Ext.application({name: 'PanelDemo',launch: function () {var panel = Ext.create('Ext.Panel', {fullscreen: true,items: [{xtype: 'button',text: 'Show Overlay',listeners: {tap: function(button){var overlay = Ext.create('Ext.Panel', {height: 100,width: 300,html: 'Panel asOverlay'});overlay.showBy(button);}}}]});Ext.Viewport.add(panel);}}); In the preceding code snippet, we have added button as the item in the panel and added listeners for the button. We are binding a tap event to a function for the button. On the tap of the button, we are creating another panel as an overlay and showing it using overlay. showBy(button). Summary This article thus provided us with details on building a Hello World application, which gave you further introduction to the most used components and features of Sencha Touch in real-time applications. Resources for Article : Further resources on this subject: Creating a Simple Application in Sencha Touch [Article] Sencha Touch: Layouts Revisited [Article] Sencha Touch: Catering Form Related Needs [Article]
Read more
  • 0
  • 0
  • 5679

article-image-mitigating-the-risks-of-chatgpt-in-finance
Julian Melanson
13 Jul 2023
5 min read
Save for later

Mitigating the Risks of ChatGPT in Finance

Julian Melanson
13 Jul 2023
5 min read
The application of advanced AI tools, such as ChatGPT, in various industries, particularly finance, has proven transformative due to its extensive language processing capabilities. ChatGPT's functions within the financial sector are diverse and impressive. It can understand financial market dynamics, suggest products, identify specific entities, and generate financial summaries, reports, and forecasts. Furthermore, the potential of training ChatGPT for fraud prevention and detection is an exciting prospect.However, as the integration of ChatGPT into the financial services realm becomes more prevalent, it brings to the fore several ethical challenges. Therefore, the onus is on both researchers and practitioners to ensure that the technology's use is responsible and advantageous to all parties involved. The solutions to these ethical challenges often require a multi-faceted approach, focusing on data exposure, misinformation, technology dependency, privacy concerns, and social engineering.The Ethical Challenges InvolvedOne of the paramount ethical challenges is data exposure. For example, ChatGPT users working with financial data might unintentionally disclose sensitive information. Additionally, during the AI model's training phase, there's a risk of exposing confidential elements such as proprietary code snippets, API keys, or login credentials.ChatGPT can sometimes generate biased or inaccurate responses, causing misinformation. The tool, at present, operates based on data sets that only run up to September 2021, which are sourced online and not always accurate. Therefore, financial professionals must exercise caution while using such advice to prevent the propagation of misinformation.Furthermore, while AI can be a powerful tool for financial decision-making, relying solely on technology can undermine human judgment and intuition. Financial professionals could fall into the trap of misinterpreting or overly depending on ChatGPT's advice, thereby overlooking the importance of human expertise in the financial sector. Therefore, it is crucial to strike a balance between utilizing AI's efficiency and maintaining human critical thinking.As ChatGPT requires an extensive amount of data for training, this raises significant privacy concerns. The information collected could pose serious risks to both individuals and organizations if exposed or used maliciously. In tandem with privacy concerns, social engineering issues arise as well. There is a potential for cybercriminals to misuse ChatGPT, impersonating individuals or organizations to conduct successful phishing attacks.Solving the ProblemAddressing these ethical challenges requires robust solutions. Firstly, the co-creation approach, which emphasizes public participation and stakeholder involvement in designing the AI algorithm. This strategy includes key choices in the algorithm, from the scope of its use to mitigating biases and tackling misinformation. It also ensures that humans keep a certain level of control over the AI tool, thus preventing total dependency on the technology.Secondly, the institutional approach can ensure the ethical use of ChatGPT in finance. This approach demands the establishment of concrete rules for managing ChatGPT, including training policy regulators to scrutinize and audit the AI algorithm and developing regulations. The focus is on creating transparent tools that ensure user privacy and constantly upgrade security measures to prevent breaches by cybercriminals.Lastly, it's vital to maintain a harmonious blend of AI-based decision-making and human intuition. While ChatGPT can crunch data and analyze trends with efficiency, human professionals have the experiential knowledge to make intuitive financial decisions. The amalgamation of both AI and human insight can lead to mutual learning and overall improvement in financial decision-making. It can also help address legal obstacles in financial domains that AI might overlook, thus ensuring the accuracy and reliability of financial decisions.The UK Finance paper on AI Fairness in Financial Services recommends a multi-disciplinary approach:Frontline business must be clear on the objective of the use of AI, the risks to individuals and to the business, and the extent to which risks of unfair treatment will be managed and explained to stakeholders.Data scientists are central to the technical aspects of the use, testing and monitoring of AI.Legal and Compliance need to be involved (including in any preliminary stages) to provide appropriate challenge, to oversee testing and to assist with fair process and related transparency principle.In addition, human application can mitigate the looming threat of job loss due to automation. While technology like ChatGPT can automate many functions, it is essential to preserve roles where human intuition, expertise, and judgment are irreplaceable.While the adoption of ChatGPT in finance is indeed a technological advancement, it comes with ethical challenges that require strategic and thoughtful solutions. Companies must adopt strategies such as co-creation and institutional approaches to ensure ethical usage. Furthermore, they need to strike a balance between AI and human insight to maintain the integrity of financial decisions. By addressing these challenges and implementing relevant strategies, we can ensure a future where AI not only augments the financial sector but also respects the values that we hold dear.Author BioJulian Melanson is one of the founders of Leap Year Learning. Leap Year Learning is a cutting-edge online school that specializes in teaching creative disciplines and integrating AI tools. We believe that creativity and AI are the keys to a successful future and our courses help equip students with the skills they need to succeed in a continuously evolving world. Our seasoned instructors bring real-world experience to the virtual classroom and our interactive lessons help students reinforce their learning with hands-on activities.No matter your background, from beginners to experts, hobbyists to professionals, Leap Year Learning is here to bring in the future of creativity, productivity, and learning!
Read more
  • 0
  • 0
  • 5677
Modal Close icon
Modal Close icon