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

How-To Tutorials

7018 Articles
article-image-developing-entity-metadata-wrappers
Packt
07 Aug 2013
8 min read
Save for later

Developing with Entity Metadata Wrappers

Packt
07 Aug 2013
8 min read
(For more resources related to this topic, see here.) Introducing entity metadata wrappers Entity metadata wrappers, or wrappers for brevity, are PHP wrapper classes for simplifying code that deals with entities. They abstract structure so that a developer can write code in a generic way when accessing entities and their properties. Wrappers also implement PHP iterator interfaces, making it easy to loop through all properties of an entity or all values of a multiple value property. The magic of wrappers is in their use of the following three classes: EntityStructureWrapper EntityListWrapper EntityValueWrapper The first has a subclass, EntityDrupalWrapper, and is the entity structure object that you'll deal with the most. Entity property values are either data, an array of values, or an array of entities. The EntityListWrapper class wraps an array of values or entities. As a result, generic code must inspect the value type before doing anything with a value, in order to prevent exceptions from being thrown. Creating an entity metadata wrapper object Let's take a look at two hypothetical entities that expose data from the following two database tables: ingredient recipe_ingredient The ingredient table has two fields: iid and name. The recipe_ingredient table has four fields: riid, iid , qty , and qty_unit. The schema would be as follows: Schema for ingredient and recipe_ingredient tables To load and wrap an ingredient entity with an iid of 1 and, we would use the following line of code: $wrapper = entity_metadata_wrapper('ingredient', 1); To load and wrap a recipe_ingredient entity with an riid of 1, we would use this line of code: $wrapper = entity_metadata_wrapper('recipe_ingredient', 1); Now that we have a wrapper, we can access the standard entity properties. Standard entity properties The first argument of the entity_metadata_wrapper function is the entity type, and the second argument is the entity identifier, which is the value of the entity's identifying property. Note, that it is not necessary to supply the bundle, as identifiers are properties of the entity type. When an entity is exposed to Drupal, the developer selects one of the database fields to be the entity's identifying property and another field to be the entity's label property. In our previous hypothetical example, a developer would declare iid as the identifying property and name as the label property of the ingredient entity. These two abstract properties, combined with the type property, are essential for making our code apply to multiple data structures that have different identifier fields. Notice how the phrase "type property" does not format the word "property"? That is not a typographical error. It is indicating to you that type is in fact the name of the property storing the entity's type. The other two, identifying property and label property are metadata in the entity declaration. The metadata is used by code to get the correct name for the properties on each entity in which the identifier and label are stored. To illustrate this, consider the following code snippet: $info = entity_get_info($entity_type);$key = isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'];return isset($entity->$key) ? $entity->$key : NULL; Shown here is a snippet of the entity_id() function in the entity module. As you can see, the entity information is retrieved at the first highlight, then the identifying property name is retrieved from that information at the second highlight. That name is then used to retrieve the identifier from the entity. Note that it's possible to use a non-integer identifier, so remember to take that into account for any generic code. The label property can either be a database field name or a hook. The entity exposing developer can declare a hook that generates a label for their entity when the label is more complicated, such as what we would need for recipe_ingredient. For that, we would need to combine the qty, qty_unit, and the name properties of the referenced ingredient. Entity introspection In order to see the properties that an entity has, you can call the getPropertyInfo() method on the entity wrapper. This may save you time when debugging. You can have a look by sending it to devel module's dpm() function or var_dump: dpm($wrapper->getPropertyInfo());var_dump($wrapper->getPropertyInfo()); Using an entity metadata wrapper The standard operations for entities are CRUD: create, retrieve, update, and delete. Let's look at each of these operations in some example code. The code is part of the pde module's Drush file: sites/all/modules/pde/pde.drush.inc. Each CRUD operation is implemented in a Drush command, and the relevant code is given in the following subsections. Before each code example, there are two example command lines. The first shows you how to execute the Drush command for the operation. ; the second is the help command. Create Creation of entities is implemented in the drush_pde_entity_create function. Drush commands The following examples show the usage of the entity-create ( ec) Drush command and how to obtain help documentation for the command: $ drush ec ingredient '{"name": "Salt, pickling"}'$ drush help ec Code snippet $entity = entity_create($type, $data);// Can call $entity->save() here or wrap to play and save$wrapper = entity_metadata_wrapper($type, $entity);$wrapper->save(); In the highlighted lines we create an entity, wrap it, and then save it. The first line uses entity_create, to which we pass the entity type and an associative array having property names as keys and their values. The function returns an object that has Entity as its base class. The save() method does all the hard work of storing our entity in the database. No more calls to db_insert are needed! Whether you use the save() method on the wrapper or on the Entity object really depends on what you need to do before and after the save() method call. For example, if you need to plug values into fields before you save the entity, it's handy to use a wrapper. Retrieve The retrieving (reading) of entities is implemented in the drush_pde_print_entity() function. Drush commands The following examples show the usage of the entity-read (er) Drush command and how to obtain help documentation for the command. $ drush er ingredient 1$ drush help er Code snippet $header = ' Entity (' . $wrapper->type();$header .= ') - ID# '. $wrapper->getIdentifier().':';// equivalents: $wrapper->value()->entityType()// $wrapper->value()->identifier()$rows = array();foreach ($wrapper as $pkey => $property) { // $wrapper->$pkey === $property if (!($property instanceof EntityValueWrapper)) { $rows[$pkey] = $property->raw() . ' (' . $property->label() . ')'; } else { $rows[$pkey] = $property->value(); }} On the first highlighted line, we call the type() method of the wrapper, which returns the wrapped entity's type. The wrapped Entity object is returned by the value() method of the wrapper. Using wrappers gives us the wrapper benefits, and we can use the entity object directly! The second highlighted line calls the getIdentifier() method of the wrapper. This is the way in which you retrieve the entity's ID without knowing the identifying property name. We'll discuss more about the identifying property of an entity in a moment. Thanks to our wrapper object implementing the IteratorAggregate interface , we are able to use a foreach statement to iterate through all of the entity properties. Of course, it is also possible to access a single property by using its key. For example, to access the name property of our hypothetical ingredient entity, we would use $wrapper->name. The last three highlights are the raw(), label(), and value() method calls. The distinction between these is very important, and is as follows: raw(): This returns the property's value straight from the database. label(): This returns value of an entity's label property. For example, name. value(): This returns a property's wrapped data: either a value or another wrapper. Finally, the highlighted raw() and value() methods retrieve the property values for us. These methods are interchangeable when simple entities are used, as there's no difference between the storage value and property value. However, for complex properties such as dates, there is a difference. Therefore, as a rule of thumb, always use the value() method unless you absolutely need to retrieve the storage value. The example code is using the raw() method only so we that can explore it, and all remaining examples in this book will stick to the rule of thumb. I promise! Storage value: This is the value of a property in the underlying storage media. for example, database. Property value: This is the value of a property at the entity level after the value is converted from its storage value to something more pleasing. For example, date formatting of a Unix timestamp. Multi-valued properties need a quick mention here. Reading these is quite straightforward, as they are accessible as an array. You can use Array notation to get an element, and use a foreach to loop through them! The following is a hypothetical code snippet to illustrate this: $output = 'First property: ';$output .= $wrapper->property[0]->value();foreach ($wrapper->property as $vwrapper) { $output .= $vwrapper->value();} Summary This article delved into development using entity metadata wrappers for safe CRUD operations and entity introspection. Resources for Article: Further resources on this subject: Microsoft SQL Server 2008 R2 MDS: Creating and Using Models [Article] EJB 3 Entities [Article] ADO.NET Entity Framework [Article]
Read more
  • 0
  • 0
  • 3124

article-image-setting-node
Packt
07 Aug 2013
10 min read
Save for later

Setting up Node

Packt
07 Aug 2013
10 min read
(For more resources related to this topic, see here.) System requirements Node runs on POSIX-like operating systems, the various UNIX derivatives (Solaris, and so on), or workalikes (Linux, Mac OS X, and so on), as well as on Microsoft Windows, thanks to the extensive assistance from Microsoft. Indeed, many of the Node built-in functions are direct corollaries to POSIX system calls. It can run on machines both large and small, including the tiny ARM devices such as the Raspberry Pi microscale embeddable computer for DIY software/hardware projects. Node is now available via package management systems, limiting the need to compile and install from source. Installing from source requires having a C compiler (such as GCC), and Python 2.7 (or later). If you plan to use encryption in your networking code you will also need the OpenSSL cryptographic library. The modern UNIX derivatives almost certainly come with these, and Node's configure script (see later when we download and configure the source) will detect their presence. If you should have to install them, Python is available at http://python.org and OpenSSL is available at http://openssl.org. Installing Node using package managers The preferred method for installing Node, now, is to use the versions available in package managers such as apt-get, or MacPorts. Package managers simplify your life by helping to maintain the current version of the software on your computer and ensuring to update dependent packages as necessary, all by typing a simple command such as apt-get update. Let's go over this first. Installing on Mac OS X with MacPorts The MacPorts project (http://www.macports.org/) has for years been packaging a long list of open source software packages for Mac OS X, and they have packaged Node. After you have installed MacPorts using the installer on their website, installing Node is pretty much this simple: $ sudo port search nodejs nodejs @0.10.6 (devel, net) Evented I/O for V8 JavaScript nodejs-devel @0.11.2 (devel, net) Evented I/O for V8 JavaScript Found 2 ports. -- npm @1.2.21 (devel) node package manager $ sudo port install nodejs npm .. long log of downloading and installing prerequisites and Node Installing on Mac OS X with Homebrew Homebrew is another open source software package manager for Mac OS X, which some say is the perfect replacement for MacPorts. It is available through their home page at http://mxcl.github.com/homebrew/. After installing Homebrew using the instructions on their website, using it to install Node is as simple as this: $ brew search node leafnode node $ brew install node ==> Downloading http://nodejs.org/dist/v0.10.7/node-v0.10.7.tar.gz ######################################################################## 100.0% ==> ./configure –prefix=/usr/local/Cellar/node/0.10.7 ==> make install ==> Caveats Homebrew installed npm. We recommend prepending the following path to your PATH environment variable to have npm-installed binaries picked up: /usr/local/share/npm/bin ==> Summary /usr/local/Cellar/node/0.10.7: 870 files, 16M, built in 21.9 minutes Installing on Linux from package management systems While it's still premature for Linux distributions or other operating systems to prepackage Node with their OS, that doesn't mean you cannot install it using the package managers. Instructions on the Node wiki currently list packaged versions of Node for Debian, Ubuntu, OpenSUSE, and Arch Linux. See: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager For example, on Debian sid (unstable): # apt-get update # apt-get install nodejs # Documentation is great. And on Ubuntu: # sudo apt-get install python-software-properties # sudo add-apt-repository ppa:chris-lea/node.js # sudo apt-get update # sudo apt-get install nodejs npm We can expect in due course that the Linux distros and other operating systems will routinely bundle Node into the OS like they do with other languages today. Installing the Node distribution from nodejs.org The nodejs.org website offers prebuilt binaries for Windows, Mac OS X, Linux, and Solaris. You simply go to the website, click on the Install button, and run the installer. For systems with package managers, such as the ones we've just discussed, it's preferable to use that installation method. That's because you'll find it easier to stay up-to-date with the latest version. However, on Windows this method may be preferred. For Mac OS X, the installer is a PKG file giving the typical installation process. For Windows, the installer simply takes you through the typical install wizard process. Once finished with the installer, you have a command line tool with which to run Node programs. The pre-packaged installers are the simplest ways to install Node, for those systems for which they're available. Installing Node on Windows using Chocolatey Gallery Chocolatey Gallery is a package management system, built on top of NuGet. Using it requires a Windows machine modern enough to support the Powershell and the .NET Framework 4.0. Once you have Chocolatey Gallery (http://chocolatey.org/), installing Node is as simple as this: C:> cinst install nodejs Installing the StrongLoop Node distribution StrongLoop (http://strongloop.com) has put together a supported version of Node that is prepackaged with several useful tools. This is a Node distribution in the same sense in which Fedora or Ubuntu are Linux distributions. StrongLoop brings together several useful packages, some of which were written by StrongLoop. StrongLoop tests the packages together, and distributes installable bundles through their website. The packages in the distribution include Express, Passport, Mongoose, Socket.IO, Engine.IO, Async, and Request. We will use all of those modules in this book. To install, navigate to the company home page and click on the Products link. They offer downloads of precompiled packages for both RPM and Debian Linux systems, as well as Mac OS X and Windows. Simply download the appropriate bundle for your system. For the RPM bundle, type the following: $ sudo rpm -i bundle-file-name For the Debian bundle, type the following: $ sudo dpkg -i bundle-file-name The Windows or Mac bundles are the usual sort of installable packages for each system. Simply double-click on the installer bundle, and follow the instructions in the install wizard. Once StrongLoop Node is installed, it provides not only the nodeand npmcommands (we'll go over these in a few pages), but also the slnodecommand. That command offers a superset of the npmcommands, such as boilerplate code for modules, web applications, or command-line applications. Installing from source on POSIX-like systems Installing the pre-packaged Node distributions is currently the preferred installation method. However, installing Node from source is desirable in a few situations: It could let you optimize the compiler settings as desired It could let you cross-compile, say for an embedded ARM system You might need to keep multiple Node builds for testing You might be working on Node itself Now that you have the high-level view, let's get our hands dirty mucking around in some build scripts. The general process follows the usual configure, make, and makeinstallroutine that you may already have performed with other open source software packages. If not, don't worry, we'll guide you through the process. The official installation instructions are in the Node wiki at https://github.com/joyent/node/wiki/Installation. Installing prerequisites As noted a minute ago, there are three prerequisites, a C compiler, Python, and the OpenSSL libraries. The Node installation process checks for their presence and will fail if the C compiler or Python is not present. The specific method of installing these is dependent on your operating system. These commands will check for their presence: $ cc --version i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3) Copyright (C) 2007 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ python Python 2.6.6 (r266:84292, Feb 15 2011, 01:35:25) [GCC 4.2.1 (Apple Inc. build 5664)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> Installing developer tools on Mac OS X The developer tools (such as GCC) are an optional installation on Mac OS X. There are two ways to get those tools, both of which are free. On the OS X installation DVD is a directory labeled Optional Installs, in which there is a package installer for—among other things—the developer tools, including Xcode. The other method is to download the latest copy of Xcode (for free) from http://developer.apple.com/xcode/. Most other POSIX-like systems, such as Linux, include a C compiler with the base system. Installing from source for all POSIX-like systems First, download the source from http://nodejs.org/download. One way to do this is with your browser, and another way is as follows: $ mkdir src $ cd src $ wget http://nodejs.org/dist/v0.10.7/node-v0.10.7.tar.gz $ tar xvfz node-v0.10.7.tar.gz $ cd node-v0.10.7 The next step is to configure the source so that it can be built. It is done with the typical sort of configure script and you can see its long list of options by running the following: $ ./configure –help. To cause the installation to land in your home directory, run it this way: $ ./configure –prefix=$HOME/node/0.10.7 ..output from configure If you want to install Node in a system-wide directory simply leave off the -prefixoption, and it will default to installing in /usr/local. After a moment it'll stop and more likely configure the source tree for installation in your chosen directory. If this doesn't succeed it will print a message about something that needs to be fixed. Once the configure script is satisfied, you can go on to the next step. With the configure script satisfied, compile the software: $ make .. a long log of compiler output is printed $ make install If you are installing into a system-wide directory do the last step this way instead: $ make $ sudo make install Once installed you should make sure to add the installation directory to your PATHvariable as follows: $ echo 'export PATH=$HOME/node/0.10.7/bin:${PATH}' >>~/.bashrc $ . ~/.bashrc For cshusers, use this syntax to make an exported environment variable: $ echo 'setenv PATH $HOME/node/0.10.7/bin:${PATH}' >>~/.cshrc $ source ~/.cshrc This should result in some directories like this: $ ls ~/node/0.10.7/ bin include lib share $ ls ~/node/0.10.7/bin node node-waf npm Maintaining multiple Node installs simultaneously Normally you won't have multiple versions of Node installed, and doing so adds complexity to your system. But if you are hacking on Node itself, or are testing against different Node releases, or any of several similar situations, you may want to have multiple Node installations. The method to do so is a simple variation on what we've already discussed. If you noticed during the instructions discussed earlier, the –prefixoption was used in a way that directly supports installing several Node versions side-by-side in the same directory: $ ./configure –prefix=$HOME/node/0.10.7 And: $ ./configure –prefix=/usr/local/node/0.10.7 This initial step determines the install directory. Clearly when Version 0.10.7, Version 0.12.15, or whichever version is released, you can change the install prefix to have the new version installed side-by-side with the previous versions. To switch between Node versions is simply a matter of changing the PATHvariable (on POSIX systems), as follows: $ export PATH=/usr/local/node/0.10.7/bin:${PATH} It starts to be a little tedious to maintain this after a while. For each release, you have to set up Node, npm, and any third-party modules you desire in your Node install; also the command shown to change your PATHis not quite optimal. Inventive programmers have created several version managers to make this easier by automatically setting up not only Node, but npmalso, and providing commands to change your PATHthe smart way: Node version manager: https://github.com/visionmedia/n Nodefront, aids in rapid frontend development: http://karthikv.github.io/nodefront/
Read more
  • 0
  • 0
  • 3130

Packt
07 Aug 2013
13 min read
Save for later

.NET 4.5 Parallel Extensions – Async

Packt
07 Aug 2013
13 min read
(For more resources related to this topic, see here.) Creating an async method The TAP is a new pattern for asynchronous programming in .NET Framework 4.5. It is based on a task, but in this case a task doesn't represent work which will be performed on another thread. In this case, a task is used to represent arbitrary asynchronous operations. Let's start learning how async and await work by creating a Windows Presentation Foundation (WPF ) application that accesses the web using HttpClient. This kind of network access is ideal for seeing TAP in action. The application will get the contents of a classic book from the web, and will provide a count of the number of words in the book. How to do it… Let's go to Visual Studio 2012 and see how to use the async and await keywords to maintain a responsive UI by doing the web communications asynchronously. Start a new project using the WPF Application project template and assign WordCountAsync as Solution name . Begin by opening MainWindow.xaml and adding the following XAML to create a simple user interface containing Button and TextBlock: <Window x_Class="WordCountAsync.MainWindow" Title="WordCountAsync" Height="350" Width="525"> <Grid> <Button x_Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="219,195,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <TextBlock x_Name="TextResults" HorizontalAlignment="Left" Margin="60,28,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="139" Width="411"/> </Grid> </Window> Next, open up MainWindow.xaml.cs. Go to the Project and add a reference to System.Net.Http. Add the following using directives to the top of your MainWindow class: using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows; At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array. char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' }; Add a button click event for the StartButton and add the async modifier to the method signature to indicate that this will be a async method. Please note that async methods that return void are normally only used for event handlers, and should be avoided. private async void StartButton_Click(object sender, RoutedEventArgs e) { } Next, let's create a async method called GetWordCountAsync that returns Task<int>. This method will create HttpClient and call its GetStringAsync method to download the book contents as a string. It will then use the Split method to split the string into a wordArray. We can return the count of the wordArray as our return value. public async Task<int> GetWordCountAsync() { TextResults.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); var bookContents = await client.GetStringAsync(@"http://www.gutenberg.org/files/2009/2009.txt"); var wordArray = bookContents.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } Finally, let's complete the implementation of our button click event. The Click event handler will just call GetWordCountAsync with the await keyword and display the results to TextBlock. private async void StartButton_Click(object sender, RoutedEventArgs e) { var result = await GetWordCountAsync(); TextResults.Text += String.Format("Origin of Species word count: {0}",result); } In Visual Studio 2012, press F5 to run the project. Click on the Start button, and your application should appear as shown in the following screenshot: How it works… In the TAP, asynchronous methods are marked with an async modifier. The async modifier on a method does not mean that the method will be scheduled to run asynchronously on a worker thread. It means that the method contains control flow that involves waiting for the result of an asynchronous operation, and will be rewritten by the compiler to ensure that the asynchronous operation can resume this method at the right spot. Let me try to put this a little more simply. When you add the async modifier to a method, it indicates that the method will wait on an asynchronous code to complete. This is done with the await keyword. The compiler actually takes the code that follows the await keyword in an async method and turns it into a continuation that will run after the result of the async operation is available. In the meantime, the method is suspended, and control returns to the method's caller. If you add the async modifier to a method, and then don't await anything, it won't cause an error. The method will simply run synchronously. An async method can have one of the three return types: void, Task, or Task<TResult>. As mentioned before, a task in this context doesn't mean that this is something that will execute on a separate thread. In this case, task is just a container for the asynchronous work, and in the case of Task<TResult>, it is a promise that a result value of type TResult will show up after the asynchronous operation completes. In our application, we use the async keyword to mark the button click event handler as asynchronous, and then we wait for the GetWordCountAsync method to complete by using the wait keyword. private async void StartButton_Click(object sender, RoutedEventArgs e) { StartButton.Enabled = false; var result = await GetWordCountAsync(); TextResults.Text += String.Format("Origin of Species word count: {0}", .................. result); StartButton.Enabled = true; } The code that follows the await keyword, in this case, the same line of code that updates TextBlock, is turned by the compiler into a continuation that will run after the integer result is available. If the Click event is fired again while this asynchronous task is in progress, another asynchronous task is created and awaited. To prevent this, it is a common practice to disable the button that is clicked. It is a convention to name an asynchronous method with an Async postfix, as we have done with GetWordCountAsync. Handling Exceptions in asynchronous code So how would you add Exception handling to code that is executed asynchronously? In previous asynchronous patterns, this was very difficult to achieve. In C# 5.0 it is much more straightforward because you just have to wrap the asynchronous function call with a standard try/catch block. On the surface this sounds easy, and it is, but there is more going on behind the scene that will be explained right after we build our next example application. For this recipe, we will return to our classic books word count scenario, and we will be handling an Exception thrown by HttpClient when it tries to get the book contents using an incorrect URL. How to do it… Let's build another WPF application and take a look at how to handle Exceptions when something goes wrong in one of our asynchronous methods. Start a new project using the WPF Application project template and assign AsyncExceptions as Solution name . Begin by opening MainWindow.xaml and adding the following XAML to create a simple user interface containing Button and a TextBlock: <Window x_Class="WordCountAsync.MainWindow" Title="WordCountAsync" Height="350" Width="525"> <Grid> <Button x_Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="219,195,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <TextBlock x_Name="TextResults" HorizontalAlignment="Left" Margin="60,28,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="139" Width="411"/> </Grid> </Window> Next, open up MainWindow.xaml.cs. Go to the Project Explorer , right-click on References , click on Framework from the menu on the left side of the Reference Manager , and then add a reference to System.Net.Http. Add the following using directives to the top of your MainWindow class: using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows; At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array. char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' }; Now let's create our GetWordCountAsync method. This method will be very similar to the last recipe, but it will be trying to access the book on an incorrect URL. The asynchronous code will be wrapped in a try/catch block to handle Exception. We will also use a finally block to dispose of HttpClient. public async Task<int> GetWordCountAsync() { ResultsTextBlock.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); try { var bookContents = await client.GetStringAsync(@"http://www.gutenberg.org/files/2009/No_Book_Here.txt"); var wordArray = bookContents.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } catch (Exception ex) { ResultsTextBlock.Text += String.Format("An error has occurred: {0} \n", ex.Message); return 0; } finally { client.Dispose(); } } Finally, let create the Click event handler for our StartButton. This is pretty much the same as the last recipe, just wrapped in a try/catch block. Don't forget to add the async modifier to the method signature. private async void StartButton_Click(object sender, RoutedEventArgs e) { try { var result = await GetWordCountAsync(); ResultsTextBlock.Text += String.Format("Origin of Species word count: {0}", result); } catch(Exception ex) { ResultsTextBlock.Text += String.Format("An error has occurred: {0} \n", ex.Message); } } Now, in Visual Studio 2012, press F5 to run the project. Click on the Start button. Your application should appear as shown in the following screenshot: How it works… Wrapping your asynchronous code in a try/catch block is pretty easy. In fact, it hides some of the complex work Visual Studio 2012 to doing for us. To understand this, you need to think about the context in which your code is running. When the TAP is used in Windows Forms or WPF applications, there's already a context that the code is running in, such as the message loop UI thread. When async calls are made in those applications, the awaited code goes off to do its work asynchronously and the async method exits back to its caller. In other words, the program execution returns to the message loop UI thread. The Console applications don't have the concept of a context. When the code hits an awaited call inside the try block, it will exit back to its caller, which in this case is Main. If there is no more code after the awaited call, the application ends without the async method ever finishing. To alleviate this issue, Microsoft included async compatible context with the TAP that is used for Console apps or unit test apps to prevent this inconsistent behavior. This new context is called GeneralThreadAffineContext. Do you really need to understand these context issues to handle async Exceptions? No, not really. That's part of the beauty of the Task-based Asynchronous Pattern. Cancelling an asynchronous operation In .NET 4.5, asynchronous operations can be cancelled in the same way that parallel tasks can be cancelled, by passing in CancellationToken and calling the Cancel method on CancellationTokenSource. In this recipe, we are going to create a WPF application that gets the contents of a classic book over the web and performs a word count. This time though we are going to set up a Cancel button that we can use to cancel the async operation if we don't want to wait for it to finish. How to do it… Let's create a WPF application to show how we can add cancellation to our asynchronous methods. Start a new project using the WPF Application project template and assign AsyncCancellation as Solution name . Begin by opening MainWindow.xaml and adding the following XAML to create our user interface. In this case, the UI contains TextBlock, StartButton, and CancelButton. <Window x_Class="AsyncCancellation.MainWindow" Title="AsyncCancellation" Height="400" Width="599"> <Grid Width="600" Height="400"> <Button x_Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="142,183,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="-0.2,0.45" Click="StartButton_Click"/> <Button x_Name="CancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="379,185,0,0" VerticalAlignment="Top" Width="75" Click="CancelButton_Click"/> <TextBlock x_Name="TextResult" HorizontalAlignment="Left" Margin="27,24,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="135" Width="540"/> </Grid> </Window> Next, open up MainWindow.xaml.cs, click on the Project Explorer , and add a reference to System.Net.Http. Add the following using directives to the top of your MainWindow class: using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Windows; At the top of the MainWindow class, add a character array constant that will be used to split the contents of the book into a word array. char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' }; Next, let's create the GetWordCountAsync method. This method is very similar to the method explained before. It needs to be marked as asynchronous with the async modifier and it returns Task<int>. This time however, the method takes a CancellationToken parameter. We also need to use the GetAsync method of HttpClient instead of the GetStringAsync method, because the former supports cancellation, whereas the latter does not. We will add a small delay in the method so we have time to cancel the operation before the download completes. public async Task<int> GetWordCountAsync(CancellationToken ct) { TextResult.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); await Task.Delay(500); try { HttpResponseMessage response = await client.GetAsync(@"http://www.gutenberg.org/files/2009/2009.txt", ct); var words = await response.Content.ReadAsStringAsync(); var wordArray = words.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } finally { client.Dispose(); } } Now, let's create the Click event handler for our CancelButton. This method just needs to check if CancellationTokenSource is null, and if not, it calls the Cancel method. private void CancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } } Ok, let's finish up by adding a Click event handler for StartButton. This method is the same as explained before, except we also have a catch block that specifically handles OperationCancelledException. Don't forget to mark the method with the async modifier. public async Task<int> GetWordCountAsync(CancellationToken ct) { TextResult.Text += "Getting the word count for Origin of Species...\n"; var client = new HttpClient(); await Task.Delay(500); try { HttpResponseMessage response = await client.GetAsync(@"http://www.gutenberg.org/files/2009/2009.txt", ct); var words = await response.Content.ReadAsStringAsync(); var wordArray = words.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); return wordArray.Count(); } finally { client.Dispose(); } } In Visual Studio 2012, press F5 to run the project Click on the Start button, then the Cancel button. Your application should appear as shown in the following screenshot: How it works… Cancellation is an aspect of user interaction that you need to consider to build a professional async application. In this example, we implemented cancellation by using a Cancel button, which is one of the most common ways to surface cancellation functionality in a GUI application. In this recipe, cancellation follows a very common flow. The caller (start button click event handler) creates a CancellationTokenSource object. private async void StartButton_Click(object sender, RoutedEventArgs e) { cts = new CancellationTokenSource(); ... } The caller calls a cancelable method, and passes CancellationToken from CancellationTokenSource (CancellationTokenSource.Token). public async Task<int> GetWordCountAsync(CancellationToken ct ) { ... HttpResponseMessage response = await client.GetAsync(@"http://www.gutenberg.org/files/2009/2009.txt", ct ); ... } The cancel button click event handler requests cancellation using the CancellationTokenSource object (CancellationTokenSource.Cancel()). private void CancelButton_Click(object sender, RoutedEventArgs e) { if (cts != null) { cts.Cancel(); } } The task acknowledges the cancellation by throwing OperationCancelledException, which we handle in a catch block in the start button click event handler.
Read more
  • 0
  • 0
  • 13941

article-image-so-what-metasploit
Packt
06 Aug 2013
9 min read
Save for later

So, what is Metasploit?

Packt
06 Aug 2013
9 min read
(For more resources related to this topic, see here.) In the IT industry, we have various flavors of operating systems ranging from Mac, Windows, *nix platforms, and other server operating systems, which run an n number of services depending on the needs of the organization. When given a task to assess the risk factor of any organization, it becomes very tedious to run single code snippets against these systems. What if, due to some hardware failure, all these code snippets are lost? Enter Metasploit. Metasploit is an exploit development framework started by H. D. Moore in 2003, which was later acquired by Rapid7. It is basically a tool for the development of exploits and the testing of these exploits on live targets. This framework has been completely written using Ruby,and is currently one of the largest frameworks ever written in the Ruby language. The tool houses more than 800 exploits in its repository and hundreds of payloads for each exploit. This also contains various encoders, which help us in the obfuscation of exploits to evade the antivirus and other intrusion detection systems ( IDS ). As we progress in this book, we shall uncover more and more features of this tool. This tool can be used for penetration testing, risk assessment, vulnerability research, and other security developmental practices such as IDS and the intrusion prevention system ( IPS ). Top features you need to know about After learning about the basics of the Metasploit framework, in this article we will find out the top features of Metasploit and learn some of the attack scenarios. This article will be a flow of the following features: The meterpreter module Using auxiliary modules in Metasploit Client-side attacks with auxiliary modules The meterpreter module In the earlier article, we have seen how to open up a meterpreter session in Metasploit. But in this article, we shall see the features of the meterpreter module and its command set in detail. Before we see the working example, let's see why meterpreter is used in exploitation: It doesn't create a new process in the target system It runs in the context of the process that is being exploited It performs multiple tasks in one go; that is, you don't have to create separate requests for each individual task It supports scripts writing Let's check out what the meterpreter shell looks like. Meterpreter allows you to provide commands and obtain results. Let's see the list of commands that are available to use under meterpreter. These can be obtained by typing help in the meterpreter command shell. The syntax for this command is as follows: meterpreter>help The following screenshot represents the core commands: The filesystem commands are as follows: The networking commands are as follows: The system commands are as follows: The user interface commands are as follows: The other miscellaneous commands are as follows: As you can see in the preceding screenshots, meterpreter has two sets of commands set apart from its core set of commands. They are as follows: Stdapi Priv The Stdapi command set contains various commands for the filesystem commands, networking commands, system commands, and user-interface commands. Depending on the exploit, if it can get higher privileges, the priv command set is loaded. By default, the stdapi command set and core command set gets loaded irrespective of the privilege an exploit gets. Let's check out the route command from the meterpreter stdapi command set. The syntax is as follows: meterpreter>route [–h] command [args] In the following screenshot, we can see the list of all the routes on the target machine: In a scenario where we wish to add other subnets and gateways we can use the concept of pivoting, where we add a couple of routes for optimizing the attack. The following are the commands supported by the route: Add [subnet] [netmask] [gateway]Delete [subnet] [netmask] [gateway] List Another command that helps during pivoting is port-forwarding. Meterpreter supports port forwarding via the following command. The syntax for this command is as follows: meterpreter>portfwd [-h] [add/delete/list] [args] As soon as an attacker breaks into any system, the first thing that he/she does is check what privilege levels he/she has to access the system. Meterpreter provides a command for working out the privilege level after breaking into the system. The syntax for this command is as follows: meterpreter>getuid The following screenshot demonstrates the working of getuid in meterpreter. In the following screenshot, the attacker is accessing the system with the SYSTEM privilege. In a Windows environment, the SYSTEM privilege is the highest possible privilege available. Suppose we failed to get access to the system as a SYSTEM user, but succeeded in getting access via the administrator, then meterpreter provides you with many ways to elevate your access levels. This is called privilege escalation. The commands are as follows: Syntax: meterpreter>getsystem Syntax: meterpreter>migrate process_id Syntax: meterpreter>steal_token process_id The first method uses an internal procedure within the meterpreter to gain the system access, whereas in the second method, we are migrating to a process that is running with a SYSTEM privilege. In this case, the exploit by default gets loaded in any process space of the Windows operating system. But, there is always a possibility that the user clears that process space by deleting that process from the process manager. In a case like this, it's wise to migrate to a process which is usually untouched by the user. This helps in maintaining a prolonged access to the victim machine. In the third method, we are actually impersonating a process which is running as a SYSTEM privileged process. This is called impersonation via token stealing. Basically, Windows assigns users with a unique ID called Secure Identifier (SID). Each thread holds a token containing information about the privilege levels. Impersonating a token happens when one particular thread temporarily assumes the identity of another process in the same system. We have seen the usage of process IDs in the preceding commands, but how do we fetch the process ID? That is exactly what we I shall be covering in this article. Windows runs various processes and the exploit itself will be running in the process space of the Windows system. To list all these processes with their PIDs and the privilege levels, we use the following meterpreter command: meterpreter>ps The following screenshot gives a clear picture of the ps command: In the preceding screenshot, we have the PIDs listed. We can use these PIDs to escalate our privileges. Once you steal a token, it can be dropped using the Drop_token command. The syntax for this command is as follows: meterpreter>drop_token Another interesting command from the stdapi set is the shell command. This spawns a shell in the target system and enables us to navigate through the system effortlessly. The syntax for this command is as follows: meterpreter>shell The following screenshot shows the usage of the shell command: The preceding screenshot shows that we are inside the target system. All the usual windows command shell scripts such as dir, cd, and md work here. After briefly covering system commands, let's start learning the filesystem commands. A filesystem contains a working directory. To find out the current working directory in the target system, we use the following command: meterpreter>pwd The following screenshot shows the command in action: Suppose you wish to search for different files on the target system, then we can use a command called search. The syntax for this command is as follows: meterpreter> search [-d dir][-r recurse] –f pattern Various options available under the search command are: -d: This is the directory to begin the search. If nothing is specified, then it searches all drives. -f: The pattern that we would like to search for. For example, *.pdf. -h: Provides the help context. -r: Used when we need to recursively search the subdirectories. By default this is set to true. Once we get the file we need, we use the download command to download it to our drive. The syntax for this command is as follows: meterpreter>download Full_relative_path By now we have covered the core commands, system commands, networking commands, and filesystem commands. The last article of the stdapi command set is the user-interface commands. The most commonly used commands are the keylogging commands. These commands are very effective in sniffing user account credentials: Syntax: meterpreter>keyscan_start Syntax: meterpreter>keyscan_dump Syntax: meterpreter>keyscan_stop This is the procedure of the usage of this command. The following screenshot explains the commands in action: The communication between the meterpreter and its targets is done via type-length-value. This means that the data is getting transferred in an encrypted manner. This leads to multiple channels of communications. The advantage of this is that multiple programs can communicate with an attacker. The creation of channels is illustrated in the following screenshot: The syntax for this command is as follows: meterpreter>execute process_name –c -c is the parameter that tells the meterpreter to channel the input/output. When the attack requires us to interact with multiple processes then the concept of channels comes in handy as a tool for the attacker. The close command is used to exit a channel. Summary In this article we learned what is Metaspoilt and also saw one of its top feature. Resources for Article: Further resources on this subject: Understanding the True Security Posture of the Network Environment being Tested [Article] Preventing Remote File Includes Attack on your Joomla Websites [Article] Tips and Tricks on BackTrack 4 [Article]
Read more
  • 0
  • 0
  • 10729

article-image-transforming-data
Packt
06 Aug 2013
17 min read
Save for later

Transforming Data

Packt
06 Aug 2013
17 min read
(For more resources related to this topic, see here.) Process overview Let's demonstrate this data transformation on an example. Imagine that we need to convert some legacy customer data into our new system. Since not all legacy data is perfect, we'll need a way to report data that, for some reason, we've failed to transfer. For example, our system allows only one address per customer. Legacy customers with more than one address will be reported. In this article we'll: Load customer data from the legacy system. The data will include address and account information. Run transformation rules over this data and build an execution report. Populate the domain model with transformed data, running validation rules (from the previous section) and saving it into our system. Getting the data As a good practice, we'll define an interface for interacting with the other system. We'll introduce a LegacyBankService interface for this purpose. It will make it easier to change the way we communicate with the legacy system, and also the tests will be easier to write. package droolsbook.transform.service;import java.util.List;import java.util.Map;public interface LegacyBankService { /** * @return all customers */ List<Map<String, Object>> findAllCustomers(); /** * @return addresses for specified customer id */ List<Map<String, Object>> findAddressByCustomerId( Long customerId); /** * @return accounts for specified customer id */ List<Map<String, Object>> findAccountByCustomerId( Long customerId);} Code listing 1: Interface that abstracts the legacy system interactions The interface defines three methods. The first one can retrieve a list of all customers, and the second and third ones retrieve a list of addresses and accounts for a specific customer. Each list contains zero or many maps. One map represents one object in the legacy system. The keys of this map are object property names (for example, addressLine1), and the values are the actual properties. We've chosen a map because it is a generic data type that can store almost any data, which is ideal for a data transformation task. However, it has a slight disadvantage in that the rules will be a bit harder to write. The implementation of this interface will be defined at the end of this article. Loading facts into the rule session Before writing some transformation rules, the data needs to be loaded into the rule session. This can be done by writing a specialized rule just for this purpose,as follows: package droolsbook.transform;import java.util.*;import droolsbook.transform.service.LegacyBankService;import droolsbook.bank.model.Address;import droolsbook.bank.model.Address.Country;global LegacyBankService legacyService;rule findAllCustomerswhen$customerMap : Map( )fromlegacyService.findAllCustomers()then$customerMap.put("_type_", "Customer");insert( $customerMap );end Code listing 2: Rule that loads all Customers into the rule session (the dataTransformation.drl file). The preceding findAllCustomers rule matches on a Map instance that is obtained from our legacyService. In the consequence part, it adds the type (so that we can recognize that this map represents a customer) and inserts this map into the session. There are a few things to be noted here, as follows: A rule is being used to insert objects into the rule session; this just shows a different way of loading objects into the rule session. Every customer returned from the findAllCustomers method is being inserted into the session. This is reasonable only if there is a small number of customers. If it is not the case, we can paginate, that is, process only N customers at once, then start over with the next N customers, and so on. Alternatively, the findAllCustomers rule can be removed and customers could be inserted into the rule session at session-creation time. We'll now focus on this latter approach (for example, only one Customer instance is in the rule session at any given time); it will make the reporting easier. A type of the map is being added to the map. This is a disadvantage of using a Map object for every type (Customer, Address, and so on): the type information is lost. It can be seen in the following rule that finds addresses for a customer: rule findAddressdialect "mvel" when $customerMap : Map( this["_type_"] == "Customer" ) $addressMap : Map( ) fromlegacyService.findAddressByCustomerId( (Long) $customerMap["customer_id"] ) then $addressMap.put("_type_", "Address"); insert( $addressMap )end Code listing 3: Rule that loads all Addresses for a Customer into the rule session (the dataTransformation.drl file) Let's focus on the first condition line. It matches on customerMap. It has to test if this Map object contains a customer's data by executing ["_type_"] == "Customer". To avoid doing these type checks in every condition, a new map can be extended from HashMap, for example LegacyCustomerHashMap. The rule might look like the following line of code: $customerMap :LegacyCustomerHashMap( ) The preceding line of code performs matching on the customer map without doing the type check. We'll continue with the second part of the condition. It matches on addressMap that comes from our legacyService as well. The from keyword supports parameterized service calls. customer_id is passed to the findAddressByCustomerId method. Another nice thing about this is that we don't have to cast the parameter to java.lang.Long; it is done automatically. The consequence part of this rule just sets the type and inserts addressMap into the knowledge session. Please note that only addresses for loaded customers are loaded into the session. This saves memory but it could also cause a lot of "chattiness" with the LegacyBankService interface if there are many child objects. It can be fixed by pre-loading those objects. The implementation of this interface is the right place for this. Similar data-loading rules can be written for other types, for example Account. Writing transformation rules Now that all objects are in the knowledge session, we can start writing some transformation rules. Let's imagine that in the legacy system there are many duplicate addresses. We can write a rule that removes such duplication: rule twoEqualAddressesDifferentInstancewhen$addressMap1 : Map( this["_type_"] == "Address" )$addressMap2 : Map( this["_type_"] == "Address",eval( $addressMap1 != $addressMap2 ),this == $addressMap1 )thenretract( $addressMap2 );validationReport.addMessage(reportFactory.createMessage(Message.Type.WARNING,kcontext.getRule().getName(), $addressMap2));end Code listing 4: Rule that loads all Addresses for a Customer into the rule session (file dataTransformation.drl) The rule matches two addresses. It checks that they don't have the same object identities by doing eval( $addressMap1 != $addressMap2 ). Otherwise, the rule could match on a single address instance. The next part, this == $addressMap1 , translates behind the scenes to $addressMap1.equal($addressMap2) . If this equal check is true that means one of the addresses is redundant and can be removed from the session. The address map that is removed is added to the report as a warning message. Testing Before we'll continue with the rest of the rules, we'll set up unit tests. We'll still use a stateless session: session = knowledgeBase.newStatelessKnowledgeSession();session.setGlobal("legacyService",newMockLegacyBankService()); Code listing 5: Section of the test setUpClass method (the DataTransformationTest file) The legacyService global is set to a new instance of MockLegacyBankService. It is a dummy implementation that simply returns null from all methods. In most tests we'll insert objects directly into the knowledge session (and not through legacyService ). We'll now write a helper method for inserting objects into the knowledge session and running the rules. The helper method will create a list of commands, execute them, and the returned object, BatchExecutionResults, will be returned back from the helper method. The following command instances will be created: One for setting the global variable – validationReport; a new validation report will be created. One for inserting all objects into the session. One for firing only rules with a specified name. This will be done through an AgendaFilter. It will help us isolate a rule that we'll be testing. org.drools.runtime.rule.AgendaFilter When a rule is activated the AgendaFilter determines if this rule can be fired or not. The AgendaFilter interface has one accept method that returns true/false. We'll create our own RuleNameEqualsAgendaFilter that fires only rules with a specific name. One command for getting back all objects in a knowledge session that are of a certain type – filterType method parameter. These objects will be returned from the helper method as part of the results object under a given key – filterOut method parameter. The following is the helper method: /*** creates multiple commands, calls session.execute and* returns results back*/protected ExecutionResults execute(Collection<?> objects,String ruleName, final String filterType,String filterOut) {ValidationReport validationReport = reportFactory.createValidationReport();List<Command<?>> commands = new ArrayList<Command<?>>();commands.add(CommandFactory.newSetGlobal("validationReport", validationReport, true));commands.add(CommandFactory.newInsertElements(objects));commands.add(new FireAllRulesCommand(new RuleNameEqualsAgendaFilter(ruleName)));if (filterType != null && filterOut != null) {GetObjectsCommand getObjectsCommand =new GetObjectsCommand( new ObjectFilter() {public boolean accept(Object object) {return object instanceof Map&& ((Map) object).get("_type_").equals(filterType);}});getObjectsCommand.setOutIdentifier(filterOut);commands.add(getObjectsCommand);}ExecutionResults results = session.execute(CommandFactory.newBatchExecution(commands));return results;} Code listing 6: Test helper method for executing the transformation rules (the DataTransformationTest file). To write a test for the redundant address rule, two address maps will be created. Both will have their street set to "Barrack Street". After we execute rules, only one address map should be in the rule session. The test looks as follows: @Test public void twoEqualAddressesDifferentInstance() throws Exception { Map addressMap1 = new HashMap(); addressMap1.put("_type_", "Address"); addressMap1.put("street", "Barrack Street"); Map addressMap2 = new HashMap(); addressMap2.put("_type_", "Address"); addressMap2.put("street", "Barrack Street"); assertEquals(addressMap1, addressMap2); ExecutionResults results = execute(Arrays.asList( addressMap1, addressMap2), "twoEqualAddressesDifferentInstance", "Address", "addresses"); Iterator<?> addressIterator = ((List<?>) results .getValue("addresses")).iterator(); Map addressMapWinner = (Map) addressIterator.next(); assertEquals(addressMap1, addressMapWinner); assertFalse(addressIterator.hasNext()); reportContextContains(results, "twoEqualAddressesDifferentInstance", addressMapWinner == addressMap1 ? addressMap2 : addressMap1); } Code listing 7: Test for the redundant address rule The execute method is called with the two address maps, the agenda filter rule name is set to twoEqualAddressesDifferentInstance (only this rule will be allowed to fire), and after the rules are executed all maps of the Address type are returned as part of the result. We can access them by results.getValue("addresses"). The test verifies that there is only one such map. Another test helper method – reportContextContains verifies that the validationReport contains expected data. The implementation method, reportContextContains, is shown as follows: /** * asserts that the report contains one message with * expected context (input parameter) */ void reportContextContains(ExecutionResults results, String messgeKey, Object object) { ValidationReport validationReport = (ValidationReport) results.getValue("validationReport"); assertEquals(1, validationReport.getMessages().size()); Message message = validationReport.getMessages() .iterator().next(); List<Object> messageContext = message.getContextOrdered(); assertEquals(1, messageContext.size()); assertSame(object, messageContext.iterator().next()); } Code listing 8: Helper method, which verifies that report the contains a supplied object Address normalization Our next rule will be a type conversion rule. It will take a String representation of country and it will convert it into Address.Countryenum. We'll start with a test: @Test public void addressNormalizationUSA() throws Exception { Map addressMap = new HashMap(); addressMap.put("_type_", "Address"); addressMap.put("country", "U.S.A"); execute(Arrays.asList(addressMap), "addressNormalizationUSA", null, null); assertEquals(Address.Country.USA, addressMap .get("country")); } Code listing 9: Test for the country type conversion rule The test creates an address map with country set to "U.S.A". It then calls the execute method, passing in the addressMap and allowing only the addressNormalizationUSA rule to fire (no filter is used in this case). Finally, the test verifies that the address map has the correct country value. Next, we'll write the rule: rule addressNormalizationUSAdialect "mvel" when $addressMap : Map( this["_type_"] == "Address", this["country"] in ("US", "U.S.", "USA", "U.S.A")) then modify( $addressMap ) { put("country", Country.USA) }end Code listing 10: Rule that converts String representation of country into enum representation(the dataTransformation.drl file) The rule matches an address map. The in operator is used to capture various country representations. The rule's consequence is interesting in this case. Instead of doing simply update( $addressMap ), the modify construct is being used. Modify takes an argument and a block of code. Before executing the block of code it retracts the argument from the rule session, then it executes the block of code, and finally the argument is inserted back into the session. This has to be done because the argument's identity is modified. If we look at the implementation of HashMap equals or the hashCode method, they take into account every element in the map. By doing $addressMap.put("country", Country.USA), we change the address map identity. Fact's identity As a general rule, do not change the object identity while it is in the knowledge session, otherwise the rule engine behavior will be undefined (same as changing an object while it is in java.util.HashMap). Testing the findAddress rule Before continuing, let's write a test for the findAddress rule from the third rule in the Loading facts into the rule session section. The test will use a special LegacyBankService mock implementation that will return the provided addressMap . public class StaticMockLegacyBankService extends MockLegacyBankService { private Map addressMap;public StaticMockLegacyBankService(Map addressMap) { this.addressMap = addressMap; } public List findAddressByCustomerId(Long customerId) { return Arrays.asList(addressMap); } } Code listing 11: StaticMockLegacyBankService which returns provided addressMap StaticMockLegacyBankService extends MockLegacyBankService and overrides the findAddressByCustomerId method. The findAddress test looks as follows: @Test public void findAddress() throws Exception { final Map customerMap = new HashMap(); customerMap.put("_type_", "Customer"); customerMap.put("customer_id", new Long(111)); final Map addressMap = new HashMap(); LegacyBankService service = new StaticMockLegacyBankService(addressMap); session.setGlobal("legacyService", service); ExecutionResults results = execute(Arrays .asList(customerMap), "findAddress", "Address", "objects"); assertEquals("Address", addressMap.get("_type_")); Iterator<?> addressIterator = ((List<?>) results .getValue("objects")).iterator(); assertEquals(addressMap, addressIterator.next()); assertFalse(addressIterator.hasNext()); // clean-up session.setGlobal("legacyService", new MockLegacyBankService()); } Code listing12: Test for the findAddress rule The test then verifies that the address map is really in the knowledge session. It also verifies that it has the "_type_" key set and that there is no other address map. Unknown country The next rule will create an error message if the country isn't recognizable by our domain model. The test creates an address map with some unknown country, executes rules, and verifies that the report contains an error. @Test public void unknownCountry() throws Exception { Map addressMap = new HashMap(); addressMap.put("_type_", "Address"); addressMap.put("country", "no country"); ExecutionResults results = execute(Arrays .asList(addressMap), "unknownCountry", null, null); ValidationReport report = (ValidationReport) results .getValue("validationReport"); reportContextContains(results, "unknownCountry", addressMap); } Code listing 13: Test for the unknownCountry rule The rule implementation will test if the country value from the addressMap is of the Address.Country type. If it isn't, an error is added to the report. rule unknownCountrysalience -10 //should fire after address normalizations when $addressMap : Map( this["_type_"] == "Address", !($addressMap.get("country") instanceof Address.Country)) then validationReport.addMessage( reportFactory.createMessage(Message.Type.ERROR, kcontext.getRule().getName(), $addressMap));end Code listing 14: Rule that reports unknown countries (the dataTransformation.drl file) The type checking is done with MVEL's instanceof operator. Note that this rule needs to be executed after all address normalization rules, otherwise we could get an incorrect error message. Currency conversion As a given requirement, the data transformation process should convert all accounts to EUR currency. The test for this rule might look like the following code snippet: @Test public void currencyConversionToEUR() throws Exception { Map accountMap = new HashMap(); accountMap.put("_type_", "Account"); accountMap.put("currency", "USD"); accountMap.put("balance", "1000"); execute(Arrays.asList(accountMap), "currencyConversionToEUR", null, null); assertEquals("EUR", accountMap.get("currency")); assertEquals(new BigDecimal("780.000"), accountMap .get("balance")); } Code listing 15: Test for the EUR conversion rule At the end of the code snippet the test verified that currency and balance were correct. The exchange rate of 0.780 was used. The rule implementation is as follows: rule currencyConversionToEUR when $accountMap : Map( this["_type_"] == "Account", this["currency"] != null && != "EUR" ) $conversionAmount : String() from getConversionToEurFrom( (String)$accountMap["currency"]) then modify($accountMap) { put("currency", "EUR"), put("balance", new BigDecimal( $conversionAmount).multiply(new BigDecimal( (String)$accountMap.get("balance")))) } end Code listing 16: Rule that converts account balance and currency to EUR (the dataTransformation.drl file) . The rule uses the default 'java' dialect. It matches on an account map and retrieves the conversion amount using the from conditional element. In this case it is a simple function that returns hardcoded values. However, it can be easily replaced with a service method that could, for example, call some web service in a real bank. function String getConversionToEurFrom(String currencyFrom) { String conversion = null; if ("USD".equals(currencyFrom)) { conversion = "0.780"; } else if ("SKK".equals(currencyFrom)) { conversion = "0.033"; } return conversion;} Code listing 17: Dummy function for calculating the exchange rate (the dataTransformation.drl file) Notice how we're calling the function. Instead of calling it directly in the consequence, it is called from a condition. This way our rule will fire only if the function returns some non-null result. The rule then sets the currency to EUR and multiplies the balance with the exchange rate. This rule doesn't cover currencies for which the getConversionToEurFrom function returns null. We have to write another rule that will report unknown currencies. rule unknownCurrency when $accountMap : Map( this["_type_"] == "Account", this["currency"] != null && != "EUR" ) not( String() from getConversionToEurFrom( (String)$accountMap["currency"]) ) then validationReport.addMessage( reportFactory.createMessage(Message.Type.ERROR, kcontext.getRule().getName(), $accountMap));end Code listing 18: Rule that adds an error message to the report if there is no conversion for a currency (the dataTransformation.drl file) Note that in this case the getConversionToEurFrom function is called from within the not construct. One account allowed Imagine that we have a business requirement that only one account from the legacy system can be imported into the new system. Our next rule will remove redundant accounts while aggregating their balances. The test inserts two accounts of the same customer into the rule session and verifies that one of them was removed and the balance has been transferred. @Test public void reduceLegacyAccounts() throws Exception { Map accountMap1 = new HashMap(); accountMap1.put("_type_", "Account"); accountMap1.put("customer_id", "00123"); accountMap1.put("balance", new BigDecimal("100.00")); Map accountMap2 = new HashMap(); accountMap2.put("_type_", "Account"); accountMap2.put("customer_id", "00123"); accountMap2.put("balance", new BigDecimal("300.00")); ExecutionResults results = execute(Arrays.asList( accountMap1, accountMap2), "reduceLegacyAccounts", "Account", "accounts"); Iterator<?> accountIterator = ((List<?>) results .getValue("accounts")).iterator(); Map accountMap = (Map) accountIterator.next(); assertEquals(new BigDecimal("400.00"), accountMap .get("balance")); assertFalse(accountIterator.hasNext()); } Code listing 19: Test for the reduceLegacyAccounts rule Before we can write this rule we have to ensure that the Account instance's balance is of the BigDecimal type. This is partially (non-EUR accounts) done by the currency conversion rules. For the EUR accounts a new rule can be written that simply converts the type to BigDecimal (we can even update the unknownCurrency rule to handle this situation). rule reduceLegacyAccounts when $accountMap1 : Map( this["_type_"] == "Account" ) $accountMap2 : Map( this["_type_"] == "Account", eval( $accountMap1 != $accountMap2 ),this["customer_id"] ==$accountMap1["customer_id"], this["currency"] == $accountMap1["currency"]) then modify($accountMap1) { put("balance", ( (BigDecimal)$accountMap1.get("balance")).add( (BigDecimal)$accountMap2.get("balance"))) } retract( $accountMap2 );end Code listing 20: Rule that removes redundant accounts and accumulates their balances (the dataTransformation.drl file) The rule matches on two accountMap instances; it ensures that they represent two different instances (eval( $accountMap1 != $accountMap2) – note that eval is important here), which both belong to the same customer (this["customer_id"] ==$accountMap1["customer_id"]) and have the same currency (this["currency"] == $accountMap1["currency"]). The consequence sums up the two balances and retracts the second accountMap. Note that the rule should fire after all currency conversion rules. This is creating dependencies between rules. In this case it is tolerable, as only a few rules are involved. However, with more complex dependencies we'll have to introduce a ruleflow.
Read more
  • 0
  • 0
  • 1222

article-image-creating-sample-project
Packt
06 Aug 2013
6 min read
Save for later

Creating a sample project

Packt
06 Aug 2013
6 min read
(For more resources related to this topic, see here.) In this article, we will create a sample project based on a transit system. We will use AutoMapper to help us transform our domain objects to ViewModel objects that we will use in the presentation layer. Before we begin, we need to explain the business overview of our domain objects used in the transit system. There are four main domain objects that we will work with. TransitStop will be the main entry point to our system; this represents a bus stop in the real world. The TransitStop domain object provides a commuter GeoLocation to give the latitude and longitude of the location, which would allow a commuter to easily locate nearby bus stops in a map if they desire to. A commuter can also use its UniqueNumber to find out when the next bus will be arriving at the bus stop. Each TransitStop has a UniqueNumber identifier so that a commuter can easily locate the stop without a GPS device. From there, we have the Transit object, which represents the vehicle that will be arriving at the stop. There could be multiples vehicles arriving, and everyone has an arrival time object representing the arrival time of the bus/transit to the stop for the commuter to go from one destination to the other. The following screenshot shows us the overall model: Step 1 – defining the ViewModel object Our first view model that we will define and use in our presentation layer will be the BusStopViewModel object, which will be mapped from TransitStop. The following screenshot represents the same: The preceding screenshot shows the class diagram of our BusStopViewModel and TransitStop domain objects. The code for the same is defined as follows: public class BusStopViewModel { public string Name { get; set; } public GeoLocationViewModel Location { get; set; } public int TransitStopNumber { get; set; } } Compare that to our domain object, as in the following code: public class TransitStop { public string StationName { get; set; } public int UniqueNumber { get; set; } public GeoLocation Location { get; set; } public IEnumerable<Transit> Buses { get; set; } public string GetName() { return string.Format("{0} - {1}", UniqueNumber, StationName); } } Our ViewModel object will most likely have a different structure than our domain object, since there are properties or data that do not pertain to the presentation layer but are required for our domain object. Note that the naming convention in BusStopViewModel is mostly identical; in doing so, we will have AutoMapper automatically map the data for us. Step 2 – creating the mapping In our mapping code, we will use a repository to get the data. Our repository will be an in-memory data that we generate; however, in real-world situations, one would use a WCF web service, WebApi, SQL, or a NoSQL solution as in the following code: public class TransitStopRepository { private readonly IList<TransitStop> _data = new List<TransitStop>(); /// <summary> /// Initializes a new instance of the <see cref="DataRepository"/> class. /// </summary> public TransitStopRepository() { Populate(); } /// <summary> /// Populates this instance. /// </summary> private void Populate() { var transitStop = new TransitStop { StationName = "Tsim Sha Tsui", UniqueNumber = 123, Location = new GeoLocation(114.171575, 22.293314), Buses = GenerateTSTBuses() }; _data.Add(transitStop); } We are using an IList that contains all our transit data. The Populate method populates the list with all the information needed. It acts like a database containing all the TransitStop information. The following code interprets the same: /// <summary> /// Generates the TST KMB buses. /// </summary> /// <returns></returns> private IEnumerable<Transit> GenerateTSTBuses() { var buses = new List<Transit> { new Transit { Number = "23A", ArrivalTimes = GenerateTimeTable(DateTime.Now), TransitColor = "Green" }, new Transit { Number = "23B", ArrivalTimes = GenerateTimeTable(DateTime.Now), TransitColor = "Yellow" } }; return buses; } We use the GenerateTSTBuses method to generate two transit routes that will be arriving at the transit stop. It will also generate a timetable for the transits arriving at the transit stop for our in-memory database so that our domain objects will be fully populated when we send a query to them, as in the following code: /// <summary> /// Generates a timetable with 2 minute apart arrival time. /// </summary> /// <param name="from">From.</param> /// <param name="incrementMinutes">The incrementMinutes.</param> /// <returns></returns> private IEnumerable<ArrivalTime> GenerateTimeTable(DateTime from) { //create a list var list = new List<ArrivalTime>(); var everyXminute = 2; //we will always generate 10 transits arriving to the stop for(var i = 0; i < 10; i++) { var arrivalTime = new ArrivalTime { Arrival = from.AddMinutes(everyXminutes) }; //double it for next everyXminutes *= 2; list.Add(arrivalTime); } return list; } The GenerateTimeTable method generates the ArrivalTime object of the transit acting as a timetable for buses; we have hardcoded it to ten transits for our in-memory database, each arriving at an increment of 2 minutes apart. Now that our repository has data fully populated, we can now use our repository to get fully populated domain objects. The following example uses the GetByUniqueNumber method to get a TransitStop object from the repository: public static BusStopViewModel GetTransitStop(int uniqueNumber) { var repo = new TransitStopRepository(); var tStop = repo.GetByUniqueNumber(uniqueNumber); //create the map from -> to object AutoMapper.Mapper.CreateMap<TransitStop, BusStopViewModel>(); We then create the declaration of our mapping of the domain object to ViewModel by calling CreateMap, where AutoMapper will automatically map the objects that have the same naming convention. The step of creating a map is essential to AutoMapper your doing so tells AutoMapper what to map and what not to map. Step 3 – mapping the object The mapping is done by calling the Map method in AutoMapper and passing the data to map from. In the preceding example, the tStop variable contains data that we will be passing into for AutoMapper. By calling the Map method, we are essentially requesting AutoMapper to create another object based on the data we have provided, as illustrated in the following code: //map the object var result = AutoMapper.Mapper.Map<TransitStop, BusStopViewModel>(tStop); return result; } In the preceding code, we are requesting AutoMapper to map the TransitStop object into BusStopViewModel object. Step 4 – not mapping certain data Let's assume that the Location object is a very computing-intense object that requires lots of memory and CPU, or that we wish to use some other means of finding geolocation from a service, such as Google or Bing Maps. We can request AutoMapper not to map the object for us in our CreateMap method by using its mapping expression method ForMember as follows: AutoMapper.Mapper.CreateMap<TransitStop, BusStopViewModel>() .ForMember(dest => dest.Location, opt => opt.Ignore()); By providing the Ignore option in the ForMember method, AutoMapper will not map the Location object. Summary In this article, we saw how to install AutoMapper, how to create a sample project, and how to transform our domain objects to ViewModel objects. Resources for Article : Further resources on this subject: ER Diagrams, Domain Model, and N-Layer Architecture with ASP.NET 3.5 (part2) [Article] Deploying .NET-based Applications on to Microsoft Windows CE Enabled Smart Devices [Article] ER Diagrams, Domain Model, and N-Layer Architecture with ASP.NET 3.5 (part1) [Article]
Read more
  • 0
  • 0
  • 1609
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-network-monitoring-essentials
Packt
06 Aug 2013
20 min read
Save for later

Network Monitoring Essentials

Packt
06 Aug 2013
20 min read
(For more resources related to this topic, see here.) Monitoring basics By now, you have already added some devices to your own Orion NPM installation and are ready to dive right in. In all actuality, network monitoring with Orion NPM fundamentally consists of two different actions. The first is Orion NPM polling devices and discovering nodes. The second is an administrator physically logging in to the Orion dashboard and looking at the statistics and node information. All monitoring is performed by viewing the pages available from the various tabs at the top of the dashboard website. By default, the Orion Summary Home page opens directly after logging into Orion NPM's dashboard. This is the page you will find yourself looking at most of the time while managing and monitoring your network with Orion NPM. There are several modules on every page of the dashboard that provide you with several pieces of information. If you are not sure what the content of a module contains or if you want more information about what the module is displaying, click on the HELP button at the top-right corner for a detailed explanation. Map The most prominent module displayed on the main page is the network map on the right-hand side. It is designed to display the "big picture" of your entire network that is monitored by Orion NPM. Questions that are quickly answered by the network map is, "Are some nodes up?", "Are some nodes down?", "Are there any network performance issues (slowness or packet loss) between WAN links?", or "Are there any network performance issues that I should be aware of?" The network map can point you in the right direction quickly if a network issue needs to be resolved. A picture truly does speak a thousand words! The map that is displayed after a fresh installation of Orion NPM is the sample map. The sample map is only a placeholder and does not display any of your nodes on it. To create and customize your own network maps, you need to use the Orion Network Atlas utility. All Nodes and All Groups The next module on the Home Summary page that will strike your attention is All Nodes. All Nodes displays every node that is being monitored by Orion NPM. On the right-hand side of the screen underneath the network map is the All Groups module. This displays any groups that I have configured in Orion NPM. This module operates in the same way that the All Nodes module does, aside from the fact that it shows the status of an entire custom group instead of just the nodes themselves. The view is configurable, but by default, the module displays first by vendor, its up or down status, and then the host name. If the host name is not known, then it will display the IP address of the node. A sample of my own network lab is displayed in the previous screenshot. All Nodes displays what nodes are up and what nodes are not responding to Orion NPM. All Triggered Alerts Continuing down to the left-hand side of the page is the All Triggered Alerts module. This module is very helpful in that it displays the time and date, node title, current value if available (such as Active ), and the description of the latest alerts that Orion NPM triggered. In the preceding example, you can see that on March 11, 2013 at 8:52 A.M., Orion NPM detected high packet loss on the JD-1130AP node. Then one minute later Orion NPM triggered an alert that it could no longer communicate with the node. To see more details about that specific node, click on the node name to open the Node Details View page. Event Summary and Last 25 Events The next modules are Event Summary and Last 25 Events. This module displays a summary list for all types of events related to network monitoring and only displays events from the last 24 hours. It is useful when needing a quick rundown of a total number of events that occurred throughout the day. Since this is only a summary of events from the day, there is very little information provided. To see more information on a specific line of item in the Event Summary module, click on one of the event titles to open a filtered view in the Events web page. Clicking on an event title, such as 5 Alert Triggered shown in the preceding example, will open the Events page with a filter to only display Alert Triggered. Clicking on 2 Node Down event will open the Events page with a filter to only display Node Downinformation. The final module on the Home Summary page is Last 25 Events and shows more event information in a historical context. A sample of what it displays is shown in the following screenshot: From the example, you can see that someone caused quite a few events within a short amount of time. On the JD-3500XL node , you can see that a FastEthernet port was administratively disabled, a port labeled Wireless Trunkcame online, the JD-1130AP node's packet loss rose above the loss threshold, and other important information. The downside of this module is that it will only show the last 25 events but it is extremely useful in assisting with troubleshooting a recent issue. Search nodes Search Nodes is a useful module where you can search a node to quickly access that node's detail view. For example, if I can't remember the name of a node but I do know where it is located, I can search the location description instead of clicking through all of the nodes until I find it. In the following example, I am searching for all nodes in the Orlando location. All of my nodes in the Orlando location are displayed in the search results. From this point, I can click on the node name to open the Node Details View page. Custom Custom Object Resource is a module that allows you to create your own modules by displaying polling data of your choice. Click on the Configure this resource link, or the EDIT button, to view the contents. If there are some modules or resources that you do not need to view or certain pieces of information that you want to add to your pages, you can do so by customizing the web pages and modules. Customizing views A view in Orion NPM is the same thing as a web page. Views are displayed when clicking on a link in the menu bar, when clicking on a node in the dashboard, or when clicking on an interface in the nodes detail view. Each module on each page can be fully re-arranged as you see fit. It is possible that you want to view the network map on the left-hand side column instead of the right-hand side on the home summary page. You can make that change from the view editor. Looking at the Orion Web Administration page, the Views module is where we are going to focus our attention. In it are the following three links: Manage Views Add New View Views by Device Type Manage Views Clicking on Manage Views will display the Manage Views editor. Orion NPM sets up default views in an out-of-the-box installation. To edit a view, highlight it and click on the Edit button. There is no Save or Undo button when editing views in Orion NPM. Once a change has been made to a view, it is permanent. Make note of what settings are in the view you are editing before changing them in case you wish to revert back. To demonstrate how to customize a page view , the following is an example of how to do so with the Orion Summary Home page. In this example I will perform the following: Move the Map from the right-hand side column to the left-hand side Gather All Nodes, All Groups, and All Dependencies together in the right-hand side column under All Nodes Place All Triggered Alerts, Event Summary, and Last 25 Events under the Map on the left-hand side column Remove the Search module Set the columns to be of equal width Perform the following steps to execute these tasks: Highlight Map,All Triggered Alerts, and Event Summary in Column 2 then click on the left arrow button. Hold the Ctrl key on the keyboard in order to select multiple options in the column. Reorder the Column 1 list by using the up arrow button until Map is first on the list. Highlight All Nodes in Column 1, then click on the right arrow button to move it to Column 2. Reorder the Column 2 list by using the up arrow button until All Nodes is first on the list. Highlight Search Nodes. Click on the red X to remove it from the column. Click on the Edit button next to the column widths. Change the column widths for columns 1 and 2 to 500. Ensure that the Layout is set for two columns. Click on SUBMIT when finished. Your page view should now look like the following screenshot: The name of the view is also the web page's title. I decided not to change the name of the view for the sake of simplicity. Also, I did not apply a view limitation. The Orion Summary Home view should not have a view limitation applied since it is the main view page to Orion NPM. Applying a view limitation may omit important node information, or the limitation may render the page useless. Click on the PREVIEW button to preview the view layout in a new web browser window. It will look similar to the following screenshot: Since I was satisfied with my changes, I clicked DONE. As you can see, the options available when editing a view are extremely straightforward. You can change the resources (or modules) for each column, edit the column sizes, and attach view limitations. The only item you cannot change when customizing a view is the type. When you change the name of a view, only the title is changed. Its contents will not change. Add New View Orion NPM already has a great deal of default pages and default views associated with each page. However, there may be cases where a default view will not suffice and you want to create your own. The following are a few examples: Create a view that consists of a specific customer's equipment Create a view for all volumes in a VSAN Create a custom view for all monitored UPS units Create a custom view that lists all nodes in a single location The list could go on, but you can see that there are several reasons to create your own view. Defining the name of the view is the first step. The name you define will be the title of the page when it is saved. Make sure it is a meaningful name and one that makes sense for what you are creating. Second, you need to choose what type of view it will be. There are several different types of views and all suit different purposes. The list of view types is as follows: Summary: Displays network-wide information. Summary is the default option. If you will be creating a view that includes multiple hardware types or locations, the Summary type will be your best option. Node Details: Displays information about a single node. This is the option that you would choose when creating a customized view page for a hardware device type. For example, you could create a custom view specifically for firewalls. Volume Details: Displays information about a single volume. Depending on your needs, you may want to create a custom view for a volume within one of your servers. Group Details: Displays information about groups. Interface Details: Displays information about a single interface. VSAN Details: Displays information about a single virtual storage area network device. UCS Chassis Details: Displays information about a Cisco Unified Computing System chassis. If you needed to create a customized view for a UCS node, this is the view type to choose. Virtualization Summary: Displays information about your VMware infrastructure. Cluster Details: Displays information about a VMware cluster. Datacenter Details: Displays information about a VMware Datacenter. Third, you need to select the resources for each column. These are the exact same resources available when editing a view as discussed in the Manage Views section. The last item that you can de fi ne is a view limitation. A view limitation is optional and it will limit the network devices that can be displayed within this view. An example of needing to apply a view limitation would be to limit this page to only display nodes from a specific hardware manufacturer. Or, you could add a limitation to only display nodes that reside in a specific location. The reasons why you would want to apply a limitation are virtually endless. Just keep in mind that view limitations are optional and are not required in order to create a new view. Only one limitation can be applied to a view. It is not possible to apply multiple view limitations. The following is an example of how to create a custom view page with a limitation applied to only display access points: In the Add New View wizard, enter the name Access Points and choose Summary as the view type. Click on SUBMIT to continue. Click on the Edit button next to the column widths. Select the two columns and set the widths of columns 1 and 2 to 500 and 400 respectively. Add resources to Column 1 by clicking on the plus button. The Add Resources page appears. Expand Node Lists – All Nodes and Grouped Node Lists and place a check mark next to All Nodes. Click on SUBMIT to continue. Add resources to Column 2 by clicking on the plus button. Expand Summary Reports – Various Reports Showing Problem Areas and place a check mark next to Current Traffic on All Interfaces . Click on SUBMIT to continue. Scroll down to View Limitation and click on the Edit button. Select Group of Nodes, scroll to the bottom of the page, and click on CONTINUE. Place a check mark next to each node. In this example, the hostnames JD-1130AP-1 and JD-1131AP-2 are access points I am applying this new view against. Click on SUBMIT to apply the limitation. Click on DONE to finish. The following is how the new custom view will look like based on the example provided. This is only one way to create a custom view for Access Points, and there are plenty of other resources that I could have added to the page. Also, I could have chosen a Node Detail view type instead of Summary type. This is only one simple example of how to create a new view in Orion NPM. I encourage you to experiment with creating your own custom views to become more familiar with the process. Views by Device Type Views By Device Type is where you can customize which page displays when looking at a specific type of node. For example, I can force Orion NPM to display a different Node Details View page for a specific model of hardware. This is helpful for when Orion NPM does not have a details view page for a hardware type, such as a UPS unit that can be monitored via SNMP. Only device types that are currently being monitored by Orion NPM will be displayed when editing views by device type. You cannot apply custom views against a specific hardware type unless Orion NPM is currently monitoring that type of device. Most options have the (default) option selected. The default view for almost every node monitored by Orion NPM is the Node Details View. Some exceptions to this rule are VMware nodes where ESX Host Details is automatically chosen. Menu bars When working with the Orion dashboard, you have already seen the tabs and menu bars at the top of the page. All of the menu bar types can be customized as you see fit. You can even create your own custom menu bars. To customize menu bars, click on the Customize Menu Bars link in Orion Web Administration. Menu bars are assigned to one of the three tabs at the top of the dashboard. As shown in the following screenshot, there are five different menu bars from an out-of-the-box installation: Orion NPM includes five default menu bars: Admin, Default, Guest, Network_TabMenu, and Virtualization_TabMenu. Admin is the only menu bar that cannot be deleted from Orion NPM but it can be edited. To edit a menu bar, click on the Edit button under its title and the Edit Menu Bar wizard will be displayed. Simply drag-and-drop the available item you wish to add to the menu bar from the right-hand side to the left-hand side column. When finished, click on the SUBMIT button. When creating a brand new menu bar, the same editing process applies. In addition, menu bars are assigned to a user account's view settings through the Manage Users wizard. This means that when you create a new menu bar, you will need to assign it to a user account from the Manage User Accounts wizard. You cannot create your own tabs (that is Home, Network, Virtualization) in Orion NPM. You can only edit and create menu bars and assign them to a tab. Editing Resources While Orion NPM's default views will suit almost every need out-of-the-box, it is still a great idea to dive into all of the view settings of a module and view what you are able to customize. Every module in the dashboard allows an administrator to edit the module in some way by clicking on the EDIT button on the top-right corner of the resource. As an example, open the All Nodes view in the home summary screen, then click on the EDIT button to be presented with a list of options. Every single module in Orion NPM can be edited, to a certain limit. You can always edit the title of the module as well as the subtitle in case the default descriptions are difficult to understand. For All Nodes, you can edit the grouping list for up to three levels. An example of creating a view for geographic locations is to set the first level to City, then leaving the second and third levels set to None. This will display only the city name at the top level, then the node names underneath. In the following example, it is easy to see how simple this type of view can be: For medium to large network sizes, a more appropriate view option is to set the first level to Location then level two to Department. Feel free to set the grouping display to one that will suit your needs. The remaining settings in the Edit Resource page are: Put nodes with null values for the grouping property Remember Expanded Groups Filter Nodes (SQL) When Orion NPM does not know a specific property for a node, such as its location or department, the Put nodes with null values for the grouping property setting tells Orion NPM how to group these nodes. There are two options available. We can place nodes In the [Unknown] group or At the bottom of the list, in no group. Placing nodes in the [Unknown] group will have Orion NPM display these nodes with unknown properties (or blank properties) with the group title [Unknown], which will be displayed at the top. The following is an example: The second option At the bottom of the list, in no group will do just that. Any node that has unknown or blank values will be placed at the bottom of the node list in the generic Unknown group. By default, the Orion dashboard website will trigger a browser page refresh every few minutes. When the page refreshes, if you a expanded a view in a module (a.k.a drill-down view by clicking on the plus button) the drill-down view will reset. The checkbox for Remember Expanded Groups is enabled by default and it is a good idea to leave this checked. The final option is Filter Nodes (SQL). This is an advanced feature of Orion NPM where you can use an explicit SQL string as a filter for these views. For example, use the filter Status<>1 to filter out all nodes that are operationally up and only view nodes that are down in the All Nodes module. SQL filters are helpful when creating custom views for administrative personnel. For more SQL filter examples, expand Show Filter Examples . Also, you can click on the Help button in the module for more examples and guidance on how to perform SQL queries. Just as in creating new views, you may have noticed by now that there is no cancel or revert option when changing a view setting in a module or a page. If you made a setting change but do not want to save the new setting, simply click on the Back button in your web browser to go back to the previous page without saving the new settings. Make sure that you don't change a view setting that you didn't intend to. Customization Orion NPM allows administrators to change a few aspects of the dashboard interface from the Customize module in Orion Web Administration. There are three different customization options available; Customize Menu Bars, Color Scheme, and External Websites. Color Scheme Orion NPM includes several color schemes that can be changed on the fly. To change the dashboard color scheme, click on the Color Scheme link in Orion Web Administration, choose the Color Scheme option, and click on the SUBMIT button. Personally, I always use the Orion Default (white) because I can never decide which color to use! External Websites The External Websites option in the Customize module is an interesting one. This option enables an administrator to add some external website to the Orion NPM dashboard as if it is a part of the dashboard itself. For example, if you have an internal Microsoft SharePoint team site on your domain, you could add it to the Admin menu bar and have the team site act as if it is a part of the console. When adding an external website, it must be in URL format such as https://URL. The following is an example of how to add an external website: Click on External Websites in Orion Web Administration and then click on the ADD button. Enter the Menu Title, Page Title, URL of the website, and which Menu Bar you want to apply the link to. In the following example, I am adding a link to www.SolarWinds.com on the NETWORK tab at the top of the dashboard page. Click on OK to finish. The external web link will appear now appear in the NETWORK tab. When clicking on the link, the web page will appear as if it is embedded in the Orion dashboard. This sums up the discussion on customizing web views, modules, and other aspects of the dashboard. Now, we will discuss how to use Orion NPM to monitor your devices.
Read more
  • 0
  • 0
  • 11122

article-image-working-sample-controlling-mouse-hand
Packt
06 Aug 2013
10 min read
Save for later

Working sample for controlling the mouse by hand

Packt
06 Aug 2013
10 min read
(For more resources related to this topic, see here.) Getting ready Create a project in Visual Studio and prepare it for working with OpenNI and NiTE. How to do it... Copy ReadLastCharOfLine() and HandleStatus() functions to the top of your source code (just below the #include lines). Then add following lines of code: class MouseController : public nite::HandTracker::NewFrameListener { private: float startPosX, startPosY; int curX, curY; nite::HandId handId; RECT desktopRect; public: MouseController(){ startPosX = startPosY = -1; POINT curPos; if (GetCursorPos(&curPos)) { curX = curPos.x; curY = curPos.y; }else{ curX = curY = 0; } handId = -1; const HWND hDesktop = GetDesktopWindow(); GetWindowRect(hDesktop, &desktopRect); } void onNewFrame(nite::HandTracker& hTracker){ nite::Status status = nite::STATUS_OK; nite::HandTrackerFrameRef newFrame; status = hTracker.readFrame(&newFrame); if (!HandleStatus(status) || !newFrame.isValid()) return; const nite::Array<nite::GestureData>& gestures = newFrame.getGestures(); for (int i = 0; i < gestures.getSize(); ++i){ if (gestures[i].isComplete()){ if (gestures[i].getType() == nite::GESTURE_CLICK){ INPUT Input = {0}; Input.type = INPUT_MOUSE; Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; SendInput(1, &Input, sizeof(INPUT)); }else{ nite::HandId handId; status = hTracker.startHandTracking( gestures[i].getCurrentPosition(), &handId); } } } const nite::Array<nite::HandData>& hands = newFrame.getHands(); for (int i = hands.getSize() -1 ; i >= 0 ; --i){ if (hands[i].isTracking()){ if (hands[i].isNew() || handId != hands[i].getId()){ status = hTracker.convertHandCoordinatesToDepth( hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z, &startPosX, &startPosY); handId = hands[i].getId(); if (status != nite::STATUS_OK){ startPosX = startPosY = -1; } }else if (startPosX >= 0 && startPosY >= 0){ float posX, posY; status = hTracker.convertHandCoordinatesToDepth( hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z, &posX, &posY); if (status == nite::STATUS_OK){ if (abs(int(posX - startPosX)) > 10) curX += ((posX - startPosX) - 10) / 3; if (abs(int(posY - startPosY)) > 10) curY += ((posY - startPosY) - 10) / 3; curX = min(curX, desktopRect.right); curX = max(curX, desktopRect.left); curY = min(curY, desktopRect.bottom); curY = max(curY, desktopRect.top); SetCursorPos(curX, curY); } } break; } } } }; Then locate the following line: int _tmain(int argc, _TCHAR* argv[]) { Add the following inside this function: nite::Status status = nite::STATUS_OK; status = nite::NiTE::initialize(); if (!HandleStatus(status)) return 1; printf("Creating hand tracker ...rn"); nite::HandTracker hTracker; status = hTracker.create(); if (!HandleStatus(status)) return 1; MouseController* listener = new MouseController(); hTracker.addNewFrameListener(listener); hTracker.startGestureDetection(nite::GESTURE_HAND_RAISE); hTracker.startGestureDetection(nite::GESTURE_CLICK); printf("Reading data from hand tracker ...rn"); ReadLastCharOfLine(); nite::NiTE::shutdown(); openni::OpenNI::shutdown(); return 0; How it works... Both the ReadLastCharOfLine() and HandleStatus() functions are present here too. These functions are well known to you and don't need any explanation. Then in the second part, we declared a class/struct that we are going to use for capturing the new data available event from the nite::HandTracker object. But the definition of this class is a little different here. Other than the onNewFrame() method, we defined a number of variables and a constructor method for this class too. We also changed its name to MouseController to be able to make more sense of it. class MouseController : public nite::HandTracker::NewFrameListener { private: float startPosX, startPosY; int curX, curY; nite::HandId handId; RECT desktopRect; As you can see, our class is still a child class of nite::HandTracker::NewFrameListener because we are going use it to listen to the nite::HandTracker events. Also, we defined six variables in our class. startPosX and startPosY are going to hold the initial position of the active hand whereas curY and curX are going to hold the position of the mouse when in motion. The handId variable is responsible for holding the ID of the active hand and desktopRecthold for holding the size of the desktop so that we can move our mouse only in this area. These variables are all private variables; this means they will not be accessible from the outside of the class. Then we have the class's constructor method that initializes some of the preceding variables. Refer to the following code: public: MouseController(){ startPosX = startPosY = -1; POINT curPos; if (GetCursorPos(&curPos)) { curX = curPos.x; curY = curPos.y; }else{ curX = curY = 0; } handId = -1; const HWND hDesktop = GetDesktopWindow(); GetWindowRect(hDesktop, &desktopRect); } In the constructor, we set both startPosX and startPosY to -1 and then store the current position of the mouse in the curX and curY variables. Then we set the handId variable to -1 to know-mark that there is no active hand currently, and retrieve the value of desktopRect using two Windows API methods, GetDesktopWindow() and GetWindowRect(). The most important tasks are happening in the onNewFrame() method. This method is the one that will be called when new data becomes available in nite::HandTracker; after that, this method will be responsible for processing this data. As the running of this method means that new data is available, the first thing to do in its body is to read this data. So we used the nite::HandTracker::readFrame() method to read the data from this object: void onNewFrame(nite::HandTracker& hTracker){ nite::Status status = nite::STATUS_OK; nite::HandTrackerFrameRef newFrame; status = hTracker.readFrame(&newFrame); When working with nite::HandTracker, the first thing to do after reading the data is to handle gestures if you expect any. We expect to have Hand Raise to detect new hands and click gesture to perform the mouse click: const nite::Array<nite::GestureData>& gestures = newFrame.getGestures(); for (int i = 0; i < gestures.getSize(); ++i){ if (gestures[i].isComplete()){ if (gestures[i].getType() == nite::GESTURE_CLICK){ INPUT Input = {0}; Input.type = INPUT_MOUSE; Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; SendInput(1, &Input, sizeof(INPUT)); }else{ nite::HandId handId; status = hTracker.startHandTracking( gestures[i].getCurrentPosition(), &handId); } } } As you can see, we retrieved the list of all the gestures using nite::HandTrackerFrameRef::getGestures() and then looped through them, searching for the ones that are in the completed state. Then if they are the nite::GESTURE_CLICK gesture, we need to perform a mouse click. We used the SendInput() function from the Windows API to do it here. But if the recognized gesture wasn't of the type nite::GESTURE_CLICK, it must be a nite::GESTURE_HAND_RAISE gesture; so, we need to request for the tracking of this newly recognized hand using the nite::HandTracker::startHandTracking() method. The next thing is to take care of the hands being tracked. To do so, we first need to retrieve a list of them using the nite::HandTrackerFrameRef::getHands() method and then loop through them. This can be done using a simple for loop as we used for the gestures. But as we want to read this list in a reverse order, we need to use a reverse for loop. The reason we need to read this list in the reverse order is that we always want the last recognized hand to control the mouse: const nite::Array<nite::HandData>& hands = newFrame.getHands(); for (int i = hands.getSize() - 1 ; i >= 0 ; --i){ Then we need to make sure that the current hand is under-tracking because we don't want an invisible hand to control the mouse. The first hand being tracked is the one we want, so we will break the looping there, of course, after the processing part, which we will remove from the following code to make it clearer. if (hands[i].isTracking()){ . . . break; } Speaking of processing, in the preceding three lines of code (with periods) we have another condition. This condition is responsible for finding out if this hand is the same one that had control of the mouse in the last frame. If it is a new hand (either it is a newly recognized hand or it is a newly active hand), we need to save its current position in the startPosX and startPosY variables. if (hands[i].isNew() || handId != hands[i].getId()){ status = hTracker.convertHandCoordinatesToDepth( hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z, &startPosX, &startPosY); handId = hands[i].getId(); if (status != nite::STATUS_OK){ startPosX = startPosY = -1; } If it was the same hand, we have another condition. Do we have the startPosX and startPosY variables already or do we not have them yet? If we have them, we can calculate the mouse's movement. But first we need to calculate the position of the hand relative to the depth frame. }else if (startPosX >= 0 && startPosY >= 0){ float posX, posY; status = hTracker.convertHandCoordinatesToDepth( hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z, &posX, &posY); Once the process of conversation ends, we need to calculate the new position of the mouse depending on how the hand's position changes. But we want to define a safe area for it to be static when small changes happen. So we calculate the new position of the mouse only if it has moved by more than 10 pixels in our depth frame: if (status == nite::STATUS_OK){ if (abs(int(posX - startPosX)) > 10) curX += ((posX - startPosX) - 10) / 3; if (abs(int(posY - startPosY)) > 10) curY += ((posY - startPosY) - 10) / 3; As you can see in the preceding code, we also divided the changes by 3 because we didn't want it to move too fast. But before setting the position of the mouse, we need to first make sure that the new positions are in the screen view port using the desktopRect variable: curX = min(curX, desktopRect.right); curX = max(curX, desktopRect.left); curY = min(curY, desktopRect.bottom); curY = max(curY, desktopRect.top); After calculating everything, we can set the new position of the mouse using SetCursorPos() from the Windows API: SetCursorPos(curX, curY); Step three and four are not markedly differently. In this step, we have the initialization process; this includes the initialization of NiTE and the creation of the nite::HandTracker variable. status = nite::NiTE::initialize(); . . . nite::HandTracker hTracker; status = hTracker.create(); Then we should add our newly defined structure/class as a listener to nite::HandTracker so that nite::HandTracker can call it later when a new frame becomes available: MouseController* listener = new MouseController(); hTracker.addNewFrameListener(listener); Also, we need to have an active search for a hand gesture because we need to locate the position of the hands and we also have to search for another gesture for the mouse click. So we need to call the nite::HandTracker::startGestureDetection() method twice for both the Click (also known as push) and Hand Raise gestures here: hTracker.startGestureDetection(nite::GESTURE_HAND_RAISE); hTracker.startGestureDetection(nite::GESTURE_CLICK); At the end, we will wait until the user presses the Enter key to end the app. We do nothing more in our main thread except just waiting. Everything happens in another thread. ReadLastCharOfLine(); nite::NiTE::shutdown(); openni::OpenNI::shutdown(); return 0; Summary In this article, we learnt how to write a working example for using nite::HandTracker, controlled the position of the mouse cursor using the NiTE hand tracker feature, and simulated a click event Resources for Article: Further resources on this subject: Getting started with Kinect for Windows SDK Programming [Article] Kinect in Motion – An Overview [Article] Active Directory migration [Article]
Read more
  • 0
  • 0
  • 2079

article-image-logging-and-reports
Packt
06 Aug 2013
24 min read
Save for later

Logging and Reports

Packt
06 Aug 2013
24 min read
(For more resources related to this topic, see here.) Logging and reporting Reporting is the most important part of any test execution, reason being it helps the user to understand the result of the test execution, point of failure, and reasons for the failure. Logging, on the other hand, is important to keep an eye on the execution flow or for debugging in case of any failures. TestNG by default generates a different type of report for its test execution. This includes an HTML and an XML report output. TestNG also allows its users to write their own reporter and use it with TestNG. There is also an option to write your own loggers, which are notified at runtime by TestNG. There are two main ways to generate a report with TestNG: Listeners : For implementing a listener class, the class has to implement the org.testng.ITestListener interface. These classes are notified at runtime by TestNG when the test starts, finishes, fails, skips, or passes. Reporters : For implementing a reporting class, the class has to implement an org.testng.IReporter interface. These classes are called when the whole suite run ends. The object containing the information of the whole test run is passed to this class when called. Each of them should be used depending upon the condition of how and when the reports have to be generated. For example, if you want to generate a custom HTML report at the end of execution then you should implement IReporter interface while writing extension. But in case you want to update a file at runtime and have to print a message as and when the tests are getting executed, then we should use the ITestListener interface. Writing your own logger We had earlier read about the different options that TestNG provides for logging and reporting. Now let's learn how to start using them. To start with, we will write a sample program in which we will use the ITestListener interface for logging purposes. Time for action – writing a custom logger Open Eclipse and create a Java project with the name CustomListener and with the following structure. Please make sure that the TestNG library is added to the build path of the project. Create a new class named SampleTest under the test.sample package and replace the following code in it: package test.sample; import org.testng.Assert; import org.testng.annotations.Test; public class SampleTest { @Test public void testMethodOne(){ Assert.assertTrue(true); } @Test public void testMethodTwo(){ Assert.assertTrue(false); } @Test(dependsOnMethods={"testMethodTwo"}) public void testMethodThree(){ Assert.assertTrue(true); } } The preceding test class contains three test methods out of which testMethodOne and testMethodThree will pass when executed, whereas testMethodTwo is made to fail by passing a false Boolean value to the Assert.assertTrue method which is used for truth conditions in the tests. In the preceding class test method testMethodThree depends on testMethodTwo. Create another new class named CustomLogging under the test.logger package and replace its code with the following code: package test.logger; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import org.testng.ITestContext; import org.testng.ITestListener; import org.testng.ITestResult; public class CustomLogging implements ITestListener{ //Called when the test-method execution starts @Override public void onTestStart(ITestResult result) { System.out.println("Test method started: "+ result.getName()+ " and time is: "+getCurrentTime()); } //Called when the test-method execution is a success @Override public void onTestSuccess(ITestResult result) { System.out.println("Test method success: "+ result.getName()+ " and time is: "+getCurrentTime()); } //Called when the test-method execution fails @Override public void onTestFailure(ITestResult result) { System.out.println("Test method failed: "+ result.getName()+ " and time is: "+getCurrentTime()); } //Called when the test-method is skipped @Override public void onTestSkipped(ITestResult result) { System.out.println("Test method skipped: "+ result.getName()+ " and time is: "+getCurrentTime()); } //Called when the test-method fails within success percentage @Override public void onTestFailedButWithinSuccessPercentage(ITestResult result) { // Leaving blank } //Called when the test in xml suite starts @Override public void onStart(ITestContext context) { System.out.println("Test in a suite started: "+ context.getName()+ " and time is: "+getCurrentTime()); } //Called when the test in xml suite finishes @Override public void onFinish(ITestContext context) { System.out.println("Test in a suite finished: "+ context.getName()+ " and time is: "+getCurrentTime()); } //Returns the current time when the method is called public String getCurrentTime(){ DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSS"); Date dt = new Date(); return dateFormat.format(dt); } } The above test class extends the ITestListener interface and defines the overriding methods of the interface. Details of each of the methods are provided as comments inside the previous code. Each method when executed prints the respective test method name or the suite name and the time when it was called. The getCurrentTime method returns the current time in HH:mm:ss:SSS format using the Date and DateFormat class. Create a new testing XML file under the project with name simple-logger-testng.xml and copy the following contents onto it: <suite name="Simple Logger Suite"> <listeners> <listener class-name="test.logger.CustomLogging" /> </listeners> <test name="Simple Logger test"> <classes> <class name="test.sample.SampleTest" /> </classes> </test> </suite> The preceding XML defines a simple test which considers the class test.sample.SampleTest for test execution. The CustomLogging class which implements the ITestListener is added as a listener to the test suite using the listeners tag as shown in the preceding XML. Select the previous testing XML file in Eclipse and run it as TestNG suite. You will see the following test result in the Console window of Eclipse: The following screenshot shows the test methods that were executed, failed, and skipped in the test run: What just happened? We created a custom logger class which implements the ITestListener interface and attached itself to the TestNG test suite as a listener. Methods of this listener class are invoked by TestNG as and when certain conditions are met in the execution, for example, test started, test failure, test success, and so on. Multiple listeners can be implemented and added to the test suite execution, TestNG will invoke all the listeners that are attached to the test suite. Logging listeners are mainly used when we need to see the continuous status of the test execution when the tests are getting executed. Writing your own reporter In the earlier section we had seen an example of writing your custom logger and attaching it to TestNG. In this section we will cover, with an example, the method of writing your custom reporter and attaching it to TestNG. To write a custom reporter class, our extension class should implement the IReporter interface. Let's go ahead and create an example with the custom reporter. Time for action – writing a custom reporter Open the previously created project named CustomListener and create a package named reporter under the test package. Create a new class named CustomReporter under the test.reporter package and add the following code to it: package test.reporter; import java.util.List; import java.util.Map; import org.testng.IReporter; import org.testng.ISuite; import org.testng.ISuiteResult; import org.testng.ITestContext; import org.testng.xml.XmlSuite; public class CustomReporter implements IReporter { @Override public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) { //Iterating over each suite included in the test for (ISuite suite : suites) { //Following code gets the suite name String suiteName = suite.getName(); //Getting the results for the said suite Map<String, ISuiteResult> suiteResults = suite.getResults(); for (ISuiteResult sr : suiteResults.values()) { ITestContext tc = sr.getTestContext(); System.out.println("Passed tests for suite '" + suiteName + "' is:" + tc.getPassedTests().getAllResults().size()); System.out.println("Failed tests for suite '" + suiteName + "' is:" + tc.getFailedTests().getAllResults().size()); System.out.println("Skipped tests for suite '" + suiteName + "' is:" + tc.getSkippedTests().getAllResults().size()); } } } } The preceding class implements the org.testng.IReporter interface. It implements the definition for the method generateReport of the IReporter interface. The method takes three arguments , the first being xmlSuite, which is the list suites mentioned in the testng XML being executed. The second one being suites which contains the suite information after the test execution; this object contains all the information about the packages, classes, test methods, and their test execution results. The third being the outputDirectory, which contains the information of the output folder path where the reports will be generated. The custom report prints the total number of tests passed, failed, and skipped for each suite included in the particular test execution when added to TestNG as a listener. Create a new file named simple-reporter-testng.xml to the project and add the following code to it: <suite name="Simple Reporter Suite"> <listeners> <listener class-name="test.reporter.CustomReporter" /> </listeners> <test name="Simple Reporter test"> <classes> <class name="test.sample.SampleTest" /> </classes> </test> </suite> The preceding XML is a testng XML configuration file. It contains a single test with the class test.sample.SampleTest to be considered for test execution. The CustomReporter class is added as a listener to the test suite using the listeners and listener tag as defined in the previous file. Select the preceding XML file and run it as TestNG test suite in Eclipse. You will see the following test results under the Console window of Eclipse: What just happened? We successfully created an example of writing custom reporter and attaching it to TestNG as a listener. The preceding example shows a simple custom reporter which prints the number of failed, passed, and skipped tests on the console for each suite included the said test execution. Reporter is mainly used to generate the final report for the test execution. The extension can be used to generate XML, HTML, XLS, CSV, or text format files depending upon the report requirement. TestNG HTML and XML report TestNG comes with certain predefined listeners as part of the library. These listeners are by default added to any test execution and generate different HTML and XML reports for any test execution. The report is generated by default under the folder named testoutput and can be changed to any other folder by configuring it. These reports consist of certain HTML and XML reports that are TestNG specific. Let's create a sample project to see how the TestNG report is generated. Time for action – generating TestNG HTML and XML reports Open Eclipse and create a Java project with the name SampleReport having the following structure. Please make sure that the TestNG library is added to the build path of the project. Create a new class named SampleTest under the test package and replace the following code in it: package test; import org.testng.Assert; import org.testng.annotations.Test; public class SampleTest { @Test public void testMethodOne(){ Assert.assertTrue(true); } @Test public void testMethodTwo(){ Assert.assertTrue(false); } @Test(dependsOnMethods={"testMethodTwo"}) public void testMethodThree(){ Assert.assertTrue(true); } } The preceding test class contains three test methods out of which testMethodOne and testMethodThree will pass when executed, whereas testMethodTwo is made to fail by passing a false Boolean value to Assert.assertTrue method. In the preceding class test method testMethodThree depends on testMethodTwo. Select the previously created test class and run it as a TestNG test through Eclipse. Now refresh the Java project in Eclipse by selecting the project and pressing the F5 button or right-clicking and selecting Refresh , and this will refresh the project. You will see a new folder named test-output under the project. Expand the folder in Eclipse and you will see the following files as shown in the screenshot: Open index.html as shown in the preceding screenshot on your default web browser. You will see the following HTML report: Now open the file testing-results.xml in the default XML editor on your system, and you will see the following results in the XML file: What just happened? We successfully created a test project and generated a TestNG HTML and XML report for the test project. TestNG by default generates multiple reports as part of its test execution. These reports mainly include TestNG HTML report, TestNG emailable report, TestNG report XML, and JUnit report XML files. These files can be found under the output report folder (in this case test-output). These default report generation can be disabled while running the tests by setting the value of the property useDefaultListeners to false. This property can be set while using the build tools like Ant or Maven as explained in the previous chapter. Generating a JUnit HTML report JUnit is one of those unit frameworks which were initially used by many Java applications as a Unit test framework. By default, JUnit tests generate a simple report XML files for its test execution. These XML files can then be used to generate any custom reports as per the testing requirement. We can also generate HTML reports using the XML files. Ant has such a utility task which takes these JUnit XML files as input and generates an HTML report from it. We had earlier learnt that TestNG by default generates the JUnit XML reports for any test execution. We can use these XML report files as input for generation of a JUnit HTML report. Assuming we already have JUnit XML reports available from the earlier execution let's create a simple Ant build configuration XML file to generate an HTML report for the test execution. Time for action – generating a JUnit report Go to the previously created Java project in Eclipse. Create a new file named junit-report-build.xml by selecting the project. Add the following code to the newly created file and save it: <project name="Sample Report" default="junit-report" basedir="."> <!-- Sets the property variables to point to respective directories --> <property name="junit-xml-dir" value="${basedir}/test-output/junitreports"/> <property name="report-dir" value="${basedir}/html-report" /> <!-- Ant target to generate html report --> <target name="junit-report"> <!-- Delete and recreate the html report directories --> <delete dir="${report-dir}" failonerror="false"/> <mkdir dir="${report-dir}" /> <mkdir dir="${report-dir}/Junit" /> <!-- Ant task to generate the html report. todir - Directory to generate the output reports fileset - Directory to look for the junit xml reports. report - defines the type of format to be generated. Here we are using "noframes" which generates a single html report. --> <junitreport todir="${report-dir}/Junit"> <fileset dir="${junit-xml-dir}"> <include name="**/*.xml" /> </fileset> <report format="noframes" todir="${report-dir}/Junit" /> </junitreport> </target> </project> The preceding XML defines a simple Ant build.xml file having a specific Ant target named junit-report that generates a JUnit report when executed. The target looks for the JUnit report XML files under the directory test-output/junitreports. For the Ant configuration file the default target to execute is configured as junit-report. Open the terminal window and go to the Java project directory in the terminal. Run the command ant –buildfile junit-report-build.xml and press Enter . Once executed a JUnit HTML report will be generated in the configured directory /html-report/Junit. Open the file named junit-noframes.html on your default web browser. You will see the following HTML report: What just happened? In this section we have seen how to use the JUnit XML report generated by TestNG and generate HTML report using Ant. There are two kinds of reports that can be generated using this method: frames and no-frames. If the report generation is configured with frames there will multiple files generated for each class and the main report will connect to them through links. A no-frames report consists of a single file with all the results of the test execution. This can be configured by providing the respective value to the format attribute of the report task in Ant. Generating a ReportNG report We had earlier seen that TestNG provides options to add custom listeners for logging and reporting. These listeners can easily be added to the TestNG execution and will be called during the execution or at the end of the execution depending upon the type of listener. ReportNG is a reporter add-on for TestNG that implements the report listener of TestNG. ReportNG reports are better looking reports compared to the original HTML reports. To generate a ReportNG report we have to add the reporting class to the list of listeners of TestNG while executing the tests. Let's see how to add ReportNG listener to TestNG and generate a ReportNG HTML report. In the following example we will use an Ant build XML file used to run our tests. Time for action – generating a ReportNG report Go to the earlier created Java project SampleProject in Eclipse. Download ReportNG from http://reportng.uncommons.org/ download site. Unzip and copy the reportng-<version>.jar and velocity-dep-<version>.jar from the unzipped folder to the lib folder under the project. Download guice from guice site https://code.google.com/p/google-guice/downloads/list. Unzip the downloaded guice zip file and copy the guice-<version>.jar to the lib folder under the project. Create a new file named testng.xml under the folder and add the following content to it: <suite name="Sample Suite"> <test name="Sample test"> <classes> <class name="test.SampleTest" /> </classes> </test> </suite> Create a new file named reportng-build.xml by selecting the project. Add the following code to the newly created file and save it: <project name="Testng Ant build" basedir="."> <!-- Sets the property varaibles to point to respective directories --> <property name="report-dir" value="${basedir}/html-report" /> <property name="testng-report-dir" value="${report-dir}/TestNG-report" /> <property name="lib-dir" value="${basedir}/lib" /> <property name="bin-dir" value="${basedir}/bin-dir" /> <property name="src-dir" value="${basedir}/src" /> <!-- Sets the classpath including the bin directory and all thejars under the lib folder --> <path id="test.classpath"> <pathelement location="${bin-dir}" /> <fileset dir="${lib-dir}"> <include name="*.jar" /> </fileset> </path> <!-- Deletes and recreate the bin and report directory --> <target name="init"> <delete dir="${bin-dir}" /> <mkdir dir="${bin-dir}" /> <delete dir="${report-dir}" /> <mkdir dir="${report-dir}" /> </target> <!-- Compiles the source code present under the "srcdir" and place class files under bin-dir --> <target name="compile" depends="init"> <javac srcdir="${src-dir}" classpathref="test.classpath" includeAntRuntime="No" destdir="${bin-dir}" /> </target> <!-- Defines a TestNG task with name "testng" --> <taskdef name="testng" classname="org.testng.TestNGAntTask" classpathref="test.classpath" /> <!--Executes the testng tests configured in the testng.xml file--> <target name="testng-execution" depends="compile"> <mkdir dir="${testng-report-dir}" /> <testng outputdir="${testng-report-dir}" classpathref="test.classpath" useDefaultListeners="false" listeners="org.uncommons.reportng.HTMLReporter"> <!-- Configures the testng xml file to use as test-suite --> <xmlfileset dir="${basedir}" includes="testng.xml" /> <sysproperty key="org.uncommons.reportng.title" value="ReportNG Report" /> </testng> </target> </project> The preceding XML defines a simple Ant build XML file that generates a ReportNG report when executed. The said XML compiles and runs the TestNG tests. ReportNG is added as a listener and the default listener of TestNG is disabled by setting a false value to the useDefaultListeners attribute while using the testng Ant task. Open the terminal window and go to the Java project directory in the terminal. Run the command ant -buildfile reportng-build.xml testing execution and then press Enter . Once executed a ReportNG HTML report will be generated in the configured directory html-reportTestNG-reporthtml under the said project directory. Go to the said directory and open the index.html file on your default web browser. You will see the following HTML report: By clicking on the Sample test link, you will see details of the test report as shown in the following screenshot: What just happened? In the previous section we learned how to generate a ReportNG HTML report for our test execution. We disabled the default TestNG reports in the previous Ant XML file but, if required, we can generate both the default as well as ReportNG reports by enabling the default report listeners. In the previous example the title of the report is configured by setting the property org.uncommons.reportng.title. There are other configuration options that we can use while generating the report, and we will cover these in the next section. ReportNG configuration options ReportNG provides different configuration options based on which the respective HTML report is generated. Following is a list of configurations that are supported: org.uncommons.reportng.coverage-report: This is configured as the link to the test coverage report. org.uncommons.reportng.escape-output: This property is used to turn off the log output in reports. By default it's turned off and is not recommended to be switched on as enabling this may require certain hacks to be implemented for proper report generation. org.uncommons.reportng.frames: This property is used to generate an HTML report with frameset and without frameset. The default value is set to true and hence it generates HTML reports with frameset by default. org.uncommons.reportng.locale: Used to override the localized messages in the generated HTML report. org.uncommons.reportng.stylesheet: This property can be used to customize the CSS property of the generated HTML report. org.uncommons.reportng.title: Used to define a report title for the generated HTML report. Have a go hero Write an Ant file to configure ReportNG to generate an HTML report without any frames for the TestNG execution. Generating a Reporty-ng (former TestNG-xslt) report While looking at the test report, senior managers might like to see the report in a graphical representation to know the status of the execution with just a glance. Reporty-ng (formerly called TestNG-xslt) is one such add-on report that generates a pie chart for your test execution with all the passed, failed, and skipped tests. This plugin uses the XSL file to convert the TestNG XML report into the custom HTML report with a pie chart. To use this plugin we will write an Ant target which will use the TestNG results XML file to generate the report. Let's go ahead and write an Ant target to generate the report. Time for action – generating a Reporty-ng report Open the previously created SampleReport in Eclipse. Download the Reporty-ng from the URL: https://github.com/cosminaru/reporty-ng At the time of writing this book the latest version available was Reporty-ng 1.2. You can download a newer version if available. Changes in the installation process should be minor if there are any at all. Unzip the downloaded zip and copy a file named testng-results.xsl from srcmainresources onto the resources folder under the said project. Copy the JARs saxon-8.7.jar and SaxonLiason.jar from the unzipped Reporty-ng lib folder to the project lib folder. Create a new Ant XML configuration file named reporty-ng-report.xml and paste the following code onto it: <project name="Reporty-ng Report" default="reporty-ng-report" basedir="."> <!-- Sets the property variables to point to respective directories --> <property name="xslt-report-dir" value="${basedir}/reporty-ng/" /> <property name="report-dir" value="${basedir}/html-report" /> <property name="lib-dir" value="${basedir}/lib" /> <path id="test.classpath"> <fileset dir="${lib-dir}"> <include name="**/*.jar" /> </fileset> </path> <target name="reporty-ng-report"> <delete dir="${xslt-report-dir}" /> <mkdir dir="${xslt-report-dir}" /> <xslt in="${basedir}/test-output/testng-results.xml" style="${basedir}/resources/testng-results.xsl" out="${xslt-report-dir}/index.html"> <param name="testNgXslt.outputDir" expression="${xslt-report-dir}" /> <param name="testNgXslt.sortTestCaseLinks" expression="true" /> <param name="testNgXslt.testDetailsFilter" expression="FAIL,SKIP,PASS,CONF,BY_CLASS" /> <param name="testNgXslt.showRuntimeTotals" expression="true" /> <classpath refid="test.classpath" /> </xslt> </target> </project> The preceding XML defines Ant build XML configuration, it contains an Ant target which generates the Reporty-ng report. The input path for testng results XML is configured through the in attribute of the xslt task in Ant. The transformation from XML to HTML is done using the testng-results.xsl of Reporty-ng, the location of which is configured by using the style attribute of the xslt task. The output HTML name is configured using the out attribute. Different configuration parameters for Reporty-ng are configured using the param task of Ant as shown in the preceding code. Now go to the said project folder through the command terminal and type the command ant –buildfile reporty-ng-build.xml and press Enter . You will see the following console output on the terminal: Now go to the configured report output folder reporty-ng (in this case) and open the file index.html in your default browser. You will see the following test report: On clicking the Default suite link on the left-hand side, a detailed report of the executed test cases will be displayed as shown in the following screenshot: What just happened? In the previous example we learned how to generate a Reporty-ng report using Ant. The report is very good from a report point of view as it gives a clear picture of the test execution through the pie chart. The output report can be configured using different configurations, which we will cover in the next section. Configuration options for Reporty-ng report As said earlier there are different configuration options that the Reporty-ng report supports while generating the report. Following is the list of supported configuration options and how they affect the report generation: testNgXslt.outputDir: Sets the target output directory for the HTML content. This is mandatory and must be an absolute path. If you are using the Maven plugin, this is set automatically so you don't have to provide it. testNgXslt.cssFile: Specifies an alternative style sheet file overriding the default settings. This parameter is not required. testNgXslt.showRuntimeTotals: Boolean flag indicating if the report should display the aggregated information about the method durations. The information is displayed for each test case and aggregated for the whole suite. Non-mandatory parameter, defaults to false. testNgXslt.reportTitle: Use this setting to specify a title for your HTML reports. This is not a mandatory parameter and defaults to TestNG Results. testNgXslt.sortTestCaseLinks: Indicates whether the test case links (buttons) in the left frame should be sorted alphabetically. By default they are rendered in the order they are generated by TestNG so you should set this to true to change this behavior. testNgXslt.chartScaleFactor: A scale factor for the SVG pie chart in case you want it larger or smaller. Defaults to 1. testNgXslt.testDetailsFilter: Specifies the default settings for the checkbox filters at the top of the test details page. Can be any combination (comma-separated) of: FAIL,PASS,SKIP,CONF,BY_CLASS. Have a go hero Write an Ant target in Ant to generate a Reporty-ng report with only Fail and Pass filter options. Pop quiz – logging and reports Q1. Which interface should the custom class implement for tracking the execution status as and when the test is executed? org.testng.ITestListener org.testng.IReporter Q2. Can we disable the default reports generated by the TestNG? Yes No Summary In this chapter we have covered different sections related to logging and reporting with TestNG. We have learned about different logging and reporting options provided by TestNG, writing our custom loggers and reporters and methods to generate different reports for our TestNG execution. Each of the reports have certain characteristics and any or all of the reports can be generated and used for test execution depending upon the requirement. Till now we have been using the XML configuration methodology of defining and writing our TestNG test suites. In the next chapter we will learn how to define/configure the TestNG test suite through code. This is helpful for defining the test suite at runtime. Resources for Article : Further resources on this subject: Developing Seam Applications [Article] The Aliens Have Landed! [Article] Visual Studio 2010 Test Types [Article]
Read more
  • 0
  • 0
  • 4751

article-image-media-module
Packt
06 Aug 2013
10 min read
Save for later

The Media module

Packt
06 Aug 2013
10 min read
(For more resources related to this topic, see here.) While there are many ways to build image integration into Drupal, they may all stem from different requirements and also each option should be carefully reviewed. Browsing around over 300 modules available in the Media category in Drupal's modules search for Drupal 7 (http://drupal.org/project/modules) may have you confused as to where to begin. We'll take a look at the Media module (http://drupal.org/project/media) which was sponsored by companies such as Acquia, Palantir, and Advomatic and was created to provide a solid infrastructure and common APIs for working with media assets and images specifically. To begin, download the 7.x-2.x version of the Media module (which is currently regarded as unstable but it is fairly different from 7.x-1.x which will be replaced soon enough) and unpack it to the sites/all/modules directory like we did before. The Media module also requires the File entity (http://drupal.org/project/file_entity) module to further extend how files are managed within Drupal by providing a fieldable file entity, display mods, and more. Use the 7.x-2.x unstable version for the File entity module too and download and unpack as always. To enable these modules navigate to the top administrative bar and click on Modules , scrolling to the bottom of the page we see the Media category with a collection of modules, toggle on all of them (Media field and Media Internet sources), and click on Save configuration . Adding a media asset field If you've noticed something missing in the rezepi content type fields earlier, you were right—what kind of recipes website would this be without some visual stimulation? Yes, we mean pictures! To add a new field, navigate to Structure | Content Types | rezepi | manage fields (/admin/structure/types/manage/rezepi/fields). Name the new field Picture and choose Image as the FIELD TYPE and Media file selector for the WIDGET select box and click on Save . As always, we are about to configure the new field settings, but a step before that presents first global settings for this new field, which is okay to leave as they are, so we will continue, and click on Save field settings . In the general field settings most defaults are suitable, except we want to toggle on the Required field setting and make sure the Allowed file extensions for uploaded files setting lists at least some common image types, so set it to PNG, GIF, JPG, JPEG . Click on Save settings to finalize and we've updated the rezepi content type, so let's start using it. When adding a rezepi, the form for filling up the fields should be similar to the following: The Picture field we defined to use as an image no longer has a file upload form element but rather a button to Select media . Once clicked on it, we can observe multiple tabbed options: For now, we are concerned only with the Upload tab and submit our picture for this rezepi entry. After browsing your local folder and uploading the file, upon clicking Save we are presented with the new media asset form: Our picture has been added to the website's media library and we can notice that it's no longer just a file upload somewhere, but rather it's a media asset with a thumbnail created and even has a way to configure the image HTML input element's attributes. We'll proceed with clicking on Save and once more on the add new content form too, to finalize this new rezepi submission. The media library To further explore the media asset tabs that we've seen before, we will edit the recently created rezepi entry and try to replace the previously uploaded picture with another. In the node's edit form, click on the Picture field's Select media button and browse the Library tab which should resemble the following: The Library tab is actually just a view (you can easily tell by the down-arrow and gear icons to the right of the screen) that lists all the files in your website. Furthermore, this view is equipped with some filters such as the filename, media type, and even sorting options. Straight-away, we can notice that our picture for the rezepi that was created earlier shows up there which is because it has been added as a media asset to the library. We can choose to use it again in further content that we create in the website. Without the media module and it's media assets management, we had to use the file field which only allowed to upload files to our content but never to re-use content that we, or other users, had created previously. Aside from possibly being annoying, this also meant that we had to duplicate files if we needed the same media file for more than one content type. The numbered images probably belong to some of the themes that we experimented before and the last two files are the images we've uploaded to our memo content type. Because these files were not created when the Media module was installed, they lack some of the metadata entries which the Media module keeps to better organize media assets. To manage our media library, we can click on Content from the top administrative bar which shows all content that has been created in your Drupal site. It features filtering and ordering of the columns to easily find content to moderate or investigate and even provides some bulk action updates on several content types. More important, after enabling the Media module we have a new option to choose from in the top right tabs, along with Content and Comments , we now have Files . The page lists all file uploads, both prior to the Media module as well as afterwards, and clearly states the relevant metadata such as media type, size, and the user who uploaded this file. We can also choose from List view or Thumbnail view using the top right tab options, which offers a nicer view and management of our content. The media library management page also features option to add media assets right from this page using the Add file and Import files links. While we've already seen how adding a single media file works, adding a bunch of files is something new. The Import files option allows you to specify a directory on your web server which contains media files and import them all to your Drupal website. After clicking on Preview , it will list the full paths to the files that were detected and will ask you to confirm and thus continue with the import process. Once that's successfully completed, you can return to the files thumbnail view (/admin/content/file/thumbnails) and edit the imported files, possibly setting some title text or removing some entries. You might be puzzled as to what's the point of importing media files directory from the server's web directory, after all, this would require one to have transferred the files there via FTP, SCP, or some other method, but definitely this is somewhat unconventional these days. Your hunch is correct, the import media is a nice to have feature but it's definitely not a replacement for bulk uploads of files from the web interface which Drupal should support and we will later on learn about adding this capability. When using the media library to manage these files, you will probably ask yourself first, before deleting or replacing an image, where is it actually being used? For that reason, Drupal's internal file handling keeps track of which entity makes use of each file and the Media module exposes this information via the web interface for us. Any information about a media asset is available in its Edit or View tabs, including where is it being used. Let's navigate through the media library to find the image we created previously for the rezepi entry and then click on Edit in the rightmost OPERATIONS column. In the Edit page, we can click on the USAGE tab at the top right of the page to get this information: We can tell which entity type is using this file, see the title of the node that it's being used for with a link to it, and finally the usage count. Using URL aliases If you are familiar with Drupal's internal URL aliases then you know that Drupal employs a convention of /node/<NID>[/ACTION], where NID is replaced by the node ID in the database and ACTION may be one of edit, view, or perhaps delete. To see this for yourself, you can click on one of the content items that we've previously created and when viewing it's full node display observe the URL in your browser's address bar. When working with media assets, we can employ the same URL alias convention for files too using the alias /file/<FID>[/ACTION]. For example, to see where the first file you've uploaded is being used, navigate in your browser to /file/1/usage. Remote media assets If we had wanted to replace the picture for this rezepi by specifying a link to an image that we've encountered in a website, maybe even our friend's personal blog, the only way to have done that without the Media module was to download it and upload using the file field's upload widget. With the Media module, we can specify the link for an image hosted and provided by a remote resource using the Web tab. I've Googled some images and after finding my choice for a picture, I simply copy-and-paste the image link to the URL input text as follows: After clicking on Submit , the image file will be downloaded to our website's files directory and the Media module will create the required metadata and present the picture's settings form before replacing our previous picture: There are plenty of modules such as Media: Flickr (http://drupal.org/project/media_flickr) which extends on the Media module by providing integration with remote resources for images and even provides support for a Flickr's photoset or slideshow. Just to list a few other modules: Media: Tableau (http://drupal.org/project/media_tableau) for integrating with the Tableau analytics platform Media: Slideshare (http://drupal.org/project/media_slideshare) for integrating with presentations at Slideshare website Media: Dailymotion (http://drupal.org/project/media_dailymotion) for integrating with the Dailymotion videos sharing website The only thing left for you is to download them from http://drupal.org/modules and start experimenting! Summary In this article, we dived into deep water with creating our very own content type for a food recipe website. In order to provide better user experience when dealing with images in Drupal sites, we learned about the prominent Media module and its extensive support for media resources such as providing a media library and key integration with other modules such as the Media Gallery. Resources for Article : Further resources on this subject: Installing and Configuring Drupal Commerce [Article] Drupal 7 Fields/CCK: Using the Image Field Modules [Article] Drupal 7 Preview [Article]
Read more
  • 0
  • 0
  • 2273
article-image-setting-your-profile
Packt
06 Aug 2013
5 min read
Save for later

Setting Up Your Profile

Packt
06 Aug 2013
5 min read
(For more resources related to this topic, see here.) Setting up your profile (Simple) While it may seem trivial, setting up your profile is one of the most important steps in starting your Edmodo experience. Your profile gives others insight into the professional you! Remember that the users on Edmodo are fellow teachers, and the ability to connect with these educators around the world is an opportunity that should not be overlooked. Thus, it is important to take care to provide a snapshot of your educational expertise. Getting ready Create your teacher account at http://www.edmodo.com. How to do it... Creating an Edmodo account only takes minutes, but is the most important step as you begin your Edmodo journey. Click on I'm a Teacher. Choose a username and password. Connect to your educational institution to verify your teacher account. Upload a photo of yourself. Join online Edmodo communities (available now, but advisable to skip at this juncture). Find teacher connections. Fill in your About Me section. How it works... The Edmodo website looks like the following: On the Edmodo home page, http://www.edmodo.com, click on I'm a Teacher to begin your Edmodo journey. Set up your teacher account by using a unique username and a password that you can remember. If your first choice of username is not available, please choose another until you are notified of a successful selection. The e-mail address that you attach to your Edmodo account should be your school e-mail. You will also need to choose your school affiliation at this time. If your school is not listed as a choice on Edmodo, you may do a manual search for your school. Selecting your school will ensure that fellow teachers within your district can easily connect with you. This also provides Edmodo with the background to be able to make suggestions on other educators with whom you may want to connect. Additionally, once you become active in the Edmodo community, your school selection will provide better insight into your teaching background, and will provide a greater context for other teachers to potentially partner with you in collaborative endeavors. Once you have created your account, you will be prompted to upload a photo of yourself. This is advisable in order to make you easier to distinguish when you interact in the professional communities, and it literally puts a face to your name. Certainly you have the option of using one of Edmodo's generic pictures. However, you will inevitably be sharing this generic picture with thousands of other users. You will be prompted to create a unique URL. This will provide additional ease to search for you when making professional connections. Your username is probably the easiest option for this. Next, you have the option of joining an array of online professional communities. We will come back to this step later in the section on Edmodo Communities. However, you will notice that Edmodo has automatically enrolled you in their Help community. This community is designated with the question mark symbol and once you have been redirected to your home page, you will notice it located in the left section of your screen, directly below your established Groups. From your profile page, you can find teacher connections. You can choose from the teacher suggestions made by Edmodo. Edmodo makes these connection suggestions based on your school district selection. These suggestions are located on the left-hand side of the profile screen. Simply click on a teacher with whom you would like to make a connection. If you would like to connect to other teachers who are not on Edmodo, you may send them an invitation from your profile page. Simply hover over the link How to improve my profile? that is located on the right-hand side of the screen. From here, enter the e-mail addresses of those educators you would like to join Edmodo. Your profile page also provides you with the ability to write an About Me description. In this portion, include the courses you teach and any educational interests you might have that could potentially pique the interest of your fellow educators. Note my personal Edmodo About Me description as seen in the preceding screenshot. There's more... You have created the basics of your profile. However, in order to gain clout in the Edmodo online community, you may want to begin earning badges. Your first chance to do so is in your profile setting. Earning teacher badges You will notice on your Edmodo profile page that you have the opportunity to earn teacher badges. Simply having your teacher account verified as being one that belongs to an educator will earn you the Verified Teacher badge. However, you can collect many others. Joining one of the subject area communities will net you a Community Member badge and following a publisher community will score you the Publisher Collaborator badge. Connect to at least 10 other educators on Edmodo and you will find yourself awarded with the Connected badge. The more educators with whom you connect, the more ways you can earn differentiated levels of this badge. The other badge you may want to covet earning is your Librarian badge. This is possible when you begin sharing resources on Edmodo that other educators find to be useful. (See Sharing Resources for additional information on how to do so.) Summary This article provided details on getting started with a simple yet effective classroom environment based set up, Edmodo. This article also listed the procedure to set up your profile on Edmodo. Resources for Article: Further resources on this subject: Getting to Grips with the Facebook Platform [Article] Introduction to Moodle Modules [Article] Getting Started with Facebook Application Development using ColdFusion/Railo [Article]
Read more
  • 0
  • 0
  • 1502

Packt
06 Aug 2013
16 min read
Save for later

Getting Dynamics NAV 2013 on Your Computer – For (Almost) Free

Packt
06 Aug 2013
16 min read
(For more resources related to this topic, see here.) Whether you're getting into Dynamics NAV because you're interested in a profession in this field, or because your company is interested in using Dynamics NAV as their ERP system, trying the software before you make the commitment will ensure you're making the best decision for you and your company. There are three components that are needed for you to get into the development environment in Dynamics NAV. They are: The Dynamics NAV installation software Visual Studio 2012 Express The license file The installation files can be downloaded for free. The installation files come with the full development environment. They will install almost everything you need to work with Dynamics NAV. The files are the same for a single-user installation and a multinational corporation. To create and modify reports in Dynamics NAV, you will need to have Visual Studio installed on your computer. Dynamics NAV uses the RDLC reporting method, which means the reports do not get processed on the SQL server; rather, it's processed on the server where the middle tier is installed. The last part of going into the development environment is getting the proper license. Yes, you can download the software for free, but the license will cost you some money in terms of an MSDN subscription. We will go through the online resources to download your copy of Dynamics NAV to be installed on your computer. We will also explore ways of trying out the development environment in Dynamics NAV by signing up for a free trial using the cloud service that is available. Getting your free copy Before we go through the trouble of downloading the software, make sure the computer you're working with has the proper specifications in order to do a full installation. For a list of the hardware requirements, take a look at the following link: http://msdn.microsoft.com/en-us/library/dd301054(v=nav.70).aspx Once we've verified that our hardware is good, we can start our journey and become familiar with the Dynamics NAV development environment by getting a copy of the software. Microsoft has the installation files available for download; however, you have to be signed up as a Microsoft partner, have MSDN access, or already be a Microsoft Dynamics customer with access to download from the Microsoft CustomerSource portal. The download for the full software can also be found on the following links: http://www.mibuso.com/dlinfo.asp?FileID=1495 http://dynamicsuser.net/media/p/312076.aspx Don't worry, these links are legitimate. Luc (owner of mibuso.com) and Erik (owner of dynamicsuser.net) are good people whom I've had the pleasure of interacting with. There are other sites that you can download the software installation files from, which you can try at your own risk. To be able to modify and create reports in Dynamics NAV, you will also need a copy of Visual Studio 2010 installed on your computer. Fortunately, all you need is the Express version, which is free. The link to download this directly from Microsoft is as follows: http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products Installing the software Once you've extracted the downloaded file, the folder where you extracted the file should look something like this: If you have any prior versions of Dynamics NAV (or Navision) installed, please make sure you uninstall them before you run the installation. Click onthe Setup.exe file and run through the installation wizard. Click on the Install Demo option and go grab some coffee. We'll wait for you. Installing Visual Studio Web Developer 2010 Express After Dynamics NAV is installed, run the Visual Studio Web Developer 2010 Express installation. Again, this is needed for creating and modifying Dynamics NAV reports. There's an additional component that you will need to install on your computer in order to properly modify Dynamics NAV reports using the Visual Studio Web Developer Express. You will need to install the free version of Microsoft Visual Studio 2010 Shell (Integrated) Redistributable Package. The link is as follows: http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=115 For this article, any additional contents on the installation files will not be needed. Running through the installation wizard will be more than enough for what we need to do. A quick overview of the additional contents of the installation files for Dynamics NAV Suppose you're a curious person like myself and you click on the Choose an installation option instead; you'll instead be presented with a list of the components that you can install. Here's a quick rundown of the components that can be installed individually. If you click on an icon, you will get a description of what it is. I will go through these options in simple English. The components you will see are as follows: Client: This is the Role Tailored Client, or as Microsoft calls it, the "Windows Client". If you wish to deploy this for your company, you will at least need to install this component. Administration Tools: This is the snap-in console that allows you to configure the Windows services related to Dynamics NAV. This is an interface that allows you to be able to, for example, change the port for a Dynamics NAV connection without having to mess with DOS prompts or the registry. Documentation: If you want the documentation or Help to be available for the NAV client, you should probably install this. Server: Dynamics NAV is a three-tier system. The middle tier is where the business logic is executed. So any device, web service, or client software will use the middle tier to get and write data into DynamicsNAV. SQL Server Database Components: Choose this option for installing SQL Server Express on your computer. Do this only if you have a version below SQL Server 2008 installed on your computer. Microsoft recommends that you should use at least SQL Server 2008 R2. Dynamics NAV 2013 will only run on a SQL Server database. Sorry! Portal Framework for SharePoint: Dynamics NAV is integrated with SharePoint. This will allow you to build SharePoint web applications in Dynamics NAV. For this to work, you will need SharePoint 2010 installed. Microsoft Office Outlook Add-in: Believe it or not, Dynamics NAV has a built-in CRM solution. This option allows you to install a component to Microsoft Outlook to synchronize contacts, tasks, and calendar items with Dynamics NAV. Automated Data Capture System: ADCS is the acronym that you'll find if you do a search online. This option allows the warehouse staff to pick and put away inventory to/from the warehouse bins in real time using handheld devices. Web Server Components: Dynamics NAV 2013 is the first version in NAV history to have an out of the box web client. This option allows you to use a web client instead of the Windows client. ClickOnce Installer Tools: If you're an in-house IT guy, you know that installing software on each and every computer is a pain. ClickOnce technology allows you to deploy Dynamics NAV through a web link. You can preconfigure the setup for each user so they can do the installation themselves. If any of the preceding components catch your eye and you would like additional information on them, Microsoft provides detailed explanations on each of the components and how to deploy them in your organization. This detailed information can be found at: http://msdn.microsoft.com/en-us/library/dd301130(v=nav.70).aspx If you want more information on these additional components and how they can benefit your business, I would highly recommend you contact your local Dynamics NAV partner and get them involved. Nothing is more frustrating than installing something and not knowing how it works. A look at what is installed After the installation finishes, if you click on your Start icon in Windows, you'll notice a few new icons. They are: Microsoft Dynamics NAV 2013 Administration Shell Microsoft Dynamics NAV 2013 Development Environment Microsoft Dynamics NAV 2013 Microsoft Dynamics NAV Administration Microsoft SQL Server 2012 folder Microsoft SQL Report Builder folder In addition, there will be two services that will be running. They are: Microsoft Dynamics NAV Server [DynamicsNAV70] SQL Server (NAVDEMO) The SQL Server (NAVDEMO) is the service for the SQL Server instance where the demo database resides. Microsoft Dynamics NAV [DynamicsNAV70]is the middle tier that the client connects to. Ensure both of these have their status as Started. Microsoft Dynamics NAV 2013 Administration Shell The Microsoft Dynamics NAV 2013 Administration Shell allows you to run scripts implemented using PowerShell 2.0. There are predefined commands (called Cmdlet) that the user can use right away. The built-in Cmdlet allows the administrator to configure and troubleshoot permissions and connection problems on a local or remote computer. Also worth mentioning is that Cmdlet should always be run as an administrator. This tool will come in very handy if you're deploying Dynamics NAV to remote locations or in your own private cloud. Microsoft Dynamics NAV 2013 Development Environment As the name suggests, this is where NAV developers come to create all sorts of wonderful things for Dynamics NAV. This is the main environment where developers work; it is called the Client/Server Integrated Development Environment(C/SIDE). Within C/SIDE, you will be developing using a language called Client/server Application Language(C/AL). All of the objects are contained within this environment and stored in the SQL Server database, so you do not have to go anywhere else to create or modify applications for the end users. Even the development of report layouts, which uses Visual Studio and not C/SIDE, is tightly integrated and is launched from within the development environment. The development environment is also where the user can update the license either on the server or for their particular session. When you start this application, it will look very empty. For Dynamics NAV developers, the majority of their time will be spent here, so having a nice clean space is a very good start. Microsoft Dynamics NAV 2013 Windows Client Microsoft Dynamics NAV 2013 Windows Client is the actual client application that the end users will be using to transact their daily operations. As mentioned earlier, the client application is called the Role Tailored Client (RTC), or Windows Client. By default, when you start the Windows Client, you will assume the role of a salesorder processor. This is okay, because you can still access any part of the system as long as you have permission to do so. Any changes we've made in the development environment will be reflected in the client application through the middle tier. If you're using any other interface, such as a mobile interface or a web interface, the changes will be reflected there as well. Microsoft Dynamics NAV Administration Do not confuse this with the previously mentioned Administration Shell. The Microsoft Dynamics NAV Administration program allows you to manage the Dynamics NAV services that are installed both on your local computer and on the server. You can also manage the services without using this program using the command prompt, but that wouldn't be very efficient, or fun. The SQL Server 2012 folder There's a tool in the SQL Server 2012 folder called Import/Export Data. As a general rule, when working with Dynamics NAV or any ERP software out there, do not ever try to import data directly into the SQL tables in your ERP software. The reason is that these import/export programs do not validate against any business logic that's built in place. By doing these imports in an external program, you risk undermining the integrity of the data in your ERP software. The SQL Server 2012 report builder The SQL Server 2012 report builder is installed primarily for the API to upgrade RDLC 2005 for Dynamics NAV 2009 to RDLC 2008 in Dynamics NAV 2013. Of course, you can use the report builder to create beautiful reports for Dynamics NAV if you do not wish to use the reporting tool within C/SIDE. The reports that you will be modifying or creating in C/SIDE will automatically be linked to the appropriate objects. However, the reporting in C/SIDE will not be done using the Report Builder application. Getting your license Now that we have all the software installed, you will need proper licensing in order to do some development. There are a couple of different licenses that you, the end user, will have access to. Demo license When you install Dynamics NAV, the demonstration license is installed by default. The demonstration license allows you to access every module in Dynamics NAV. However, the areas you're able to develop are severely restricted. The demo license is intended for you to click around and test a few transactions with certain date ranges. It's not really meant for learning development, which is the reason why you're reading this article. In case you're already familiar with Dynamics NAV object numbers, here are the properties of the Dynamics NAV 2013 demo license from the Microsoft website: Start up to two simultaneous client sessions on any platform. Create up to two companies. Support an unlimited number of web users. Run in any supported language. Use all application functionalities, including add-on products, local extensions, and customizations in current and previous versions. This means that you can run, but not modify, all object types within the range 1 to 99,999,999. Run and modify table 18, 2000000061 and 2000000064 through 2000000200, pages 21 and 22, report 101, and XML ports 99,008,503 and 99,008,510. Each object in Dynamics NAV is assigned an ID, so when we say we can modify table 18, it means we can make modifications to the table with ID 18. Run, modify, and create fields 99,990 to 99,999, page 99,998 and 99,999, report 99,999, Query 99,9999, and MenuSuite 90. The database data restriction is as follows: Enter transactions in months other than November, December, January, and February MSDN license If you're looking for more development capability, you can also subscribe to MSDN to get a Dynamics NAV license with more capability than the out of the box demo license. At the time of writing, Microsoft had not released the MSDN license for Dynamics NAV 2013. If you want the MSDN license, check the MSDN site regularly. The MSDN license for Dynamics NAV is meant for you to learn development in Dynamics NAV; however, you will not be able to use this license to run your company. The license has the same restrictions as the demo license, in that you can only enter transactions in a certain date range. If you're already familiar with Dynamics NAV object numbers, here are the properties of the MSDN licensing: Table 18 can be modified. Fields 99,990 through 99,999 can be inserted into table 18. Pages 21 and 22 can be modified. Forms 99,998 and 99,999 can be inserted. Report 101 can be modified. Report 99,999 can be inserted. MenuSuite 90 can be inserted. XML Port 99,999 can be inserted. Create new objects in the object range 123,456,700 through 123,456,799. The restriction on the database data is as follows: Enter transactions in months other than November, December, January, and February A full On-Premise license The pricing for an MSDN subscription and a full user license is comparable. In fact, buying a full Dynamics NAV user license is cheaper than an MSDN subscription. If buying Dynamics NAV is what your company is going to do anyway, it may be better to purchase the Starter Pack without any additional users. This will allow you to use the license and the database that you will use when Dynamics NAV is implemented for your company. If you choose to buy the full license, you will be able to follow the majority of exercises in this article. You will also not get to use the more advanced modules, such as manufacturing or warehouse management. However, this article will not be getting into these advanced granules. The On-Premise license will allow you to modify everything except the following: Code units Code behind the pages Code behind the tables In addition, the On-Premise license comes with the following: 10 custom tables you can create 100 custom pages you can create 100 custom reports you can create In order to buy the On-Premise license, you will need to find your local Dynamics NAV Solution Provider. Thankfully, Microsoft provides a directory for partners that provide service and software, as follows: http://www.microsoft.com/en-us/dynamics/partner-information.aspx When you go on this site, make sure the company also provides services for Dynamics NAV. I would caution you against buying Dynamics NAV from a company that does not provide the service for it. Finding the right partner is a science in itself. The cloud license Part of the big push with the release of Dynamics NAV 2013 is the ability to move the product into the cloud environment. There are a few companies that have cloud offerings. The company that has graciously allowed us to use their environment for you to log in and follow along with the book is called Data Resolution, Inc. Coincidentally, they were named Hosting Partner of the Year by Microsoft in 2012. You can get a 30-day free trial at http://navappdev.erpclouds.com/. Unfortunately, Data Resolution does not offer a longer free trial. The licensing available when you sign up would be the same as buying the On-Premise Dynamics NAV license with full development capabilities for your company. Again, you will not get to use the more advanced modules, such as manufacturing or warehouse management. This article will not be getting into these advanced granules; if you're interested in these by the end of this article, you can ask your local Dynamics NAV solution provider to discuss using this solution for your company. If you wish to continue following through the article and continue doing examples after the 30-day trial, you will need to sign up. So, use your time with this article wisely. Summary In this article, we've gone over finding your copy of Dynamics NAV and the installation process. There are localized versions of Dynamics NAV databases, for example, the US Dynamics NAV database will be a little different than the Indian Dynamics NAV database. For the purpose of learning development, which is described in this article, it does not matter which database you use. To maximize the experience you get out of this article, I would highly recommend you utilize the 30-day free trial on the cloud. Even if you need more than 30 days, the cost involved in that additional 30 days would be significantly lower than if you were to get a MSDN subscription or buy the On-Premise Starter Pack. Getting the software itself is the easiest part. Finding the right license that you will need is actually more of a challenge. Resources for Article : Further resources on this subject: Foreword by Microsoft Dynamics Sure Step Practitioners [Article] Overview of Microsoft Dynamics CRM 2011 [Article] Setting up the Microsoft Dynamics GP System [Article]
Read more
  • 0
  • 0
  • 4070

article-image-top-geany-features-you-need-know-about
Packt
05 Aug 2013
4 min read
Save for later

Top Geany features you need to know about

Packt
05 Aug 2013
4 min read
(For more resources related to this topic, see here.) Quick text editing Geany provides quick text-editing facilities. You can make use of these by going to Edit | Commands, Edit | Format and the Documents menu. The following are some examples; please note that there are many facilities and I cannot list all of them here: Use the Tab key to quickly indent a block of code: Use a shortcut key or a command to quickly comment out a block of code: For other tools, you can see the keyboard shortcut on the menu and you can set it by going to Edit | Preferences | Keybindings: The <Primary> key in the preceding screenshot is the Ctrl key. Navigating through the source code I feel that navigating through source code in Geany is better and faster than any other IDE I've ever tried. To navigate in the same file, you can use the following features as shown in the following screenshot and explained thereafter: Symbol list: Click on one item in the list and you will be brought to where the symbol is defined. Very few free IDEs or editors provide this feature; if they do, it is not as capable or fast as Geany Go to Tag Definition: Clicking on this will take you to where a function is defined and from where it is used. This feature has limitations when used alone in Geany. It can only look for definitions in the files being opened. So, to enable it to look for definitions in other closed files in the same folder, you must use this feature inside a certain project created by Geany's built-in Project feature, with the File patterns defined, and combine it with other project plugins that support Generate file list on load. Find Usage and Find Document Usage: These features help you look for the lines in which a function/variable is used. You can also jump to these lines from the result list. These are more convenient than the usual find/search dialog. To navigate between files, you may find the tree browser (1) and addons (2) plugins useful, as shown in the following screenshot: Project Not only can Geany work with a single file on demand, but it also provides some basic management for working with a group of files in a project. This feature helps you reopen files from the last session when reopening Geany. It also helps you change some default parameters (indentation type, command to build and run, and so on) and apply those on all files. And don't forget its role in navigating through the source code, as described previously. Using the Project feature is easy. All things related to it can be found in the Project menu. Clearing files With the increasing use of source version control (SVN and Git), cleaning the space automatically gets important in order to avoid polluting the Commit history with meaningless changes, caused accidentally by some whitespace additions/removals. You should make Geany do this automatically, by con figuring the settings after going to Edit | Preferences | Files as shown in the following screenshot: Plugins If you want a feature that you cannot find built in to Geany, or you want to discover something new and funny, you need to pick and turn on some Geany plugins. To see the list of installed plugins, please go to Tools | Plugin Manager. Some noteworthy plugins are as follows: Addons: It shows a button with a dropdown menu displaying a list of opened files, to allow you to switch quickly between them Code navigation: It adds more support to the built-in code-navigation feature Debugger: It helps to integrate with GDB ( GNU Debugger) and other debuggers Summary In this article, we discussed the most commonly used tasks in Geany and how to perform them, along with the most widely used features and plugins of Geany. Resources for Article: Further resources on this subject: 10 Minute Guide to the Enterprise Service Bus and the NetBeans SOA Pack [Article] User Extensions and Add-ons in Selenium 1.0 Testing Tools [Article] Installing and Setting up JavaFX for NetBeans and Eclipse IDE [Article]
Read more
  • 0
  • 0
  • 9328
article-image-working-local-files-simple
Packt
05 Aug 2013
8 min read
Save for later

Working with local files (Simple)

Packt
05 Aug 2013
8 min read
(For more resources related to this topic, see here.) Getting ready Now that you have Mercurial set up on your computer, you are going to use it to track all the changes that can happen in a configuration directory, for example, in the configuration files of Apache2 (a web server). It could of course be used in any other type of directory and files: your home directory configuration files, some personal development projects, or some Mozilla Firefox configuration files such as user.js and prefs.js, and likewise. If you do not have Apache2 installed on your computer, you can do the same type of exercises, for instance, in $HOME/src. Also, with the owners of the /etc/apache2 files being root, you will need to change their owner to your account. For example, your username/group both being mary/mary: $ sudo chown -R mary:mary /etc/apache2/ How to do it... The first thing to do is to initialize a new repository by typing these lines: $ cd /etc/apache2/$ hg init You can then show the status of the files in your new repository, that is, which files are tracked and which are unknown to Mercurial, and also which files have changed, which have been deleted, and so on. This is done using the Mercurial status command: $ hg status? apache2.conf? conf.d/charset? […]? sites-available/default-ssl? sites-enabled/000-default As you can see, everything is untracked right now. The way to tell Mercurial which files you want to be version controlled and added to your newly created repository is by using the add command. For example, if you want to track changes in the apache2.conf file, and all the files of the sites-available directory, you need to type: $ hg add apache2.conf sites-available/ Showing the status again, Mercurial now tells us that three files have been added (by showing A, which denotes Added ; instead of ?—, denoting Unknown , in front of its path): $ hg statusA apache2.confA sites-available/defaultA sites-available/default-ssl? conf.d/charset[…] Now if you want to record the initial version of these files (the one provided by your distribution), you need to use the commit command and add a log message using the -m option: $ hg commit -m"initial version" Using the log command, you can print the entire history of your repository (here you used commit only once, so only one change—called a changeset —is listed): $ hg logchangeset: 0:7b3b5fcb16d0tag: tipuser: Mary <mary@company.com> […date…]summary: initial version Whenever you change a file, you can record the modification in Mercurial with the commit command. Let's type the following sequence using the status, diff, commit, and log commands: $ vi apache2.conf..make some changes: add "# new line" at the end of the file$ hg status -mM apache2.conf$ hg diffdiff -r 7b3b5fcb16d0 apache2.conf […]+# new line$ hg commit -m"added a line at the end"$ hg logchangeset: 1:02704fcf58b1user: Mary <mary@company.com>summary: added a line at the endchangeset: 0:7b3b5fcb16d0 […] You can also reverse the modifications; for instance, if you modify a line again in apache2.conf, such as changing # new line by # new linez, using the diff command, you can see your modification: $ hg diffdiff -r b0d2bfb95d81 apache2.conf […]-# new line+# new linez It is then possible to come back to the latest saved version using the revert command: $ hg revert apache2.conf$ hg status? apache2.conf.orig$ tail -1 apache2.conf# new line At this stage, you can't see the M apache2.conf file any more, and the value at the last line is the one that is not modified, but there is a new file called apache2.conf.orig; this is because the revert command saves your modifications in a backup file. You can either delete it afterward or you can use the --no-backup option if you know what you do. Mercurial has a conservative philosophy to avoid losing data. Finally, if you realize that you do not need a file any more, you can remove it from being version controlled and from your working directory by using the remove command. For example, if you don't need the SSL configuration file of Apache, you can remove it by typing: $ hg remove sites-available/default-ssl$ hg statusR sites-available/default-ssl Instant Mercurial Distributed SCM Essential How-to13$ hg commit -m"removed unused ssl conf file"$ ls sites-available/default Note that the file has disappeared from your working directory, and even if you create a file with the same name, it will not be version controlled anymore (unless you add it again). But, of course, it is still possible to get it back or to get a version of apache2.conf exactly how it came with the distribution. In order to do that, you need to use the update command and ask to switch to revision 0: $ hg update -C 0$ ls sites-available/default default-ssl$ tail -1 apache2.confInclude sites-enabled/ In order to switch back to the latest revision (it is coined the tip), you can just call update again with no revision passed as an argument: $ hg update -C2 files updated, 0 files merged, 1 files removed, 0 files unresolved How it works... With this sequence, you already know how to manage versions of your personal projects: save a version, undo a change, retrieve an older version, and so on. Let's take a closer look at what happens under the hood. After you type hg init, a directory called .hg is created: $ ls -a. apache2.conf envvars magic mods-enabled sites-availabl.. conf.d .hg mods-available ports.conf sites- This directory, called a repository, contains the history of your project and some Mercurial metadata. This is where Mercurial records all your revisions of tracked files (actually, it stores only the differences between each revision, like RCS/CVS/Subversion, and in compressed form, so the size is usually less than that of the the actual data!). And also, this is where it stores the commit messages, information about branches, tags, bookmarks, and so on. All the other files and directories beside .hg are your project files; they form what is called a working directory. If ever you would like to forget version control in this zone, you can just remove the .hg directory. Another interesting thing to note is that, contrary to Subversion for instance, in a local .hg repository, you do not have less information than in a repository in a central server! Your installation of Mercurial actually allows you to have both a client command (or GUI), with which you can work locally or with distant servers, and a server, with which you can share your work with colleagues. This is because, with DVCS ( Distributed VCS ), there is no difference between the data checked out to work with and the data ready to be published. When you either create a local repository or clone an existing one from a server, you have all the necessary information (in the .hg directory) to become a publisher yourself. There's more... This section will discuss supplementary commands or options. Other commands First of all, Mercurial has many commands (the list of which you can get with the command hg help); in addition to adding and removing files from being version controlled, you can also use the copy command to copy a file into a new file, and similarly, you have a rename command. The difference between the OS file copy and the one that Mercurial has is that when you receive a change made by somebody else to the original file, it will also be applied by Mercurial to the new file; but for this magic to work, you have to tell Mercurial about the copies/renames so it can track them. There are also convenient commands, such as addremove, which allows you to automatically add all new files and remove (from version control) files that have been deleted. Options Please refer to the built-in help system or to reference documentation, such as Mercurial: The Definitive Guide, available online at o http://hgbook.red-bean.com/, to get the complete list of options for each command. You have seen -m (or --message) in the commit command to specify a log message; if you omit this option, Mercurial will prompt you to get a message using $EDITOR. Also, you have seen -C (or --clean) in the update command; it will tell Mercurial to discard uncommitted changes when switching to another version (otherwise, local changes would automatically be merged into the requested version). Summary This article explained how to work with local files, either personal projects or files that you wanted to be version controlled (for example, source code or configuration files). You also learned how to create a new repository, make changes, and track them (selecting files to track, recording changes, and reversing those changes). Resources for Article : Further resources on this subject: Apache Continuum: Ensuring the Health of your Source Code (Part 1) [Article] Apache Continuum: Ensuring the Health of your Source Code (Part 2) [Article] Negotiation Strategy for Effective Implementation of COTS Software [Article]
Read more
  • 0
  • 0
  • 1641

article-image-configuring-alerts
Packt
02 Aug 2013
8 min read
Save for later

Configuring the alerts

Packt
02 Aug 2013
8 min read
(For more resources related to this topic, see here.) Getting ready Armed with knowledge on how to write rules, we could just toggle alerting levels for all rules individually. This would be tedious, unless we used a generic catchall. However, that would destroy the granularity and precision of OSSEC analysis. It would be better to combine the two to maintain granularity and get e-mail alerting down to reasonable levels. Every rule must set a level. The higher the level, the more severe the alert is considered. Alert levels are integers from 0 (ignore) to 15 (certain high-level security event). The official documentation has the definitive list of levels and their meanings and can be found via the following link: http://www.ossec.net/doc/manual/rules-decoders/rule-levels.html. Rules can also append groups to the alert metadata. OSSEC ships with a number of defaults, and you're able to create your own. Here are a few interesting groups from the default rule set: invalid_login, authentication_success, authentication_failed, connection_attempt, and attacks. Using just the level and group of an alert, you can configure a vast amount of alerts in a few lines of configuration. How to do it... Perform the following steps to configure alerts: To do this, start with configuring the default log and the e-mail level for the alerts as follows: <alerts> <log_alert_level>1</log_alert_level> <email_alert_level>7</email_alert_level></alerts> To increase the default level to 9, change a single character in the ossec.conf file as follows: <alerts> <log_alert_level>1</log_alert_level> <email_alert_level>9</email_alert_level></alerts> OSSEC reports feature provides a flexible way to send condensed reports to give you daily insight into alerts that are interesting, though not actionable as individual events. To configure a report of successful logins, add this to your ossec.conf file: <reports> <category>authentication_success</category> <user type="relation">srcip</user> <title>OSSEC: Authentication Report</title> <email_to>security.alerts@example.com</email_to></reports> How it works... The e-mail alerting defaults on large networks may inundate administrators in the alerts for the log lines containing the word error. Log level 8 is reserved for first-time events, and you may not want to know the first time every administrator logs into every server in the network. Given that, we can reasonably adjust the level of the e-mail alerts to level 9 that is reserved for Errors from Invalid Sources. This single-digit change may dramatically improve the volume of the alerts. You may wish to still receive periodic reports on the events not generating e-mails. The use of reports, alerts matching the source IDs, groups, or levels that are specified is summed up and broken down by key elements in the events: usernames, source IPs, event source hosts, levels, and groups. Our sample report generates a daily report of authentication history for all installed and active OSSEC agents. This happens to be a common requirement for PCI-DSS, SOX, FISMA, or FERPA. The report will contain an aggregation of various categories; here's a sampling of some interesting data: Report 'OSSEC: Authentication Report' completed.------------------------------------------------->Processed alerts: 1293->Post-filtering alerts: 120->First alert: 2013 May 10 02:00:02->Last alert: 2013 May 10 22:05:25Top entries for 'Source ip':------------------------------------------------192.168.0.1 |100127.0.0.1 |191.2.3.4 |1Top entries for 'Username':------------------------------------------------compliance_scanner |100root |19mallory |1Related entries for 'Username':------------------------------------------------compliance_scanner |120 srcip: '192.168.0.1'root |19 srcip: '127.0.0.1'mallory |1 srcip: '1.2.3.4' This report uses the relation attribute to aggregate users by source IP to generate the last stanza of the report. It provides some clarity on the Username and Source ip sections to let us know where particular users originated. Each report requires an email_to attribute to be set to valid. Another option that is often useful for very specific reports referencing a particular rule, or set of rules, is the showlogs option. Using this option, you can include a complete history of every log message that generated the alerts in the report. This option may generate large e-mails. To use it, add this to your report declaration: <showlogs>yes</showlogs> There's more... These basic tweaks provide a lot of value, but there are a few additional tweaks we can use to clear up a few noisy alerts and integrate OSSEC with existing security workflows. What is rule 1002 and why is it spamming me? Even when toggling the default alert level, you will notice that they occasionally receive alerts below the threshold set in the ossec.conf file. This is because some rules override this setting by explicitly setting the e-mail alert flag. The "bad words" rule, ID 1002, is another example that overrides the default e-mail behavior. The rule is defined as follows: <!-- Slightly modified for simplicity --><rule id="1002" level="2"> <match>failure|error|attack|bad |illegal|denied|refused|unauthorized</match> <options>alert_by_email</options> <description>Unknown problem somewhere in the system.</description></rule> This rule uses the alert_by_email option, which always alerts you regardless of the settings of the alert levels. Another set of rules that uses this override detects a restart of the OSSEC process. Rules that detect the start or stop of the OSSEC daemon also use this option to ensure that an e-mail alert is always sent. If you're not interested in these alerts, you can overwrite the rule and change its behavior using the overwrite attribute. This rule should be created in the local_rules.xml file as follows: <rule id="1002" level="2" overwrite="yes"> <match>failure|error|attack|bad |illegal|denied|refused|unauthorized</match> <options>no_email_alerts</options> <description>Unknown problem somewhere on the system (no_email_alert)</description></rule> We need to redefine the rule with our modifications because the overwrite flag replaces the existing rule of that source ID entirely. The purpose of this method in creating a new source ID is so other rules dependent on this rule will not need to also be rewritten to accommodate the change. The downside is that if an improvement to the default rule is made in an OSSEC release, you Playing nice with others Once at a larger scale, it may become more useful to integrate OSSEC's alert logs into a larger Security Information and Event Manager (SIEM) such as Splunk or ArcSight. Luckily, OSSEC also supports the logging of events via syslog. Any event that OSSEC logs, which is level 1 and above by default, is also written to syslog. OSSEC supports multiple logging formats, including the following: default: This is the default full syslog output that can be used in hybrid mode CEF: This refers to ArcSight's Common Event Format splunk: This is the key-value output json: This refers to JSON-encoded events; it is most useful for LogStash or Graylog2 To enable one or more syslog outputs, you just need to declare the server and port values. OSSEC uses UDP for all syslog events as follows: <syslog_output> <server>logserver.example.com</server> <port>514</port> <format>json</format></syslog_output> This is plain syslog traffic and will not be encrypted. You can change the level for which a certain syslog server receives events by setting the log level in the syslog definition: <syslog_output> <level>10</level> <server>critical-events.example.com</server> <port>514</port> <format>json</format></syslog_output> You can also route certain events by rule group as follows: <syslog_output> <group>authentication_success</group> <server>authenticationlogs.example.com</server> <port>514</port> <format>json</format></syslog_output> Or, you can also route specific events by the source ID directly: <syslog_output> <rule_id>1002</rule_id> <server>errors.example.com</server> <port>514</port> <format>json</format></syslog_output> You may choose to completely disable e-mail alerting from OSSEC and use this syslog mechanism to work within your existing SIEM workflow. Summary In this article we looked at various options for adjusting the alert volume for OSSEC HIDS. We started with some broad, sweeping approaches to decrease e-mails and gradually increased our granularity. We also explored the different channels for alerting. Resources for Article: Further resources on this subject: BackTrack Forensics [Article] Analyzing network forensic data (Become an expert) [Article] Digging into Windows Azure Diagnostics [Article]
Read more
  • 0
  • 0
  • 2344
Modal Close icon
Modal Close icon