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

How-To Tutorials

7019 Articles
article-image-arcgis-spatial-analyst
Packt
20 Jan 2015
16 min read
Save for later

ArcGIS Spatial Analyst

Packt
20 Jan 2015
16 min read
In this article by Daniela Cristiana Docan, author of ArcGIS for Desktop Cookbook, we will learn that the ArcGIS Spatial Analyst extension offers a lot of great tools for geoprocessing raster data. Most of the Spatial Analyst tools generate a new raster output. Before starting a raster analysis session, it's best practice to set the main analysis environment parameters settings (for example, scratch the workspace, extent, and cell size of the output raster). In this article, you will store all raster datasets in file geodatabase as file geodatabase raster datasets. (For more resources related to this topic, see here.) Analyzing surfaces In this recipe, you will represent 3D surface data in a two-dimensional environment. To represent 3D surface data in the ArcMap 2D environment, you will use hillshades and contours. You can use the hillshade raster as a background for other raster or vector data in ArcMap. Using the surface analysis tools, you can derive new surface data, such as slope and aspect or locations visibility. Getting ready In the surface analysis context: The term slope refers to the steepness of raster cells Aspect defines the orientation or compass direction of a cell Visibility identifies which raster cells are visible from a surface location In this recipe, you will prepare your data for analysis by creating an elevation surface named Elevation from vector data. The two feature classes involved are the PointElevation point feature class and the ContourLine polyline feature class. All other output raster datasets will derive from the Elevation raster. How to do it... Follow these steps to prepare your data for spatial analysis: Start ArcMap and open the existing map document, SurfaceAnalysis.mxd, from <drive>:PacktPublishingDataSpatialAnalyst. Go to Customize | Extensions and check the Spatial Analyst extension. Open ArcToolbox, right-click on the ArcToolbox toolbox, and select Environments. Set the geoprocessing environment as follows: Workspace | Current Workspace: DataSpatialAnalystTOPO5000.gdb and Scratch Workspace: DataSpatialAnalystScratchTOPO5000.gdb. Output Coordinates: Same as Input. Raster Analysis | Cell Size: As Specified below: type 0.5 with unit as m. Mask: SpatialAnalystTOPO5000.gdbTrapezoid5k. Raster Storage | Pyramid: check Build pyramids and Pyramid levels: type 3. Click on OK. In ArcToolbox, expand Spatial Analyst Tools | Interpolation, and double-click on the Topo to Raster tool to open the dialog box. Click on Show Help to see the meaning of every parameter. Set the following parameters: Input feature data: PointElevation Field: Elevation and Type: PointElevation ContourLine Field: Elevation and Type: Contour WatercourseA Type: Lake Output surface raster: ...ScratchTOPO5000.gdbElevation Output extent (optional): ContourLine Drainage enforcement (optional): NO_ENFORCE Accept the default values for all other parameters. Click on OK. The Elevation raster is a continuous thematic raster. The raster cells are arranged in 4,967 rows and 4,656 columns. Open Layer Properties | Source of the raster and explore the following properties: Data Type (File Geodatabase Raster Dataset), Cell Size (0.5 meters) or Spatial Reference (EPSG: 3844). In the Layer Properties window, click on the Symbology tab. Select the Stretched display method for the continuous raster cell values as follows: Show: Stretched and Color Ramp: Surface. Click on OK. Explore the cell values using the following two options: Go to Layer Properties | Display and check Show MapTips Add the Spatial Analyst toolbar, and from Customize | Commands, add the Pixel Inspector tool Let's create a hillshade raster using the Elevation layer: Expand Spatial Analyst Tools | Interpolation and double-click on the Hillshade tool to open the dialog box. Set the following parameters: Input raster: ScratchTOPO5000.gdbElevation Output raster: ScratchTOPO5000.gdbHillshade Azimuth (optional): 315 and Altitude (optional): 45 Accept the default value for Z factor and leave the Model shadows option unchecked. Click on OK. From time to time, please ensure to save the map document as MySurfaceAnalysis.mxd at ...DataSpatialAnalyst. The Hillshade raster is a discrete thematic raster that has an associated attribute table known as Value Attribute Table (VAT). Right-click on the Hillshade raster layer and select Open Attribute Table. The Value field stores the illumination values of the raster cells based on the position of the light source. The 0 value (black) means that 25406 cells are not illuminated by the sun, and 254 value (white) means that 992 cells are entirely illuminated. Close the table. In the Table Of Contents section, drag the Hillshade layer below the Elevation layer, and use the Effects | Transparency tool to add a transparency effect for the Elevation raster layer, as shown in the following screenshot: In the next step, you will derive a raster of slope and aspect from the Elevation layer. Expand Spatial Analyst Tools | Interpolation and double-click on the Slope tool to open the dialog box. Set the following parameters: Input raster: Elevation Output raster: ScratchTOPO5000.gdbSlopePercent Output measurement (optional): PERCENT_RISE Click on OK. Symbolize the layer using the Classified method, as follows: Show: Classified. In the Classification section, click on Classify and select the Manual classification method. You will add seven classes. To add break values, right-click on the empty space of the Insert Break graph. To delete one, select the break value from the graph, and right-click to select Delete Break. Do not erase the last break value, which represents the maximum value. Secondly, in the Break Values section, edit the following six values: 5; 7; 15; 20; 60; 90, and leave unchanged the seventh value (496,6). Select Slope (green to red) for Color Ramp. Click on OK. The green areas represent flatter slopes, while the red areas represent steep slopes, as shown in the following screenshot: Expand Spatial Analyst Tools | Interpolation and double click on the Aspect tool to open the dialog box. Set the following parameters: Input raster: Elevation Output raster: ScratchTOPO5000.gdbAspect Click on OK. Symbolize the Aspect layer. For Classify, click on the Manual classification method. You will add five classes. To add or delete break values, right-click on the empty space of the graph, and select Insert / Delete Break. Secondly, edit the following four values: 0; 90; 180; 270, leaving unchanged the fifth value in the Break Values section. Click on OK. In the Symbology window, edit the labels of the five classes as shown in the following picture. Click on OK. In the Table Of Contents section, select the <VALUE> label, and type Slope Direction. The following screenshot is the result of this action: In the next step, you will create a raster of visibility between two geodetic points in order to plan some topographic measurements using an electronic theodolite. You will use the TriangulationPoint and Elevation layers: In the Table Of Contents section, turn on the TriangulationPoint layer, and open its attribute table to examine the fields. There are two geodetic points with the following supplementary fields: OffsetA and OffsetB. OffsetA is the proposed height of the instrument mounted on its tripod above stations 8 and 72. OffsetB is the proposed height of the reflector (or target) above the same points. Close the table. Expand Spatial Analyst Tools | Interpolation and double-click on the Visibility tool to open the dialog box. Click on Show Help to see the meaning of every parameter. Set the following parameters: Input raster: Elevation Input point or polyline observer features: TOPO5000.gdbGeodeticPointsTriangulationPoint Output raster: ScratchTOPO5000.gdbVisibility Analysis type (optional): OBSERVERS Observer parameters | Surface offset (optional): OffsetB Observer offset (optional): OffsetA Outer radius (optional): For this, type 1600 Notice that OffsetA and OffsetB were automatically assigned. The Outer radius parameter limits the search distance, and it is the rounded distance between the two geodetic points. All other cells beyond the 1,600-meter radius will be excluded from the visibility analysis. Click on OK. Open the attribute table of the Visibility layer to inspect the fields and values. The Value field stores the value of cells. Value 0 means that cells are not visible from the two points. Value 1 means that 6,608,948 cells are visible only from point 8 (first observer OBS1). Value 2 means that 1,813,578 cells are visible only from point 72 (second observer OBS2). Value 3 means that 4,351,861 cells are visible from both points. In conclusion, there is visibility between the two points if the height of the instrument and reflector is 1.5 meters. Close the table. Symbolize the Visibility layer, as follows: Show: Unique Values and Value Field: Value. Click on Add All Values and choose Color Scheme: Yellow-Green Bright. Select <Heading> and change the Label value to Height 1.5 meters. Double-click on the symbol for Value as 0, and select No Color. Click on OK. The Visibility layer is symbolized as shown in the following screenshot: Turn off all layers except the Visibility, TriangulationPoint, and Hillshade layers. Save your map as MySurfaceAnalysis.mxd and close ArcMap. You can find the final results at <drive>:PacktPublishingDataSpatialAnalystSurfaceAnalysis. How it works... You have started the exercise by setting the geoprocessing environment. You will override those settings in the next recipes. At the application level, you chose to build pyramids. By creating pyramids, your raster will be displayed faster when you zoom out. The pyramid levels contain the copy of the original raster at a low resolution. The original raster will have a cell size of 0.5 meters. The pixel size will double at each level of the pyramid, so the first level will have a cell size of 1 meter; the second level will have a cell size of 2 meters; and the third level will have a cell size of 4 meters. Even if the values of cells refer to heights measured above the local mean sea level (zero-level surface), you should consider the planimetric accuracy of the dataset. Please remember that TOPO5000.gdb refers to a product at the scale 1:5,000. This is the reason why you have chosen 0.5 meters for the raster cell size. At step 4, you used the PointElevation layer as supplementary data when you created the Elevation raster. If one of your ArcToolbox tools fails to execute or you have obtained an empty raster output, you have some options here: Open the Results dialog from the Geoprocessing menu to explore the error report. This will help you to identify the parameter errors. Right-click on the previous execution of the tool and choose Open (step 1). Change the parameters and click on OK to run the tool. Choose Re Run if you want to run the tool with the parameters unchanged (step 2) as shown in the following screenshot: Run the ArcToolbox tool from the ArcCatalog application. Before running the tool, check the geoprocessing environment in ArcCatalog by navigating to Geoprocessing | Environments. There's more... What if you have a model with all previous steps? Open ArcCatalog, and go to ...DataSpatialAnalystModelBuilder. In the ModelBuilder folder, you have a toolbox named MyToolbox, which contains the Surface Analysis model. Right-click on the model and select Properties. Take your time to study the information from the General, Parameters, and Environments tabs. The output (derived data) will be saved in Scratch Workspace: ModelBuilder ScratchTopo5000.gdb. Click on OK to close the Surface Analysis Properties window. Running the entire model will take you around 25 minutes. You have two options: Tool dialog option: Right-click on the Surface Analysis model and select Open. Notice the model parameters that you can modify and read the Help information. Click on OK to run the model. Edit mode: Right-click on the Surface Analysis model and select Edit. The colored model elements are in the second state—they are ready to run the Surface Analysis model by using one of those two options: To run the entire model at the same time, select Run Entire Model from the Model menu. To run the tools (yellow rounded rectangle) one by one, select the Topo to Raster tool with the Select tool, and click on the Run tool from the Standard toolbar. Please remember that a shadow behind a tool means that the model element has already been run. You used the Visibility tool to check the visibility between two points with 1.5 meters for the Observer offset and Surface offset parameters. Try yourself to see what happens if the offset value is less than 1.5 meters. To again run the Visibility tool in the Edit mode, right-click on the tool, and select Open. For Surface offset and Observer offset, type 0.5 meters and click on OK to run the tool. Repeat these steps for a 1 meter offset. Interpolating data Spatial interpolation is the process of estimating an unknown value between two known values taking into account Tobler's First Law: "Everything is related to everything else, but near things are more related than distant things." This recipe does not undertake to teach you the advanced concept of interpolation because it is too complex for this book. Instead, this recipe will guide you to create a terrain surface using the following: A feature class with sample elevation points Two interpolation methods: Inverse Distance Weighted (IDW) and Spline For further research, please refer to: Geographic Information Analysis, David O'Sullivan and David Unwin, John Wiley & Sons, Inc., 2003, specifically the 8.3 Spatial interpolation recipe of Chapter 8, Describing and Analyzing Fields, pp.220-234. Getting ready In this recipe, you will create a terrain surface stored as a raster using the PointElevation sample points. Your sample data has the following characteristics: The average distance between points is 150 meters The density of sample points is not the same on the entire area of interest There are not enough points to define the cliffs and the depressions There are not extreme differences in elevation values How to do it... Follow these steps to create a terrain surface using the IDW tool: Start ArcMap and open an existing map document Interpolation.mxd from <drive>:PacktPublishingDataSpatialAnalyst. Set the geoprocessing environment, as follows: Workspace | Current Workspace: DataSpatialAnalystTOPO5000.gdb and Scratch Workspace: DataSpatialAnalystScratchTOPO5000.gdb Output Coordinates: Same as the PointElevation layer Raster Analysis | Cell Size: As Specified Below: 1 Mask: DataSpatialAnalystTOPO5000.gdbTrapezoid5k In the next two steps, you will use the IDW tool. Running IDW with barrier polyline features will take you around 15 minutes: In ArcToolbox, go to Spatial Analyst Tools | Interpolation, and double-click on the IDW tool. Click on Show Help to see the meaning of every parameter. Set the following parameters: Input point features: PointElevation Z value field: Elevation Output raster: ScratchTOPO5000.gdbIDW_1 Power (optional): 0.5 Search radius (optional): Variable Search Radius Settings | Number of points: 6 Maximum distance: 500 Input barrier polyline features (optional): TOPO5000.gdbHydrographyWatercourseL Accept the default value of Output cell size (optional). Click on OK. Repeat step 3 by setting the following parameters: Input point features: PointElevation Z value field: Elevation Output raster: ScratchTOPO5000.gdbIDW_2 Power (optional): 2 The rest of the parameters are the same as in step 3. Click on OK. Symbolize the IDW_1 and IDW_2 layers as follows: Show: Classified; Classification: Equal Interval: 10 classes; Color Scheme: Surface. Click on OK. You should obtain the following results: In the following steps, you will use the Spline tool to generate the terrain surface: In ArcToolbox, go to Spatial Analyst Tools | Interpolation, and double-click on the Spline tool. Set the following parameters: Input point features: PointElevation Z value field: Elevation Output raster: ScratchTOPO5000.gdbSpline_Regular Spline type (optional): REGULARIZED Weight (optional): 0.1 and Number of points (optional): 6 Accept the default value of Output cell size (optional). Click on OK. Run again the Spline tool with the following parameters: Input point features: PointElevation Z value field: Elevation Output raster: ScratchTOPO5000.gdbSpline_Tension Spline type (optional): TENSION Weight (optional): 0.1 and Number of points (optional): 6 Accept the default value of Output cell size (optional). Click on OK. Symbolize the Spline_Regular and Spline_Tension raster layers using the Equal Interval method classification with 10 classes and the Surface color ramp: In the next steps, you will use the Spline with Barriers tool to generate a terrain surface using an increased number of sample points. You will transform the ContourLine layer in a point feature class. You will combine those new points with features from the PointElevation layer: In ArcToolbox, go to Data Management Tools | Features, and double-click on the Feature vertices to Points tool. Set the following parameters: Input features: ContourLine Output Feature Class: TOPO5000.gdbRelief ContourLine_FeatureVertices Point type (optional): ALL Click on OK. Inspect the attribute table of the newly created layer. In the Catalog window, go to ...TOPO5000.gdbRelief and create a copy of the PointElevation feature class. Rename the new feature class as ContourAndPoint. Right-click on ContourAndPoint and select Load | Load Data. Set the following parameters from the second and fourth panels: Input data: ContourLine_FeatureVertices Target Field: Elevation Matching Source Field: Elevation Accept the default values for the rest of the parameters and click on Finish. In ArcToolbox, go to Spatial Analyst Tools | Interpolation, and double-click on the Spline with Barriers tool. Set the following parameters: Input point features: ContourAndPoint Z value field: Elevation Input barrier features (optional): TOPO5000.gdbHydrographyWatercourseA Output raster: ScratchTOPO5000.gdbSpline_WaterA Smoothing Factor (optional): 0 Accept the default value of Output cell size (optional). Click on OK. You should obtain a similar terrain surface to what's shown here: Explore the results by comparing the similarities or differences of the terrain surface between interpolated raster layers and the ContourLine vector layer. The IDW method works well with a proper density of sample points. Try to create a new surface using the IDW tool and the ContourAndPoint layer as sample points. Save your map as MyInterpolation.mxd and close ArcMap. You can find the final results at <drive>:PacktPublishingDataSpatialAnalyst Interpolation. How it works... The IDW method generated an average surface that will not cross through the known point elevation values and will not estimate the values below the minimum or above the maximum given point values. The IDW tool allows you to define polyline barriers or limits in searching sample points for interpolation. Even if the WatercourseL polyline feature classes do not have elevation values, river features can be used to interrupt the continuity of interpolated surfaces. To obtain fewer averaged estimated values (reduce the IDW smoother effect) you have to: Reduce the sample size to 6 points Choose a variable search radius Increase the power to 2 The Power option defines the influence of sample point values. This value increases with the distance. There is a disadvantage because around a few sample points, there are small areas raised above the surrounding surface or small hollows below the surrounding surface. The Spline method has generated a surface that crosses through all the known point elevation values and estimates the values below the minimum or above the maximum sample point values. Because the density of points is quite low, we reduced the sample size to 6 points and defined a variable search radius of 500 meters in order to reduce the smoothening effect. The Regularized option estimates the hills or depressions that are not cached by the sample point values. The Tension option will force the interpolated values to stay closer to the sample point values. Starting from step 12, we increased the number of sample points in order to better estimate the surface. At step 14, notice that the Spline with Barriers tool allows you to use the polygon feature class as breaks or barriers in searching sample points for interpolation. Summary In this article, we learned about the ArcGIS Spatial Analyst extension and its tools. Resources for Article:   Further resources on this subject: Posting Reviews, Ratings, and Photos [article] Enterprise Geodatabase [article] Adding Graphics to the Map [article]
Read more
  • 0
  • 0
  • 3445

article-image-creating-photo-sharing-application
Packt
16 Jan 2015
34 min read
Save for later

Creating a Photo-sharing Application

Packt
16 Jan 2015
34 min read
In this article by Rob Foster, the author of CodeIgniter Web Application Blueprints, we will create a photo-sharing application. There are quite a few image-sharing websites around at the moment. They all share roughly the same structure: the user uploads an image and that image can be shared, allowing others to view that image. Perhaps limits or constraints are placed on the viewing of an image, perhaps the image only remains viewable for a set period of time, or within set dates, but the general structure is the same. And I'm happy to announce that this project is exactly the same. We'll create an application allowing users to share pictures; these pictures are accessible from a unique URL. To make this app, we will create two controllers: one to process image uploading and one to process the viewing and displaying of images stored. We'll create a language file to store the text, allowing you to have support for multiple languages should it be needed. We'll create all the necessary view files and a model to interface with the database. In this article, we will cover: Design and wireframes Creating the database Creating the models Creating the views Creating the controllers Putting it all together So without further ado, let's get on with it. (For more resources related to this topic, see here.) Design and wireframes As always, before we start building, we should take a look at what we plan to build. First, a brief description of our intent: we plan to build an app to allow the user to upload an image. That image will be stored in a folder with a unique name. A URL will also be generated containing a unique code, and the URL and code will be assigned to that image. The image can be accessed via that URL. The idea of using a unique URL to access that image is so that we can control access to that image, such as allowing an image to be viewed only a set number of times, or for a certain period of time only. Anyway, to get a better idea of what's happening, let's take a look at the following site map: So that's the site map. The first thing to notice is how simple the site is. There are only three main areas to this project. Let's go over each item and get a brief idea of what they do: create: Imagine this as the start point. The user will be shown a simple form allowing them to upload an image. Once the user presses the Upload button, they are directed to do_upload. do_upload: The uploaded image is validated for size and file type. If it passes, then a unique eight-character string is generated. This string is then used as the name of a folder we will make. This folder is present in the main upload folder and the uploaded image is saved in it. The image details (image name, folder name, and so on) are then passed to the database model, where another unique code is generated for the image URL. This unique code, image name, and folder name are then saved to the database. The user is then presented with a message informing them that their image has been uploaded and that a URL has been created. The user is also presented with the image they have uploaded. go: This will take a URL provided by someone typing into a browser's address bar, or an img src tag, or some other method. The go item will look at the unique code in the URL, query the database to see if that code exists, and if so, fetch the folder name and image name and deliver the image back to the method that called it. Now that we have a fairly good idea of the structure and form of the site, let's take a look at the wireframes of each page. The create item The following screenshot shows a wireframe for the create item discussed in the previous section. The user is shown a simple form allowing them to upload an image. Image2 The do_upload item The following screenshot shows a wireframe from the do_upload item discussed in the previous section. The user is shown the image they have uploaded and the URL that will direct other users to that image. The go item The following screenshot shows a wireframe from the go item described in the previous section. The go controller takes the unique code in a URL, attempts to find it in the database table images, and if found, supplies the image associated with it. Only the image is supplied, not the actual HTML markup. File overview This is a relatively small project, and all in all we're only going to create seven files, which are as follows: /path/to/codeigniter/application/models/image_model.php: This provides read/write access to the images database table. This model also takes the upload information and unique folder name (which we store the uploaded image in) from the create controller and stores this to the database. /path/to/codeigniter/application/views/create/create.php: This provides us with an interface to display a form allowing the user to upload a file. This also displays any error messages to the user, such as wrong file type, file size too big, and so on. /path/to/codeigniter/application/views/create/result.php: This displays the image to the user after it has been successfully uploaded, as well as the URL required to view that image. /path/to/codeigniter/application/views/nav/top_nav.php: This provides a navigation bar at the top of the page. /path/to/codeigniter/application/controllers/create.php: This performs validation checks on the image uploaded by the user, creates a uniquely named folder to store the uploaded image, and passes this information to the model. /path/to/codeigniter/application/controllers/go.php: This performs validation checks on the URL input by the user, looks for the unique code in the URL and attempts to find this record in the database. If it is found, then it will display the image stored on disk. /path/to/codeigniter/application/language/english/en_admin_lang.php: This provides language support for the application. The file structure of the preceding seven files is as follows: application/ ├── controllers/ │   ├── create.php │   ├── go.php ├── models/ │   ├── image_model.php ├── views/create/ │   ├── create.php │   ├── result.php ├── views/nav/ │   ├── top_nav.php ├── language/english/ │   ├── en_admin_lang.php Creating the database First, we'll build the database. Copy the following MySQL code into your database: CREATE DATABASE `imagesdb`; USE `imagesdb`;   DROP TABLE IF EXISTS `images`; CREATE TABLE `images` ( `img_id` int(11) NOT NULL AUTO_INCREMENT, `img_url_code` varchar(10) NOT NULL, `img_url_created_at` timestamp NOT NULL DEFAULT     CURRENT_TIMESTAMP, `img_image_name` varchar(255) NOT NULL, `img_dir_name` varchar(8) NOT NULL, PRIMARY KEY (`img_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; Right, let's take a look at each item in every table and see what they mean: Table: images Element Description img_id This is the primary key. img_url_code This stores the unique code that we use to identify the image in the database. img_url_created_at This is the MySQL timestamp for the record. img_image_name This is the filename provided by the CodeIgniter upload functionality. img_dir_name This is the name of the directory we store the image in. We'll also need to make amends to the config/database.php file, namely setting the database access details, username, password, and so on. Open the config/database.php file and find the following lines: $db['default']['hostname'] = 'localhost'; $db['default']['username'] = 'your username'; $db['default']['password'] = 'your password'; $db['default']['database'] = 'imagesdb'; Edit the values in the preceding code ensuring you substitute those values for the ones more specific to your setup and situation—so enter your username, password, and so on. Adjusting the config.php and autoload.php files We don't actually need to adjust the config.php file in this project as we're not really using sessions or anything like that. So we don't need an encryption key or database information. So just ensure that you are not autoloading the session in the config/autoload.php file or you will get an error, as we've not set any session variables in the config/config.php file. Adjusting the routes.php file We want to redirect the user to the create controller rather than the default CodeIgniter welcome controller. To do this, we will need to amend the default controller settings in the routes.php file to reflect this. The steps are as follows: Open the config/routes.php file for editing and find the following lines (near the bottom of the file): $route['default_controller'] = "welcome"; $route['404_override'] = ''; First, we need to change the default controller. Initially, in a CodeIgniter application, the default controller is set to welcome. However, we don't need that, instead we want the default controller to be create, so find the following line: $route['default_controller'] = "welcome"; Replace it with the following lines: $route['default_controller'] = "create"; $route['404_override'] = ''; Then we need to add some rules to govern how we handle URLs coming in and form submissions. Leave a few blank lines underneath the preceding two lines of code (default controller and 404 override) and add the following three lines of code: $route['create'] = "create/index"; $route['(:any)'] = "go/index"; $route['create/do_upload'] = "create/do_upload"; Creating the model There is only one model in this project, image_model.php. It contains functions specific to creating and resetting passwords. Create the /path/to/codeigniter/application/models/image_model.php file and add the following code to it: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');   class Image_model extends CI_Model { function __construct() {    parent::__construct(); }   function save_image($data) {    do {       $img_url_code = random_string('alnum', 8);        $this->db->where('img_url_code = ', $img_url_code);      $this->db->from('images');      $num = $this->db->count_all_results();    } while ($num >= 1);      $query = "INSERT INTO `images` (`img_url_code`,       `img_image_name`, `img_dir_name`) VALUES (?,?,?) ";    $result = $this->db->query($query, array($img_url_code,       $data['image_name'], $data['img_dir_name']));      if ($result) {      return $img_url_code;    } else {      return flase;    } }   function fetch_image($img_url_code) {    $query = "SELECT * FROM `images` WHERE `img_url_code` = ? ";    $result = $this->db->query($query, array($img_url_code));      if ($result) {      return $result;    } else {      return false;    } } } There are two main functions in this model, which are as follows: save_image(): This generates a unique code that is associated with the uploaded image and saves it, with the image name and folder name, to the database. fetch_image(): This fetches an image's details from the database according to the unique code provided. Okay, let's take save_image() first. The save_image() function accepts an array from the create controller containing image_name (from the upload process) and img_dir_name (this is the folder that the image is stored in). A unique code is generated using a do…while loop as shown here: $img_url_code = random_string('alnum', 8); First a string is created, eight characters in length, containing alpha-numeric characters. The do…while loop checks to see if this code already exists in the database, generating a new code if it is already present. If it does not already exist, this code is used: do { $img_url_code = random_string('alnum', 8);   $this->db->where('img_url_code = ', $img_url_code); $this->db->from('images'); $num = $this->db->count_all_results(); } while ($num >= 1); This code and the contents of the $data array are then saved to the database using the following code: $query = "INSERT INTO `images` (`img_url_code`, `img_image_name`,   `img_dir_name`) VALUES (?,?,?) "; $result = $this->db->query($query, array($img_url_code,   $data['image_name'], $data['img_dir_name'])); The $img_url_code is returned if the INSERT operation was successful, and false if it failed. The code to achieve this is as follows: if ($result) { return $img_url_code; } else { return false; } Creating the views There are only three views in this project, which are as follows: /path/to/codeigniter/application/views/create/create.php: This displays a form to the user allowing them to upload an image. /path/to/codeigniter/application/views/create/result.php: This displays a link that the user can use to forward other people to the image, as well as the image itself. /path/to/codeigniter/application/views/nav/top_nav.php: This displays the top-level menu. In this project it's very simple, containing a project name and a link to go to the create controller. So those are our views, as I said, there are only three of them as it's a simple project. Now, let's create each view file. Create the /path/to/codeigniter/application/views/create/create.php file and add the following code to it: <div class="page-header"> <h1><?php echo $this->lang->line('system_system_name');     ?></h1> </div>   <p><?php echo $this->lang->line('encode_instruction_1');   ?></p>   <?php echo validation_errors(); ?>   <?php if (isset($success) && $success == true) : ?> <div class="alert alert-success">    <strong><?php echo $this->lang->line('     common_form_elements_success_notifty'); ?></strong>     <?php echo $this->lang->     line('encode_encode_now_success'); ?> </div> <?php endif ; ?>   <?php if (isset($fail) && $fail == true) : ?> <div class="alert alert-danger">    <strong><?php echo $this->lang->line('     common_form_elements_error_notifty'); ?> </strong>     <?php echo $this->lang->line('encode_encode_now_error     '); ?>    <?php echo $fail ; ?> </div> <?php endif ; ?>   <?php echo form_open_multipart('create/do_upload');?> <input type="file" name="userfile" size="20" /> <br /> <input type="submit" value="upload" /> <?php echo form_close() ; ?> <br /> <?php if (isset($result) && $result == true) : ?> <div class="alert alert-info">    <strong><?php echo $this->lang->line('     encode_upload_url'); ?> </strong>    <?php echo anchor($result, $result) ; ?> </div> <?php endif ; ?> This view file can be thought of as the main view file; it is here that the user can upload their image. Error messages are displayed here too. Create the /path/to/codeigniter/application/views/create/result.php file and add the following code to it: <div class="page-header"> <h1><?php echo $this->lang->line('system_system_name');     ?></h1> </div>   <?php if (isset($result) && $result == true) : ?>    <strong><?php echo $this->lang->line('     encode_encoded_url'); ?> </strong>    <?php echo anchor($result, $result) ; ?>    <br />    <img src="<?php echo base_url() . 'upload/' .       $img_dir_name . '/' . $file_name ;?>" /> <?php endif ; ?> This view will display the encoded image resource URL to the user (so they can copy and share it) and the actual image itself. Create the /path/to/codeigniter/application/views/nav/top_nav.php file and add the following code to it: <!-- Fixed navbar --> <div class="navbar navbar-inverse navbar-fixed-top"   role="navigation"> <div class="container">    <div class="navbar-header">      <button type="button" class="navbar-toggle" data- toggle="collapse" data-target=".navbar-collapse">        <span class="sr-only">Toggle navigation</span>        <span class="icon-bar"></span>        <span class="icon-bar"></span>        <span class="icon-bar"></span>      </button>      <a class="navbar-brand" href="#"><?php echo $this-       >lang->line('system_system_name'); ?></a>  </div>    <div class="navbar-collapse collapse">      <ul class="nav navbar-nav">        <li class="active"><?php echo anchor('create',           'Create') ; ?></li>      </ul>    </div><!--/.nav-collapse --> </div> </div>   <div class="container theme-showcase" role="main"> This view is quite basic but still serves an important role. It displays an option to return to the index() function of the create controller. Creating the controllers We're going to create two controllers in this project, which are as follows: /path/to/codeigniter/application/controllers/create.php: This handles the creation of unique folders to store images and performs the upload of a file. /path/to/codeigniter/application/controllers/go.php: This fetches the unique code from the database, and returns any image associated with that code. These are two of our controllers for this project, let's now go ahead and create them. Create the /path/to/codeigniter/application/controllers/create.php file and add the following code to it: <?php if (!defined('BASEPATH')) exit('No direct script access   allowed');   class Create extends MY_Controller { function __construct() {    parent::__construct();      $this->load->helper(array('string'));      $this->load->library('form_validation');      $this->load->library('image_lib');      $this->load->model('Image_model');      $this->form_validation->set_error_delimiters('<div         class="alert alert-danger">', '</div>');    }   public function index() {    $page_data = array('fail' => false,                        'success' => false);    $this->load->view('common/header');    $this->load->view('nav/top_nav');    $this->load->view('create/create', $page_data);    $this->load->view('common/footer'); }   public function do_upload() {    $upload_dir = '/filesystem/path/to/upload/folder/';    do {      // Make code      $code = random_string('alnum', 8);        // Scan upload dir for subdir with same name      // name as the code      $dirs = scandir($upload_dir);        // Look to see if there is already a      // directory with the name which we      // store in $code      if (in_array($code, $dirs)) { // Yes there is        $img_dir_name = false; // Set to false to begin again      } else { // No there isn't        $img_dir_name = $code; // This is a new name      }      } while ($img_dir_name == false);      if (!mkdir($upload_dir.$img_dir_name)) {      $page_data = array('fail' => $this->lang->       line('encode_upload_mkdir_error'),                          'success' => false);      $this->load->view('common/header');      $this->load->view('nav/top_nav');      $this->load->view('create/create', $page_data);      $this->load->view('common/footer');    }      $config['upload_path'] = $upload_dir.$img_dir_name;    $config['allowed_types'] = 'gif|jpg|jpeg|png';    $config['max_size'] = '10000';    $config['max_width'] = '1024';    $config['max_height'] = '768';      $this->load->library('upload', $config);      if ( ! $this->upload->do_upload()) {      $page_data = array('fail' => $this->upload->       display_errors(),                          'success' => false);      $this->load->view('common/header');      $this->load->view('nav/top_nav');      $this->load->view('create/create', $page_data);       $this->load->view('common/footer');    } else {      $image_data = $this->upload->data();      $page_data['result'] = $this->Image_model->save_image(       array('image_name' => $image_data['file_name'],         'img_dir_name' => $img_dir_name));    $page_data['file_name'] = $image_data['file_name'];      $page_data['img_dir_name'] = $img_dir_name;        if ($page_data['result'] == false) {        // success - display image and link        $page_data = array('fail' => $this->lang->         line('encode_upload_general_error'));        $this->load->view('common/header');        $this->load->view('nav/top_nav');        $this->load->view('create/create', $page_data);        $this->load->view('common/footer');      } else {        // success - display image and link        $this->load->view('common/header');        $this->load->view('nav/top_nav');        $this->load->view('create/result', $page_data);        $this->load->view('common/footer');      }    } } } Let's start with the index() function. The index() function sets the fail and success elements of the $page_data array to false. This will suppress any initial messages from being displayed to the user. The views are loaded, specifically the create/create.php view, which contains the image upload form's HTML markup. Once the user submits the form in create/create.php, the form will be submitted to the do_upload() function of the create controller. It is this function that will perform the task of uploading the image to the server. First off, do_upload() defines an initial location for the upload folder. This is stored in the $upload_dir variable. Next, we move into a do…while structure. It looks something like this: do { // something } while ('…a condition is not met'); So that means do something while a condition is not being met. Now with that in mind, think about our problem—we have to save the image being uploaded in a folder. That folder must have a unique name. So what we will do is generate a random string of eight alpha-numeric characters and then look to see if a folder exists with that name. Keeping that in mind, let's look at the code in detail: do { // Make code $code = random_string('alnum', 8);   // Scan uplaod dir for subdir with same name // name as the code $dirs = scandir($upload_dir);   // Look to see if there is already a // directory with the name which we // store in $code if (in_array($code, $dirs)) { // Yes there is    $img_dir_name = false; // Set to false to begin again } else { // No there isn't    $img_dir_name = $code; // This is a new name } } while ($img_dir_name == false); So we make a string of eight characters, containing only alphanumeric characters, using the following line of code: $code = random_string('alnum', 8); We then use the PHP function scandir() to look in $upload_dir. This will store all directory names in the $dirs variable, as follows: $dirs = scandir($upload_dir); We then use the PHP function in_array() to look for the value in $code in the list of directors from scandir(): If we don't find a match, then the value in $code must not be taken, so we'll go with that. If the value is found, then we set $img_dir_name to false, which is picked up by the final line of the do…while loop: ... } while ($img_dir_name == false); Anyway, now that we have our unique folder name, we'll attempt to create it. We use the PHP function mkdir(), passing to it $upload_dir concatenated with $img_dir_name. If mkdir() returns false, the form is displayed again along with the encode_upload_mkdir_error message set in the language file, as shown here: if (!mkdir($upload_dir.$img_dir_name)) { $page_data = array('fail' => $this->lang->   line('encode_upload_mkdir_error'),                      'success' => false); $this->load->view('common/header'); $this->load->view('nav/top_nav'); $this->load->view('create/create', $page_data); $this->load->view('common/footer'); } Once the folder has been made, we then set the configuration variables for the upload process, as follows: $config['upload_path'] = $upload_dir.$img_dir_name; $config['allowed_types'] = 'gif|jpg|jpeg|png'; $config['max_size'] = '10000'; $config['max_width'] = '1024'; $config['max_height'] = '768'; Here we are specifying that we only want to upload .gif, .jpg, .jpeg, and .png files. We also specify that an image cannot be above 10,000 KB in size (although you can set this to any value you wish—remember to adjust the upload_max_filesize and post_max_size PHP settings in your php.ini file if you want to have a really big file). We also set the minimum dimensions that an image must be. As with the file size, you can adjust this as you wish. We then load the upload library, passing to it the configuration settings, as shown here: $this->load->library('upload', $config); Next we will attempt to do the upload. If unsuccessful, the CodeIgniter function $this->upload->do_upload() will return false. We will look for this and reload the upload page if it does return false. We will also pass the specific error as a reason why it failed. This error is stored in the fail item of the $page_data array. This can be done as follows:    if ( ! $this->upload->do_upload()) {      $page_data = array('fail' => $this->upload-       >display_errors(),                          'success' => false);      $this->load->view('common/header');      $this->load->view('nav/top_nav');      $this->load->view('create/create', $page_data);      $this->load->view('common/footer');    } else { ... If, however, it did not fail, we grab the information generated by CodeIgniter from the upload. We'll store this in the $image_data array, as follows: $image_data = $this->upload->data(); Then we try to store a record of the upload in the database. We call the save_image function of Image_model, passing to it file_name from the $image_data array, as well as $img_dir_name, as shown here: $page_data['result'] = $this->Image_model-> save_image(array('image_name' => $image_data['file_name'],   'img_dir_name' => $img_dir_name)); We then test for the return value of the save_image() function; if it is successful, then Image_model will return the unique URL code generated in the model. If it is unsuccessful, then Image_model will return the Boolean false. If false is returned, then the form is loaded with a general error. If successful, then the create/result.php view file is loaded. We pass to it the unique URL code (for the link the user needs), and the folder name and image name, necessary to display the image correctly. Create the /path/to/codeigniter/application/controllers/go.php file and add the following code to it: <?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Go extends MY_Controller {function __construct() {parent::__construct();   $this->load->helper('string');} public function index() {   if (!$this->uri->segment(1)) {     redirect (base_url());   } else {     $image_code = $this->uri->segment(1);     $this->load->model('Image_model');     $query = $this->Image_model->fetch_image($image_code);      if ($query->num_rows() == 1) {       foreach ($query->result() as $row) {         $img_image_name = $row->img_image_name;         $img_dir_name = $row->img_dir_name;       }          $url_address = base_url() . 'upload/' . $img_dir_name .'/' . $img_image_name;        redirect (prep_url($url_address));      } else {        redirect('create');      }    } } } The go controller has only one main function, index(). It is called when a user clicks on a URL or a URL is called (perhaps as the src value of an HTML img tag). Here we grab the unique code generated and assigned to an image when it was uploaded in the create controller. This code is in the first value of the URI. Usually it would occupy the third parameter—with the first and second parameters normally being used to specify the controller and controller function respectively. However, we have changed this behavior using CodeIgniter routing. This is explained fully in the Adjusting the routes.php file section of this article. Once we have the unique code, we pass it to the fetch_image() function of Image_model: $image_code = $this->uri->segment(1); $this->load->model('Image_model'); $query = $this->Image_model->fetch_image($image_code); We test for what is returned. We ask if the number of rows returned equals exactly 1. If not, we will then redirect to the create controller. Perhaps you may not want to do this. Perhaps you may want to do nothing if the number of rows returned does not equal 1. For example, if the image requested is in an HTML img tag, then if an image is not found a redirect may send someone away from the site they're viewing to the upload page of this project—something you might not want to happen. If you want to remove this functionality, remove the following lines in bold from the code excerpt: ....        $img_dir_name = $row->img_dir_name;        }          $url_address = base_url() . 'upload/' . $img_dir_name .'/'           . $img_image_name;        redirect (prep_url($url_address));      } else {        redirect('create');      }    } } } .... Anyway, if the returned value is exactly 1, then we'll loop over the returned database object and find img_image_name and img_dir_name, which we'll need to locate the image in the upload folder on the disk. This can be done as follows: foreach ($query->result() as $row) { $img_image_name = $row->img_image_name; $img_dir_name = $row->img_dir_name; } We then build the address of the image file and redirect the browser to it, as follows: $url_address = base_url() . 'upload/' . $img_dir_name .'/'   . $img_image_name; redirect (prep_url($url_address)); Creating the language file We make use of the language file to serve text to users. In this way, you can enable multiple region/multiple language support. Create the /path/to/codeigniter/application/language/english/en_admin_lang.php file and add the following code to it: <?php if (!defined('BASEPATH')) exit('No direct script access   allowed');   // General $lang['system_system_name'] = "Image Share";   // Upload $lang['encode_instruction_1'] = "Upload your image to share it"; $lang['encode_upload_now'] = "Share Now"; $lang['encode_upload_now_success'] = "Your image was uploaded, you   can share it with this URL"; $lang['encode_upload_url'] = "Hey look at this, here's your   image:"; $lang['encode_upload_mkdir_error'] = "Cannot make temp folder"; $lang['encode_upload_general_error'] = "The Image cannot be saved   at this time"; Putting it all together Let's look at how the user uploads an image. The following is the sequence of events: CodeIgniter looks in the routes.php config file and finds the following line: $route['create'] = "create/index"; It directs the request to the create controller's index() function. The index() function loads the create/create.php view file that displays the upload form to the user. The user clicks on the Choose file button, navigates to the image file they wish to upload, and selects it. The user presses the Upload button and the form is submitted to the create controller's index() function. The index() function creates a folder in the main upload directory to store the image in, then does the actual upload. On a successful upload, index() sends the details of the upload (the new folder name and image name) to the save_image() model function. The save_model() function also creates a unique code and saves it in the images table along with the folder name and image name passed to it by the create controller. The unique code generated during the database insert is then returned to the controller and passed to the result view, where it will form part of a success message to the user. Now, let's see how an image is viewed (or fetched). The following is the sequence of events: A URL with the syntax www.domain.com/226KgfYH comes into the application—either when someone clicks on a link or some other call (<img src="">). CodeIgniter looks in the routes.php config file and finds the following line: $route['(:any)'] = "go/index"; As the incoming request does not match the other two routes, the preceding route is the one CodeIgniter applies to this request. The go controller is called and the code of 226KgfYH is passed to it as the 1st segment of uri. The go controller passes this to the fetch_image() function of the Image_model.php file. The fetch_image() function will attempt to find a matching record in the database. If found, it returns the folder name marking the saved location of the image, and its filename. This is returned and the path to that image is built. CodeIgniter then redirects the user to that image, that is, supplies that image resource to the user that requested it. Summary So here we have a basic image sharing application. It is capable of accepting a variety of images and assigning them to records in a database and unique folders in the filesystem. This is interesting as it leaves things open to you to improve on. For example, you can do the following: You can add limits on views. As the image record is stored in the database, you could adapt the database. Adding two columns called img_count and img_count_limit, you could allow a user to set a limit for the number of views per image and stop providing that image when that limit is met. You can limit views by date. Similar to the preceding point, but you could limit image views to set dates. You can have different URLs for different dimensions. You could add functionality to make several dimensions of image based on the initial upload, offering several different URLs for different image dimensions. You can report abuse. You could add an option allowing viewers of images to report unsavory images that might be uploaded. You can have terms of service. If you are planning on offering this type of application as an actual web service that members of the public could use, then I strongly recommend you add a terms of service document, perhaps even require that people agree to terms before they upload an image. In those terms, you'll want to mention that in order for someone to use the service, they first have to agree that they do not upload and share any images that could be considered illegal. You should also mention that you'll cooperate with any court if information is requested of you. You really don't want to get into trouble for owning or running a web service that stores unpleasant images; as much as possible you want to make your limits of liability clear and emphasize that it is the uploader who has provided the images. Resources for Article: Further resources on this subject: UCodeIgniter MVC – The Power of Simplicity! [article] Navigating Your Site using CodeIgniter 1.7: Part 1 [article] Navigating Your Site using CodeIgniter 1.7: Part 2 [article]
Read more
  • 0
  • 0
  • 3671

article-image-part1-learning-aws-cli
Yohei Yoshimuta
15 Jan 2015
4 min read
Save for later

Part1. Learning AWS CLI

Yohei Yoshimuta
15 Jan 2015
4 min read
As an application developer, you must be familiar with the CLI. Using the CLI (instead of UI) has the benefit that the operations can be documented and then they become reproducible and shareable. Fortunately, AWS provides both API and the unified CLI tool named aws-cli. You must use and understand AWS CLI, especially when you want to control anything, AWS UI doesn't provide yet; for example, Scheduled Scaling - Auto Scaling can be available only via AWS CLI. Before explaining the full process, I will assume that you are using AWS VPC & S3. Also, ensure to have all of your network resources like security group inside VPC, and you know an access key and a secret key of your own AWS account or IAM account. Let's see how we can control EC2 instances and S3 : Install aws-cli package The first thing you need to do is to install aws-cli package on your machine. # Install pip if your machine doesn't have pip yet $ sudo easy_install pip # Install awscli with pip $ sudo pip install awscli # Configure AWS credential and config $ aws configure AWS Access Key ID: foo AWS Secret Access Key: bar Default region name [us-west-2]: us-west-2 Default output format [None]: json Note: You have to configure AWS Access Key ID and Secret Access Key to which an IAM account is attached by necessary but minimum policies. For now, I recommend you create an IAM account attached AmazonEC2FullAccess-AMI-201412181939 and AmazonS3FullAccess-AMI-201502041017. # AmazonEC2FullAccess-AMI-201412181939 { "Version": "2012-10-17", "Statement": [ { "Action": "ec2:*", "Effect": "Allow", "Resource": "*" }, { "Effect": "Allow", "Action": "elasticloadbalancing:*", "Resource": "*" }, { "Effect": "Allow", "Action": "cloudwatch:*", "Resource": "*" }, { "Effect": "Allow", "Action": "autoscaling:*", "Resource": "*" } ] } # AmazonS3FullAccess-AMI-201502041017 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] } Run an EC2 instance Okay, you are ready to control AWS resources via CLI. The most important thing to run an EC2 instance is preparing option parameters. You can confirm these details in run-instances — AWS CLI documentation. This command generates a JSON file which has skeleton option parameters: $ aws ec2 run-instances --generate-cli-skeleton > /tmp/run-instances_base.json # We overwrite this skeleton file to be shorter and easier to understand $ vi /tmp/run-instances_base.json $ cat /tmp/run-instances_base.json { "ImageId": "ami-936d9d93", "KeyName": "YOUR Key pair name", "InstanceType": "t2.micro", "Placement": { "AvailabilityZone": "us-west-2" }, "NetworkInterfaces": [ { "DeviceIndex": 0, "SubnetId": "subnet-***", "Groups": [ "sg-***" ], "DeleteOnTermination": true, "AssociatePublicIpAddress": true } ] } # Run an instance $ aws ec2 run-instances --cli-input-json file:///tmp/run-instances_base.json List running EC2 instances Now confirm your running EC2 instances. The detail of using the command is here : describe-instances — AWS CLI documentation. I recommend you use the jq tool because the output is formatted as JSON and you might be overwhelmed by its volume. You can install jq via brew or make the tool. # Install jq if your machine doesn't have it yet and you want to use it on MacOSX $ brew install jq # List EC2 instances $ aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | [.LaunchTime, .State.Name, .InstanceId, .InstanceType, .PrivateIpAddress, (.Tags[] | select(.Key=="Name").Value)] | join("t")' 2015-09-22T10:16:41.000Z running i-f19f6e54 t2.micro 10.0.1.61 Terminate an EC2 instance Well, it's time to terminate an EC2 instance to save money. The detail of using the command is here: terminate-instances — AWS CLI documentation # DryRun the command $ aws ec2 terminate-instances --instance-ids i-f19f6e54 --dry-run # Terminate an EC2 instance $ aws ec2 terminate-instances --instance-ids i-f19f6e54 List S3 directory contents You want to find and grep AWS ELB access logs, especially if you are an operations engineer and have some problems. To start, find the specific file. The detail of using the command is here: ls — AWS CLI documentation. # List ELB access logs created at 2015/09/18 $ aws s3 ls s3://example-elb-log/example-app-elb/AWSLogs/717669809617/elasticloadbalancing/us-west-2/2015/09/18/ Download a S3 content Then you can download a concerned file and grep with a specific keyword. The detail of using the command is here: cp — AWS CLI documentation. # Find access logs whose SSL cipher are ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 $ aws s3 cp s3://example-elb-log/example-app-elb/AWSLogs/717669809617/elasticloadbalancing/us-west-2/2015/09/18/717669809617_elasticloadbalancing_us-west-2_example-app-elb_20150918T0230Z_54.92.79.213_5wo8k1of.log - | head -n 1000 | grep 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2' Conclusion AWS CLI is a very useful tool. It supports extensive and important services; for example, I recently upgraded an SSL certificate of ELB from a SHA-1 signed to a SHA-2 before the iOS9 release due to iOS9 ATS. During these operations, I received a peer review for the planned aws-cli commands asynchronously. It's one of AWS CLI's benefits. About the author Yohei Yoshimuta is a software engineer with a proven record of delivering high quality software in both game and advertising industries. He has extensive experience building products from scratch in both small and large teams. His primary focuses are Perl, Go, and AWS technologies. You can reach him at @yoheimuta on GitHub and Twitter.
Read more
  • 0
  • 0
  • 19705

article-image-getting-started-electronic-projects
Packt
13 Jan 2015
7 min read
Save for later

Getting Started with Electronic Projects

Packt
13 Jan 2015
7 min read
Welcome to my second book produced by the good folks at Packt Publishing LLC. This book is somewhat different from my other book in that, instead of one large project this book is a collection of several small, medium, and large projects. While the name of the book is called Getting Started with Electronics Projects, I convinced the folks at Packt to let me write a book with several projects for the electronics hacker and experimenter groups. The first few projects do not even involve a BeagleBone, something which had my reviewers shaking their heads at first. So what follows is a brief taste of what you can look forward to, in this book. Before we go any further I should explain who this book is for. If you are a software person who has never heated up a soldering iron before, you might want to practice a bit before attempting the more difficult assembly (electronics assembly, not assembly language programming) projects. If you are a hardware guy, who just wants it to work out of the box, then I suggest you download the image and burn yourself a microSD card. If you feel adventurous, you can always play with the code sections. If you succeed in giving the Kernel a heart attack( also known as Kernel Panic), no worries. Just burn the image again. The book is divided into eight chapters and seven different projects. The first four don't involve a BeagleBone at all. (For more resources related to this topic, see here.) Chapter 1 – Introduction – Our First Project This chapter is for the hardware guys and the adventurous programmers. In this chapter, you will build your own infrared flashlight. If you can use a soldering iron and a solder sucker you can build this project. IR flashlight Chapter 2 – Infrared Beacon In this chapter, we continue with the theme of infrared devices, by building a somewhat more challenging project from a construction prospective. Files for the PCB are available for download from the Packt site, if you bought the book of course. What this beacon does is flash two infrared LED's on and off at a rate that can be selected by the builder. The beacon is only visible when viewed through night-vision goggles or on a black-and-white video camera. IR beacon While it may not be obvious from the preceding image, the case is actually made from ABS water pipe I purchased from a local hardware store. I like ABS pipe because it is so easy to work with. Chapter 3 – Motion Alarm Once again we will be using ABS pipe to construct a cool project. This time we will be building a motion sensor. Most alarm sensors use some sort of Passive Infrared (PIR) sensor or a millimetre wave radar to detect motion. This project uses a simple (cheap) mercury switch to detect motion. How you reset the alarm is a carefully guarded secret, so you will have to by the book to learn the secret! Motion sensor Notice the ring at the right end of the tube? That is so you can hang it up like a Christmas ornament! As with the last chapter, the PCB files are available for download from the Packt site. Chapter 4 – Sound Card-based Oscilloscope This chapter uses a USB soundcard connected to a PC, because the software I found appears to only run on a PC. If you can find a MAC version of the software, go for it. This project will work for MAC or Linux users too. By the way, I tested all of the software in this chapter on a Pentium 4 class machine running Windows XP, so here is an opportunity to recycle/repurpose that old PC you were going to junk! Soundblaster oscilloscope The title of the chapter is somewhat misleading, because the project also includes plans for building a sound card-based audio signal generator. There are a number of commercial and freeware versions of software that take advantage of this hardware. Soundblaster software on PC There are a number of commercial software packages that have a freeware version available for download. The preceding screenshot shows one of the better ones I found running under Windows XP. Chapter 5 – Calibrated RF Source In this chapter we will be building a clean calibrated RF signal source. In addition to being of use to ham radio enthusiasts, it will also be used the chapters that follow. Clean 50MHzsignal This is the first project that actually makes use of the BeagleBone Black. The BeagleBone is used to control a digitally controlled step attenuator. This allows us to output a calibrated signal level from our 50MHz source. In addition to its use in upcoming chapters, once again ham radio enthusiasts will no doubt find a clean RF source with a calibrated output which is selectable in .5dB steps. GUI running on BeagleBone Black Chapter 6 – RF Power Meter – Hardware In this chapter we will be building and RF power meter capable of measuring RF power from 40MHz to over 6GHz. The circuit is based on the Linear Technology LTC5582 RMS power detector. The beauty of this device is that it outputs a DC voltage proportional to the RMS power it detects. There is no need for conversion as there is with other detectors. RMS power is the AC power measured by your digital voltmeter when you have it set to AC. RF detector mounter on protoboard The connector near the notch in the protoboard allows the BeagleBone to both read the RF power and control the step attenuator mentioned earlier. Chapter 7 – RF Power Meter – Software In this chapter we will be building a development system based on Ubuntu and using a docking station available from https://specialcomp.com/beaglebone/index.htm This could be considered the "deluxe" version. It is also possible to complete the next two chapters using the debug port on the BeagleBone and a communications program like PuTTY. BeagleBone development system This configuration also contains the hardware to build a combination wired and wireless alarm system. More on that is in the following chapter. Chapter 8 – Creating a ZigBee Network of Sensors This is the longest and by far the most complex chapter in the book. In this chapter we will learn how to configure XBee modules from Digi International Inc. using the XCTU Windows application. We will then build a standalone wireless alarm system. This alarm system will be based on hardware developed and presented in my previous book: http://www.packtpub.com/building-a-home-security-system-with-beaglebone/book If you purchased my previous book and build any of the alarm system hardware, you can also use it in this chapter to convert your wired alarm system to wireless! The following image is of the XBee module mounted on top of the alarm boards. Each wireless remote module has two alarm zone inputs and four isolated alarm outputs. Completed wireless alarm remote module Summary This book will hopefully have something of interest to a large variety of electronics enthusiasts, from hams to hackers. I would say that, as long as you have at least intermediate programming and construction skills, you should have no problem completing the projects in this book. All the projects use through-hole parts to make assembly easier. Resources for Article: Further resources on this subject: Building robots that can walk [article] Beagle Boards [article] Protecting GPG Keys in BeagleBone [article]
Read more
  • 0
  • 0
  • 21943

article-image-getting-started-firebase
Asbjørn Enge
13 Jan 2015
5 min read
Save for later

Getting started with Firebase

Asbjørn Enge
13 Jan 2015
5 min read
Firebase is a real-time database for your web or mobile app. Data in your Firebase is stored as JSON and synchronized in real time to every connected client. This makes it really easy to share a state between clients. Firebase is a great fit for cross-platform applications and especially collaborative applications. However, you can use it for almost anything. Firebase is a NoSQL database. This is a great fit for many web and mobile applications, but as with any NoSQL store, we need to be mindful when structuring related data. Firebase is also SSL by default, which is great! In this post, we will take a look at building a basic web application using Firebase as our backend. Create a project Our app will be an SPA (Single Page Application) and we will leverage modern JavaScript tooling. We will be using npm as a package manager, babel for ES2015+ transpilation, and browserify for bundling together our scripts. Let's get started. Use the following command to create a folder: $ mkdir fbapp && cd fbapp $ npm init $ npm install --save-dev budo $ npm install --save-dev babelify We have now made a folder for our app fbapp. Inside there we have created a new project using npm init (defaults are fine) and we have installed some packages. I didn't mention budo before, but it is a great browserify development server. Set up Firebase Before we can start talking with our Firebase, we need to head over to https://www.firebase.com/ and sign up for an account. No credit card required. Firebases have quite a bit of free storage and data transfer before they start charging, so it's great for getting started. Once you are logged in, create a new Firebase. Notice the APP URL https://<name>.firebaseio.com/ depending on what you named your Firebase. We are going to use that url in a second. Click your newly created firebase will navigate to the administration UI for this Firebase. Connect to Firebase Now that we have created a Firebase, it's time to start talking to it. First, we need to install the firebase library using npm: $ npm install --save firebase $ touch index.js I also created a file named index.js. This will be the entrypoint for our app. Open index.js in your favorite editor. It's time to hack: import Firebase from 'firebase/lib/firebase-web' let ref = new Firebase('https://<name>.firebaseio.com/') ref.set({ counter : 0 }) console.log('wuhu') The firebase npm package includes both node.js and browser libraries for Firebase. Since we are going to use our app in the browser, we need to import those libraries. Now we can run that application in a browser using budo (browserify under the hood). budo index.js --live -- -t babelify Navigate to http://localhost:9966/ and verify that you can see wuhu printed in the console (developer tools). Notice the --live parameter we pass to budo. It automatically enables livereload for the bundle. If you're not familiar with it, get familiar with it! Next, try and navigate to the Firebase admin UI and verify that it now has a property counter set to 0. Reading from Firebase Now, let's try to read this data back. Since Firebase is a real-time database it pushes data to us. So, we need to set up listeners. Also, remember to remove the ref.set line from your code as it would reset this property every time someone loads our page and we do not want that. import Firebase from 'firebase/lib/firebase-web' let ref = new Firebase('https://<name>.firebaseio.com/') ref.child('counter').on('value', (snap) => { console.log(snap.val()) }) Navigate to http://localhost:9966 and verify that 0 is printed in the console. Making it useful Now, let's see if we can make this counter "useful": import Firebase from 'firebase/lib/firebase-web' let ref = new Firebase('https://getting-started.firebaseio.com/') let counter = 0 ref.child('counter').on('value', (snap) => { counter = snap.val() render(counter) }) let render = (counter) => { if (!button) createNodes() else buttonText.nodeValue = "Click Me ("+counter+")" } let increaseCounter = () => { ref.child('counter').set(counter+1) } let button, buttonText; let createNodes = () => { button = document.createElement('button') buttonText = document.createTextNode("Click Me") button.appendChild(buttonText) button.addEventListener('click', increaseCounter) document.body.appendChild(button) } render() Notice the render and increaseCounter functions. Whenever the counter property gets updated on Firebase, the render function is called. Whenever we click on the button increaseCounter is called, setting the value on Firebase which in turn triggers render. This is a typical way of working with Firebase. You use it both as a database and as an event source. Navigate your browser to http://localhost:9966 again. Click the button a few times and watch the counter increase. Then, open another browser window and navigate to the same URL. Have the browser windows side-by-side so you can see them both at the same time, then click one of the buttons. It's a little bit like magic isn't it? Once you get familiar with the way Firebase works it is a really great store for your data. It encourages event driven design and functional code. It frees you from manually coordinating state between different parts of your application. Firebase holds your state, so all you have to do is listen. About the author Asbjørn Enge is a software enthusiast living in Sandes, Norway. He is passionate about free software and the web. His interests includes the engineering and designing of web clients, APIs, and server side services, as well as cloud architecture and devops. He cares about modular design, simplicity and readability. He can be found on Twitter @asbjornenge.
Read more
  • 0
  • 0
  • 1846

article-image-particle-core-powered-laser-tank-system
Pawel Szymczykowski
12 Jan 2015
14 min read
Save for later

Build your own Particle Core Powered Laser Tank

Pawel Szymczykowski
12 Jan 2015
14 min read
Laser Tanks Recently, we had the pleasure of interviewing a group of summer intern candidates at Zappos, and we were in need of a fun and quick coding challenge. I set them each to the task of developing a robot program for RoboCode to battle it out on the virtual arena. RoboCode is an open source programming game in which participants write a program to control an autonomous robot tank. Each tank is equipped with a cannon, a radar scanner that can detect other tanks, and wheels to move around on the arena. You write to an interface in Java or .NET, and control an event loop for default behaviors like moving around and looking for enemies, and various triggers for events like scanning another tank, getting shot or hitting a wall. This all happens in a software simulation. The challenge turned out to be a lot of fun for the candidates as well as the spectators, but I couldn't help but think about how much more fun it would be with real robot tanks. My hobby projects in educational robotics and irrational enthusiasm made me think that this was not only possible, but fairly easily done. Here is a sample RoboCode program: public class SampleRobot extends Robot { public void run() { while(true) { ahead(100); turnRight(30); fire(1); turnRight(30); fire(1); turnRight(30); fire(1); } } public void onHitByBullet(HitByBulletEvent e) { turnLeft(90); ahead(100); } } In that code, the tank goes forward for 100 units, turns 30 degrees and fires, then turns 30 degrees and fires, and turns 30 degrees one last time (for a total of 90 degrees) and fires one last time before repeating its behavior. When it is hit by a bullet it turns 90 degrees to the left and moves ahead in order to break its pattern and hopefully avoid more bullets. In this build, we're going to attempt to replicate this functionality in hardware. Since a single tank isn't going to be that much fun, you might want to pull a friend or family member in to build a pair together. Let's take a look at the parts we'll need for this project: Qty Item Source 1 Particle Core https://www.Particle.io/ 2 Continuous Rotation Servo Motors Pololu 1 Photo Resistor Adafruit 1 Laser Diode eBay 2 10k Ohm Resistor Adafruit 1 2N222A Transistor Adafruit 1 Ping Pong Ball Anywhere Brains For the brains of our laser tank, we'll use the Particle Core, a wifi capable Arduino compatible microcontroller. It's currently one of my favorite boards for prototyping because it comes in a breadboard friendly form factor and can be flashed wirelessly without need for a USB cable. If you've never used your Particle Core before, you'll have to register it and tell it about your WiFi internet connection. To do that, you can use their iPhone or Android app. If you are comfortable with the command line, you can also connect it via USB and use the 'Particle-cli' npm package to do the same thing more expediently. You should follow the 'getting started' tutorial here: http://docs.Particle.io/start/ to get set up quickly. Once you've registered your Particle Core, you'll be writing and uploading your code in their web based IDE. Movement First we'll need a movable tank base. A two wheeled design that uses differential steering with a ball caster or skid for is popular and very easy to implement. I'll be using a laser cut sumobot kit for our base. You can get the files to laser cut or 3D print from http://sumobotkit.com, but if you don't have access to a 3D printer or laser cutter, you can also just use any old box and a pair of wheels that you find. A body made out of Lego® bricks would work fantastically. Standard servo motors can only rotate between a fixed range of degrees, so make sure you have continuous rotation servo motos that can rotate well, continuously. We use continuous rotation servo motors because they are easy to control without any special motor control boards. We'll wire up the red (+) and black (-) wires to the 4xAA battery pack, and then run each signal wire to a PWM pin on the Particle Core. PWM stands for Pulse Width Modulation and is a method of controlling an electronic component by sending it instructions encoded as variable width pulses of electricity. Not all pins are PWM capable, but on the Particle Core, A0 and A1 are both PWM pins. Motor Wiring With all of our connections made, moving our tank is a simple matter. We just need to remember that since our motors are mirrored, we'll need to move one of them clockwise and the other counter-clockwise to achieve forward motion. Reverse the directions of the motors to go in reverse, and turn them both in the same direction to turn right or left. In normal servos, you can specify a position in degrees to move the servo to. Continuous rotation servos have the hardware to tell it when to stop removed, so they behave a little differently. 90 degrees is stopped, 0 degrees it full reverse, and 180 degrees is full speed ahead. This also works for any number in between - for example, 45 degrees is half speed reverse. Servo left; Servo right; void setup() { left.attach(A0); right.attach(A1); } void ahead(int duration) { left.write(180); right.write(0); delay( duration * 10 ); left.write(90); right.write(90); } void back(int duration) { left.write(0); right.write(180); delay( duration * 10 ); left.write(90); right.write(90); } In RoboCode, you can specify a distance in some arbitrary unit of distance. However, we can't accurately specify a distance to move with continuous rotation servos without installing an optical encoder that measures how fast the wheel is turning. Instead of complicating our build with an encoder, we'll just cheat a little and make our calls time-based instead. The distance your servo will move in a given slice of time will vary with your specific model of servo motor, voltage, and wheel size, but with a little trial and error we can tune it in accurately enough. My numbers are for Spring RC SM-S4303R servos and 50mm wheels. // How long it takes to turn 90 degrees const int ninetyDegrees = 650; void turnRight(int degrees) { left.write(180); right.write(180); delay( ( degrees / 90 ) * ninetyDegrees ); left.write(90); right.write(90); runOnHitCode(); } void turnLeft(int degrees) { left.write(0); right.write(0); delay( ( degrees / 90 ) * ninetyDegrees ); left.write(90); right.write(90); } Shooting Commercial Laser Tag systems use infrared beams for safety and practicality, but I personally think that using real lasers would be a lot more fun. Since our laser tank will be fairly low to the ground, under 5mw, and because I trust you not to shine the laser directly into an eyeball, I think we're OK. Red laser tubes are extremely inexpensive on eBay. I bought a 10 pack of 5 volt, 5 milliwatt lasers for about $5 shipped, but I've seen them recently for even less than that. The laser tube is pretty simple to connect: run the blue wire to ground, and the red wire to a 5 volt power source and it will fire. On a standard Arduino like the Uno R3, you could wire it up to any pin and trigger it by setting the pin to HIGH. This is also mostly true of the Particle Core, but it will be underpowered because the logic level of the Particle Core is 3.3v instead of the 5v of the Uno. Thus, we are using a transistor to boost the signal strength to 6v from the battery pack. Laser Wiring Now to shoot, we just set the pin to high for a little bit, say 500 milliseconds. int laser = A3; void setup() { pinMode(laser, OUTPUT); } void fire(int power) { digitalWrite(laser, HIGH); delay(500); digitalWrite(laser, LOW); } Getting Shot Awesome! We now have a roving robot that can roll around and shoot at things! But how do we detect if it's been shot? The answer is a photoresistor. A photoresistor changes resistance in relation to surrounding light levels. Unfortunately, the sensitive area of the photo resistor is just under one millimeter wide. That is a tiny target! That is why we'll need a diffuser of some sort, and a fairly large one. If we drill a small hole in a ping-pong ball and insert the photoresistor, it will bring the ambient light level around the photoresistor down. Then, when a laser beam hits the ball, the thin shell will illuminate and the light level inside of the ball will shoot up! That is how we'll detect a shot. We connect the photoresistor to a 3.3v line and to ground in series with a 10k ohm resistor, acting as what we call a pull down resistor. Then we connect an analog pin (A2) between the pull down resistor and the photoresistor. If we only connected the photoresistor directly to the analog pin, the impedence would be too high and no current would flow through the photoresistor. Adding the resistor provides a path to ground the pulls the voltage down to ground and ensures there is always current flowing. The analog pin just 'observes' the voltage of the current flowing by like a water wheel. Without the pulldown resistor, it's more like a wooden dam. Detector Wiring Then, we just need to watch the reading of the analog pin. If it drops below 600, that means the inside of the ball is pretty bright, and we are likely shot. int photoCell = A2; void setup() { pinMode(photoCell, INPUT); attachInterrupt(photoCell, checkHitStatus, CHANGE); } void checkHitStatus() { lightLevel = analogRead(photoCell); if ( lightLevel < lightThreshold ) { gotHit = true; } } Here we have set up what's called an interrupt. Because we are using delays for our motor movement and laser shooting, it 'blocks' the program or keeps it from doing anything else useful while we wait for the delay to end. If a laser were to hit our ping pong ball during a delay, we wouldn't be able to detect it. An interrupt monitors a pin for a change and calls a function. Since this function is executed very often, we want to keep it small and fast. In our checkHitStatus function, we just get the exact reading from the pin and set a global variable signifying that we've been hit if it's passed the threshold we specified. Keeping Score Finally, we need a way to tell how many times we've been hit and keep track of 'hit points'. We can do this really simply by connecting three LEDs between pins D5, D6, D7 and ground. The LEDs will stay off by default, and then light up each time the tank is hit. When all three are lit up, we will halt the program and blink the LEDs. Game over, you lose. You can reset to the game's beginning state by hitting the reset button on the Particle Core. Score Wiring int led1 = D5; int led2 = D6; int led3 = D7; void setup() { pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); } void runOnHitCode() { if ( gotHit ) { hitCount++; if ( hitCount == 1 ) digitalWrite(led1, HIGH); if ( hitCount == 2 ) digitalWrite(led2, HIGH); if ( hitCount == 3 ) digitalWrite(led3, HIGH); onHitByBullet(); gotHit = false; } } Here we are checking for the gotHit variable and returning if it's not set. If it is set, we increase the number of times we were hit, light the appropriate LED, run the onHitByBullet() function, and reset gotHit so we can get hit again. We want to react to getting hit quickly, so we'll run runOnHitCode() after ever action by inserting it into the ahead, fire, turnLeft and turnRight functions. Putting Everything Together When you are done, your board should look something like this. Note that there are some components hidden below the ping pong ball: Top View I used a 3D printed holder for the ping pong ball and laser, and you can download the STL file here. The holder isn't necessary, as long as you mount the laser in the approximate middle of the ping pong ball and at the same height as any other tanks that you will play against. You might also want to mask out the back of your laser diode with black electrical tape so that any back reflection doesn't accidentally trigger your own hit counter. Let's take a look at the combined code: Servo left; Servo right; int photoCell = A2; int laser = A3; int led1 = D5; int led2 = D6; int led3 = D7; volatile int gotHit = false; volatile int hitCount = 0; volatile int lightLevel = 0; // How long it takes to turn 90 degrees const int ninetyDegrees = 650; // Goes lower with more light - when to trigger a 'hit' const int lightThreshold = 1000; void setup() { pinMode(photoCell, INPUT); attachInterrupt(photoCell, checkHitStatus, CHANGE); pinMode(laser, OUTPUT); pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); left.attach(A0); right.attach(A1); // Set a variable on the Particle API for debugging Particle.variable("lightLevel", &lightLevel, INT); } void checkHitStatus() { lightLevel = analogRead(photoCell); if ( lightLevel < lightThreshold ) { gotHit = true; } } void loop() { if ( hitCount < 3 ) { ahead(100); turnRight(30); fire(1); turnRight(30); fire(1); turnRight(30); fire(1); delay(500); } } void runOnHitCode() { if ( gotHit ) { hitCount++; if ( hitCount == 1 ) digitalWrite(led1, HIGH); if ( hitCount == 2 ) digitalWrite(led2, HIGH); if ( hitCount == 3 ) digitalWrite(led3, HIGH); onHitByBullet(); gotHit = false; } } void ahead(int duration) { left.write(180); right.write(0); delay( duration * 10 ); left.write(90); right.write(90); runOnHitCode(); } void turnRight(int degrees) { left.write(180); right.write(180); delay( ( degrees / 90 ) * ninetyDegrees ); left.write(90); right.write(90); runOnHitCode(); } void turnLeft(int degrees) { left.write(0); right.write(0); delay( ( degrees / 90 ) * ninetyDegrees ); left.write(90); right.write(90); runOnHitCode(); } void fire(int power) { digitalWrite(laser, HIGH); delay(500); digitalWrite(laser, LOW); runOnHitCode(); } void onHitByBullet() { turnLeft(90); ahead(100); } Our code has a few more extra things in it than the original RoboCode example, but the user serviceable part of the API is very similar! Battling There's not much left to do but to get two tanks built and have them battle it out on a flat, smooth surface. The tanks will move around on the playing field and shoot according to the actions you and your opponent programmed in to the main run loop. When one of them hits the other, an LED will light up, and when all three are lit the opponent's tank will stop dead. You can vary the starting positions as the tanks will interact in different ways depending on where they started. You can and should also compete by modifying the runtime code to find the most optimal way to take out your opponent's tank. You don't have to stick to a specific pattern. You can also add some entropy to make your tank move less predictably by using the rand() function like so: // Turn randomly between 0 and 99 degrees turnLeft( rand() % 100 ); Summary If you'd like to have a little more fun, you can set down a few bowls of dry ice and water on the perimeter of your playing field to create a layer of fog that will make the red laser beams visible! Of course these tanks are still shooting a little blindly. In real RoboCode, there is a scanner that is able to detect enemy tanks so as not to waste bullets. The gun can also move independently of the tank's body on a rotatable turret and the tank can avoid obstacles. It would take another article of this size to get into the specifics of all that, but in the mean time I challenge you to think about how it might be done. About the author Pawel Szymczykowski is a software engineer with Zappos.com and an enthusiastic maker at his local Las Vegas hackerspace, SYN Shop. He has been programming ever since his parents bought him a Commodore 64 for Christmas. He is responsible for coming up with a simple open source design for a wooden laser cut sumo bot kit, now available at http://sumobotkit.com. As a result of the popularity of the kit, he was invited to run the robotics workshop at RobotsConf, a JSConf offshoot as well as the next JSConf (which he did attend) and Makerland Conf in his home country of Poland. He developed a healthy passion for teaching robotics through these conferences as well as local NodeBots events and programs with Code for America. He can be found on Twitter @makenai.
Read more
  • 0
  • 0
  • 2844
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-why-should-i-make-cross-platform-games
Packt
12 Jan 2015
10 min read
Save for later

Why should I make cross-platform games?

Packt
12 Jan 2015
10 min read
In this article by Emanuele Feronato, author of the book Learning Cocos2d-JS Game Development, we will see why we need to make cross-platform games and how to do it using Cocos2d-JS. This is a very important question. I asked it to myself a lot of times when HTML5 mobile gaming started to become popular. I was just thinking it was a waste of time to simply care about the different screen resolutions and aspect ratios, so my first HTML5 game was made to perfectly fit my iPad 2 tablet. When I finally showed it to sponsors, most of them said something like "Hey, I like the game, but unfortunately it does not look that good on my iPhone". "Don't worry", I said, "you'll get the game optimized for iPad and iPhone". Unfortunately, it did not look that good on the Galaxy Note. Neither did it on the Samsung S4. You can imagine the rest of this story. I found myself almost rewriting the game with a series of if.. then.. else loops, trying to make it look good on any device. This is why you should make a cross-platform game: To code once and rule them all. Focus on game development and let a framework do the dirty work for you. What Cocos2d-JS is and how it works Cocos2d-JS is a free open source 2D game framework. It can help you to develop cross-platform browser games and native applications. This framework allows you to write games in JavaScript. So, if you have already developed JavaScript applications, you don't have to learn a new language from scratch. Throughout this book, you will learn how to create almost any kind of cross-platform game using a familiar and intuitive language. Requirements to run Cocos2d-JS Before you start, let's see what software you need to install on your computer in order to start developing with Cocos2d-JS: Firstly, you need a text editor. The official IDE for Cocos2d-JS coding is Cocos Code IDE, which you can download for free at http://www.cocos2d-x.org/products/codeide. It features auto completion, code hinting, and some more interesting characteristics to speed up your coding. If you are used to your favorite code editor, that's fine. There are plenty of them, but I personally use PSPad (you can find this at http://www.pspad.com/) on my Windows machine and TextWrangler (you can find this at http://www.barebones.com/products/textwrangler/) on the Mac. They are both free and easy to use, so you can download and have them installed in a matter of minutes. To test your Cocos2d-JS projects, you will need to install a web server on your computer to override security limits when running your project locally. I am using WAMP (http://www.wampserver.com/) on my Windows machine, and MAMP (http://www.mamp.info/) on the Mac. Again, both are free to use as you won't need the PRO version, which is also available for Mac computers. Explaining all the theory behind this is beyond the scope of this book, but you can find all the required information as well as the installation documentation on the official sites. If you prefer, you can test your projects directly online by uploading them on an FTP space you own and call them directly from the web. In this case, you don't need to have a web server installed on your computer, but I highly recommend using WAMP or MAMP instead. I personally use Google Chrome as the default browser to test my projects. As these projects are meant to be cross-platform games, it should run in the same way on every browser, so feel free to use the browser you prefer. The latest information about Cocos2d-JS can be found on the official page http://www.cocos2d-x.org/wiki/Cocos2d-JS, while the latest version can be downloaded at http://www.cocos2d-x.org/download. Cocos2d-JS is updated quite frequently, but at the time of writing, the latest stable release is v3.1. Although new releases always bring some changes, all examples included in this book should work fine with any release marked as 3.x as there aren't huge changes in the roadmap. You will notice the download file is a ZIP file that is greater than 250 MB. Don't worry. Most of the content of the package is made by docs, graphic assets, and examples, while the only required folder, at the moment, is the one called cocos2d-html5. The structure of your Cocos2d-JS project Every HTML5 game is basically a web page with some magic in it; this is what you are going to create with Cocos2d-JS: a web page with some magic in it. To perform this magic, a certain file structure needs to be created, so let's take a look at a screenshot of a folder with a Cocos2d-JS project in it: This is what you are going to build; to tell you the truth, this is a picture of the actual project folder I built for the example to be explained in this article, which is placed in the WAMP localhost folder on my computer. It couldn't be any more real. So, let's take a look at the files to be created: cocos2d-html5: This is the folder you will find in the zip archive. index.html: This is the web page that will contain the game. main.js:This is a file required by Cocos2d-JS with the Cocos2d-JS function calls to make the game start. You will create this within the next few minutes. project.json: This is a JavaScript Object Notation (JSON) with some basic configurations. This is what you need to make your game run. Well, almost, because the actual game will be placed in the src folder. Let's see a few other things first. Hello Cross-World The time has come, the boring theory has ended, and we can now start coding our first project. Let's begin! Firstly, create a page called index.html in the root of the game folder and write this HTML code: <!DOCTYPE html> <head>    <title>      My Awesome game    </title>    <script src="cocos2d-html5/CCBoot.js" type="text/javascript"> </script>    <script src="main.js" type="text/javascript"> </script> </head> <body style="padding:0;margin:0;background-color:#000000;"> </body> </html> There's nothing interesting in it as it is just plain HTML. Let's take a closer look at these lines to see what is going on: <script src=" cocos2d-html5/CCBoot.js "></script> Here, I am including the Cocos2d-JS boot file to make the framework start: <script src="main.js"></script> From the preceding line, this is where we call the script with the actual game we are going to build. Next, we have the following code: <canvas id="gameCanvas"></canvas> This is the canvas we will use to display the game. Notice here that the canvas does not have a width and height, as they will be defined by the game itself. Next is the creation of main.js: the only file we will call from our main index.html page. This is more of a configuration file rather than the game itself, so you won't code anything that is game-related at the moment. However, the file you are going to build will be the blueprint you will be using in all your Cocos2d-JS games. The content of main.js is as follows: cc.game.onStart = function(){   cc.view.setDesignResolutionSize(320, 480, cc.ResolutionPolicy.SHOW_ALL);   cc.director.runScene(new gameScene());};cc.game.run(); Don't worry about the code at the moment; it looks a lot more complicated than it really is. At the moment, the only line we have to worry about is the one that defines the resolution policy. One of the most challenging tasks in cross-platform development is to provide a good gaming experience, no matter what browser or what device the game is running on. However, the problem here is that each device has its own resolution, screen size, and ratio. Cocos2d-JS allows us to handle different resolutions in a similar way web designers do when building responsive design. At the moment, we just want to adapt the game canvas to fit the browser window while targeting the most popular resolution, which is 320x480 (portrait mode). That's what this line does: cc.view.setDesignResolutionSize(320, 480, cc.ResolutionPolicy.SHOW_ALL); Using these settings, you should be pretty sure that your game will run on every device, although you will be working in a low resolution. Also, have a look at this line: cc.director.runScene(new gameScene()); Basically, a Cocos2d-JS game is made by a scene where the game itself runs. There can be more scenes in the same game. Imagine a scene with the title screen, a scene with the game over screen, and a scene with the game itself. At the moment, you only have one scene called gameScene. Remember this name because you are going to use it later. Following this, the next required blueprint file you are going to build is project.json, which has some interesting settings. Let's take a look at the file first: {"debugMode" : 0,"showFPS" : false,"frameRate" : 60,"id" : "gameCanvas","renderMode" : 0,"engineDir":"cocos2d-html5/", "modules" : ["cocos2d"], "jsList" : [   "src/gamescript.js"]} What do these lines mean? Let's see them one by one: debugMode: This is the object key that determines the level of debug warnings. It has a range from 0 to 6. Leave it at 0 at the moment since the project is very simple and we won't make any errors. showFPS: This object can be true or false; it shows or hides the FPS meter on the screen. frameRate: This object sets the frame rate of your game. Set it to 60 to have a smooth game. id: This is the DOM element that is required to run the game. Do you remember you gave your canvas the gameCanvas id? Here you are. engineDir: This is the folder where Cocos2d-JS is installed. modules: This object engines the modules to load. At the moment, we only need the basic Cocos2d library. jsList: This is an array with the files used in the game. This means we are going to create our game in src/gamescript.js. Finally, we arrive at the game script itself. This is the one that will contain the actual game, gamescript.js, which at the moment is just a plain declaration of the game scene: var gameScene = cc.Scene.extend({onEnter:function () {   this._super();   console.log("my awesome game starts here");}}); Here, you want to save everything and call index.html page from your localhost (refer to your WAMP or MAMP docs) in your browser. If you now open the developer console, you should see: my awesome game starts here Congratulations! This means you have successfully managed to create a Cocos2d-JS template file to build your future games. Summary In this article we learned the importance of cross-platform games and how to make them using Cocos2d-JS.
Read more
  • 0
  • 0
  • 4131

article-image-creating-simple-gamemanager-using-unity3d
Ellison Leao
09 Jan 2015
5 min read
Save for later

Creating a simple GameManager using Unity3D

Ellison Leao
09 Jan 2015
5 min read
Using the so called "Game Managers" in games is just as common as eating when making games. Probably every game made has their natural flow: Start -> Play -> Pause -> Die -> Game Over , etc. To handle these different game states, we need a proper manager who can provide a mechanism to know when to change to state "A" to state "B" during gameplay. In this post we will show you how to create a simple game manager for Unity3D games. We will assume that you have some previous knowledge in Unity, but if you haven't get the chance to know it, please go to the Official Learn Unity page and get started. We are going to create the scripts using the C# language. 1 - The Singleton Pattern For the implementation, we will use the Singleton pattern. Why? Some reasons: One instance for all the game implementation, with no possible duplications. The instance is never destroyed on scene changes. It stores the current game state to be accessible anytime. We will not explain the design of the Singleton pattern because it's not the purpose of this post. If you wish to know more about it, you can go here. 2 - The GameManager code Create a new project on Unity and add a first csharp script called SimpleGameManager.cs and add the following code: using UnityEngine; using System.Collections; // Game States // for now we are only using these two public enum GameState { INTRO, MAIN_MENU } public delegate void OnStateChangeHandler(); public class SimpleGameManager { protected SimpleGameManager() {} private static SimpleGameManager instance = null; public event OnStateChangeHandler OnStateChange; public GameState gameState { get; private set; } public static SimpleGameManager Instance{ get { if (SimpleGameManager.instance == null){ DontDestroyOnLoad(SimpleGameManager.instance); SimpleGameManager.instance = new SimpleGameManager(); } return SimpleGameManager.instance; } } public void SetGameState(GameState state){ this.gameState = state; OnStateChange(); } public void OnApplicationQuit(){ SimpleGameManager.instance = null; } } Explaining the code in parts, we have: First we are making some enums for easily check the Game State, so for this example we will have: public enum GameState { INTRO, MAIN_MENU } Then we will have an event delegate method that we will use as a callback when a game state changes. This is ideal for changing scenes. public delegate void OnStateChangeHandler(); Moving forward we will have the gameState attribute, that is a getter for the current Game State. public GameState gameState {get; private set;} Then we will have our class. Taking a look at the singleton implementation we can see that we will use the Instance static variable to get our Game Manager current instance or create a new one if it doesn't exists. It's also interesting to see that we call the DontDestroyOnLoad method in the Game Manager instanciation. On doing that, Unity makes sure that our instance is never destroyed between scenes. The method used to change the Game State is SetGameState, which we only need to pass the GameState enum variable as the parameter. public void SetGameState(GameState state){ this.gameState = state; OnStateChange(); } It automatically sets the new gameState for the instance and call the callback OnStateChangemethod. 3 - Creating Sample Scenes For testing our new Game Manager, we will create 2 Unity scenes: Intro and Menu. The Intro scene will just show some debug messages, simulating an Intro game scene, and after 3 seconds it will change to the Menu Scene were we have the Game Menu code. Create a new scene called Intro and create a csharp script called Intro.cs. Put the following code into the script: using UnityEngine; using System.Collections; public class Intro : MonoBehaviour { SimpleGameManager GM; void Awake () { GM = SimpleGameManager.Instance; GM.OnStateChange += HandleOnStateChange; Debug.Log("Current game state when Awakes: " + GM.gameState); } void Start () { Debug.Log("Current game state when Starts: " + GM.gameState); } public void HandleOnStateChange () { GM.SetGameState(GameState.MAIN_MENU); Debug.Log("Handling state change to: " + GM.gameState); Invoke("LoadLevel", 3f); } public void LoadLevel(){ Application.LoadLevel("Menu"); } } You can see here that we just need to call the Game Manager instance inside the Awake method. The same initialization will happen on the others scripts, to get the current Game Manager state. After getting the Game Manager instance we set the OnStateChange event, which is load the Menu scene after 3 seconds. You can notice that the first line of the event sets the new Game State by calling the SetGameState method. If you run this scene however, you will get an error because we don't have the Menu.cs Scene yet. So let's create it! Create a new scene called Menu and add a csharp script called Menu.cs into this Scene. Add the following code to Menu.cs: using UnityEngine; using System.Collections; public class Menu : MonoBehaviour { SimpleGameManager GM; void Awake () { GM = SimpleGameManager.Instance; GM.OnStateChange += HandleOnStateChange; } public void HandleOnStateChange () { Debug.Log("OnStateChange!"); } public void OnGUI(){ //menu layout GUI.BeginGroup (new Rect (Screen.width / 2 - 50, Screen.height / 2 - 50, 100, 800)); GUI.Box (new Rect (0, 0, 100, 200), "Menu"); if (GUI.Button (new Rect (10, 40, 80, 30), "Start")){ StartGame(); } if (GUI.Button (new Rect (10, 160, 80, 30), "Quit")){ Quit(); } GUI.EndGroup(); } public void StartGame(){ //start game scene GM.SetGameState(GameState.GAME); Debug.Log(GM.gameState); } public void Quit(){ Debug.Log("Quit!"); Application.Quit(); } } We added simple Unity GUI elements for this scene just for example. Run the Intro Scene and check the Debug logs, You should see the messages when the Game State is changing from the old state to the new state and keeping the instance between scenes. And there you have it! You can add more GameStates for multiple screens like Credits, High Score, Levels, etc. The code for this examples is on github, feel free to fork and use it in your games! https://github.com/bttfgames/SimpleGameManager About this Author  Ellison Leão (@ellisonleao) is a passionate software engineer with more than 6 years of experience in web projects and contributor to the MelonJS framework and other open source projects. When he is not writing games, he loves to play drums.
Read more
  • 0
  • 1
  • 33390

article-image-building-information-radiator-part-2
Andrew Fisher
31 Dec 2014
9 min read
Save for later

Building an information radiator, Part 2

Andrew Fisher
31 Dec 2014
9 min read
Code: https://gist.github.com/ajfisher/844975b824ec96c27c7c // An information radiator light showing the forecast temperature in Melbourne. I love lights; specifically I love LEDs - which have been described to me as "catnip for geeks". LEDs are low powered but bright which means they can be embedded into all sorts of interesting places and, when coupled with a network, can be used for all sorts of ambient display purposes. In the first part in this series I explained how to use an Arduino and an RGB light disc attached to the network in order to create a networked light. In this part I’ll show you how to scrape some data from a weather web site and use that to make your light change colors periodically to show the day or night time forecast temperature. Scraping the data Weather data is easy to get hold of using APIs, however I'm going to do this the old fashioned way as many interesting data sources may not have an API available for you to hit. This technique is going to use good old fashioned html scraping. To scrape the data I'll use a simple python script. If you’ve never used python before then go start here to get installed and get some familiarity with the language. I’ll use the standard library’s urllib2 module to request a page from Accuweather’s site The URL in this case being the for the weather in Melbourne, Australia (where I live). You'll want to change this to your own home town. Once the request comes back, you get the html content of the page. It's possible to parse through all of this using regular expressions, however a python module called beautifulsoup can interpret the response as a document of nodes rather than just text. Install beautifulsoup using: pip install beautifulsoup To get to the appropriate data you need to walk the document structure using selectors. If you read through the html you can see that the temperature data is available in two LIs (li#feed-sml-1 and li#feed-sml-2), both of which use IDs so that makes the job very easy to pull the information out. The resulting text can be cleaned up with some string manipulation. The code below shows how to do this in beautifulsoup in order to get the forecasted max and overnight min temperatures. // code snippet# load this up into beautiful soup                                              soup = BeautifulSoup(response.read())                                           # these IDs were discovered by reading the html and gettng to the               # point where the next forecast occurs                                                                              temp = soup.find('li', {'id':'feed-sml-1'})                                                               # grab the temps and remove the cruft                                           temp_val = int(today.find('strong', {'class':'temp'}).text.replace("&deg;", ""))print("Forecast: %d" % temp_val) The other main part of the code looks at the temperature and makes some decisions about what colours mean what. In this case I'm using the following ranges: <10C is bright blue - it's really chilly 10-20C is green - cool to mild 20-30C is yellow - lovely weather 30-40C is orange - hot >40C is red - really really hot! These ranges are based on the climate I have here in Melbourne where it never gets below 0C but in summer regularly goes over 40, you can adjust these to what makes sense for where you live. This final snippet of code uses the telnet library to connect to the Arduino and send the payload.  tn = Telnet()                                                                   tn.open(arduino_ip)                                                             tn.write(str(colour))                                                           tn.write("n") The full code listing is below:  #!/usr/bin/python # This script will periodically go and check the weather for a given # location and return the max and min forecasted temperature for the next# 24 hours.# Once this is retrieved it sends a message to a networked arduino in the form# of an RGB colour map in order to control a light showing expected temps.## Author:   Andrew Fisher <ajfisher># Version:  0.1 from datetime import datetimefrom telnetlib import Telnetimport urllib2from BeautifulSoup import BeautifulSoup # This is specific to melbourne, change it to your location weather_url = "http://www.accuweather.com/en/au/melbourne/26216/weather-forecast/26216" # details of your arduino on you network. Change to yours arduino_ip = "10.0.1.91" response = urllib2.urlopen(weather_url) # load this up into beautiful soup soup = BeautifulSoup(response.read()) # these IDs were discovered by reading the html and gettng to the # point where the forecast exists. Use the "next" item forecast = soup.find('li', {'id':'feed-sml-1'}) # grab the temps and remove the cruft temp_val = int(forecast.find('strong', {'class':'temp'}).text.replace("&deg;", ""))  print("Forecast temp is %d" % temp_val) # convert to colour rangeif temp_val <= 10:    red = 0    blue = 255    green = 20elif temp_val > 10 and temp_val <=20:    red = 128    blue = 0    green = 255elif temp_val >20 and temp_val <= 30:    red = 128    blue = 0    green = 128elif temp_val > 30 and temp_val <= 40:    red = 255    blue = 0    green = 128else:    red = 255    blue = 0    green = 0 colour = { "r": red, "b": blue,"g": green } # Send message to arduinotn = Telnet()tn.open(arduino_ip)tn.write(str(colour))tn.write("n") You can test this now by running python weather.py If everything is working you will see the arduino light up with the appropriate colour based on your forecast. To automate this, make a scheduled task on windows or a cron job on linux / mac to run each hour to run this script and it will update the light display. Mount your light somewhere you can see it and you’ll be able to determine whether it’s shorts weather or you’ll need an extra blanket on the bed tonight. // A lovely day in Melbourne forecast. Perfect t-shirt weather. ## Going further Now you know how to make an information radiator from a networked device you can make your own. Here are some ideas to take it further Upgrade the protocol to include a duration so the light will turn off or dim after a period of time (good for frequent messages). Use python to talk to an API instead of scraping - eg twitter's public stream looking for keywords. Include multiple lights in a display in order to show probability of rain as well as forecasted temperature Mill, mould or 3d print a light fitting to go around your device and make it a piece of ambient art. About the author Andrew Fisher is a creator (and destroyer) of things that combine mobile web, ubicomp, and lots of data. He is a programmer, interaction researcher, and CTO at JBA, a data consultancy in Melbourne, Australia. He can be found on Twitter at @ajfisher.
Read more
  • 0
  • 0
  • 2896

article-image-ride-through-worlds-best-etl-tool-informatica-powercenter
Packt
30 Dec 2014
25 min read
Save for later

A ride through world's best ETL tool – Informatica PowerCenter

Packt
30 Dec 2014
25 min read
In this article, by Rahul Malewar, author of the book, Learning Informatica PowerCenter 9.x, we will go through the basics of Informatica PowerCenter. Informatica Corporation (Informatica), a multi-million dollar company incorporated in February 1993, is an independent provider of enterprise data integration and data quality software and services. The company enables a variety of complex enterprise data integration products, which include PowerCenter, Power Exchange, enterprise data integration, data quality, master data management, business to business (B2B) data exchange, application information lifecycle management, complex event processing, ultra messaging, and cloud data integration. Informatica PowerCenter is the most widely used tool of Informatica across the globe for various data integration processes. Informatica PowerCenter tool helps integration of data from almost any business system in almost any format. This flexibility of PowerCenter to handle almost any data makes it most widely used tool in the data integration world. (For more resources related to this topic, see here.) Informatica PowerCenter architecture PowerCenter has a service-oriented architecture that provides the ability to scale services and share resources across multiple machines. This lets you access the single licensed software installed on a remote machine via multiple machines. High availability functionality helps minimize service downtime due to unexpected failures or scheduled maintenance in the PowerCenter environment. Informatica architecture is divided into two sections: server and client. Server is the basic administrative unit of Informatica where we configure all services, create users, and assign authentication. Repository, nodes, Integration Service, and code page are some of the important services we configure while we work on the server side of Informatica PowerCenter. Client is the graphical interface provided to the users. Client includes PowerCenter Designer, PowerCenter Workflow Manager, PowerCenter Workflow Monitor, and PowerCenter Repository Manager. The best place to download the Informatica software for training purpose is from EDelivery (www.edelivery.com) website of Oracle. Once you download the files, start the extraction of the zipped files. After you finish extraction, install the server first and later client part of PowerCenter. For installation of Informatica PowerCenter, the minimum requirement is to have a database installed in your machine. Because Informatica uses the space from the Oracle database to store the system-related information and the metadata of the code, which you develop in client tool. Informatica PowerCenter client tools Informatica PowerCenter Designer client tool talks about working of the source files and source tables and similarly talks about working on targets. Designer tool allows import/create flat files and relational databases tables. Informatica PowerCenter allows you to work on both types of flat files, that is, delimited and fixed width files. In delimited files, the values are separated from each other by a delimiter. Any character or number can be used as delimiter but usually for better interpretation we use special characters as delimiter. In delimited files, the width of each field is not a mandatory option as each value gets separated by other using a delimiter. In fixed width files, the width of each field is fixed. The values are separated by each other by the fixed size of the column defined. There can be issues in extracting the data if the size of each column is not maintained properly. PowerCenter Designer tool allows you to create mappings using sources, targets, and transformations. Mappings contain source, target, and transformations linked to each other through links. The group of transformations which can be reused is called as mapplets. Mapplets are another important aspect of Informatica tool. The transformations are most important aspect of Informatica, which allows you to manipulate the data based on your requirements. There are various types of transformations available in Informatica PowerCenter. Every transformation performs specific functionality. Various transformations in Informatica PowerCenter The following are the various transformations in Informatica PowerCenter: Expression transformation is used for row-wise manipulation. For any type of manipulation you wish to do on an individual record, use Expression transformation. Expression transformation accepts the row-wise data, manipulates it, and passes to the target. The transformation receives the data from input port and it sends the data out from output ports. Use the Expression transformation for any row-wise calculation, like if you want to concatenate the names, get total salary, and convert in upper case. Aggregator transformation is used for calculations using aggregate functions on a column as against in the Expression transformation, which is used for row-wise manipulation. You can use aggregate functions, such as SUM, AVG, MAX, MIN, and so on in Aggregator transformation. When you use Aggregator transformation, Integration Services stores the data temporarily in cache memory. Cache memory is created because the data flows in row-wise manner in Informatica and the calculations required in Aggregator transformation is column wise. Unless we store the data temporarily in cache, we cannot perform the aggregate calculations to get the result. Using Group By option in Aggregator transformation, you can get the result of the Aggregate function based on group. Also it is always recommended that we pass sorted input to Aggregator transformation as this will enhance the performance. When you pass the sorted input to Aggregator transformation, Integration Services enhances the performance by storing less data into cache. When you pass unsorted data, Aggregator transformation stores all the data into cache which takes more time. When you pass the sorted data to Aggregator transformation, Aggregator transformation stores comparatively lesser data in the cache. Aggregator passes the result of each group as soon the data for particular group is received. Note that Aggregator transformation does not sort the data. If you have unsorted data, use Sorter transformation to sort the data and then pass the sorted data to Aggregator transformation. Sorter transformation is used to sort the data in ascending or descending order based on single or multiple key. Apart from ordering the data in ascending or descending order, you can also use Sorter transformation to remove duplicates from the data using the distinct option in the properties. Sorter can remove duplicates only if complete record is duplicate and not only particular column. Filter transformation is used to remove unwanted records from the mapping. You define the Filter condition in the Filter transformation. Based on filter condition, the records will be rejected or passed further in mapping. The default condition in Filter transformation is TRUE. Based on the condition defined, if the record returns True, the Filter transformation allows the record to pass. For each record which returns False, the Filter transformation drops those records. It is always recommended to use Filter transformation as early as possible in the mapping for better performance. Router transformation is single input group multiple output group transformation. Router can be used in place of multiple Filter transformations. Router transformation accepts the data once through input group and based on the output groups you define, it sends the data to multiple output ports. You need to define the filter condition in each output group. It is always recommended to use Router in place of multiple filters in the mapping to enhance the performance. Rank transformation is used to get top or bottom specific number of records based on the key. When you create a Rank transformation, a default output port RANKINDEX comes with the transformation. It is not mandatory to use the RANKINDEX port. Sequence Generator transformation is used to generate sequence of unique numbers. Based on the property defined in the Sequence Generator transformation, the unique values are generated. You need to define the start value, the increment by value, and the end value in the properties. Sequence Generator transformation has only two ports: NEXTVAL and CURRVAL. Both the ports are output port. Sequence Generator does not have any input port. You cannot add or delete any port in Sequence Generator. It is recommended that you should always use the NEXTVAL port first. If the NEXTVAL port is utilized, use the CURRVAL port. You can define the value of CURRVAL in the properties of Sequence Generator transformation. Joiner transformation is used to join two heterogeneous sources. You can join data from same source type also. The basic criteria for joining the data are a matching column in both the source. Joiner transformation has two pipelines, one is called mater and other is called as detail. We do not have left or right join as we have in SQL database. It is always recommended to make table with lesser number of record as master and other one as details. This is because Integration Service picks the data from master source and scans the corresponding record in details table. So if we have lesser number of records in master table, lesser number of times the scanning will happen. This enhances the performance. Joiner transformation has four types of joins: normal join, full outer join, master outer join, details outer join. Union transformation is used the merge the data from multiple sources. Union is multiple input single output transformation. This is opposite of Router transformation, which we discussed earlier. The basic criterion for using Union transformation is that you should have data with matching data type. If you do not have data with matching data type coming from multiple sources, Union transformation will not work. Union transformation merges the data coming from multiple sources and do not remove duplicates, that is, it acts as UNION ALL of SQL statements. As mentioned earlier, Union requires data coming from multiple sources. Union reads the data concurrently from multiple sources and processes the data. You can use heterogeneous sources to merge the data using Union transformation. Source Qualifier transformation acts as virtual source in Informatica. When you drag relational table or flat file in Mapping Designer, Source Qualifier transformation comes along. Source Qualifier is the point where actually Informatica processing starts. The extraction process starts from the Source Qualifier. Lookup transformation is used to lookup of source, Source Qualifier, or target to get the relevant data. You can look up on flat file or relational tables. Lookup transformation works on the similar lines as Joiner with few differences like Lookup does not require two source. Lookup transformations can be connected and unconnected. Lookup transformation extracts the data from the lookup table or file based on the lookup condition. When you create the Lookup transformation you can configure the Lookup transformation to cache the data. Caching the data makes the processing faster since the data is stored internally after cache is created. Once you select to cache the data, Lookup transformation caches the data from the file or table once and then based on the condition defined, lookup sends the output value. Since the data gets stored internally, the processing becomes faster as it does not require checking the lookup condition in file or database. Integration Services queries the cache memory as against checking the file or table for fetching the required data. The cache is created automatically and also it is deleted automatically once the processing is complete. Lookup transformation has four different types of ports. Input ports (I) receive the data from other transformation. This port will be used in Lookup condition. You need to have at least one input port. Output port (O) passes the data out of the Lookup transformation to other transformations. Lookup port (L) is the port for which you wish to bring the data in mapping. Each column is assigned as lookup and output port when you create the Lookup transformation. If you delete the lookup port from the flat file lookup source, the session will fail. If you delete the lookup port from relational lookup table, Integration Services extracts the data only with Lookup port. This helps in reducing the data extracted from the lookup source. Return port (R) is only used in case of unconnected Lookup transformation. This port indicates which data you wish to return in the Lookup transformation. You can define only one port as return port. Return port is not used in case on connected Lookup transformation. Cache is the temporary memory, which is created when you execute the process. Cache is created automatically when the process starts and is deleted automatically once the process is complete. The amount of cache memory is decided based on the property you define in the transformation level or session level. You usually set the property as default, so as required it can increase the size of the cache. If the size required for caching the data is more than the cache size defined, the process fails with the overflow error. There are different types of caches available for lookup transformation. You can define the session property to create the cache either sequentially or concurrently. When you select to create the cache sequentially, Integration Service caches the data in row-wise manner as the records enters the Lookup transformation. When the first record enters the Lookup transformation, lookup cache gets created and stores the matching record from the lookup table or file in the cache. This way the cache stores only matching data. It helps in saving the cache space by not storing the unnecessary data. When you select to create cache concurrently, Integration Service does not wait for the data to flow from the source, but it first caches complete data. Once the caching is complete, it allows the data to flow from the source. When you select concurrent cache, the performance enhances as compared to sequential cache, since the scanning happens internally using the data stored in cache. You can configure the cache to permanently save the data. By default, the cache is created as non-persistent, that is, the cache will be deleted once the session run is complete. If the lookup table or file does not change across the session runs, you can use the existing persistent cache. A cache is said to be static if it does not change with the changes happening in the lookup table. The static cache is not synchronized with the lookup table. By default Integration Service creates a static cache. Lookup cache is created as soon as the first record enters the Lookup transformation. Integration Service does not update the cache while it is processing the data. A cache is said to be dynamic if it changes with the changes happening in the lookup table. The static cache is synchronized with the lookup table. You can choose from the Lookup transformation properties to make the cache as dynamic. Lookup cache is created as soon as the first record enters the lookup transformation. Integration Service keeps on updating the cache while it is processing the data. The Integration Service marks the record as insert for new row inserted in dynamic cache. For the record which is updated, it marks the record as update in the cache. For every record which no change, the Integration Service marks it as unchanged. Update Strategy transformation is used to INSERT, UPDATE, DELETE, or REJECT record based on defined condition in the mapping. Update Strategy transformation is mostly used when you design mappings for SCD. When you implement SCD, you actually decide how you wish to maintain historical data with the current data. When you wish to maintain no history, complete history, or partial history, you can either use property defined in the session task or you use Update Strategy transformation. When you use Session task, you instruct the Integration Service to treat all records in the same way, that is, either insert, update or delete. When you use Update Strategy transformation in the mapping, the control is no more with the session task. Update Strategy transformation allows you to insert, update, delete or reject record based on the requirement. When you use Update Strategy transformation, the control is no more with session task. You need to define the following functions to perform the corresponding operation: DD_INSERT: This can be used when you wish to insert the records. It is also represented by numeric 0. DD_UPDATE: This can be used when you wish to update the records. It is also represented by numeric 1. DD_DELETE: This can be used when you wish to delete the records. It is also represented by numeric 2. DD_REJECT: This can be used when you wish to reject the records. It is also represented by numeric 3. Normalizer transformation is used in place of Source Qualifier transformation when you wish to read the data from Cobol Copybook source. Also, the Normalizer transformation is used to convert column-wise data to row-wise data. This is similar to transpose feature of MS Excel. You can use this feature if your source is Cobol Copybook file or relational database tables. Normalizer transformation converts column to row and also generate index for each converted row. Stored procedure is a database component. Informatica uses the stored procedure similar to database tables. Stored procedures are set of SQL instructions, which require certain set of input values and in return stored procedure returns output value. The way you either import or create database tables, you can import or create the stored procedure in mapping. To use the Stored Procedure in mapping the stored procedure should exist in the database. Similar to Lookup transformation, stored procedure can also be connected or unconnected transformation in Informatica. When you use connected stored procedure, you pass the value to stored procedure through links. When you use unconnected stored procedure, you pass the value using :SP function. Transaction Control transformation allows you to commit or rollback individual records, based on certain condition. By default, Integration Service commits the data based on the properties you define at the session task level. Using the commit interval property Integration Service commits or rollback the data into target. Suppose you define commit interval as 10,000, Integration Service will commit the data after every 10,000 records. When you use Transaction Control transformation, you get the control at each record to commit or rollback. When you use Transaction Control transformation, you need to define the condition in expression editor of the Transaction Control transformation. When you run the process, the data enters the Transaction Control transformation in row-wise manner. The Transaction Control transformation evaluates each row, based on which it commits or rollback the data. Classification of Transformations The transformations, which we discussed are classified into two categories—active/passive and connected/unconnected. Active/Passive classification of transformations is based on the number of records at the input and output port of the transformation. If the transformation does not change the number of records at its input and output port, it is said to be passive transformation. If the transformation changes the number of records at the input and output port of the transformation, it is said to be active transformation. Also if the transformation changes the sequence of records passing through it, it will be an active transformation as in case of Union transformation. A transformation is said to be connected if it is connected to any source or any target or any other transformation by at least a link. If the transformation is not connected by any link is it classed as unconnected. Only Lookup and stored procedure transformations can be connected and unconnected, rest all transformations are connected. Advanced Features of designer screen Talking about the advanced features of PowerCenter Designer tool, debugger helps you to debug the mappings to find the error in your code. Informatica PowerCenter provides a utility called as debugger to debug the mapping so that you can easily find the issue in the mapping which you created. Using the debugger, you can see the flow of every record across the transformations. Another feature is target load plan, a functionality which allows you to load data in multiple targets in a same mapping maintaining their constraints. The reusable transformations are transformations which allow you to reuse the transformations across multiple mapping. As source and target are reusable components, transformations can also be reused. When you work on any technology, it is always advised that your code should be dynamic. This means you should use the hard coded values as less as possible in your code. It is always recommended that you use the parameters or the variable in your code so you can easily pass these values and need not frequently change the code. This functionality is achieved by using parameter file in Informatica. The value of a variable can change between the session run. The value of parameter will remain constant across the session runs. The difference is very minute so you should define parameter or variable properly as per your requirements. Informatica PowerCenter allows you to compare objects present within repository. You can compare sources, targets, transformations, mapplets, and mappings in PowerCenter Designer under Source Analyzer, Target Designer, Transformation Developer, Mapplet Designer, Mapping Designer respectively. You can compare the objects in the same repository or in multiple repositories. Tracing level in Informatica defines the amount of data you wish to write in the session log when you execute the workflow. Tracing level is a very important aspect in Informatica as it helps in analyzing the error. Tracing level is very helpful in finding the bugs in the process. You can define tracing level in every transformation. Tracing level option is present in every transformation properties window. There are four types of tracing level available: Normal: When you set the tracing level as normal, Informatica stores status information, information about errors, and information about skipped rows. You get detailed information but not at individual row level. Terse: When you set the tracing level as terse, Informatica stores error information and information of rejected records. Terse tracing level occupies lesser space as compared to normal. Verbose initialization: When you set the tracing level as verbose initialize, it stores process details related to startup, details about index and data files created and more details of transformation process in addition to details stored in normal tracing. This tracing level takes more space as compared to normal and terse. Verbose data: This is the most detailed level of tracing level. It occupies more space and takes longer time as compared to other three. It stores row level data in the session log. It writes the truncation information whenever it truncates the data. It also writes the data to error log if you enable row error logging. Default tracing level is normal. You can change the tracing level to terse to enhance the performance. Tracing level can be defined at individual transformation level or you can override the tracing level by defining it at session level. Informatica PowerCenter Workflow Manager Workflow Manager screen is the second and last phase of our development work. In the Workflow Manager session task and workflows are created, which is used to execute mapping. Workflow Manager screen allows you to work on various connections like relations, FTP, and so on. Basically, Workflow Manager contains set of instructions which we define as workflow. The basic building block of workflow is tasks. As we have multiple transformations in designer screen, we have multiple tasks in Workflow Manager Screen. When you create a workflow, you add tasks into it as per your requirement and execute the workflow to see the status in the monitor. Workflow is a combination of multiple tasks connected with links that trigger in proper sequence to execute a process. Every workflow contains start task along with other tasks. When you execute the workflow, you actually trigger start task, which in turn triggers other tasks connected in the flow. Every task performs a specific functionality. You need to use the task based on the functionality you need to achieve. Various tasks in Workflow Manager The following are the tasks in Workflow Manager: Session task is used to execute the mapping. Every session task can execute a single mapping. You need to define the path/connection of the source and target used in the mapping, so the session can extract the data from the defined path and send the data to the mapping for processing. Email task is used to send success or failure email notifications. You can configure your outlook or mailbox with the email task to directly send the notification. Command task is used to execute Unix scripts/commands or Windows commands. Timer task is used to add some time gap or to add delay between two tasks. Timer task have properties related to absolute time and relative time. Assignment task is used to assign a value to workflow variable. Control task is used to control the flow of workflow by stopping or aborting the workflow in case on some error. You can control the flow of complete workflow using control task. Decision task is used to check the status of multiple tasks and hence control the execution of workflow. Link task as against decision task can only check the status of the previous task. Event task is used to wait for a particular event to occur. Usually it is used as file watcher task. Using event wait task we can keep looking for a particular file and then trigger the next task. Evert raise task is used to trigger a particular event defined in workflow. Advanced Workflow Manager Workflow Manager screen has some very important features called as scheduling and incremental aggregation, which allows in easier and convenient processing of data. Scheduling allows you to schedule the workflow as specified timing so the workflow runs at the desired time. You need not manually run the workflow every time, schedule can do the needful. Incremental aggregation and partitioning are advanced features, which allows you to process the data faster. When you run the workflow, Integration Service extracts the data in row wise manner from the source path/connection you defined in session task and makes it flow from the mapping. The data reaches the target through the transformations you defined in the mapping. The data always flow in a row wise manner in Informatica, no matter what so ever is your calculation or manipulation. So if you have 10 records in source, there will be 10 Source to target flows while the process is executed. Informatica PowerCenter Workflow Monitor The Workflow Monitor screen allows the monitoring of the workflows execute in Workflow Manager. Workflow Monitor screen allows you check the status and log files for the Workflow. Using the logs generated, you can easily find the error and rectify the error. Workflow Manager also shows the statistics for number of records extracted from source and number of records loaded into target. Also it gives statistics of error records and bad records. Informatica PowerCenter Repository Manager Repository Manager screen is the fourth client screen, which is basically used for migration (deployment) purpose. This screen is also used for some administration related activities like configuring server with client and creating users. Performance Tuning in Informatica PowerCenter The performance tuning has the contents for the optimizations of various components of Informatica PowerCenter tool, such as source, targets, mappings, sessions, systems. Performance tuning at high level involves two stages, finding the issues called as bottleneck and resolving them. Informatica PowerCenter has features like pushdown optimization and partitioning for better performance. With the defined steps and using the best practices for coding the performance can be enhanced drastically. Slowly Changing Dimensions Using all the understanding of the different client tools you can implement the Data warehousing concepts called as SCD, slowly changing dimensions. Informatica PowerCenter provides wizards, which allow you to easily create different types of SCDs, that is, SCD1, SCD2, and SCD3. Type 1 Dimension mapping (SCD1): It keeps only current data and do not maintain historical data. Type 2 Dimension/Version Number mapping (SCD2): It keeps current as well as historical data in the table. SCD2 allows you to insert new record and changed records using a new column (PM_VERSION_NUMBER) by maintaining the version number in the table to track the changes. We use a new column PM_PRIMARYKEY to maintain the history. Type 2 Dimension/Flag mapping: It keeps current as well as historical data in the table. SCD2 allows you to insert new record and changed records using a new column (PM_CURRENT_FLAG) by maintaining the flag in the table to track the changes. We use a new column PRIMARY_KEY to maintain the history. Type 2 Dimension/Effective Date Range mapping: It keeps current as well as historical data in the table. SCD2 allows you to insert new record and changed records using two new columns (PM_BEGIN_DATE and PM_END_DATE) by maintaining the date range in the table to track the changes. We use a new column PRIMARY_KEY to maintain the history. Type 3 Dimension mapping: It keeps current as well as historical data in the table. We maintain only partial history by adding new column. Summary With this we have discussed the complete PowerCenter tool in brief. The PowerCenter is the best fit tool for any size and any type of data, which you wish to handle. It also provides compatibility with all the files and databases for processing purpose. The transformations present allow you to manipulate any type of data in any form you wish. The advanced features make your work simple by providing convenient options. The PowerCenter tool can make your life easy and can offer you some great career path if you learn it properly as Informatica PowerCenter tool have huge demand in job market and it is one of the highly paid technologies in IT market. Just grab a book and start walking the path. The end will be a great career. We are always available for help. For any help in installation or any issues related to PowerCenter you can reach me at info@dw-learnwell.com. Resources for Article:  Further resources on this subject: Building Mobile Apps [article] Adding a Geolocation Trigger to the Salesforce Account Object [article] Introducing SproutCore [article]
Read more
  • 0
  • 0
  • 4260
article-image-middleware
Packt
30 Dec 2014
13 min read
Save for later

Middleware

Packt
30 Dec 2014
13 min read
In this article by Mario Casciaro, the author of the book, "Node.js Design Patterns", has described the importance of using a middleware pattern. One of the most distinctive patterns in Node.js is definitely middleware. Unfortunately it's also one of the most confusing for the inexperienced, especially for developers coming from the enterprise programming world. The reason for the disorientation is probably connected with the meaning of the term middleware, which in the enterprise architecture's jargon represents the various software suites that help to abstract lower level mechanisms such as OS APIs, network communications, memory management, and so on, allowing the developer to focus only on the business case of the application. In this context, the term middleware recalls topics such as CORBA, Enterprise Service Bus, Spring, JBoss, but in its more generic meaning it can also define any kind of software layer that acts like a glue between lower level services and the application (literally the software in the middle). (For more resources related to this topic, see here.) Middleware in Express Express (http://expressjs.com) popularized the term middleware in theNode.js world, binding it to a very specific design pattern. In express, in fact, a middleware represents a set of services, typically functions, that are organized in a pipeline and are responsible for processing incoming HTTP requests and relative responses. An express middleware has the following signature: function(req, res, next) { ... } Where req is the incoming HTTP request, res is the response, and next is the callback to be invoked when the current middleware has completed its tasks and that in turn triggers the next middleware in the pipeline. Examples of the tasks carried out by an express middleware are as the following: Parsing the body of the request Compressing/decompressing requests and responses Producing access logs Managing sessions Providing Cross-site Request Forgery (CSRF) protection If we think about it, these are all tasks that are not strictly related to the main functionality of an application, rather, they are accessories, components providing support to the rest of the application and allowing the actual request handlers to focus only on their main business logic. Essentially, those tasks are software in the middle. Middleware as a pattern The technique used to implement middleware in express is not new; in fact, it can be considered the Node.js incarnation of the Intercepting Filter pattern and the Chain of Responsibility pattern. In more generic terms, it also represents a processing pipeline,which reminds us about streams. Today, in Node.js, the word middleware is used well beyond the boundaries of the express framework, and indicates a particular pattern whereby a set of processing units, filters, and handlers, under the form of functions are connected to form an asynchronous sequence in order to perform preprocessing and postprocessing of any kind of data. The main advantage of this pattern is flexibility; in fact, this pattern allows us to obtain a plugin infrastructure with incredibly little effort, providing an unobtrusive way for extending a system with new filters and handlers. If you want to know more about the Intercepting Filter pattern, the following article is a good starting point: http://www.oracle.com/technetwork/java/interceptingfilter-142169.html. A nice overview of the Chain of Responsibility pattern is available at this URL: http://java.dzone.com/articles/design-patterns-uncovered-chain-of-responsibility. The following diagram shows the components of the middleware pattern: The essential component of the pattern is the Middleware Manager, which is responsible for organizing and executing the middleware functions. The most important implementation details of the pattern are as follows: New middleware can be registered by invoking the use() function (the name of this function is a common convention in many implementations of this pattern, but we can choose any name). Usually, new middleware can only be appended at the end of the pipeline, but this is not a strict rule. When new data to process is received, the registered middleware is invoked in an asynchronous sequential execution flow. Each unit in the pipeline receives in input the result of the execution of the previous unit. Each middleware can decide to stop further processing of the data by simply not invoking its callback or by passing an error to the callback. An error situation usually triggers the execution of another sequence of middleware that is specifically dedicated to handling errors. There is no strict rule on how the data is processed and propagated in the pipeline. The strategies include: Augmenting the data with additional properties or functions Replacing the data with the result of some kind of processing Maintaining the immutability of the data and always returning fresh copies as result of the processing The right approach that we need to take depends on the way the Middleware Manager is implemented and on the type of processing carried out by the middleware itself. Creating a middleware framework for ØMQ Let's now demonstrate the pattern by building a middleware framework around the ØMQ (http://zeromq.org) messaging library. ØMQ (also known as ZMQ, or ZeroMQ) provides a simple interface for exchanging atomic messages across the network using a variety of protocols; it shines for its performances, and its basic set of abstractions are specifically built to facilitate the implementation of custom messaging architectures. For this reason, ØMQ is often chosen to build complex distributed systems. The interface of ØMQ is pretty low-level, it only allows us to use strings and binary buffers for messages, so any encoding or custom formatting of data has to be implemented by the users of the library. In the next example, we are going to build a middleware infrastructure to abstract the preprocessing and postprocessing of the data passing through a ØMQ socket, so that we can transparently work with JSON objects but also seamlessly compress the messages traveling over the wire. Before continuing with the example, please make sure to install the ØMQ native libraries following the instructions at this URL: http://zeromq.org/intro:get-the-software. Any version in the 4.0 branch should be enough for working on this example. The Middleware Manager The first step to build a middleware infrastructure around ØMQ is to create a component that is responsible for executing the middleware pipeline when a new message is received or sent. For the purpose, let's create a new module called zmqMiddlewareManager.js and let's start defining it: function ZmqMiddlewareManager(socket) { this.socket = socket; this.inboundMiddleware = []; //[1] this.outboundMiddleware = []; var self = this; socket.on('message', function(message) { //[2] self.executeMiddleware(self.inboundMiddleware, { data: message }); }); } module.exports = ZmqMiddlewareManager; This first code fragment defines a new constructor for our new component. It accepts a ØMQ socket as an argument and: Creates two empty lists that will contain our middleware functions, one for the inbound messages and another one for the outbound messages. Immediately, it starts listening for the new messages coming from the socket by attaching a new listener to the message event. In the listener, we process the inbound message by executing the inboundMiddleware pipeline. The next method of the ZmqMiddlewareManager prototype is responsible for executing the middleware when a new message is sent through the socket: ZmqMiddlewareManager.prototype.send = function(data) { var self = this; var message = { data: data}; self.executeMiddleware(self.outboundMiddleware, message,    function() {    self.socket.send(message.data);    } ); } This time the message is processed using the filters in the outboundMiddleware list and then passed to socket.send() for the actual network transmission. Now, we need a small method to append new middleware functions to our pipelines; we already mentioned that such a method is conventionally called use(): ZmqMiddlewareManager.prototype.use = function(middleware) { if(middleware.inbound) {    this.inboundMiddleware.push(middleware.inbound); }if(middleware.outbound) {    this.outboundMiddleware.unshift(middleware.outbound); } } Each middleware comes in pairs; in our implementation it's an object that contains two properties, inbound and outbound, that contain the middleware functions to be added to the respective list. It's important to observe here that the inbound middleware is pushed to the end of the inboundMiddleware list, while the outbound middleware is inserted at the beginning of the outboundMiddleware list. This is because complementary inbound/outbound middleware functions usually need to be executed in an inverted order. For example, if we want to decompress and then deserialize an inbound message using JSON, it means that for the outbound, we should instead first serialize and then compress. It's important to understand that this convention for organizing the middleware in pairs is not strictly part of the general pattern, but only an implementation detail of our specific example. Now, it's time to define the core of our component, the function that is responsible for executing the middleware: ZmqMiddlewareManager.prototype.executeMiddleware = function(middleware, arg, finish) {var self = this;(    function iterator(index) {      if(index === middleware.length) {        return finish && finish();      }      middleware[index].call(self, arg, function(err) { if(err) {        console.log('There was an error: ' + err.message);      }      iterator(++index);    }); })(0); } The preceding code should look very familiar; in fact, it is a simple implementation of the asynchronous sequential iteration pattern. Each function in the middleware array received in input is executed one after the other, and the same arg object is provided as an argument to each middleware function; this is the trickthat makes it possible to propagate the data from one middleware to the next. At the end of the iteration, the finish() callback is invoked. Please note that for brevity we are not supporting an error middleware pipeline. Normally, when a middleware function propagates an error, another set of middleware specifically dedicated to handling errors is executed. This can be easily implemented using the same technique that we are demonstrating here. A middleware to support JSON messages Now that we have implemented our Middleware Manager, we can create a pair of middleware functions to demonstrate how to process inbound and outbound messages. As we said, one of the goals of our middleware infrastructure is having a filter that serializes and deserializes JSON messages, so let's create a new middleware to take care of this. In a new module called middleware.js; let's include the following code: module.exports.json = function() { return {    inbound: function(message, next) {      message.data = JSON.parse(message.data.toString());      next();    },    outbound: function(message, next) {      message.data = new Buffer(JSON.stringify(message.data));      next();    } } } The json middleware that we just created is very simple: The inbound middleware deserializes the message received as an input and assigns the result back to the data property of message, so that it can be further processed along the pipeline The outbound middleware serializes any data found into message.data Design Patterns Please note how the middleware supported by our framework is quite different from the one used in express; this is totally normal and a perfect demonstration of how we can adapt this pattern to fit our specific need. Using the ØMQ middleware framework We are now ready to use the middleware infrastructure that we just created. To do that, we are going to build a very simple application, with a client sending a ping to a server at regular intervals and the server echoing back the message received. From an implementation perspective, we are going to rely on a request/reply messaging pattern using the req/rep socket pair provided by ØMQ (http://zguide. zeromq.org/page:all#Ask-and-Ye-Shall-Receive). We will then wrap the socketswith our zmqMiddlewareManager to get all the advantages from the middleware infrastructure that we built, including the middleware for serializing/deserializing JSON messages. The server Let's start by creating the server side (server.js). In the first part of the module we initialize our components: var zmq = require('zmq'); var ZmqMiddlewareManager = require('./zmqMiddlewareManager'); var middleware = require('./middleware'); var reply = zmq.socket('rep'); reply.bind('tcp://127.0.0.1:5000'); In the preceding code, we loaded the required dependencies and bind a ØMQ 'rep' (reply) socket to a local port. Next, we initialize our middleware: var zmqm = new ZmqMiddlewareManager(reply); zmqm.use(middleware.zlib()); zmqm.use(middleware.json()); We created a new ZmqMiddlewareManager object and then added two middlewares, one for compressing/decompressing the messages and another one for parsing/ serializing JSON messages. For brevity, we did not show the implementation of the zlib middleware. Now we are ready to handle a request coming from the client, we will do this by simply adding another middleware, this time using it as a request handler: zmqm.use({ inbound: function(message, next) { console.log('Received: ',    message.data); if(message.data.action === 'ping') {     this.send({action: 'pong', echo: message.data.echo});  }    next(); } }); Since this last middleware is defined after the zlib and json middlewares, we can transparently use the decompressed and deserialized message that is available in the message.data variable. On the other hand, any data passed to send() will be processed by the outbound middleware, which in our case will serialize then compress the data. The client On the client side of our little application, client.js, we will first have to initiate a new ØMQ req (request) socket connected to the port 5000, the one used by our server: var zmq = require('zmq'); var ZmqMiddlewareManager = require('./zmqMiddlewareManager'); var middleware = require('./middleware'); var request = zmq.socket('req'); request.connect('tcp://127.0.0.1:5000'); Then, we need to set up our middleware framework in the same way that we did for the server: var zmqm = new ZmqMiddlewareManager(request); zmqm.use(middleware.zlib()); zmqm.use(middleware.json()); Next, we create an inbound middleware to handle the responses coming from the server: zmqm.use({ inbound: function(message, next) {    console.log('Echoed back: ', message.data);    next(); } }); In the preceding code, we simply intercept any inbound response and print it to the console. Finally, we set up a timer to send some ping requests at regular intervals, always using the zmqMiddlewareManager to get all the advantages of our middleware: setInterval(function() { zmqm.send({action: 'ping', echo: Date.now()}); }, 1000); We can now try our application by first starting the server: node server We can then start the client with the following command: node client At this point, we should see the client sending messages and the server echoing them back. Our middleware framework did its job; it allowed us to decompress/compress and deserialize/serialize our messages transparently, leaving the handlers free to focus on their business logic! Summary In this article, we learned about the middleware pattern and the various facets of the pattern, and we also saw how to create a middleware framework and how to use. Resources for Article:  Further resources on this subject: Selecting and initializing the database [article] Exploring streams [article] So, what is Node.js? [article]
Read more
  • 0
  • 0
  • 7953

article-image-websockets-wildfly
Packt
30 Dec 2014
22 min read
Save for later

WebSockets in Wildfly

Packt
30 Dec 2014
22 min read
In this article by the authors, Michał Ćmil and Michał Matłoka, of Java EE 7 Development with WildFly, we will cover WebSockets and how they are one of the biggest additions in Java EE 7. In this article, we will explore the new possibilities that they provide to a developer. In our ticket booking applications, we already used a wide variety of approaches to inform the clients about events occurring on the server side. These include the following: JSF polling Java Messaging Service (JMS) messages REST requests Remote EJB requests All of them, besides JMS, were based on the assumption that the client will be responsible for asking the server about the state of the application. In some cases, such as checking if someone else has not booked a ticket during our interaction with the application, this is a wasteful strategy; the server is in the position to inform clients when it is needed. What's more, it feels like the developer must hack the HTTP protocol to get a notification from a server to the client. This is a requirement that has to be implemented in most nontrivial web applications, and therefore, deserves a standardized solution that can be applied by the developers in multiple projects without much effort. WebSockets are changing the game for developers. They replace the request-response paradigm in which the client always initiates the communication with a two-point bidirectional messaging system. After the initial connection, both sides can send independent messages to each other as long as the session is alive. This means that we can easily create web applications that will automatically refresh their state with up-to-date data from the server. You probably have already seen this kind of behavior in Google Docs or live broadcasts on news sites. Now we can achieve the same effect in a simpler and more efficient way than in earlier versions of Java Enterprise Edition. In this article, we will try to leverage these new, exciting features that come with WebSockets in Java EE 7 thanks to JSR 356 (https://jcp.org/en/jsr/detail?id=356) and HTML5. In this article, you will learn the following topics: How WebSockets work How to create a WebSocket endpoint in Java EE 7 How to create an HTML5/AngularJS client that will accept push notifications from an application deployed on WildFly (For more resources related to this topic, see here.) An overview of WebSockets A WebSocket session between the client and server is built upon a standard TCP connection. Although the WebSocket protocol has its own control frames (mainly to create and sustain the connection) coded by the Internet Engineering Task Force in the RFC 6455 (http://tools.ietf.org/html/rfc6455), whose peers are not obliged to use any specific format to exchange application data. You may use plaintext, XML, JSON, or anything else to transmit your data. As you probably remember, this is quite different from SOAP-based WebServices, which had bloated specifications of the exchange protocol. The same goes for RESTful architectures; we no longer have the predefined verb methods from HTTP (GET, PUT, POST, and DELETE), status codes, and the whole semantics of an HTTP request. This liberty means that WebSockets are pretty low level compared to the technologies that we used up to this point, but thanks to this, the communication overhead is minimal. The protocol is less verbose than SOAP or RESTful HTTP, which allows us to achieve higher performance. This, however, comes with a price. We usually like to use the features of higher-level protocols (such as horizontal scaling and rich URL semantics), and with WebSockets, we would need to write them by hand. For standard CRUD-like operations, it would be easier to use a REST endpoint than create everything from scratch. What do we get from WebSockets compared to the standard HTTP communication? First of all, a direct connection between two peers. Normally, when you connect to a web server (which can, for instance, handle a REST endpoint), every subsequent call is a new TCP connection, and your machine is treated like it is a different one every time you make a request. You can, of course, simulate a stateful behavior (so that the server would recognize your machine between different requests) using cookies and increase the performance by reusing the same connection in a short period of time for a specific client, but basically, it is a workaround to overcome the limitations of the HTTP protocol. Once you establish a WebSocket connection between a server and client, you can use the same session (and underlying TCP connection) during the whole communication. Both sides are aware of it, and can send data independently in a full-duplex manner (both sides can send and receive data simultaneously). Using plain HTTP, there is no way for the server to spontaneously start sending data to the client without any request from its side. What's more, the server is aware of all of its WebSocket clients connected, and can even send data between them! The current solution that includes trying to simulate real-time data delivery using HTTP protocol can put a lot of stress on the web server. Polling (asking the server about updates), long polling (delaying the completion of a request to the moment when an update is ready), and streaming (a Comet-based solution with a constantly open HTTP response) are all ways to hack the protocol to do things that it wasn't designed for and have their own limitations. Thanks to the elimination of unnecessary checks, WebSockets can heavily reduce the number of HTTP requests that have to be handled by the web server. The updates are delivered to the user with a smaller latency because we only need one round-trip through the network to get the desired information (it is pushed by the server immediately). All of these features make WebSockets a great addition to the Java EE platform, which fills the gaps needed to easily finish specific tasks, such as sending updates, notifications, and orchestrating multiple client interactions. Despite these advantages, WebSockets are not intended to replace REST or SOAP WebServices. They do not scale so well horizontally (they are hard to distribute because of their stateful nature), and they lack most of the features that are utilized in web applications. URL semantics, complex security, compression, and many other features are still better realized using other technologies. How does WebSockets work To initiate a WebSocket session, the client must send an HTTP request with an upgraded, WebSocket header field. This informs the server that the peer client has asked the server to switch to the WebSocket protocol. You may notice that the same happens in WildFly for Remote EJBs; the initial connection is made using an HTTP request, and is later switched to the remote protocol thanks to the Upgrade mechanism. The standard Upgrade header field can be used to handle any protocol, other than HTTP, which is accepted by both sides (the client and server). In WildFly, this allows to reuse the HTTP port (80/8080) for other protocols and therefore, minimise the number of required ports that should be configured. If the server can understand the WebSocket protocol, the client and server then proceed with the handshaking phase. They negotiate the version of the protocol, exchange security keys, and if everything goes well, the peers can go to the data transfer phase. From now on, the communication is only done using the WebSocket protocol. It is not possible to exchange any HTTP frames using the current connection. The whole life cycle of a connection can be summarized in the following diagram: A sample HTTP request from a JavaScript application to a WildFly server would look similar to this: GET /ticket-agency-websockets/tickets HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: localhost:8080 Origin: http://localhost:8080 Pragma: no-cache Cache-Control: no-cache Sec-WebSocket-Key: TrjgyVjzLK4Lt5s8GzlFhA== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36 Cookie: [45 bytes were stripped] We can see that the client requests an upgrade connection with WebSocket as the target protocol on the URL /ticket-agency-websockets/tickets. It additionally passes information about the requested version and key. If the server supports the request protocol and all the required data is passed by the client, then it would respond with the following frame: HTTP/1.1 101 Switching Protocols X-Powered-By: Undertow 1 Server: Wildfly 8 Origin: http://localhost:8080 Upgrade: WebSocket Sec-WebSocket-Accept: ZEAab1TcSQCmv8RsLHg4RL/TpHw= Date: Sun, 13 Apr 2014 17:04:00 GMT Connection: Upgrade Sec-WebSocket-Location: ws://localhost:8080/ticket-agency-websockets/tickets Content-Length: 0 The status code of the response is 101 (switching protocols) and we can see that the server is now going to start using the WebSocket protocol. The TCP connection initially used for the HTTP request is now the base of the WebSocket session and can be used for transmissions. If the client tries to access a URL, which is only handled by another protocol, then the server can ask the client to do an upgrade request. The server uses the 426 (upgrade required) status code in such cases. The initial connection creation has some overhead (because of the HTTP frames that are exchanged between the peers), but after it is completed, new messages have only 2 bytes of additional headers. This means that when we have a large number of small messages, WebSocket will be an order of magnitude faster than REST protocols simply because there is less data to transmit! If you are wondering about the browser support of WebSockets, you can look it up at http://caniuse.com/websockets. All new versions of major browsers currently support WebSockets; the total coverage is estimated (at the time of writing) at 74 percent. You can see it in the following screenshot: After this theoretical introduction, we are ready to jump into action. We can now create our first WebSocket endpoint! Creating our first endpoint Let's start with a simple example: package com.packtpub.wflydevelopment.chapter8.boundary; import javax.websocket.EndpointConfig; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import java.io.IOException; @ServerEndpoint("/hello") public class HelloEndpoint {    @OnOpen    public void open(Session session, EndpointConfig conf) throws IOException {        session.getBasicRemote().sendText("Hi!");    } } Java EE 7 specification has taken into account developer friendliness, which can be clearly seen in the given example. In order to define your WebSocket endpoint, you just need a few annotations on a Plain Old Java Object (POJO). The first POJO @ServerEndpoint("/hello") defines a path to your endpoint. It's a good time to discuss the endpoint's full address. We placed this sample in the application named ticket-agency-websockets. During the deployment of application, you can spot information in the WildFly log about endpoints creation, as shown in the following command line: 02:21:35,182 INFO [io.undertow.websockets.jsr] (MSC service thread 1-7)UT026003: Adding annotated server endpoint class com.packtpub.wflydevelopment.chapter8.boundary.FirstEndpoint for path /hello 02:21:35,401 INFO [org.jboss.resteasy.spi.ResteasyDeployment](MSC service thread 1-7) Deploying javax.ws.rs.core.Application: classcom.packtpub.wflydevelopment.chapter8.webservice.JaxRsActivator$Proxy$_$$_WeldClientProxy 02:21:35,437 INFO [org.wildfly.extension.undertow](MSC service thread 1-7) JBAS017534: Registered web context:/ticket-agency-websockets The full URL of the endpoint is ws://localhost:8080/ticket-agency-websockets/hello, which is just a concatenation of the server and application address with an endpoint path on an appropriate protocol. The second used annotation @OnOpen defines the endpoint behavior when the connection from the client is opened. It's not the only behavior-related annotation of the WebSocket endpoint. Let's look to the following table: Annotation Description @OnOpen Connection is open. With this annotation, we can use the Session and EndpointConfig parameters. The first parameter represents the connection to the user and allows further communication. The second one provides some client-related information. @OnMessage This annotation is executed when a message from the client is being received. In such a method, you can just have Session and for example, the String parameter, where the String parameter represents the received message. @OnError There are bad times when some errors occur. With this annotation, you can retrieve a Throwable object apart from standard Session. @OnClose When the connection is closed, it is possible to get some data concerning this event in the form of the CloseReason type object. There is one more interesting line in our HelloEndpoint. Using the Session object, it is possible to communicate with the client. This clearly shows that in WebSockets, two-directional communication is easily possible. In this example, we decided to respond to a connected user synchronously (getBasicRemote()) with just a text message Hi! (sendText (String)). Of course, it's also possible to communicate asynchronously and send, for example, sending binary messages using your own binary bandwidth saving protocol. We will present some of these processes in the next example. Expanding our client application It's time to show how you can leverage the WebSocket features in real life. We created the ticket booking application based on the REST API and AngularJS framework. It was clearly missing one important feature; the application did not show information concerning ticket purchases of other users. This is a perfect use case for WebSockets! Since we're just adding a feature to our previous app, we will describe the changes we will introduce to it. In this example, we would like to be able to inform all current users about other purchases. This means that we have to store information about active sessions. Let's start with the registry type object, which will serve this purpose. We can use a Singleton session bean for this task, as shown in the following code: @Singleton public class SessionRegistry {    private final Set<Session> sessions = new HashSet<>();    @Lock(LockType.READ)    public Set<Session> getAll() {        return Collections.unmodifiableSet(sessions);    }    @Lock(LockType.WRITE)    public void add(Session session) {        sessions.add(session);    }    @Lock(LockType.WRITE)    public void remove(Session session) {        sessions.remove(session);    } } We could use Collections.synchronizedSet from standard Java libraries but it's a great chance to remember what we described earlier about container-based concurrency. In SessionRegistry, we defined some basic methods to add, get, and remove sessions. For the sake of collection thread safety during retrieval, we return an unmodifiable view. We defined the registry, so now we can move to the endpoint definition. We will need a POJO, which will use our newly defined registry as shown: @ServerEndpoint("/tickets") public class TicketEndpoint {    @Inject    private SessionRegistry sessionRegistry;    @OnOpen    public void open(Session session, EndpointConfig conf) {        sessionRegistry.add(session);    }    @OnClose    public void close(Session session, CloseReason reason) {        sessionRegistry.remove(session);    }    public void send(@Observes Seat seat) {        sessionRegistry.getAll().forEach(session -> session.getAsyncRemote().sendText(toJson(seat)));    }    private String toJson(Seat seat) {        final JsonObject jsonObject = Json.createObjectBuilder()                .add("id", seat.getId())                .add("booked", seat.isBooked())                .build();        return jsonObject.toString();    } } Our endpoint is defined in the /tickets address. We injected a SessionRepository to our endpoint. During @OnOpen, we add Sessions to the registry, and during @OnClose, we just remove them. Message sending is performed on the CDI event (the @Observers annotation), which is already fired in our code during TheatreBox.buyTicket(int). In our send method, we retrieve all sessions from SessionRepository, and for each of them, we asynchronously send information about booked seats. We don't really need information about all the Seat fields to realize this feature. That's the reason why we don't use the automatic JSON serialization here. Instead, we decided to use a minimalistic JSON object, which provides only the required data. To do this, we used the new Java API for JSON Processing (JSR-353). Using a fluent-like API, we're able to create a JSON object and add two fields to it. Then, we just convert JSON to the String, which is sent in a text message. Because in our example we send messages in response to a CDI event, we don't have (in the event handler) an out-of-the-box reference to any of the sessions. We have to use our sessionRegistry object to access the active ones. However, if we would like to do the same thing but, for example, in the @OnMessage method, then it is possible to get all active sessions just by executing the session.getOpenSessions() method. These are all the changes required to perform on the backend side. Now, we have to modify our AngularJS frontend to leverage the added feature. The good news is that JavaScript already includes classes that can be used to perform WebSocket communication! There are a few lines of code we have to add inside the module defined in the seat.js file, which are as follows: var ws = new WebSocket("ws://localhost:8080/ticket-agency-websockets/tickets"); ws.onmessage = function (message) {    var receivedData = message.data;    var bookedSeat = JSON.parse(receivedData);    $scope.$apply(function () {        for (var i = 0; i < $scope.seats.length; i++) {           if ($scope.seats[i].id === bookedSeat.id) {                $scope.seats[i].booked = bookedSeat.booked;                break;            }        }    }); }; The code is very simple. We just create the WebSocket object using the URL to our endpoint, and then we define the onmessage function in that object. During the function execution, the received message is automatically parsed from the JSON to JavaScript object. Then, in $scope.$apply, we just iterate through our seats, and if the ID matches, we update the booked state. We have to use $scope.$apply because we are touching an Angular object from outside the Angular world (the onmessage function). Modifications performed on $scope.seats are automatically visible on the website. With this, we can just open our ticket booking website in two browser sessions, and see that when one user buys a ticket, the second users sees almost instantly that the seat state is changed to booked. We can enhance our application a little to inform users if the WebSocket connection is really working. Let's just define onopen and onclose functions for this purpose: ws.onopen = function (event) {    $scope.$apply(function () {        $scope.alerts.push({            type: 'info',            msg: 'Push connection from server is working'        });    }); }; ws.onclose = function (event) {    $scope.$apply(function () {        $scope.alerts.push({            type: 'warning',            msg: 'Error on push connection from server '        });    }); }; To inform users about a connection's state, we push different types of alerts. Of course, again we're touching the Angular world from the outside, so we have to perform all operations on Angular from the $scope.$apply function. Running the described code results in the notification, which is visible in the following screenshot: However, if the server fails after opening the website, you might get an error as shown in the following screenshot: Transforming POJOs to JSON In our current example, we transformed our Seat object to JSON manually. Normally, we don't want to do it this way; there are many libraries that will do the transformation for us. One of them is GSON from Google. Additionally, we can register an encoder/decoder class for a WebSocket endpoint that will do the transformation automatically. Let's look at how we can refactor our current solution to use an encoder. First of all, we must add GSON to our classpath. The required Maven dependency is as follows: <dependency>    <groupId>com.google.code.gson</groupId>    <artifactId>gson</artifactId>    <version>2.3</version> </dependency> Next, we need to provide an implementation of the javax.websocket.Encoder.Text interface. There are also versions of the javax.websocket.Encoder.Text interface for binary and streamed data (for both binary and text formats). A corresponding hierarchy of interfaces is also available for decoders (javax.websocket.Decoder). Our implementation is rather simple. This is shown in the following code snippet: public class JSONEncoder implements Encoder.Text<Object> {    private Gson gson;    @Override    public void init(EndpointConfig config) {        gson = new Gson(); [1]    }    @Override    public void destroy() {        // do nothing    }    @Override    public String encode(Object object) throws EncodeException {        return gson.toJson(object); [2]    } } First, we create an instance of GSON in the init method; this action will be executed when the endpoint is created. Next, in the encode method, which is called every time, we send an object through an endpoint. We use JSON to create JSON from an object. This is quite concise when we think how reusable this little class is. If you want more control on the JSON generation process, you can use the GsonBuilder class to configure the GSON object before creation of the GsonBuilder class. We have the encoder in place. Now it's time to alter our endpoint: @ServerEndpoint(value = "/tickets", encoders={JSONEncoder.class})[1] public class TicketEndpoint {    @Inject    private SessionRegistry sessionRegistry;    @OnOpen    public void open(Session session, EndpointConfig conf) {        sessionRegistry.add(session);    }    @OnClose    public void close(Session session, CloseReason reason) {        sessionRegistry.remove(session);    }    public void send(@Observes Seat seat) {        sessionRegistry.getAll().forEach(session -> session.getAsyncRemote().sendObject(seat)); [2]    } } The first change is done on the @ServerEndpoint annotation. We have to define a list of supported encoders; we simply pass our JSONEncoder.class wrapped in an array. Additionally, we have to pass the endpoint name using the value attribute. Earlier, we used the sendText method to pass a string containing a manually created JSON. Now, we want to send an object and let the encoder handle the JSON generation; therefore, we'll use the getAsyncRemote().sendObject() method. That's all! Our endpoint is ready to be used. It will work the same as the earlier version, but now our objects will be fully serialized to JSON, so they will contain every field, not only IDs and be booked. After deploying the server, you can connect to the WebSocket endpoint using one of the Chrome extensions, for instance, the Dark WebSocket terminal from the Chrome store (use the ws://localhost:8080/ticket-agency-websockets/tickets address). When you book tickets using the web application, the WebSocket terminal should show something similar to the output shown in the following screenshot: Of course, it is possible to use different formats other than JSON. If you want to achieve better performance (when it comes to the serialization time and payload size), you may want to try out binary serializers such as Kryo (https://github.com/EsotericSoftware/kryo). They may not be supported by JavaScript, but may come in handy if you would like to use WebSockets for other clients also. Tyrus (https://tyrus.java.net/) is a reference implementation of the WebSocket standard for Java; you can use it in your standalone desktop applications. In that case, besides the encoder (which is used to send messages), you would also need to create a decoder, which can automatically transform incoming messages. An alternative to WebSockets The example we presented in this article is possible to be implemented using an older, lesser-known technology named Server-Sent Events (SSE). SSE allows for one-way communication from the server to client over HTTP. It is much simpler than WebSockets but has a built-in support for things such as automatic reconnection and event identifiers. WebSockets are definitely more powerful, but are not the only way to pass events, so when you need to implement some notifications from the server side, remember about SSE. Another option is to explore the mechanisms oriented around the Comet techniques. Multiple implementations are available and most of them use different methods of transportation to achieve their goals. A comprehensive comparison is available at http://cometdaily.com/maturity.html. Summary In this article, we managed to introduce the new low-level type of communication. We presented how it works underneath and compares to SOAP and REST introduced earlier. We also discussed how the new approach changes the development of web applications. Our ticket booking application was further enhanced to show users the changing state of the seats using push-like notifications. The new additions required very little code changes in our existing project when we take into account how much we are able to achieve with them. The fluent integration of WebSockets from Java EE 7 with the AngularJS application is another great showcase of flexibility, which comes with the new version of the Java EE platform. Resources for Article: Further resources on this subject: Various subsystem configurations [Article] Running our first web application [Article] Creating Java EE Applications [Article]
Read more
  • 0
  • 0
  • 15855

article-image-open-source-intelligence
Packt
30 Dec 2014
30 min read
Save for later

Open Source Intelligence

Packt
30 Dec 2014
30 min read
This article is written by Douglas Berdeaux, the author of Penetration Testing with Perl. Open source intelligence (OSINT) refers to intelligence gathering from open and public sources. These sources include search engines, the client target's web-accessible software or sites, social media sites and forums, Internet routing and naming authorities, public information sites, and more. If done properly and thoroughly, the practice of OSINT can prove to be useful to strengthen social engineering and remote exploitation attacks on our client target as we search for ways to gain access to their systems and buildings during a penetration test. What's covered In this article, we will cover how to gather the information listed using Perl: E-mail addresses from our client target using search engines and social media sites Networking, hosting, routing, and system data of our client target using online resources and simple networking utilities To gather this data, we rely heavily on the LWP::UserAgent Perl module. We will also discover how to use this module with a secured socket layer SSL/TLS (HTTPS) connection. In addition to this, we will learn about a few new Perl modules that are listed here: Net::Whois::Raw Net::DNS::Dig Net::DNS Net::Traceroute XML::LibXML Google dorks Before we use Google for intelligence gathering, we should briefly touch upon using Google dorks, which we can use to refine and filter our Google searches. A Google dork is a string of special syntax that we pass to Google's request handler using the q= option. The dork can comprise operators and keywords separated by a colon and concatenated strings using a plus symbol + as a delimiter. Here is a list of simple Google dorks that we can use to narrow our Google searches: intitle:<string> searches for pages whose HTML title tags contain the string <string> filetype:<ext> searches for files that have the extension <ext> site:<domain> narrows the search to only results that are located on the <domain> target servers inurl:<string> returns results that contain <string> in their URL -<word> negates the word following the minus symbol - in a search filter link:<page> searches for pages that contain the HTML HREF links to the page This is just a small list and a complete guide of Google search operators that can be found on their support servers. A list of well-known exploited Google dorks for information gathering can be found in a Google hacker's database at http://www.exploit-db.com/google-dorks/. E-mail address gathering Getting e-mail addresses from our target can be a rather hard task and can also mean gathering usernames used within the target's domain, remote management systems, databases, workstations, web applications, and much more. As we can imagine, gathering a username is 50 percent of the intrusion for target credential harvesting; the other 50 percent being the password information. So how do we gather e-mail addresses from a target? Well, there are several methods; the first we will look at will be simply using search engines to crawl the web for anything useful, including forum posts, social media, e-mail lists for support, web pages and mailto links, and anything else that was cached or found from ever-spidering search engines. Using Google for e-mail address gathering Automating queries to search engines is usually always best left to application programming interfaces (APIs). We might be able to query the search engine via a simple GET request, but this leaves enough room for error, and the search engine can potentially temporarily block our IP address or force us to validate our humanness using an image of words as it might assume that we are using a bot. Unfortunately, Google only offers a paid version of their general search API. They do, however, offer an API for a custom search, but this is restricted to specified domains. We want to be as thorough as possible and search as much of the web as we can, time permitting, when intelligence gathering. Let's go back to our LWP::UserAgent Perl module and make a simple request to Google, searching for any e-mail addresses and URLs from a given domain. The URLs are useful as they can be spidered to within our application if we feel inclined to extend the reach of our automated OSINT. In the following examples, we want to impersonate a browser as much as possible to not raise flags at Google by using automation. We accomplish this by using the LWP::UserAgent Perl module and spoofing a valid Firefox user agent: #!/usr/bin/perl -w use strict; use LWP::UserAgent; use LWP::Protocol::https; my $usage = "Usage ./email_google.pl <domain>"; my $target = shift or die $usage; my $ua = LWP::UserAgent->new; my %emails = (); # unique my $url = 'https://www.google.com/search?num=100&start=0&hl=en&meta=&q=%40%22'.$target.'%22'; $ua->agent("Mozilla/5.0 (Windows; U; Windows NT 6.1 en-US; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); $ua->timeout(10); # setup a timeout $ua->show_progress(1); # display progress bar my $res = $ua->get($url); if($res->is_success){ my @urls = split(/url?q=/,$res->as_string); foreach my $gUrl (@urls){ # Google URLs next if($gUrl =~ m/(webcache.googleusercontent)/i or not $gUrl =~ m/^http/); $gUrl =~ s/&amp;sa=U.*//; print $gUrl,"n"; } my @emails = $res->as_string =~ m/[a-z0-9_.-]+@/ig; foreach my $email (@emails){ if(not exists $emails{$email}){    print "Possible Email Match: ",$email,$target,"n";    $emails{$email} = 1; # hashes are faster } } } else{ die $res->status_line; } The LWP::UserAgent module used in the previous code is not new to us. We did, however, add SSL support using the LWP::Protocol::https module. Our URL $url object is a simple Google search URL that anyone would browse to with a normal browser. The num= value pertains the returned results from Google in a single page, which we have set to 100. To also act as a browser, we needed to set the user agent with the agent() method, which we did as a Mozilla browser. After this, we set a timeout and Boolean to show a simple progress bar. The rest is just simple Perl string manipulation and pattern matching. We use the regular expression url?q= to split the string returned by the as_string method from the $res object. Then, for each URL string, we use another regular expression, &amp;sa=U.*, to remove excess analytic garbage that Google adds. Then, we simply parse out all e-mail addresses found using the same method but different regexp. We stuff all matches into the @emails array and loop over them, displaying them to our screen if they don't exist in the $emails{} Perl hash. Let's run this program against the weaknetlabs.com domain and analyze the output: root@wnld960:~# perl email_google.pl weaknetlabs.com ** GET https://www.google.com/search?num=100&start=0&hl=en&meta=&q=%40%22weaknetlabs.com%22 ==> 200 OK (1s) http://weaknetlabs.com/ http://weaknetlabs.com/main/%3Fpage_id%3D479 … http://www.securitytube.net/video/2039 Possible Email Match: Douglas@weaknetlabs.com Possible Email Match: weaknetlabs@weaknetlabs.com root@wnld960:~# This is the (trimmed) output when we run an automated Google search for an e-mail address from weaknetlabs.com. Using social media for e-mail address gathering Now, let's turn our attention to using social media sites such as Google+, LinkedIn, and Facebook to try to gather e-mail addresses using Perl. Social media sites can sometimes reflect information about an employee's attitude towards their employer, their status within the company, position, e-mail addresses, and more. All of this information is considered OSINT and can be useful when advancing our attacks. Google+ We can also search plus.google.com for contact information from users belonging to our target. The following is the URL-encoded Google dork we will use to search the Google+ profiles for an employee of our target: intitle%3A"About+-+Google%2B"+"Works+at+'.$target.'"+site%3Aplus.google.com The URL-encoded symbols are as follows: %3A: This is a colon, that is, : %2B: This is a plus symbol, that is, + The plus symbol + is a special component of Google dork, as we mentioned in the previous section. The intitle keyword tells Google to display results whose HTML <title> tag contains the About – Google+ text. Then, we add the string (in quotations) "Works at " (notice the space at the end), and then the target name as the string object $target. The site keyword tells the Google search engine to only display results from the plus.google.com site. Let's implement this in our Perl program and see what results are returned for Google employees: #!/usr/bin/perl -w use strict; use LWP::UserAgent; use LWP::Protocol::https; my $ua = LWP::UserAgent->new; my $usage = "Usage ./googleplus.pl <target name>"; my $target = shift or die $usage; $target =~ s/s/+/g; my $gUrl = 'https://www.google.com/search?safe=off&noj=1&sclient=psy-ab&q=intitle%3A"About+-+Google%2B"+"Works+at+' .$target.'"+site%3Aplus.google.com&oq=intitle%3A"About+-+Google%2B"+"Works+at+'.$target.'"+site%3Aplus.google.com'; $ua->agent("Mozilla/5.0 (Windows; U; Windows NT 6.1 en-US; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); $ua->timeout(10); # setup a timeout my $res = $ua->get($gUrl); if($res->is_success){ foreach my $string (split(/url?q=/,$res->as_string)){ next if($string =~ m/(webcache.googleusercontent)/i or not $string =~ m/^http/); $string =~ s/&amp;sa=U.*//; print $string,"n"; } } else{ die $res->status_line; } This Perl program is quite similar to our last search program. Now, let's run this to find possible Google employees. Since a target client company can have spaces in its name, we accommodate them by encoding them for Google as plus symbols: root@wnld960:~# perl googleplus.pl google https://plus.google.com/%2BPaulWilcox/about https://plus.google.com/%2BNatalieVillalobos/about ... https://plus.google.com/%2BAndrewGerrand/about root@wnld960:~# The preceding (trimmed) output proves that our Perl script works as we browse to the returned results. These two Google search scripts provided us with some great information quickly. Let's move on to another example, not using Google but LinkedIn, a social media site for professionals. LinkedIn LinkedIn can provide us with the contact information and IT skill levels of our client target during a penetration test. Here, we will focus on the contact information. By now, we should feel very comfortable making any type of web request using LWP::UserAgent and parsing its output for intelligence data. In fact, this LinkedIn example should be a breeze. The trick is fine-tuning our filters and regular expressions to get only relevant data. Let's just dive right into the code and then analyze some sample output: #!/usr/bin/perl -w use strict; use LWP::UserAgent; use LWP::Protocol::https; my $ua = LWP::UserAgent->new; my $usage = "Usage ./googlepluslinkedin.pl <target name>"; my $target = shift or die $usage; my $gUrl = 'https://www.google.com/search?q=site:linkedin.com+%22at+'.$target.'%22'; my %lTargets = (); # unique $ua->agent("Mozilla/5.0 (Windows; U; Windows NT 6.1 en-US; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); $ua->timeout(10); # setup a timeout my $google = getUrl($gUrl); # one and ONLY call to Google foreach my $title ($google =~ m/shref="/url?.*">[a-z0-9_. -]+s?.b.at $target..b.s-slinked/ig){ my $lRurl = $title; $title =~ s/.*">([^<]+).*/$1/; $lRurl =~ s/.*url?.*q=(.*)&amp;sa.*/$1/; print $title,"-> ".$lRurl."n"; my @ln = split(/15?12/,getUrl($lRurl)); foreach(@ln){ if(m/title="/i){    my $link = $_;    $link =~ s/.*href="([^"]+)".*/$1/;    next if exists $lTargets{$link};    $lTargets{$link} = 1;    my $name = $_;    $name =~ s/.*title="([^"]+)".*/$1/;    print "t",$name," : ",$link,"n"; } } } sub getUrl{ sleep 1; # pause... my $res = $ua->get(shift); if($res->is_success){ return $res->as_string; }else{ die $res->status_line; } } The preceding Perl program makes one query to Google to find all possible positions from the target; for each position found, it queries LinkedIn to find employees of the target. The regular expressions used were finely crafted after inspection of the returned HTML object from a simple query to both Google and LinkedIn. This is a great example of how we can spider off from our initial Google results to gather even more intelligence using Perl automation. Let's take a look at some sample outputs from this program when used against Walmart.com: root@wnld960:~# perl linkedIn.pl Walmart Buyer : http://www.linkedin.com/title/buyer/at-walmart/        Jason Kloster : http://www.linkedin.com/in/jasonkloster       Rajiv Ahirwal : http://www.linkedin.com/in/rajivahirwal ... Store manager : http://www.linkedin.com/title/store%2Bmanager/at-walmart/        Benjamin Hunt 13k+ (LION) #1 Connected Leader at Walmart : http://www.linkedin.com/in/benjaminhunt01 ... Shift manager : http://www.linkedin.com/title/shift%2Bmanager/at-walmart/        Frank Burns : http://www.linkedin.com/pub/frank-burns/24/83b/285 ... Assistant store manager : http://www.linkedin.com/title/assistant%2Bstore%2Bmanager/at-walmart/        John Cole : http://www.linkedin.com/pub/john-cole/67/392/b39        Crystal Herrera : http://www.linkedin.com/pub/crystal-herrera/92/74a/97b root@wnld960:~# The preceding (trimmed) output provided some great insight into employee positions, and even real employees in those positions of the target, with a simple call to one script. All of this information is publicly available information and we are not directly attacking Walmart or its employees; we are just using this as an example of intelligence-gathering techniques during a penetration test using Perl programming. This information can further be used for reporting, and we can even extend this data into other areas of research. For instance, we can easily follow the LinkedIn links with LWP::UserAgent and pull even more data from the publicly available LinkedIn profiles. This data, when compared to Google+ profile data and simple Google searches, should help in providing a background to create a more believable pretext for social engineering. Now, let's see if we can use Google to search more social media websites for information on our client target. Facebook We can easily argue that Facebook is one of the largest social networking sites around during the writing of this book. Facebook can easily return a large amount of data about a person, and we don't even have to go to the site to get it! We can easily extend our reach into the Web with the gathered employee names, from our previous code, by searching Google using the site:faceboook.com parameter and the exact same syntax as from the first example in the Google section of the E-mail address gathering section. The following are a few simple Google dorks that can possibly reveal information about our client target: site:facebook.com "manager at target" site:facebook.com "ceo at target" site:facebook.com "owner of target" site:facebook.com "experience at target" This information can return customer and employee criticism that can be used for a wide array of penetration-testing purposes, including social engineering pretexting. We can narrow our focus even further by adding other keywords and strings from our previously gathered intelligence, such as city names, company names, and more. Just about anything returned can be compiled into a unique wordlist for password cracking, and contrasted with the known data with Digital Credential Analysis (DCA). Domain Name Services Domain Name Services (DNS) are used to translate IP addresses into hostnames so that we can use alphanumeric addresses instead of IP addresses for websites or services. It makes our lives a lot easier when typing in a URL with a name rather than a 4-byte numerical value. Any client target can potentially have full control over their naming services. DNS A records can be assigned to any IP address. We can easily write our own record with domain control for an IPv4 class A address, such as 10.0.0.1, which is commonly done for an internal network to allow its users to easily connect to different internal services. The Whois query Sometimes, when we can get an IP address for a client target, we can pass this IP address to the Whois database, and in return, we can get a range of IP addresses in which our IP lies and the organization that owns the range. If the organization is our target, then we now know a range of IP addresses pointing directly to their resources. Usually, this information is given during a penetration test, and the limitations on the lengths that we are allowed to go to for IP ranges are set so that we can be limited simply to reporting. Let's use Perl and the Net::Whois::Raw module to interact with the American Registry for Internet Numbers (ARIN) database for an IP address: #!/usr/bin/perl -w use strict; use Net::Whois::Raw; die "Usage: perl netRange.pl <IP Address>" unless $ARGV[0]; foreach(split(/n/,whois(shift))){ print $_,"n" if(m/^(netrange|orgname)/i); } The preceding code, when run, should produce information about the network range and organization name that owns the range. It is very simple, and it can be compared to calling the whois program form the Linux command line. If we were to script this to run through a number of different IP addresses and run the Whois query against each one, we could be violating the terms of service set by ARIN. Let's test it and see what we get with a random IP address: root@wnld960:~# perl whois.pl 198.123.2.22 NetRange:       198.116.0.0 - 198.123.255.255 OrgName:       National Aeronautics and Space Administration root@wnld960:~# This is the output from our Perl program, which reveals an IP range that can belong to the organization listed. If this fails, and we need to find more than one hostname owned by our client target, we can try a brute force method that simply checks our name servers; we will do just that in the next section. The DIG query DIG stands for domain information groper and is a utility to do just that using DNS queries. The DIG Linux utility has actually replaced the older host and nslookup. In making these queries, one thing to note is that when we don't specify a name server to use, the DIG utility will simply use the Linux OS default resolver. We can, however, pass a name server to DIG; we will cover this in the upcoming section, Zone transfers. There is a nice object-oriented Perl module for DIG that we will examine, which is called Net::DNS::Dig. Let's quickly look at an example to query our DNS with this module: #!/usr/bin/perl -w use Net::DNS::Dig; use strict; my $dig = new Net::DNS::Dig(); my $dom = shift or die "Usage: perl dig.pl <domain>"; my $dobj = $dig->for($dom, 'A'); # print $dobj->sprintf; # print entire dig query response print "CODE: ",$dobj->rcode(1),"n"; # Dig Response Code my %mx = Net::DNS::Dig->new()->for($dom,'MX')->rdata(); while(my($val,$server) = each(%mx)){ print "MX: ",$server," - ",$val,"n"; } The preceding code is simple. We create a DIG object $dig and call the for() method, passing the domain name we pulled by shifting the command-line arguments and types for A records. We print the returned response with sprintf(), and then the response code alone with the rcode() method. Finally, we create a hash object %mx from the rdata() method. We pass the rdata() object returned from making a new Net::DNS::Dig object, and call the for() method on it with a type of MX for the mail server. Let's try this against a domain and see what is returned: root@wnld960:~# perl dig.pl weaknetlabs.com ; <<>> Net::DNS::Dig 0.12 <<>> -t a weaknetlabs.com. ;; ;; Got answer. ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34071 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;weaknetlabs.com.               IN     A ;; ANSWER SECTION: weaknetlabs.com.       300    IN     A       198.144.36.192 ;; Query time: 118 ms ;; SERVER: 75.75.76.76# 53(75.75.76.76) ;; WHEN: Mon May 19 18:26:31 2014 ;; MSG SIZE rcvd: 49 -- XFR size: 2 records CODE: NOERROR MX: mailstore1.secureserver.net - 10 MX: smtp.secureserver.net – 0 The output is just as expected. Everything above the line starting with CODE is the response from making the DIG query. CODE is returned from the rcode() method. Since we passed a true value to rcode(), we got a string type, NOERROR, returned. Next, we printed the key and value pairs of the %mx Perl hash, which displayed our target's e-mail server names. Brute force enumeration Keeping the previous lesson in mind, and knowing that Linux offers a great wealth of networking utilities, we might be inclined to write our own DNS brute force tool to enumerate any possible A records that our client target could have made prior to our penetration test. Let's take a quick look at the nslookup utility we can use to check if a record exists: trevelyn@wnld960:~$ nslookup admin.warcarrier.org Server:         75.75.76.76 Address:       75.75.76.76#53 Non-authoritative answer: Name:   admin.warcarrier.org Address: 10.0.0.1 trevelyn@wnld960:~$ nslookup admindoesntexist.warcarrier.org Server:         75.75.76.76 Address:        75.75.76.76#53 ** server can't find admindoesntexist.warcarrier.org: NXDOMAIN trevelyn@wnld960:~$ This is the output of two calls to nslookup, the networking utility used for returning IP addresses of hostnames, and vice versa. The first A record check was successful, and the second, the admindoesntexist subdomain, was not. We can easily see from the output of this program how we can parse it to check whether the subdomain exists. We can also see from the two subdomains that we can use a simple word list of commonly used subdomains for efficiency, before trying many possible combinations. A lot of intelligence gathering might have already been done for you by search engines such as Google. In fact, the keyword search site: can return more than just the www subdomains. If we broaden our num= URL GET parameter and loop through all possible results by incrementing the start= parameter, we can potentially get results from other subdomains of our target. Now that we have seen the basic query for a subdomain, let's turn our focus to use Perl and a new Perl module, Net::DNS, to enumerate a few subdomains: #!/usr/bin/perl -w use strict; use Net::DNS; my $dns = Net::DNS::Resolver->new; my @subDomains = ("admin","admindoesntexist","www","mail","download","gateway"); my $usage = "perl domainbf.pl <domain name>"; my $domain = shift or die $usage; my $total = 0; dns($_) foreach(@subDomains); print $total," records testedn"; sub dns{ # search sub domains: $total++; # record count my $hn = shift.".".$domain; # construct hostname my $dnsLookup = $dns->search($hn); if($dnsLookup){ # successful lookup my $t=0; foreach my $ip ($dnsLookup->answer){    return unless $ip->type eq "A" and $t<1; # A records    print $hn,": ",$ip->address,"n"; # just the IP    $t++; } } return; } The preceding Perl program loops through the @domains array and calls the dns() subroutine on each, which returns or prints a successful query. The $t integer token is used for subdomains, which has several identical records to avoid repetition in the program's output. After this, we simply print the total of the records tested. This program can be easily modified to open a word list file, and we can loop through each by passing them to the dns() subroutine, with something similar to the following: open(FLE,"file.txt"); while(<FLE>){ dns($_); } Zone transfers As we have seen with an A record, the admin.warcarrier.org entry provided us with some insight as to the IP range of the internal network, or the class A address 10.0.0.1. Sometimes, when a client target is controlling and hosting their own name servers, they accidentally allow DNS zone transfers from their name servers into public name servers, providing the attacker with information where the target's resources are. Let's use the Linux host utility to check for a DNS zone transfer: [trevelyn@shell ~]$ host -la warcarrier.org beth.ns.cloudflare.com Trying "warcarrier.org" Using domain server: Name: beth.ns.cloudflare.com Address: 2400:cb00:2049:1::adf5:3a67#53 Aliases: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20461 ;; flags: qr aa; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ; warcarrier.org.           IN     AXFR ;; ANSWER SECTION: warcarrier.org.     300     IN     SOA     beth.ns.cloudflare.com. warcarrier.org. beth.ns.cloudflare.com. 2014011513 18000 3600 86400 1800 warcarrier.org.     300     IN     NS     beth.ns.cloudflare.com. warcarrier.org.     300     IN     NS     hank.ns.cloudflare.com. warcarrier.org.     300     IN     A      50.97.177.66 admin.warcarrier.org. 300     IN     A       10.0.0.1 gateway.warcarrier.org. 300 IN   A       10.0.0.124 remote.warcarrier.org. 300     IN     A       10.0.0.15 partner.warcarrier.org. 300 IN   CNAME   warcarrier.weaknetlabs.com. calendar.warcarrier.org. 300 IN   CNAME   login.secureserver.net. direct.warcarrier.org. 300 IN   CNAME   warcarrier.org. warcarrier.org.     300     IN     SOA     beth.ns.cloudflare.com. warcarrier.org. beth.ns.cloudflare.com. 2014011513 18000 3600 86400 1800 Received 401 bytes from 2400:cb00:2049:1::adf5:3a67#53 in 56 ms [trevelyn@shell ~]$ As we see from the output of the host command, we have found a successful DNS zone transfer, which provided us with even more hostnames used by our client target. This attack has provided us with a few CNAME records, which are used as aliases to other servers owned or used by our target, the subnet (class A) IP addresses used by the target, and even the name servers used. We can also see that the default name, direct, used by CloudFlare.com is still set for the cloud service to allow connections directly to the IP of warcarrier.org, which we can use to bypass the cloud service. The host command requires the name server, in our case beth.ns.cloudflare.com, before performing the transfer. What this means for us is that we will need the name server information before querying for a potential DNS zone transfer in our Perl programs. Let's see how we can use Net::DNS for the entire process: #!/usr/bin/perl -w use strict; use Net::DNS; my $usage = "perl dnsZt.pl <domain name>"; die $usage unless my $dom = shift; my $res = Net::DNS::Resolver->new; # dns object my $query = $res->query($dom,"NS"); # query method call for nameservers if($query){ # query of NS was successful foreach my $rr (grep{$_->type eq 'NS'} $query->answer){ $res->nameservers($rr->nsdname); # set the name server print "[>] Testing NS Server: ".$rr->nsdname."n"; my @subdomains = $res->axfr($dom); if ($#subdomains > 0){    print "[!] Successful zone transfer:n";    foreach (@subdomains){    print $_->name."n"; # returns a Net::DNS::RR object    } }else{ # 0 returned domains    print "[>] Transfer failed on " . $rr->nsdname . "n"; } } }else{ # Something went wrong: warn "query failed: ", $res->errorstring,"n"; } The preceding program that uses the Net::DNS Perl module will first query for the name servers used by our target and then test the DNS zone transfer for each target. The grep() function returns a list to the foreach() loop of all name servers (NS) found. The foreach() loop then simply attempts the DNS zone transfer (AXFR) and returns the results if the array is larger than zero elements. Let's test the output on our client target: [trevelyn@shell ~]$ perl dnsZt.pl warcarrier.org [>] Testing NS Server: hank.ns.cloudflare.com [!] Successful zone transfer: warcarrier.org warcarrier.org admin.warcarrier.org gateway.warcarrier.org remote.warcarrier.org partner.warcarrier.org calendar.warcarrier.org direct.warcarrier.org [>] Testing NS Server: beth.ns.cloudflare.com [>] Transfer failed on beth.ns.cloudflare.com [trevelyn@shell ~]$ The preceding (trimmed) output is a successful DNS zone transfer on one of the name servers used by our client target. Traceroute With knowledge of how to glean hostnames and IP addresses from simple queries using Perl, we can take the OSINT a step further and trace our route to the hosts to see what potential target-owned hardware can intercept or relay traffic. For this task, we will use the Net::Traceroute Perl module. Let's take a look at how we can get the IP host information from relaying hosts between us and our target, using this Perl module and the following code: #!/usr/bin/perl -w use strict; use Net::Traceroute; my $dom = shift or die "Usage: perl tracert.pl <domain>"; print "Tracing route to ",$dom,"n"; my $tr = Net::Traceroute->new(host=>$dom,use_tcp=>1); for(my$i=1;$i<=$tr->hops;$i++){        my $hop = $tr->hop_query_host($i,0);        print "IP: ",$hop," hop time: ",$tr->hop_query_time($i,0),               "ms hop status: ",$tr->hop_query_stat($i,0),                " query count: ",$tr->hop_queries($i),"n" if($hop); } In the preceding Perl program, we used the Net::Traceroute Perl module to perform a trace route to the domain given by a command-line argument. The module must be used by first calling the new() method, which we do when defining $tr as a query object. We tell the trace route object $tr that we want to use TCP and also pass the host, which we shift from the command-line arguments. We can pass a lot more parameters to the new() method, one of which is debug=>9 to debug our trace route. A full list can be obtained from the CPAN Search page of the Perl module that can be accessed at http://search.cpan.org/~hag/Net-Traceroute/Traceroute.pm. The hops method is used when constructing the for() loop, which returns an integer value of the hop count. We then assign this to $i and loop through all hop and print statistics, using the methods hop_query_host for the IP address of the host, hop_query_time for the time taken to reach the host, and hop_query_stat that returns the status of the query as an integer value (on our lab machines, it is returned in milliseconds), which can be mapped to the export list of Net::Traceroute according to the module's documentation. Now, let's test this trace route program with a domain and check the output: root@wnld960:~# sudo perl tracert.pl weaknetlabs.com Tracing route to weaknetlabs.com IP: 10.0.0.1 hop time: 0.724ms hop status: 0 query count: 3 IP: 68.85.73.29 hop time: 14.096ms hop status: 0 query count: 3 IP: 69.139.195.37 hop time: 19.173ms hop status: 0 query count: 3 IP: 68.86.94.189 hop time: 31.102ms hop status: 0 query count: 3 IP: 68.86.87.170 hop time: 27.42ms hop status: 0 query count: 3 IP: 50.242.150.186 hop time: 27.808ms hop status: 0 query count: 3 IP: 144.232.20.144 hop time: 33.688ms hop status: 0 query count: 3 IP: 144.232.25.30 hop time: 38.718ms hop status: 0 query count: 3 IP: 144.232.229.46 hop time: 31.242ms hop status: 0 query count: 3 IP: 144.232.9.82 hop time: 99.124ms hop status: 0 query count: 3 IP: 198.144.36.192 hop time: 30.964ms hop status: 0 query count: 3 root@wnld960:~# The output from tracert.pl is just as we expected using the traceroute program of the Linux shell. This functionality can be easily built right into our port scanner application. Shodan Shodan is an online resource that can be used for hardware searching within a specific domain. For instance, a search for hostname:<domain> will provide all the hardware entities found within this specific domain. Shodan is both a public and open source resource for intelligence. Harnessing the full power of Shodan and returning a multipage query is not free. For the examples in this article, the first page of the query results, which are free, were sufficient to provide a suitable amount of information. The returned output is XML, and Perl has some great utilities to parse XML. Luckily, for the purpose of our example, Shodan offers an example query for us to use as export_sample.xml. This XML file contains only one node per host, labeled host. This node contains attributes for the corresponding host and we will use the XML::LibXML::Node class from the XML::LibXML::Node Perl module. First, we will download the XML file and use XML::LibXML to open the local file with the parse_file() method, as shown in the following code: #!/usr/bin/perl -w use strict; use XML::LibXML; my $parser = XML::LibXML->new(); my $doc = $parser->parse_file("export_sample.xml"); foreach my $host ($doc->findnodes('/shodan/host')) { print "Host Found:n"; my @attribs = $host->attributes('/shodan/host'); foreach my $host (@attribs){ # get host attributes print $host =~ m/([^=]+)=.*/," => "; print $host =~ m/.*"([^"]+)"/,"n"; } # next print "nn"; } The preceding Perl program will open the export_sample.xml file and navigate through the host nodes using the simple xpath of /shodan/host. For each <host> node, we call the attribute's method from the XML::LibXML::Node class, which returns an array of all attributes with information such as the IP address, hostname, and more. We then run a regular expression pattern on the $host string to parse out the key, and again with another regexp to get the value. Let's see how this returns data from our sample XML file from ShodanHQ.com: root@wnld960:~#perl shodan.pl Host Found: hostnames => internetdevelopment.ro ip => 109.206.71.21 os => Linux recent 2.4 port => 80 updated => 16.03.2010 Host Found: ip => 113.203.71.21 os => Linux recent 2.4 port => 80 updated => 16.03.2010 Host Found: hostnames => ip-173-201-71-21.ip.secureserver.net ip => 173.201.71.21 os => Linux recent 2.4 port => 80 updated => 16.03.2010 The preceding output is from our shodan.pl Perl program. It loops through all host nodes and prints the attributes. As we can see, Shodan can provide us with some very useful information that we can possibly use to exploit later in our penetration testing. It's also easy to see, without going into elementary Perl coding examples, that we can find exactly what we are looking for from an XML object's attributes using this simple method. We can use this code for other resources as well. More intelligence Gaining information about the actual physical address is also important during a penetration test. Sure, this is public information, but where do we find it? Well, the PTES describes how most states require a legal entity of a company to register with the State Division, which can provide us with a one-stop go-to place for the physical address information, entity ID, service of process agent information, and more. This can be very useful information on our client target. If obtained, we can extend this intelligence by finding out more about the property owners for physical penetration testing and social engineering by checking the city/county's department of land records, real estate, deeds, or even mortgages. All of this data, if hosted on the Web, can be gathered by automated Perl programs, as we did in the example sections of this article using LWP::UserAgent. Summary As we have seen, being creative with our information-gathering techniques can really shine with the power of regular expressions and the ability to spider links. As we learned in the introduction, it's best to do an automated OSINT gathering process along with a manual process because both processes can reveal information that one might have missed. Resources for Article: Further resources on this subject: Ruby and Metasploit Modules [article] Linux Shell Scripting – various recipes to help you [article] Linux Shell Script: Logging Tasks [article]
Read more
  • 0
  • 0
  • 16836
article-image-how-vector-features-are-displayed
Packt
30 Dec 2014
23 min read
Save for later

How Vector Features are Displayed

Packt
30 Dec 2014
23 min read
In this article by Erik Westra, author of the book Building Mapping Applications with QGIS, we will learn how QGIS symbols and renderers are used to control how vector features are displayed on a map. In addition to this, we will also learn saw how symbol layers work. The features within a vector map layer are displayed using a combination of renderer and symbol objects. The renderer chooses which symbol is to be used for a given feature, and the symbol does the actual drawing. There are three basic types of symbols defined by QGIS: Marker symbol: This displays a point as a filled circle Line symbol: This draws a line using a given line width and color Fill symbol: This draws the interior of a polygon with a given color These three types of symbols are implemented as subclasses of the qgis.core.QgsSymbolV2 class: qgis.core.QgsMarkerSymbolV2 qgis.core.QgsLineSymbolV2 qgis.core.QgsFillSymbolV2 You might be wondering why all these classes have "V2" in their name. This is a historical quirk of QGIS. Earlier versions of QGIS supported both an "old" and a "new" system of rendering, and the "V2" naming refers to the new rendering system. The old rendering system no longer exists, but the "V2" naming continues to maintain backward compatibility with existing code. Internally, symbols are rather complex, using "symbol layers" to draw multiple elements on top of each other. In most cases, however, you can make use of the "simple" version of the symbol. This makes it easier to create a new symbol without having to deal with the internal complexity of symbol layers. For example: symbol = QgsMarkerSymbolV2.createSimple({'width' : 1.0,                                        'color' : "255,0,0"}) While symbols draw the features onto the map, a renderer is used to choose which symbol to use to draw a particular feature. In the simplest case, the same symbol is used for every feature within a layer. This is called a single symbol renderer, and is represented by the qgis.core.QgsSingleSymbolRenderV2class. Other possibilities include: Categorized symbol renderer (qgis.core.QgsCategorizedSymbolRendererV2): This renderer chooses a symbol based on the value of an attribute. The categorized symbol renderer has a mapping from attribute values to symbols. Graduated symbol renderer (qgis.core.QgsGraduatedSymbolRendererV2): This type of renderer has a series of ranges of attribute values, and maps each range to an appropriate symbol. Using a single symbol renderer is very straightforward: symbol = ... renderer = QgsSingleSymbolRendererV2(symbol) layer.setRendererV2(renderer) To use a categorized symbol renderer, you first define a list of qgis.core.QgsRendererCategoryV2 objects, and then use that to create the renderer. For example: symbol_male = ... symbol_female = ...   categories = [] categories.append(QgsRendererCategoryV2("M", symbol_male, "Male")) categories.append(QgsRendererCategoryV2("F", symbol_female,                                        "Female"))   renderer = QgsCategorizedSymbolRendererV2("", categories) renderer.setClassAttribute("GENDER") layer.setRendererV2(renderer) Notice that the QgsRendererCategoryV2 constructor takes three parameters: the desired value, the symbol to use, and the label used to describe that category. Finally, to use a graduated symbol renderer, you define a list of qgis.core.QgsRendererRangeV2 objects and then use that to create your renderer. For example: symbol1 = ... symbol2 = ...   ranges = [] ranges.append(QgsRendererRangeV2(0, 10, symbol1, "Range 1")) ranges.append(QgsRendererRange(11, 20, symbol2, "Range 2"))   renderer = QgsGraduatedSymbolRendererV2("", ranges) renderer.setClassAttribute("FIELD") layer.setRendererV2(renderer) Working with symbol layers Internally, symbols consist of one or more symbol layers that are displayed one on top of the other to draw the vector feature: The symbol layers are drawn in the order in which they are added to the symbol. So, in this example, Symbol Layer 1 will be drawn before Symbol Layer 2. This has the effect of drawing the second symbol layer on top of the first. Make sure you get the order of your symbol layers correct, or you may find a symbol layer completely obscured by another layer. While the symbols we have been working with so far have had only one layer, there are some clever tricks you can perform with multilayer symbols. When you create a symbol, it will automatically be initialized with a default symbol layer. For example, a line symbol (an instance of QgsLineSymbolV2) will be created with a single layer of type QgsSimpleLineSymbolLayerV2. This layer is used to draw the line feature onto the map. To work with symbol layers, you need to remove this default layer and replace it with your own symbol layer or layers. For example: symbol = QgsSymbolV2.defaultSymbol(layer.geometryType()) symbol.deleteSymbolLayer(0) # Remove default symbol layer.   symbol_layer_1 = QgsSimpleFillSymbolLayerV2() symbol_layer_1.setFillColor(QColor("yellow"))   symbol_layer_2 = QgsLinePatternFillSymbolLayer() symbol_layer_2.setLineAngle(30) symbol_layer_2.setDistance(2.0) symbol_layer_2.setLineWidth(0.5) symbol_layer_2.setColor(QColor("green"))   symbol.appendSymbolLayer(symbol_layer_1) symbol.appendSymbolLayer(symbol_layer_2) The following methods can be used to manipulate the layers within a symbol: symbol.symbolLayerCount(): This returns the number of symbol layers within this symbol symbol.symbolLayer(index): This returns the given symbol layer within the symbol. Note that the first symbol layer has an index of zero. symbol.changeSymbolLayer(index, symbol_layer): This replaces a given symbol layer within the symbol symbol.appendSymbolLayer(symbol_layer): This appends a new symbol layer to the symbol symbol.insertSymbolLayer(index, symbol_layer): This inserts a symbol layer at a given index symbol.deleteSymbolLayer(index): This removes the symbol layer at the given index Remember that to use the symbol once you've created it, you create an appropriate renderer and then assign that renderer to your map layer. For example: renderer = QgsSingleSymbolRendererV2(symbol) layer.setRendererV2(renderer) The following symbol layer classes are available for you to use: PyQGIS class Description Example QgsSimpleMarkerSymbolLayerV2 This displays a point geometry as a small colored circle.   QgsEllipseSymbolLayerV2 This displays a point geometry as an ellipse.   QgsFontMarkerSymbolLayerV2 This displays a point geometry as a single character. You can choose the font and character to be displayed.   QgsSvgMarkerSymbolLayerV2 This displays a point geometry using a single SVG format image.   QgsVectorFieldSymbolLayer This displays a point geometry by drawing a displacement line. One end of the line is the coordinate of the point, while the other end is calculated using attributes of the feature.   QgsSimpleLineSymbolLayerV2 This displays a line geometry or the outline of a polygon geometry using a line of a given color, width, and style.   QgsMarkerLineSymbolLayerV2 This displays a line geometry or the outline of a polygon geometry by repeatedly drawing a marker symbol along the length of the line.   QgsSimpleFillSymbolLayerV2 This displays a polygon geometry by filling the interior with a given solid color and then drawing a line around the perimeter.   QgsGradientFillSymbolLayerV2 This fills the interior of a polygon geometry using a color or grayscale gradient.   QgsCentroidFillSymbolLayerV2 This draws a simple dot at the centroid of a polygon geometry.   QgsLinePatternFillSymbolLayer This draws the interior of a polygon geometry using a repeated line. You can choose the angle, width, and color to use for the line.   QgsPointPatternFillSymbolLayer This draws the interior of a polygon geometry using a repeated point.   QgsSVGFillSymbolLayer This draws the interior of a polygon geometry using a repeated SVG format image.   These predefined symbol layers, either individually or in various combinations, give you enormous flexibility in how features are to be displayed. However, if these aren't enough for you, you can also implement your own symbol layers using Python. We will look at how this can be done later in this article. Combining symbol layers By combining symbol layers, you can achieve a range of complex visual effects. For example, you could combine an instance of QgsSimpleMarkerSymbolLayerV2 with a QgsVectorFieldSymbolLayer to display a point geometry using two symbols at once: One of the main uses of symbol layers is to draw different LineString or PolyLine symbols to represent different types of roads. For example, you can draw a complex road symbol by combining multiple symbol layers, like this: This effect is achieved using three separate symbol layers: Here is the Python code used to generate the above map symbol: symbol = QgsLineSymbolV2.createSimple({}) symbol.deleteSymbolLayer(0) # Remove default symbol layer.   symbol_layer = QgsSimpleLineSymbolLayerV2() symbol_layer.setWidth(4) symbol_layer.setColor(QColor("light gray")) symbol_layer.setPenCapStyle(Qt.FlatCap) symbol.appendSymbolLayer(symbol_layer)   symbol_layer = QgsSimpleLineSymbolLayerV2() symbol_layer.setColor(QColor("black")) symbol_layer.setWidth(2) symbol_layer.setPenCapStyle(Qt.FlatCap) symbol.appendSymbolLayer(symbol_layer)   symbol_layer = QgsSimpleLineSymbolLayerV2() symbol_layer.setWidth(1) symbol_layer.setColor(QColor("white")) symbol_layer.setPenStyle(Qt.DotLine) symbol.appendSymbolLayer(symbol_layer) As you can see, you can set the line width, color, and style to create whatever effect you want. As always, you have to define the layers in the correct order, with the back-most symbol layer defined first. By combining line symbol layers in this way, you can create almost any type of road symbol that you want. You can also use symbol layers when displaying polygon geometries. For example, you can draw QgsPointPatternFillSymbolLayer on top of QgsSimpleFillSymbolLayerV2 to have repeated points on top of a simple filled polygon, like this: Finally, you can make use of transparency to allow the various symbol layers (or entire symbols) to blend into each other. For example, you can create a pinstripe effect by combining two symbol layers, like this: symbol = QgsFillSymbolV2.createSimple({}) symbol.deleteSymbolLayer(0) # Remove default symbol layer.   symbol_layer = QgsGradientFillSymbolLayerV2() symbol_layer.setColor2(QColor("dark gray")) symbol_layer.setColor(QColor("white")) symbol.appendSymbolLayer(symbol_layer)   symbol_layer = QgsLinePatternFillSymbolLayer() symbol_layer.setColor(QColor(0, 0, 0, 20)) symbol_layer.setLineWidth(2) symbol_layer.setDistance(4) symbol_layer.setLineAngle(70) symbol.appendSymbolLayer(symbol_layer) The result is quite subtle and visually pleasing: In addition to changing the transparency for a symbol layer, you can also change the transparency for the symbol as a whole. This is done by using the setAlpha() method, like this: symbol.setAlpha(0.3) The result looks like this: Note that setAlpha() takes a floating point number between 0.0 and 1.0, while the transparency of a QColor object, like the ones we used earlier, is specified using an alpha value between 0 and 255. Implementing symbol layers in Python If the built-in symbol layers aren't flexible enough for your needs, you can implement your own symbol layers using Python. To do this, you create a subclass of the appropriate type of symbol layer (QgsMarkerSymbolLayerV2, QgsLineSymbolV2, or QgsFillSymbolV2) and implement the various drawing methods yourself. For example, here is a simple marker symbol layer that draws a cross for a Point geometry: class CrossSymbolLayer(QgsMarkerSymbolLayerV2):    def __init__(self, length=10.0, width=2.0):        QgsMarkerSymbolLayerV2.__init__(self)        self.length = length        self.width = width   def layerType(self):        return "Cross"   def properties(self):        return {'length' : self.length,               'width' : self.width}      def clone(self): return CrossSymbolLayer(self.length, self.width)      def startRender(self, context):        self.pen = QPen()        self.pen.setColor(self.color()) self.pen.setWidth(self.width)      def stopRender(self, context): self.pen = None   def renderPoint(self, point, context):        left = point.x() - self.length        right = point.x() + self.length        bottom = point.y() - self.length        top = point.y() + self.length          painter = context.renderContext().painter()        painter.setPen(self.pen)        painter.drawLine(left, bottom, right, top)        painter.drawLine(right, bottom, left, top) Using this custom symbol layer in your code is straightforward: symbol = QgsMarkerSymbolV2.createSimple({}) symbol.deleteSymbolLayer(0)   symbol_layer = CrossSymbolLayer() symbol_layer.setColor(QColor("gray"))   symbol.appendSymbolLayer(symbol_layer) Running this code will draw a cross at the location of each point geometry, as follows: Of course, this is a simple example, but it shows you how to use custom symbol layers implemented in Python. Let's now take a closer look at the implementation of the CrossSymbolLayer class, and see what each method does: __init__(): Notice how the __init__ method accepts parameters that customize the way the symbol layer works. These parameters, which should always have default values assigned to them, are the properties associated with the symbol layer. If you want to make your custom symbol available within the QGIS Layer Properties window, you will need to register your custom symbol layer and tell QGIS how to edit the symbol layer's properties. We will look at this shortly. layerType(): This method returns a unique name for your symbol layer. properties(): This should return a dictionary that contains the various properties used by this symbol layer. The properties returned by this method will be stored in the QGIS project file, and used later to restore the symbol layer. clone(): This method should return a copy of the symbol layer. Since we have defined our properties as parameters to the __init__ method, implementing this method simply involves creating a new instance of the class and copying the properties from the current symbol layer to the new instance. startRender(): This method is called before the first feature in the map layer is rendered. This can be used to define any objects that will be required to draw the feature. Rather than creating these objects each time, it is more efficient (and therefore faster) to create them only once to render all the features. In this example, we create the QPen object that we will use to draw the Point geometries. stopRender(): This method is called after the last feature has been rendered. This can be used to release the objects created by the startRender() method. renderPoint(): This is where all the work is done for drawing point geometries. As you can see, this method takes two parameters: the point at which to draw the symbol, and the rendering context (an instance of QgsSymbolV2RenderContext) to use for drawing the symbol. The rendering context provides various methods for accessing the feature being displayed, as well as information about the rendering operation, the current scale factor, etc. Most importantly, it allows you to access the PyQt QPainter object needed to actually draw the symbol onto the screen. The renderPoint() method is only used for symbol layers that draw point geometries. For line geometries, you should implement the renderPolyline() method, which has the following signature: def renderPolyline(self, points, context): The points parameter will be a QPolygonF object containing the various points that make up the LineString, and context will be the rendering context to use for drawing the geometry. If your symbol layer is intended to work with polygons, you should implement the renderPolygon() method, which looks like this: def renderPolygon(self, outline, rings, context): Here, outline is a QPolygonF object that contains the points that make up the exterior of the polygon, and rings is a list of QPolygonF objects that define the interior rings or "holes" within the polygon. As always, context is the rendering context to use when drawing the geometry. A custom symbol layer created in this way will work fine if you just want to use it within your own external PyQGIS application. However, if you want to use a custom symbol layer within a running copy of QGIS, and in particular, if you want to allow end users to work with the symbol layer using the Layer Properties window, there are some extra steps you will have to take, which are as follows: If you want the symbol to be visually highlighted when the user clicks on it, you will need to change your symbol layer's renderXXX() method to see if the feature being drawn has been selected by the user, and if so, change the way it is drawn. The easiest way to do this is to change the geometry's color. For example: if context.selected():    color = context.selectionColor() else:    color = self.color To allow the user to edit the symbol layer's properties, you should create a subclass of QgsSymbolLayerV2Widget, which defines the user interface to edit the properties. For example, a simple widget for the purpose of editing the length and width of a CrossSymbolLayer can be defined as follows: class CrossSymbolLayerWidget(QgsSymbolLayerV2Widget):    def __init__(self, parent=None):        QgsSymbolLayerV2Widget.__init__(self, parent)        self.layer = None          self.lengthField = QSpinBox(self)        self.lengthField.setMinimum(1)        self.lengthField.setMaximum(100)        self.connect(self.lengthField,                      SIGNAL("valueChanged(int)"),                      self.lengthChanged)          self.widthField = QSpinBox(self)        self.widthField.setMinimum(1)        self.widthField.setMaximum(100)        self.connect(self.widthField,                      SIGNAL("valueChanged(int)"),                      self.widthChanged)          self.form = QFormLayout()        self.form.addRow('Length', self.lengthField)        self.form.addRow('Width', self.widthField)          self.setLayout(self.form)      def setSymbolLayer(self, layer):        if layer.layerType() == "Cross":            self.layer = layer            self.lengthField.setValue(layer.length)            self.widthField.setValue(layer.width)      def symbolLayer(self):        return self.layer      def lengthChanged(self, n):        self.layer.length = n        self.emit(SIGNAL("changed()"))      def widthChanged(self, n):        self.layer.width = n        self.emit(SIGNAL("changed()")) We define the contents of our widget using the standard __init__() initializer. As you can see, we define two fields, lengthField and widthField, which let the user change the length and width properties respectively, for our symbol layer. The setSymbolLayer() method tells the widget which QgsSymbolLayerV2 object to use, while the symbolLayer() method returns the QgsSymbolLayerV2 object this widget is editing. Finally, the two XXXChanged() methods are called when the user changes the value of the fields, allowing us to update the symbol layer's properties to match the value set by the user. Finally, you will need to register your symbol layer. To do this, you create a subclass of QgsSymbolLayerV2AbstractMetadata and pass it to the QgsSymbolLayerV2Registry object's addSymbolLayerType() method. Here is an example implementation of the metadata for our CrossSymbolLayer class, along with the code to register it within QGIS: class CrossSymbolLayerMetadata(QgsSymbolLayerV2AbstractMetadata):    def __init__(self):        QgsSymbolLayerV2AbstractMetadata.__init__(self, "Cross", "Cross marker", QgsSymbolV2.Marker)      def createSymbolLayer(self, properties):        if "length" in properties:            length = int(properties['length'])        else:            length = 10        if "width" in properties:            width = int(properties['width'])        else:            width = 2        return CrossSymbolLayer(length, width)      def createSymbolLayerWidget(self, layer):        return CrossSymbolLayerWidget()   registry = QgsSymbolLayerV2Registry.instance() registry.addSymbolLayerType(CrossSymbolLayerMetadata()) Note that the parameters of QgsSymbolLayerV2AbstractMetadata.__init__() are as follows: The unique name for the symbol layer, which must match the name returned by the symbol layer's layerType() method. A display name for this symbol layer, as shown to the user within the Layer Properties window. The type of symbol that this symbol layer will be used for. The createSymbolLayer() method is used to restore the symbol layer based on the properties stored in the QGIS project file when the project was saved. The createSymbolLayerWidget() method is called to create the user interface widget that lets the user view and edit the symbol layer's properties. Implementing renderers in Python If you need to choose symbols based on more complicated criteria than what the built-in renderers will provide, you can write your own custom QgsFeatureRendererV2 subclass using Python. For example, the following Python code implements a simple renderer that alternates between odd and even symbols as point features are displayed: class OddEvenRenderer(QgsFeatureRendererV2):    def __init__(self): QgsFeatureRendererV2.__init__(self, "OddEvenRenderer")        self.evenSymbol = QgsMarkerSymbolV2.createSimple({})        self.evenSymbol.setColor(QColor("light gray"))        self.oddSymbol = QgsMarkerSymbolV2.createSimple({})        self.oddSymbol.setColor(QColor("black"))        self.n = 0      def clone(self):        return OddEvenRenderer()      def symbolForFeature(self, feature):        self.n = self.n + 1        if self.n % 2 == 0:            return self.evenSymbol        else:            return self.oddSymbol      def startRender(self, context, layer):        self.n = 0        self.oddSymbol.startRender(context)        self.evenSymbol.startRender(context)      def stopRender(self, context):        self.oddSymbol.stopRender(context)        self.evenSymbol.stopRender(context)      def usedAttributes(self):        return [] Using this renderer will cause the various point geometries to be displayed in alternating colors, for example: Let's take a closer look at how this class was implemented, and what the various methods do: __init__(): This is your standard Python initializer. Notice how we have to provide a unique name for the renderer when calling the QgsFeatureRendererV2.__init__() method; this is used to keep track of the various renderers within QGIS itself. clone(): This creates a copy of this renderer. If your renderer uses properties to control how it works, this method should copy those properties into the new renderer object. symbolForFeature(): This returns the symbol to use for drawing the given feature. startRender(): This prepares to start rendering the features within the map layer. As the renderer can make use of multiple symbols, you need to implement this so that your symbols are also given a chance to prepare for rendering. stopRender(): This finishes rendering the features. Once again, you need to implement this so that your symbols can have a chance to clean up once the rendering process has finished. usedAttributes():This method should be implemented to return the list of attributes that the renderer requires if your renderer makes use of feature attributes to choose between the various symbols,. If you wish, you can also implement your own widget that lets the user change the way the renderer works. This is done by subclassing QgsRendererV2Widget and setting up the widget to edit the renderer's various properties in the same way that we implemented a subclass of QgsSymbolLayerV2Widget to edit the properties for a symbol layer. You will also need to provide metadata about your new renderer (by subclassing QgsRendererV2AbstractMetadata) and use the QgsRendererV2Registry object to register your new renderer. If you do this, the user will be able to select your custom renderer for new map layers, and change the way your renderer works by editing the renderer's properties. Summary In this article, we learned how QGIS symbols and renderers are used to control how vector features are displayed on a map. We saw that there are three standard types of symbols: marker symbols for drawing points, line symbols for drawing lines, and fill symbols for drawing the interior of a polygon. We then learned how to instantiate a "simple" version of each of these symbols for use in your programs. We next looked at the built-in renderers, and how these can be used to choose the same symbol for every feature (using the QgsSingleSymbolRenderV2 class), to select a symbol based on the exact value of an attribute (using QgsCategorizedSymbolRendererV2), and to choose a symbol based on a range of attribute values (using the QgsGraduatedSymbolRendererV2 class). We then saw how symbol layers work, and how to manipulate the layers within a symbol. We looked at all the different types of symbol layers built into QGIS, and learned how they can be combined to produce sophisticated visual effects. Finally, we saw how to implement our own symbol layers using Python, and how to write your own renderer from scratch if one of the existing renderer classes doesn't meet your needs. Using these various PyQGIS classes, you have an extremely powerful set of tools at your disposal for displaying vector data within a map. While simple visual effects can be achieved with a minimum of fuss, you can produce practically any visual effect you want using an appropriate combination of built-in or custom-written QGIS symbols and renderers. Resources for Article: Further resources on this subject: Combining Vector and Raster Datasets [article] QGIS Feature Selection Tools [article] Creating a Map [article]
Read more
  • 0
  • 0
  • 4082

article-image-customization-microsoft-dynamics-crm
Packt
30 Dec 2014
24 min read
Save for later

Customization in Microsoft Dynamics CRM

Packt
30 Dec 2014
24 min read
 In this article by Nicolae Tarla, author of the book Dynamics CRM Application Structure, we looked at the basic structure of Dynamics CRM, the modules comprising the application, and what each of these modules contain. Now, we'll delve deeper into the application and take a look at how we can customize it. In this chapter, we will take a look at the following topics: Solutions and publishers Entity elements Entity types Extending entities Entity forms, quick view, and quick create forms Entity views and charts Entity relationships Messages Business rules We'll be taking a look at how to work with each of the elements comprising the sales, service, and marketing modules. We will go through the customization options and see how we can extend the system to fit new business requirements. (For more resources related to this topic, see here.) Solutions When we are talking about customizations for Microsoft Dynamics CRM, one of the most important concepts is the solution. The solution is a container of all the configurations and customizations. This packaging method allows customizers to track customizations, export and reimport them into other environments, as well as group specific sets of customizations by functionality or project cycle. Managing solutions is an aspect that should not be taken lightly, as down the road, a properly designed solution packaging model can help a lot, or an incorrect one can create difficulties. Using solutions is a best practice. While you can implement customizations without using solutions, these customizations will be merged into the base solutions and "you will not be able to export the customizations separately from the core elements of the platform. For a comprehensive description of solutions, you can refer to the MSDN documentation available at http://msdn.microsoft.com/en-gb/library/gg334576.aspx#BKMK_UnmanagedandManagedSolutions. Types of solutions Within the context of Dynamics CRM, there are two types of solutions that you will commonly use while implementing customizations: Unmanaged solutions Managed solutions Each one of these solution types has its own strengths and properties and are recommended to be used in various circumstances. In order to create and manage solutions as well as perform system customizations, the user account must be configured as a system customizer or system administrator. Unmanaged solutions An unmanaged solution is the default state of a newly created solution. A solution is unmanaged for the period of time while customization work is being performed in the context of the solution. You cannot customize a managed solution. An unmanaged solution can be converted to a managed solution by exporting it as managed. When the work is completed and the unmanaged solution is ready to be distributed, it is recommended that you package it as a managed solution for distribution. A managed solution, if configured as such, prevents further customizations to the solution elements. For this reason, solution vendors package their solutions as managed. In an unmanaged solution, the system customizer can perform various tasks, "which include: Adding and removing components Deleting components that allow deletion Exporting and importing the solution as an unmanaged solution Exporting the solution as a managed solution Changes made to the components in an unmanaged solution are also applied to all the unmanaged solutions that include these components. This means that all changes from all unmanaged solutions are also applied to the default solution. Deleting an unmanaged solution results in the removal of the container alone, "while the unmanaged components of the solution remain in the system. Deleting a component in an unmanaged solution results in the deletion of this component from the system. In order to remove a component from an unmanaged solution, the component should be removed from the solution, not deleted. Managed solutions Once work is completed in an unmanaged solution and the solution is ready to be distributed, it can be exported as a managed solution. Packaging a solution as a managed solution presents the following advantages: Solution components cannot be added or removed from a managed solution A managed solution cannot be exported from the environment it was deployed in Deleting a managed solution results in the uninstallation of all the component customizations included with the solution. It also results "in the loss of data associated with the components being deleted. A managed solution cannot be installed in the same organization that contains the unmanaged solution which was used to create it. Within a managed solution, certain components can be configured to allow further customization. Through this mechanism, the managed solution provider can enable future customizations that modify aspects of the solution provided. The guidance provided by Microsoft when working with various solution types states that a solution should be used in an unmanaged state between development and test environments, and it should be exported as a managed solution when it is ready to be deployed to a production environment. Solution properties Besides the solution type, each solution contains a solution publisher. This is a set of properties that allow the solution creators to communicate different information to the solution's users, including ways to contact the publisher for additional support. The solution publisher record will be created in all the organizations where the solution is being deployed. The solution publisher record is also important when releasing an update to an existing solution. Based on this common record and the solution properties, an update solution can be released and deployed on top of an existing solution. Using a published solution also allows us to define a custom prefix for all new custom fields created in the context of the solution. The default format for new custom field names is a new field name. Using a custom publisher, we can change "the "new" prefix to a custom prefix specific to our solution. Solution layering When multiple solutions are deployed in an organization, there are two methods by which the system defines the order in which changes take precedence. These methods are merge and top wins. The user interface elements are merged by default. As such, elements such as the default forms, ribbons, command bars, and site map are merged, and all base elements and new custom elements are rendered. For all other solution components, the top wins approach is taken, where the last solution that makes a customization takes precedence. The top wins approach is also taken into consideration when a subset of customizations is being applied on top of a previously applied customization. The system checks the integrity of all solution exports, imports, and other operations. As such, when exporting a solution, if dependent entities are not included, a warning is presented. The customizer has the option to ignore this warning. When importing a solution, if the dependent entities are missing, the import is halted and it fails. Also, deleting a component from a solution is prevented if dependent entities require it to be present. The default solution Dynamics CRM allows you to customize the system without taking advantage of solutions. By default, the system comes with a solution. This is an unmanaged solution, and all system customizations are applied to it by default. The default solution includes all the default components and customizations defined within Microsoft Dynamics CRM. This solution defines the default application behavior. Most of the components in this solution can be further customized. "This solution includes all the out-of-the-box customizations. Also, customizations applied through unmanaged solutions are being merged into the default solution. Entity elements Within a solution, we work with various entities. In Dynamics CRM, there are three main entity types: System entities Business entities Custom entities Each entity is composed of various attributes, while each attribute is defined as a value with a specific data type. We can consider an entity to be a data table. Each "row represents and entity record, while each column represents an entity attribute. As with any table, each attribute has specific properties that define its data type. The system entities in Dynamics CRM are used internally by the application and "are not customizable. Also, they cannot be deleted. As a system customizer or developer, we will work mainly with business management entities and custom entities. Business management entities are the default entities that come with the application. Some are customizable and can "be extended as required. Custom entities are all net new entities that are created "as part of our system customizations. The aspects related to customizing an entity include renaming the entity; modifying, adding, or removing entity attributes; or changing various settings and properties. Let's take a look at all these in detail. Renaming an entity One of the ways to customize an entity is by renaming it. In the general properties "of the entity, the field's display name allows us to change the name of an entity. "The plural name can also be updated accordingly. When renaming an entity, make sure that all the references and messages are updated to reflect the new entity name. Views, charts, messages, business rules, hierarchy settings, and even certain fields can reference the original name, and they should be updated to reflect the new name assigned to the entity. The display name of an entity can be modified for the default value. This is a very common customization. In many instances, we need to modify the default entity name to match the business for which we are customizing the system. For instance, many customers use the term organization instead of account. This is a very easy customization achieved by updating the Display Name and Plural Name fields. While implementing this change, make sure that you also update the entity messages, as a lot of them use the original name of the entity by default.   You can change a message value by double-clicking on the message and entering the new message into the Custom Display String field. Changing entity settings and properties When creating and managing entities in Dynamics CRM, there are generic "entity settings that we have to pay attention to. We can easily get to these settings and properties by navigating to Components | Entities within a solution and selecting an entity from the list. We will get an account entity screen similar to "the following screenshot:   The settings are structured in two main tabs, with various categories on each tab. We will take a look at each set of settings and properties individually in the next sections. Entity definition This area of the General tab groups together general properties and settings related to entity naming properties, ownership, and descriptions. Once an entity is created, the Name value remains fixed and cannot be modified. If the internal Name field needs to be changed, a new entity with the new Name field must be created. Areas that display this entity This section sets the visibility of this entity. An entity can be made available in only one module or more standard modules of the application. The account is a good example as it is present in all the three areas of the application. Options for entity The Options for Entity section contains a subset of sections with various settings "and properties to configure the main properties of the entity, such as whether the entity can be customized by adding business process flows, notes and activities, "and auditing as well as other settings. Pay close attention to the settings marked with a plus, as once these settings are enabled, they cannot be disabled. If you are not sure whether you need these features, disable them. The Process section allows you to enable the entity for Business Process Flows. When enabling an entity for Business Process Flows, specific fields to support this functionality are created. For this reason, once an entity is enabled for Business Process Flows, it cannot be disabled at a later time. In the communication and collaboration area, we can enable the use of notes, related activities, and connections as well as enable sending of e-mails and queues on the entity. Enabling these configurations creates the required fields and relationships in the system, and you cannot disable them later. In addition, you can enable the entity for mail merge for use with access teams and also for document management. Enabling an entity for document management allows you to store documents related to the records of this type in SharePoint if the organization is configured to integrate with SharePoint. The data services section allows you to enable the quick create forms for this entity's records as well as to enable or disable duplicate detection and auditing. When you are enabling auditing, auditing must also be enabled at the organization level. Auditing is a two-step process. The next subsections deal with Outlook and mobile access. Here, we can define whether the entity can be accessed from various mobile devices as well as Outlook and whether the access is read-only or read/write on tablets. The last section allows us to define a custom help section for a specific entity. "Custom help must be enabled at the organization level first. Primary field settings The Primary Field settings tab contains the configuration properties for the entity's primary field. Each entity in the Dynamics CRM platform is defined by a primary field. This field can only be a text field, and the size can be customized as needed. The display name can be adjusted as needed. Also, the requirement level can be selected from one of the three values: optional, business-recommended, or business-required. When it is marked as business-required, the system will require users to enter a value if they are creating or making changes to an entity record form. The primary fields are also presented for customization in the entity field's listing. Business versus custom entities As mentioned previously, there are two types of customizable entities in Dynamics CRM. They are business entities and custom entities. Business entities are customizable entities that are created by Microsoft and come as part of the default solution package. They are part of the three modules: sales, service, and marketing. Custom entities are all the new entities that are being created as part of the customization and platform extending process. Business entities Business entities are part of the default customization provided with the application by Microsoft. They are either grouped into one of the three modules of functionality or are spread across all three. For example, the account and contact entities are present in all the modules, while the case entity belongs to the service module. "Some other business entities are opportunity, lead, marketing list, and so on. Most of the properties of business entities are customizable in Dynamics CRM. However, there are certain items that are not customizable across these entities. These are, in general, the same type of customizations that are not changeable "when creating a custom entity. For example, the entity internal name (the schema name) cannot be changed once an entity has been created. In addition, the primary field properties cannot be modified once an entity is created. Custom entities All new entities created as part of a customization and implemented in Dynamics CRM are custom entities. When creating a new custom entity, we have the freedom to configure all the settings and properties as needed from the beginning. We can use a naming convention that makes sense to the user and generate all the messages from the beginning, taking advantage of this name. A custom entity can be assigned by default to be displayed in one or more of the three main modules or in the settings and help section. If a new module is created and custom entities need to be part of this new module, we can achieve this by customizing the application navigation, commonly referred to as the application sitemap. While customizing the application navigation might not be such a straightforward process, the tools released to the community are available, which makes this job a lot easier and more visual. The default method to customize the navigation is described in detail in the SDK, and it involves exporting a solution with the navigation sitemap configuration, modifying the XML data, and reimporting the updated solution. Extending entities Irrespective of whether we want to extend a customizable business entity or a custom entity, the process is similar. We extend entities by creating new entity forms, views, charts, relationships, and business rules.   Starting with Dynamics CRM 2015, entities configured for hierarchical relationships now support the creation and visualization of hierarchies through hierarchy settings. We will be taking a look at each of these options in detail in the next sections. Entity forms Entities in Dynamics CRM can be accessed from various parts of the system, and their information can be presented in various formats. This feature contributes to "the 360-degree view of customer data. In order to enable this functionality, the entities in Dynamics CRM present a variety of standard views that are available for customization. These include standard entity forms, quick create forms, and quick view forms. In addition, for mobile devices, "we can customize mobile forms. Form types With the current version of Dynamics CRM 2015, most of the updated entities now have four different form types, as follows: The main form The mobile form The quick create form The quick view form Various other forms can be created on an entity, either from scratch or by opening an existing form and saving it with a new name. When complex forms need to be created, in many circumstances, it is much easier to start from an existing entity "form rather than recreating everything. We have role-based forms, which change based on the user's security role, and we can also have more than one form available for users to select from. We can customize which view is presented to the user based on specific form rules or "other business requirements. It is a good practice to define a fallback form for each entity and to give all the users view permissions to this form. Once more than one main forms are created for an entity, you can define the order in which the forms are presented based on permissions. If the user does not have access to any of the higher precedence forms, they will be able to access the fallback form. Working with contingency forms is quite similar; here, a form is defined to be available to users who cannot access any other forms on an entity. The approach for configuring this is a little different though. You create a form with minimal information being displayed on it. Only assign a system administrator role to this form, and select enable for a fallback. With this, you specify a form that will not be visible to anybody other that the system administrator. In addition, configuring the form in this manner also makes it available to users whose security roles do not have a form specified. With such a configuration, if a user is added to a restrictive group that does not allow them to see most forms, they will have this one form available. The main form The main form is the default form associated with an entity. This form will be available by default when you open a record. There can be more than one main form, and these forms can be configured to be available to various security roles. A role must have at least one form available for the role. If more than one form is available for a specific role, then the users will be given the option to select the form they want to use to visualize a record available for it to be selected by the user. Forms that are available for various roles are called role-based forms. As an example, the human resource role can have a specific view in an account, showing more information than a form available for a sales role. At the time of editing, the main form of an entity will look similar to the "following screenshot: A mobile form A mobile form is a stripped-down form that is available for mobile devices with small screens. When customizing mobile forms, you should not only pay attention to the fact that a small screen can only render so much before extensive scrolling becomes exhaustive but also the fact that most mobile devices transfer data wirelessly and, as such, the amount of data should be limited. At the time of editing, the Mobile Entity form looks similar to the Account Mobile form shown in the following screenshot. This is basically just a listing of the fields that are available and the order in which they are presented to the user.   The quick create form The quick create form, while serving a different purpose than quick view forms, "are confined to the same minimalistic approach. Of course, a system customizer "is not necessarily limited to a certain amount of data to be added to these forms, "but it should be mindful of where these forms are being used and how much real estate is dedicated to them. In a quick create form, the minimal amount of data to be added is the required "fields. In order to save a new record, all business-required fields must be filled in. "As such, they should be added to the quick create form. The quick create form are created in the same way as any other type of form. In the solution package, navigate to entities, select the entity in which you want to customize an existing quick create form or add a new one, and expand the forms section; you will see all the existing forms for the specific entity. Here, you can select the form you want to modify or click on New to create a new one. Once the form is open for editing, the process of customizing the form is exactly the same for all forms. You can add or remove fields, customize labels, rearrange fields in the form, and so on. In order to remind the customizer that this is a quick create form, a minimalistic three-column grid is provided by default for this type of form in edit mode, "as shown in the following screenshot: Pay close attention to the fact that you can add only a limited type of controls to a quick create form. Items such as iframes and sub-grids are not available. That's not to say that the layout cannot be changed. You can be as creative as needed when customizing the quick create view. Once you have created the form, save and publish it. Since we have created a relationship between the account and the project earlier, we can add a grid view "to the account displaying all the related child projects. Now, navigating to an account, we can quickly add a new child project by going "to the project's grid view and clicking on the plus symbol to add a project. This will launch the quick create view of the project we just customized. This is how the project window will look:   As you can see in the previous screenshot, the quick create view is displayed as an overlay over the main form. For this reason, the amount of data should be kept to a minimum. This type of form is not meant to replace a full-fledged form but to allow a user to create a new record type with minimal inputs and with no navigation to other records. Another way to access the quick create view for an entity is by clicking on the "Create button situated at the top-right corner of most Dynamics CRM pages, "right before the field that displays your username. This presents the user with "the option to create common out-of-the-box record types available in the system, "as seen in the following screenshot:   Selecting any one of the Records options presents the quick create view. If you opt to create activities in this way, you will not be presented with a quick create form; rather, you will be taken to the full activity form. Once a quick create form record is created in the system, the quick create form closes and a notification is displayed to the user with an option to navigate to the newly created record. This is how the final window should look:   The quick view form The quick view form is a feature added with Dynamics CRM 2013 that allows system customizers to create a minimalistic view to be presented in a related record form. This form presents a summary of a record in a condensed format that allows you to insert it into a related record's form. The process to use a quick view form comprises the following two steps: Create the quick view form for an entity Add the quick view form to the related record The process of creating a quick view form is similar to the process of creating "any other form. The only requirement here is to keep the amount of information minimal, in order to avoid taking up too much real estate on the related record "form. The following screenshot describes the standard Account quick create form:   A very good example is the quick view form for the account entity. This view is created by default in the system. It only includes the account name, e-mail and "phone information, as well as a grid of recent cases and recent activities. We can use this view in a custom project entity. In the project's main form, add a lookup field to define the account related to the project. In the project's form customization, add a Quick View Form tab from the ribbon, as shown in the following screenshot:   Once you add a Quick View Form tab, you are presented with a Quick View Control Properties window. Here, define the name and label for the control and whether you want the label to be displayed in the form. In addition, on this form, you get to define the rules on what is to be displayed "on the form. In the Data Source section, select Account in the Lookup Field and Related Entity dropdown list and in the Quick View Form dropdown list, select "the account card form. This is the name of the account's quick view form defined "in the system. The following screenshot shows the Data Source configuration and the Selected quick view forms field:   Once complete, save and publish the form. Now, if we navigate to a project record, we can select the related account and the quick view will automatically be displayed on the project form, as shown in the "next screenshot:   The default quick view form created for the account entity is displayed now on the project form with all the specified account-related details. This way any updates to the account are immediately reflected in the project form. Taking this approach, it is now much easier to display all the needed information on the same screen so that the user does not have to navigate away and click through a maze to get to all the data needed. Summary Throughout this chapter, we looked at the main component of the three system modules: an entity. We defined what an entity is and we looked at what an entity is composed of. Then, we looked at each of the components in detail and we discussed ways in which we can customize the entities and extend the system. We investigated ways to visually represent the data related to entities and how to relate entities for data integrity. We also looked at how to enhance entity behavior with business rules and the limitations that the business rules have versus more advanced customizations, using scripts or other developer-specific methods. The next chapter will take you into the business aspect of the Dynamics CRM platform, with an in-depth look at all the available business processes. We will revisit business rules, and we will take a look at other ways to enforce business-specific rules and processes using the wizard-driven customizations available with the platform. Resources for Article: Further resources on this subject: Form customizations [article] Introduction to Reporting in Microsoft Dynamics CRM [article] Overview of Microsoft Dynamics CRM 2011 [article]
Read more
  • 0
  • 0
  • 3983
Modal Close icon
Modal Close icon