Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-managing-and-enhancing-multi-author-blogs-wordpress-27part-1
Packt
16 Oct 2009
18 min read
Save for later

Managing and Enhancing Multi-Author Blogs with WordPress 2.7(Part 1)

Packt
16 Oct 2009
18 min read
Creating an author page template If you have different authors on your blog, then my suggestion to you would be to display the biographical and contact information of each author on his own dedicated page. Luckily, WordPress allow us to do just that. Getting ready In this recipe, we're going to create an author page template for the purpose of displaying author related information. Make sure that you have understood the creation and usage of a page template. How to do it Create a new file named authors.php on your WordPress theme directory. Insert the following code into your file named authors.php: <?php/*Template Name: Authors Page*/?><?php get_header(); ?><div id="content" class="narrowcolumn"><?phpif(isset($_GET['author_name'])) :$curauth = get_userdatabylogin($author_name);else :$curauth = get_userdata(intval($author));endif;?><h2>About <?php echo $curauth->nickname; ?></h2><div class="excerpt"><?php echo $curauth->nickname; ?> personal website:<a href="<?php echo $curauth->user_url; ?>"><?php echo $curauth->user_url; ?></a></div><?php echo $curauth->user_description; ?><h2>Latest posts by <?php echo $curauth->nickname; ?>:</h2>Chapter 6133<ul><?php if ( have_posts() ) : while ( have_posts() ) :the_post(); ?><li><a href="<?php the_permalink() ?>"><?php the_title();?></a> on <?php the_time('d M Y'); ?></li><?php endwhile; else: ?><p><?php _e('No posts by this author.'); ?></p><?php endif; ?></ul></div><!--/content--><?php get_sidebar(); ?><?php get_footer(); ?> Save the file and upload it to the wp-content/themes/yourtheme folder of your WordPress install. Log in to your WordPress dashboard, create a new page, and select the Authors Page as a page template. Give it the title of your choice, such as, About the Author and publish the page. Open the single.php file from your theme. Depending on the theme that you're using, you may need to add the following code in order to display the author's name and a link to the author's page: Posted by <?php the_author_posts_link(); ?> Once you have saved the modifications made in your single.php file, visit one of your blog posts and click on the author name. The author page is displayed showing the author name, description, and web site. How it works The first thing that we need to know is the name of the author whose information is to be displayed. To do so, we have to get the author_name parameter sent via the GET method. With this value, we can initialize a $curauth php object that will allow us to get some personal information about the author, such as his web site, email, biography, and so on, with the help of the classic php syntax, that is, $curauth->nickname;. Once the author data, that is to be displayed, has been retrieved, we shall add a WordPress loop in order to be able to view the recent posts by this author. The following screenshot shows a well-prepared author page: There's more... In the preceding example we retrieved the author name, description, and web site URL. However, as you may know, users can provide much more information (in Administration, Profile, Your Profile options) such as their email address, AIM and Yahoo! messenger nickname, and login information. A few more template tags can be used to retrieve another kind of information from the author data. These tags are listed under the There's more! section of Displaying author-related information on posts, which we will see later in this article. Displaying a custom login form in your blog's sidebar It doesn't matter whether you're running a multi-author blog, or a blog where readers can register. Having a login form embedded in your sidebar will make your blog look a lot more professional and user friendly. Here is what you can expect from this recipe. In the following screenshot, a login form has been added to the K2 theme sidebar. Getting ready To achieve this recipe, you'll have to edit the sidebar.php file from your theme. The following hack works with WordPress 2.0 to 2.8. How to do it Open the sidebar.php file for editing. Find the opening <ul> tag and paste the following code under it: <li><?php global $user_ID, $user_identity, $user_level ?><?php if ( $user_ID ) : ?><h2><?php echo $user_identity ?></h2><ul><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=logout&amp;redirect_to=<?php echo urlencode($_SERVER['REQUEST_URI']) ?>">Logout</a></li></ul><?php elseif ( get_option('users_can_register') ) : ?>Managing and Enhancing Multi-Author Blogs136<h2>Identification</h2><ul><li><form action="<?php bloginfo('url')?>/wp-login.php" method="post"><p><label for="log"><input type="text" name="log"id="log" value="<?php echo wp_specialchars (stripslashes($user_login), 1) ?>" size="22" /> User</label><br /><label for="pwd"><input type="password"name="pwd" id="pwd" size="22" /> Password</label><br /><input type="submit" name="submit" value="Login"class="button" /><label for="rememberme"><input name="rememberme"id="rememberme" type="checkbox" checked="checked"value="forever" /> Remember me</label><br /></p><input type="hidden" name="redirect_to" value="<?php echo$_SERVER['REQUEST_URI']; ?>"/></form></li><li><a href="<?php bloginfo('url')?>/wp-register.php">Register</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=lostpassword">Recover password</a></li></ul><?php endif ?></li> Save the file. Your users can now login directly from your blog's sidebar. How it works The working of this code is quite simple. First, you initialize the global variables to get the user ID, name, and level. Then, you check the value of the $user_ID variable. If the value is not null, which means that the current user is logged in, you then display a quick hello user text and a link to log out. If the user isn't logged in, you check whether registering is allowed on the blog. If the user is logged in, then you simply display an HTML form that allows the user to log in directly from the blog. A link has also been included for registration if the current user doesn't have an account yet. This code was inspired from a tutorial available at www.wpdesigner.com. Adding a control panel to your blog's sidebar Now that you have learned how to check whether a user is logged in or not, why not learn how to add a small control panel to your blog's sidebar that is only visible to the logged in users. In this recipe, you'll learn how to achieve this task. Getting ready The upcoming piece of code works in exactly the same way as the code from the previous recipe does. It is all about checking if the user is logged in and whether he or she has the right to do a certain kind of thing. The following screenshot shows a simple, but useful, control panel which is similar to the one we're about to create: How to do it Open sidebar.php for editing. Find the first opening <ul> HTML tag, and paste the following code under the <ul> tag: <li><?php global $user_ID, $user_identity, $user_level ?><?php if ( $user_ID ) : ?><h2>Control panel</h2><ul><li>Identified as <strong><?php echo $user_identity ?></strong>.<ul><li><a href="<?php bloginfo('url') ?>/wp-admin/">Dashboard</a></li><?php if ( $user_level >= 1 ) : ?><li><a href="<?php bloginfo('url') ?>/wp-admin/post-new.php">Write an article</a></li><?php endif; ?><li><a href="<?php bloginfo('url') ?>/wp-admin/profile.php">Profile and personal options</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=logout&amp;redirect_to=<?php echourlencode($_SERVER['REQUEST_URI']) ?>">Logout</a></li><?phpif (is_single()) {?><li><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a></li><?php } ?></ul></li></ul><?php endif; ?></li> Once you are done, save the file. The allowed users can now go to their dashboard, edit their profile, or write a new post directly from the blog. How it works As mentioned earlier, this code works in the same way as the code that was used to create a login form in the sidebar. After you've made sure that the $user_ID variable isn't null, you work towards displaying the options available to the user. It is possible to define what a user can perform according to his role (administrator, author, contributor, subscriber, and so on). We're going to have a look at this in the next recipe. There's more... Now that you have learned how to add a control panel to the blog's sidebar, let's go ahead and try out something new. Adding a login form and a control panel Now that you know how to add a login form and a mini control panel to your blog's sidebar, why not try mixing the two codes? If the user isn't logged in, we'll display the login form. Otherwise, the custom panel will be shown to the user. The code below works in the same way as the two that we studied previously. Add the following code to the sidebar.php file of your theme: <li><?php global $user_ID, $user_identity, $user_level ?><?php if ( $user_ID ) : ?><h2>Control panel</h2><ul><li>Identified as <strong><?php echo $user_identity ?></strong>.<ul><li><a href="<?php bloginfo('url') ?>/wp-admin/">Dashboard</a></li><?php if ( $user_level >= 1 ) : ?><li><a href="<?php bloginfo('url') ?>/wp-admin/post-new.php">Write an article</a></li><?php endif; ?><li><a href="<?php bloginfo('url') ?>/wp-admin/profile.php">Profile and personal options</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=logout&amp;redirect_to=<?php echo urlencode($_SERVER['REQUEST_URI']) ?>">Logout</a></li><?phpif (is_single()) {?><li><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a></li><?php } ?></ul></li></ul><?php elseif ( get_option('users_can_register') ) : ?><h2>Identification</h2><ul><li><form action="<?php bloginfo('url') ?>/wp-login.php"method="post"><p><label for="log"><input type="text" name="log" id="log" value="<?php echo wp_specialchars(stripslashes($user_login), 1)?>" size="22" /> User</label><br /><label for="pwd"><input type="password" name="pwd" id="pwd"size="22" /> Password</label><br /><input type="submit" name="submit" value="Login"class="button" /><label for="rememberme"><input name="rememberme" id="rememberme" type="checkbox" checked="checked"value="forever" /> Remember me</label><br /></p><input type="hidden" name="redirect_to" value="<?php echo$_SERVER['REQUEST_URI']; ?>"/></form></li><li><a href="<?php bloginfo('url') ?>/wp-register.php">Register</a></li><li><a href="<?php bloginfo('url') ?>/wp-login.php?action=lostpassword">Recover password</a></li></ul><?php endif; ?></li> The custom logging form for unregistered users will look similar to the following screenshot: And the control panel for logged in users will look similar to the following screenshot: Configuring author roles Now that you have learned about the different aspects of the user's roles and capabilities, there's probably something that you're finding a little frustrating. By default, you can't configure author roles to fit your blog's needs. For example, a contributor can't upload images. Moreover, by default, you can't change it. Luckily, there's a plugin called Role Manager which allows you to configure author roles in the way that you want. Getting ready The Role Manager plugin can be found at the following link: http://www.im-web-gefunden.de/wordpress-plugins/role-manager/ Download it, unzip it onto your hard drive, and install it as any other WordPress plugin. How to do it Once the Role Manager plugin is installed, log in to your WordPress dashboard and go to Users | Roles. A list of all of the available user roles will be displayed. For each role you can define what the user can do. For example, you can choose to let a contributor upload images. What is even better is that you're not limited to the 5 default user roles that are provided by WordPress. The Role Manager plugin allows you to create new roles, as well as the ability to rename, copy, or delete existing ones. How it works The job of the Role Manager plugin is pretty easy. It simply creates custom roles with the options that you have defined and save it on the WordPress database. There's more... Now that we have configured the author roles, let's learn how to control the author's actions. Controlling what authors can do Even if your blog is powered by multiples authors, it is still your blog. Therefore, you shouldn't allow every author to have the right to edit posts or delete comments. Since version 2.0, WordPress features user roles. User roles are defined as a group of actions that can be accomplished by a specific range of users. For example, the administrator can edit theme files, but the subscribers can't. User roles and their capabilities Here are the 5 predefined roles for WordPress users: Administrator: The administrator is the blog owner. He has unlimited access to all of the administration features such as writing posts, editing his own posts along with the posts from other authors, installing plugins, selecting a new theme, editing themes, and editing plugin files. Editor: The editor can write or publish posts, upload images, edit his own posts, and manage other's posts. Author: The author can write, publish, and edit his own his own posts. He's also allowed to upload images for use in his posts. Contributor: A contributor can write posts but can't publish them himself. Once he has written a post, the post is pending approval from the administrator. The contributor can't upload images either. This role is very good for guest authors on your own blog. Subscriber: A subscriber is a registered user of your blog, but can't write posts. For an exhaustive description of user roles and capabilities, you should read the related page in WordPress Codex: http://codex.wordpress.org/Roles_and_Capabilities. Controlling what users can see in your theme In the previous example, we built a sidebar control panel that allows the user to edit the current post. However, the code doesn't let you control which kind of author is allowed to edit the current post. For now, even if only the users with a sufficient role level will be capable of editing the post, every logged in user can see the related link. The solution to that problem is a built-in WordPress function, called current_user_can(). As an argument, this function takes a string describing the action or the required role level to perform a specific task. For example, the following code will provide a link to edit the current post to the administrators only: <?phpif (current_user_can('level_10')){ ?><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a><?php } ?> The current_user_can() function accepts user_0 to user_10 as a parameter. Here is the conversion table between the role levels and the roles: Suscriber: level_0 Contributor: level_1 Author: level_2 to level_4 Editor: level_5 to level_7 Administrator: level_8 to level_10 The current_user_can() function can also be used with a specific action as a parameter. This is the recommended use, as the level parameter is becoming obsolete. The following example checks if the current user can edit a post he previously published. If yes, then a link to edit the post will be displayed. <?phpif (current_user_can('edit_published_posts')){ ?><a href="<?php bloginfo('wpurl');?>/wp-admin/edit.php?p=<?php the_ID(); ?>">Edit Post</a><?php } ?> Here are all of the arguments that are accepted by the current_user_can() function:      switch_themes      edit_themes      activate_plugins      edit_plugins      edit_users      edit_files      manage_options      moderate_comments      manage_categories      manage_links      upload_files      import      unfiltered_html      edit_posts      edit_others_posts      edit_published_posts      edit_pages      edit_others_pages      edit_published_pages      edit_published_pages      delete_pages      delete_others_pages      delete_published_pages      delete_posts      delete_others_posts      delete_published_posts      delete_private_posts      edit_private_posts      read_private_posts      delete_private_pages      edit_private_pages      read_private_pages      delete_users      create_users      unfiltered_upload      edit_dashboard      update_plugins      delete_plugins Displaying author-related information on posts In a multi-author blog, it's always good for the reader to know the author of the article that they're currently reading. It's even better if they can grab some extra information about the author, such as his website, a short bio, and so on. In this recipe, you'll learn how to edit your single.php theme file to automatically retrieve the author-related information, and display it at the top of the page. Getting ready As we're going to display author information on posts, the first thing to do is to make sure that your contributing authors have entered their biography and other information into the WordPress database. Any author can enter his information by logging in to the WordPress dashboard, and then going to Profile. The blog administrator can edit all of the profiles. The following screenshot shows the WordPress 2.7 profile editor for the authors. How to do it Once you have made sure that your authors have successfully filled their information, you can start coding by carrying out the following steps: Open the file single.php for addition. Paste the following code within the loop: <div id="author-info"><h2>About the author: <?php the_author();?></h2><?php the_author_description(); ?><?php the_author();?>'s website: <a href="<?php the_author_url();?>"><?php the_author_url(); ?></a><br />Other posts by <?php the_author_posts_link(); ?></div><!--/author-info--> Save the file and visit your blog. You will notice that your posts now automatically display the author-related information, as shown in the following screenshot: How it works WordPress provides a dozen of author-related template tags, which are an easy way to retrieve information that is entered by authors in their profile. Note that all of these tags must be used within the loop for them to work. There's more... Here are all the available template tags related to authors:
Read more
  • 0
  • 1
  • 2846

article-image-file-sharing-grails
Packt
09 Oct 2009
7 min read
Save for later

File Sharing in Grails

Packt
09 Oct 2009
7 min read
File domain object The first step, as usual, is to create a domain object to represent a file. We want to store the following information: Name The data of the file A description of the file The file size Who uploaded the file The date the file was created and last modified Create the File domain class as follows: package app class File { private static final int TEN_MEG_IN_BYTES = 1024*1024*10 byte[] data String name String description int size String extension User user Date dateCreated Date lastUpdated static constraints = { data( nullable: false, minSize: 1, maxSize: TEN_MEG_IN_BYTES ) name( nullable: false, blank: false ) description( nullable: false, blank: false ) size( nullable: false ) extension( nullable: false ) user( nullable: false ) } } There should be nothing unfamiliar here. You have created a new domain class to represent a file. The file data will be stored in the data property. The other properties of the file are all metadata. Defining the user property creates the association to a user object. The constraints are then defined to make sure that all of the information that is needed for a file has been supplied. There is one important side effect of setting the maxSize constraint on the data property. GORM will use this value as a hint when generating the database schema for the domain objects. For example, if this value is not specified, the underlying database may end up choosing a data type to store the binary file data that is too small for the size of files that you wish to persist. FileController Now, we will need a controller. Let's name it FileController. Our controller will allow users to perform the following actions: Go to a page that allows users to select a file Submit a file to the server Download the file Create the FileController groovy class, alongside our existing MessageController, by following the actions shown below: package app class FileController { def create = { return [ file: new File() ] } def save = { } def download = { } } In the create action, we are simply constructing a new file instance that can be used as the backing object when rendering the file-upload form. We will fill in the implementation details of the save and download actions as and when we will need them. File Upload GSP The next step is to create a GSP to render the form that allows users to upload a file to the application. Create the file grails-app/views/file/create.gsp and enter the following markup: <%@ page contentType="text/html;charset=UTF-8" %> <html> <head> <meta http-equiv="Content-Type" content= "text/html; charset=UTF-8"/> <meta name="layout" content="main"/> <title>Post File</title> </head> <body> <g:hasErrors bean="${file}"> <div class="validationerror"> <g:renderErrors bean="${file}" as="list"/> </div> </g:hasErrors> <g:form action="save" method="post" enctype="multipart/form-data" class="inputform"> <fieldset> <dl> <dt>Title <span class="requiredfield">required</span></dt> <dd><g:textField name="name" value="${file.name}" size="35" class="largeinput"/></dd> <dt>File <span class="requiredfield">required</span></dt> <dd><input type="file" name="data"/></dd> <dt>File description <span class="requiredfield">required</span></dt> <dd><g:textArea name="description" value="${file .description}" cols="40" rows="10"/></dd> </dl> </fieldset> <g:submitButton name="Save" value="Save"/> | <g:link controller="home">Cancel</g:link> </g:form> </body> </html> This GSP looks very similar to the create.gsp file for messages. Obviously, it has different fields that correspond to fields on the File domain class. The important difference is that this form tells the browser it will be submitting the file data: <g:form action="save" method="post" enctype="multipart/form-data">   Run the application, go to http://localhost:8080/teamwork/file/create and sign in with the username flancelot and the password password. You should see the window as shown in the following screenshot: Saving the file Now that our users can select files to upload, we need to implement the save action so that these files can be persisted and can be viewed by other users. Grails file upload Grails provides two methods of handling file upload. We are going to use both of them. The two approaches are: Using data binding Using the Spring MultipartFile interface Data binding makes receiving the data of the file very simple, but is quite limited if used on its own. There is no way of binding anything other than the data of the file, such as the filename or the size of the file, to our domain object. By also providing access to the Spring MultipartFile interface, Grails allows us to programmatically access any other information we might want from the file. The save action Update the FileController class and implement the save action as follows: package app import org.springframework.web.multipart.MultipartFile class FileController { def userService def create = { return [ file: new File() ] } def save = { def file = new File( params ) file.user = userService.getAuthenticatedUser() MultipartFile f = request.getFile( 'data' ) file.size = f.getSize() / 1024 file.extension = extractExtension( f ) if(file.save()) { flash.userMessage = "File [${file.name}] has been uploaded." redirect(controller: 'home') } else { render(view: 'create', model: [file: file]) } } def extractExtension( MultipartFile file ) { String filename = file.getOriginalFilename() return filename.substring(filename.lastIndexOf( "." ) + 1 ) } def download = { } } Apart from the implementation of the save action, we have had to import Spring MultipartFile and also inject the userService. The first highlighted line within the save action performs the binding of request parameters to the File domain object. The usual binding will take place, that is, the name and the description properties of the File object will be populated from the request. In addition, since we have a property on our domain object that is an array of bytes, the contents of the file object in the request will also be bound into our File object. A quick review of our code shows that we have the following property on theFile class: byte[] data Also the create.gsp defines the file input field with the same name: <dd><input type="file" name="data" /></dd> Grails is also capable of binding the contents of a file to a String property. In this case, we could just declare the data property in our File class as a String, and Grails would bind the file contents as a String. The next line of interest occurs when we fetch the MultipartFile off the request by using the getFile method. We simply specify the request parameter that contains the file data and Grails does the rest. With an instance of MultipartFile we can access the file size and the original file name to extract the file extension. Once we have finished populating our File object, we can call the save method and GORM will manage the persistence of the file object and the file data to the database. Validation messages The last thing we need to remember to add is the validation messages that will be displayed if the users don't enter all the data that is needed to save a file. Add the following to grails-app/i18n/messages.properties: file.name.blank=You must give the file a name file.description.blank=The file must have a description file.data.minSize.notmet=No file has been uploaded file.data.maxSize.exceeded=The file is too large. The maximum file size is 10MB
Read more
  • 0
  • 0
  • 2845

article-image-how-create-lesson-moodle-2
Packt
24 Jun 2011
7 min read
Save for later

How to Create a Lesson in Moodle 2

Packt
24 Jun 2011
7 min read
  History Teaching with Moodle 2 Create a History course in Moodle packed with lessons and activities to make learning and teaching History interactive and fun  Approaching the lesson We plan to introduce our Year 7 History class to the idea of the Doomsday Book as a means by which William reinforced his control over the country. William was naturally curious about the country he had just conquered. He was particularly keen to find out how much it was worth. He despatched officials to every village with detailed questions to ask about the land that they worked on and the animals that they farmed with. He also sent soldiers who threatened to kill people who lied. All of the records from these village surveys were collated into the Doomsday Book. Many Saxons detested the process and the name of the book is derived from this attitude of loathing towards something they regarded as intrusive and unfair. William died before the process could be completed. Clear lesson objectives can be stated at the start of the lesson. Students would be expected to work through each page and answer questions identical to those found in the Quiz module. The lesson gives students the opportunity to return to a page if the required level of understanding has not been achieved. The lesson questions help students to reach an understanding at their own pace. The short video clips we intend to use will come from the excellent National Archive website. It has links to short sequences of approximately ninety seconds in which actors take on the role of villagers and commissioners and offer a variety of opinions about the nature and purpose of the survey that they are taking part in. At the end of the lesson, we want the students to have an understanding of: The purpose of the Domesday Book How the information was compiled A variety of attitudes towards the whole process Our starting point is to create a flow diagram that captures the routes a student might take through the lesson: The students will see the set of objectives, a short introduction to the Doomsday Book, and a table of contents. They can select the videos in any order. When they have watched each video and answered the questions associated with the content they will be asked to write longer answers to a series of summative questions. These answers are marked individually by the teacher who thus gets a good overall idea of how well the students have absorbed the information. The assessment of these questions could easily include our essay outcomes marking scale. The lesson ends when the student has completed all of the answers. The lesson requires: A branch table (the table of contents). Four question pages based upon a common template. One end of branch page. A question page for the longer answers. An end of lesson page. The lesson awards marks for the correct answers to questions on each page in much the same way as if they were part of a quiz. Since we are only adding one question per page the scores for these questions are of less significance than a student's answers to the essay questions at the end of the lesson. It is after all, these summative questions that allow the students to demonstrate their understanding of the content they have been working with. Moodle allows this work to be marked in exactly the same way as if it was an essay. This time it will be in the form of an online essay and will take up its place in the Gradebook. We are, therefore, not interested in a standard mark for the students' participation in the lesson and when we set the lesson up, this will become apparent through the choices we make.   Setting up a lesson It is important to have a clear idea of the lesson structure before starting the creation of the lesson. We have used paper and pen to create a flow diagram. We know which images, videos, and text are needed on each page and have a clear idea of the formative and summative questions that will enable us to challenge our students and assess how well they have understood the significance of the Doomsday Book. We are now in a position to create the lesson: Enter the Year 7 History course and turn on editing. In Topic 1, select Add an Activity and click Lesson. In the Name section, enter an unambiguous name for the lesson as this is the text that students will click on to enter the lesson. Enter the values as shown in the following screenshot: In the General section, we do not want to impose a time limit on the lesson. We do need to state how many options there are likely to be on each question page. For multiple choice questions, there are usually four options. In the Grade section, we want the essay that they compose at the end of the lesson to be marked in the same way that other essays have been marked. In the Grade options, our preference is to avoid using the lesson questions as an assessment activity. We want it to be a practice lesson where students can work through the activities without needing to earn a score. We have turned off scoring. The students' final essay submission will be marked in line with our marking policy. Students can retake it as many times as they want to. In the Flow control section, we have clicked the Show advanced button to see all of the options available. We want students to be able to navigate the pages to check answers and go back to review answers if necessary. They can take the lesson as often as they want as we intend it to be used for revision purposes for a timed essay or in the summer examination. We have ignored the opportunity to add features such as menus and progress bars as we will be creating our own navigation system. This section also concerns the look and feel of the pages if set to a slide show, an option we are not planning to use. We are planning to create a web link on each page rather than have students download files so we will not be using the Popup to file or web page option. If you are concerned about the stability of your Internet connection for the weblinks to videos you plan to show, there is an alternative option. This would involve downloading the files to your computer and converting them to .flv files. They can then be uploaded to the file picker in the usual way and a link can be created to each one using the Choose a file button shown here. Moodle's video player would play the videos and you would not be reliant on an unstable Internet connection to see the results. The Dependent on section allows further restrictions to be imposed that are not appropriate for this lesson. We do however, want to mark the essay that will be submitted in accordance with the custom marking scheme developed earlier in the course. The box in the Outcomes section must be checked. Clicking the Save and return to course button ensures that the newly created lesson, The Domesday Book, awaits in Topic 1.  
Read more
  • 0
  • 0
  • 2844

article-image-applying-special-effects-3d-game-development-microsoft-silverlight-3-part-1
Packt
18 Nov 2009
7 min read
Save for later

Applying Special Effects in 3D Game Development with Microsoft Silverlight 3: Part 1

Packt
18 Nov 2009
7 min read
  A 3D game must be attractive. It has to offer amazing effects for the main characters and in the background. A spaceship has to fly through a meteor shower. An asteroid belt has to draw waves while a UFO pursues a spaceship. A missile should make a plane explode. The real world shows us things moving everywhere. Most of these scenes, however, aren't repetitive sequences. Hence, we have to combine great designs, artificial intelligence (AI), and advanced physics to create special effects. Working with 3D characters in the background So far, we have added physics, collision detection capabilities, life, and action to our 3D scenes. We were able to simulate real-life effects for the collision of two 3D characters by adding some artificial intelligence. However, we need to combine this action with additional effects to create a realistic 3D world. Players want to move the camera while playing so that they can watch amazing effects. They want to be part of each 3D scene as if it were a real life situation. How can we create complex and realistic backgrounds capable of adding realistic behavior to the game? We can do this combining everything we have learned so far with a good object-oriented design. We have to create random situations combined with more advanced physics. We have to add more 3D characters with movement to the scenes. We must add complexity to the backgrounds. We can work with many independent physics engines to work with parallel worlds. In real-life, there are concurrent and parallel words. We have to reproduce this behavior in our 3D scenes. Time for action – adding a transition to start the game Your project manager does not want the game to start immediately. He wants you to add a butt on in order to allow the player to start the game by clicking on it. As you are using Balder, adding a butt on is not as simple as expected. We are going to add a butt on to the main page, and we are going to change Balder's default game initialization: Stay in the 3DInvadersSilverlight project. Expand App.xaml in the Solution Explorer and open App.xaml.cs––the C# code for App.xaml. Comment the following line of code (we are not going to use Balder's services in this class):  //using Balder.Silverlight.Services; Comment the following line of code in the event handler for the Application_Startup event, after the line this.RootVisual = new MainPage();: //TargetDevice.Initialize<InvadersGame>(); Open the XAML code for MainPage.xaml and add the following lines of code after the line (You will see a butt on with the ti tle Start the game.): <!-- A button to start the game --><Button x_Name="btnStartGame" Content="Start the game!" Canvas.Left="200" Canvas.Top="20" Width="200" Height="30" Click="btnStartGame_Click"></Button> Now, expand MainPage.xaml in the Solution Explorer and open MainPage.xaml.cs––the C# code for MainPage.xaml. Add the following line of code at the beginning (As we are going to use many of Balder's classes and interfaces.): using Balder.Silverlight.Services; Add the following lines of code to program the event handler for the button's Click event (this code will initialize the game using Balder's services): private void btnStartGame_Click(object sender, RoutedEventArgs e){ btnStartGame.Visibility = Visibility.Collapsed; TargetDevice.Initialize<InvadersGame>();} Build and run the solution. Click on the Start the game! butt on and the UFOs will begin their chase game. The butt on will make a transition to start the game, as shown in the following screenshots:   What just happened? You could use a Start the game! butt on to start a game using Balder's services. Now, you will be able to offer the player more control over some parameters before starting the game. We commented the code that started the game during the application start-up. Then, we added a button on the main page (MainPage). The code programmed in its Click event handler initializes the desired Balder.Core.Game subclass (InvadersGame) using just one line: TargetDevice.Initialize<InvadersGame>(); This initialization adds a new specific Canvas as another layout root's child, controlled by Balder to render the 3D scenes. Thus, we had to make some changes to add a simple butt on to control this initialization. Time for action – creating a low polygon count meteor model The 3D digital artists are creating models for many aliens. They do not have the time to create simple models. Hence, they teach you to use Blender and 3D Studio Max to create simple models with low polygon count. Your project manager wants you to add dozens of meteors, to the existing chase game. A gravitational force must attract these meteors and they have to appear in random initial positions in the 3D world. First, we are going to create a low polygon count meteor using 3D Studio Max. Then, we are going to add a texture based on a PNG image and export the 3D model to the ASE format, compatible with Balder. As previously explained, we have to do this in order to export the ASE format with a bitmap texture definition enveloping the meshes. We can also use Blender or any other 3D DCC tool to create this model. We have already learned how to export an ASE format from Blender. Thus, this time, we are going to learn the necessary steps to do it using 3D Studio Max. Start 3D Studio Max and create a new scene. Add a sphere with six segments. Locate the sphere in the scene's center. Use the Uniform Scale tool to resize the low polygon count sphere to 11.329 in the three axis, as shown in the following screenshot: Click on the Material Editor button. Click on the first material sphere, on the Material Editor window's upper-left corner. Click on the small square at the right side of the Diffuse color rectangle, as shown in the following screenshot: Select Bitmap from the list shown in the Material/Map Browser window that pops up and click on OK. Select the PNG file to be used as a texture to envelope the sphere. You can use Bricks.PNG, previously downloaded from http://www.freefoto.com/. You just need to add a reference to a bitmap file. Then, click on Open. The Material Editor preview panel will show a small sphere thumbnail enveloped by the selected bitmap, as shown in the following screenshot: Drag the new material and drop it on the sphere. If you are facing problems, remember that the 3D digital artist created a similar sphere a few days ago and he left the meteor.max file in the following folder (C:Silverlight3DInvaders3D3DModelsMETEOR). Save the file using the name meteor.max in the previously mentioned folder. Now, you have to export the model to the ASE format with the reference to the texture. Therefore, select File | Export and choose ASCII Scene Export (*.ASE) on the Type combo box. Select the aforementioned folder, enter the file name meteor.ase and click on Save. Check the following options in the ASCII Export dialog box. (They are unchecked by default): Mesh Normals Mapping Coordinates Vertex Colors The dialog box should be similar to the one shown in the following screenshot: Click on OK. Now, the model is available as an ASE 3D model with reference to the texture. You will have to change the absolute path for the bitmap that defines the texture in order to allow Balder to load the model in a Silverlight application.
Read more
  • 0
  • 0
  • 2844

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

article-image-story-management-php-nuke
Packt
23 Mar 2010
12 min read
Save for later

Story Management with PHP-Nuke

Packt
23 Mar 2010
12 min read
The Story Story In PHP-Nuke, a story is a general-purpose, piece of content. Maybe the story is an announcement, a press release or news item, or a piece of commentary or opinion, or maybe a tutorial article. The possibilities are almost endless! With PHP-Nuke driving your site, the stories that appear on your site are not restricted to ones written by you. Users of the site—either registered or unregistered visitors, or other administrators—can write and submit stories to your site. The process of a story appearing on the site is known as publishing the story. Of course this does not mean that your site is a free-for-all—stories submitted by users and others do not necessarily get published automatically—they are submitted for moderation by an administrator, and once approved, appear on your site. In this way, the content on your site grows itself through your community, but always (if you want) under your control. PHP-Nuke keeps track of such things as the author of the story, the date when the story first appeared on the site, and the number of times the story has been read, and also allows users to vote on the quality of the story. An impressive feature of PHP-Nuke stories is that users can comment on a posted story to build an open, topical discussion within your site. You will see community-contributed stories when you visit any typical PHP-Nuke site; for example, on phpnuke.org itself, PHP-Nuke users and developers submit stories describing their latest PHP-Nuke add-on, or drawing attention to the latest theme that they've designed. The 'story' engine in PHP-Nuke is provided by the News module. The total story functionality is actually spread across a number of modules, including Submit News, Stories Archive, Search, Topics, Your Account, and Surveys. We will explore all of these in this article. The Story Publication Process The path taken by a story from writing to publication depends upon who submits the story. The super user or an administrator with permissions for the News module can post a story through the administration area of the site. In this case, the publication process is simple, and the story appears on the site immediately, or can be scheduled for publication on a particular date. Since the super user and any other administrators with the appropriate privileges are trusted (they have full power over stories, so they had better be trustworthy), there is no need to moderate or approve the text in the story, and the story is ready to go. Registered and unregistered visitors can post stories through the Submit News module. When a story is submitted through this route, the publication process is lengthier. The visitor enters the story through a form in the Submit News module. The story administrator is notified that a new story has been submitted. The story administrator checks over the story, editing, rejecting (deleting), or approving it. The administrator is also able to add notes to the story. If the story is rejected, that is the end of the process, and the story is not published. If the story is approved, it is either published to the site immediately, or can be scheduled for publication on a particular date. Once the story is published to the site, the administrator can edit it further if needed. For a visitor, once they submit their story to the site they have no more control over the story. Finding and Interacting with Stories Stories on the site can be accessed in a number of ways, from a number of different places on the site. A limited number of stories can appear on the homepage, with older stories gradually moving down the list as newer stories are posted. Stories can be retrieved by date from the Stories Archive module. The text in the story is also searchable from the Search module, so that specific stories can be located easily. Stories are organized into topics, and by browsing the list of topics from the Topics module stories can be tracked down. Stories are not the end of content, they are actually the beginning. Comments can be posted about stories, and comments can be posted to these comments creating a discussion about the story. The quality of submitted comments can be assessed by users of the site and rated accordingly. The quality or value of the story itself can be voted on by users, links to related stories can be created, and you can view a special printer-friendly version of the story for printing, or even send the story to a friend. So many features... did I mention that you can also attach a poll to the story so that readers can participate in a survey related to the content of the story? So many features... Organizing Stories When you have even a reasonable number of stories on your PHP-Nuke site (and you will have—that's why you're reading this article series!), you will be in need of some organization for this content. PHP-Nuke provides two ways of organizing story content: Topics: what it's about Categories: what type of story it is Topics Topics define what a story is about. By organizing your stories into topics, stories about similar subjects will be grouped together for easy browsing and reading, and also to make it easier for people to contribute their stories to the right place. When you're reading through a number of dinosaur-related stories, the sudden appearance of a story about cars would be rather off-putting (unless it was actually about fossil fuels or dinosaurs eating/driving cars). PHP-Nuke does indeed offer organization of stories into topics, and before we can think of adding stories, we need to set up some topics for our stories. A topic has an associated image that is displayed on the site whenever you view a story that belongs to that topic, or whenever you are browsing the list of topics. The image overleaf shows a 'teaser' of a story displayed on our site; the topic image is shown to the right-hand side of the story: The Read More… link will take the visitor to the remainder of the story. Note that this arrangement of the story text and the topic image appearing to the right of the story is just the default layout due to the basic theme. When we come to look at creating our own themes, we will see how the topic image can be made to appear elsewhere relative to the story text. By default, there is a single topic called PHP-Nuke. This has its own image, which should only be used for the PHP-Nuke topic. Categories As topics define what a story is about, categories define the 'type' of story. A category could be something like a weblog entry, a security announcement, or a press release. There is one category defined by default, Article. This category has the following properties: You cannot change this category's name or delete it. Any story of type Article automatically appears on the homepage. Users can only submit stories of type Article. Compared to topics, categories do not have particularly extensive support in PHP-Nuke. Planning the Dinosaur Portal Topics and Categories Before we move on to looking at managing topics and categories, we'll quickly discuss the kind of topics and categories that we would like for organizing our stories on the Dinosaur Portal. These are not set in stone, and after we create them, we can edit or delete them, or even add new ones. First of all, there will probably be stories about the Dinosaur Portal itself that will contain general information about the site, such as new features that have been added to it, or warnings about planned site downtime (or apologies about unplanned site downtime!). We will also have stories about dinosaurs, fossils, and dinosaur hunting; these can be the other topics on the site. What types of story will we have? In addition to the standard article, we can have new theories, technologies, or discoveries, maybe even tutorials (for example, how to identify fossils, or how to avoid being eaten when dinosaur hunting). There will also be stories about Project Chimera, but we can't reveal what that is just yet. Thus a story about a controversial new dinosaur extinction theory could be given the 'dinosaur' topic, and the 'new theory' category. This isn't an exhaustive list, but it is enough to give an idea of the topic-category split. Topic Management Before we do anything else, we'll create our topics. For each topic, we'll add the images first. After we create our topics, we'll look at how to modify them, and the consequences of deleting topics. Before we get started creating our topics, we will add the topic images. To do this, you will need to copy all the files from the topics folder in the Ch06 folder of the code download to the images/topics/ folder in the root of your PHP-Nuke installation. You should have these files: thedinosaurportal.gif, dinosaurs.gif, fossils.gif, and dinosaurhunting.gif, in addition to files called index.html, phpnuke.gif, and AllTopics.gif, which were already present in the folder. The images/topics folder is the place where PHP-Nuke will look for the topic icons. When adding image files to the images/topics folder, ensure that only alphanumeric characters or the underscore are used in your filename, or else PHP-Nuke will fail to pick up the filename when displaying the list of topic images. Note also that the total length of the filename and its extension must not exceed twenty characters, or PHP-Nuke will truncate the name when it stores a record of the filename. In this case, your topic image will not be displayed, because PHP-Nuke has not stored the correct name of the file. Also, if your image has an extension of more than three characters (such as jpeg) then it will be missed by PHP-Nuke. The AllTopics.gif file in the images/topics folder does not correspond to a single topic, but is the image used when displaying the lists of topics. This file can be replaced by an image of your own. Time For Action—Creating New Topics Log in to your site as the administrator. From the Modules Administration menu, click on the Topics icon: You will come to the Topics Manager area. Scroll down to the Add a New Topic panel The first topic we create will be Dinosaur Hunting. Enter the text dinosaurhunting in the Topic Name field, enter Dinosaur Hunting into the Topic Text field, and select the file dinosaurhunting.gif from the Topic Image drop-down box: Click on the Add Topic button. When the screen refreshes, the newly created topic will be displayed in the Current Active Topics panel: This process can be repeated for our other topics: Topic Text   Topic Name   Topic Image   Fossils   fossils fossils.gif Dinosaurs   dinosaurs dinosaurs.gif What Just Happened? The Topics Manager, reached through the Topics icon in the administration menu, is the area from where we can add, edit, or delete topics. The Topics Manager has two panels, one showing the Current Active Topics, and the other being the Add a New Topic panel. A topic requires three pieces of data: A topic name, which is a short piece of text with no spaces. This is mostly used internally by PHP-Nuke. The topic name is usually the same as the topic text, but in lower case and with no spaces. The topic text, which is the title of the topic. The topic image, the name of which is selected from the list of files in the images/topics folder. You can use the same image for more than one topic if you choose. Once you have saved a topic, clicking on its image in the Current Active Topics panel takes you to the Edit Topic area, from where you can edit or delete your topic. You may have noted that we haven't created the Dinosaur Portal topic. We'll do that now by editing the existing default topic, since we would like this to be the default topic for the portal anyway. Time For Action—Editing Topics We will edit the default topic to get the Dinosaur Portal topic: In the Topic Manager area, click on the PHP-Nuke topic icon in the list of Current Active Topics. When the page loads, enter the details as shown below: Click on the Save Changes button to complete your editing. When the page reloads, you will need to click on the Topics icon again to return to the Topic Manager area, since you will be returned to the page for editing the topic. What Just Happened? We just edited the properties of an already existing topic. To get at a topic's properties, you click on its icon in the list of Current Active Topics in the Topic Manager area. The possible topic icons are again picked from the images/topics folder and displayed in a drop-down list for you to choose. Once you are done making changes to the topic, clicking on the Save Changes button updates the topic. Note that there is no cancel button, and if you decide to make no changes here, you can click on the Topics icon in the Modules Administration menu to return to the Topic Manager area, or use the back button on your browser. Deleting a Topic It is possible to delete topics by clicking on the Delete link next to the Save Changes button. However, deleting a topic will delete all the stories that belong to that topic. Fortunately, there is a confirmation screen before the topic is deleted: Since the Delete link is positioned so close to Save Changes, it's probably good that there is this screen. There is no turning back after you click on Yes on this screen. Your topic is gone, and so are all the stories, and any comments attached to those stories. Note that the image associated with the topic is not deleted, and it still remains on the server, and can be used for another topic if wished.
Read more
  • 0
  • 0
  • 2839
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-data-analytics
Packt
19 Nov 2013
4 min read
Save for later

Data Analytics

Packt
19 Nov 2013
4 min read
Introduction Data Analytics is the art of taking data and deriving information from it in order to make informed decisions. A large part of building and validating datasets for the decision making process is data integration—the moving, cleansing, and transformation of data from the source to a target. This article will focus on some of the tools that take Kettle beyond the normal data processing capabilities and integrate processes into analytical tools. Reading data from a SAS datafile SAS is one of the leading analytics suites, providing robust commercial tools for decision making in many different fields. Kettle can read files written in SAS' specialized data format known as sas7bdat using a new (since Version 4.3) input step called SAS Input. While SAS does support other format types (such as CSV and Excel), sas7bdat is a format most similar to other analytics packages' special formats (such as Weka's ARFF file format). This recipe will show you how to do it. Why read a SAS file? There are two main reasons for wanting to read a SAS file as part of a Kettle process. The first is that a dataset created by a SAS program is already in place, but the output of this process is used elsewhere in other Business Intelligence solutions (for instance, using the output for integration into reports, visualizations, or other analytic tools). The second is when there is already a standard library of business logic and rules built in Kettle that the dataset needs to run through before it can be used. Getting ready To be able to use the SAS Input step, a sas7bdat file will be required. The Centers for Disease Control and Prevention have some sample datasets as part of the NHANES Dietary dataset. Their tutorial datasets can be found at their website at http://www.cdc.gov/nchs/tutorials/dietary/downloads/downloads.htm. We will be using the calcmilk.sas7bdat dataset for this recipe. How to do it... Perform the following steps to read in the calcmilk.sas7bd dataset: Open Spoon and create a new transformation. From the input folder of the Design pallet, bring over a Get File Names step. Open the Get File Names step. Click on the Browse button and find the calcmilk. sas7bd file downloaded for the recipe and click on OK. From the input folder of the Design pallet, bring over a SAS Input step. Create a hop from the Get File Names step to the SAS Input step. Open the SAS Input step. For the Field in the input to use as filename field, select the Filename field from the dropdown. Click on Get Fields. Select the calcmilk.sas7bd file and click on OK. If you are using Version 4.4 of Kettle, you will receive a java.lang.NoClassDefFoundError message. There is a work around which can be found on the Pentaho wiki at http://wiki.pentaho.com/display/EAI/SAS+Input. To clean the stream up and only have the calcmilk data, add a Select Values step and add a hop between the SAS Input step to the Select Values step. Open the Select Values step and switch to the Remove tab. Select the fields generated from the Get File Names step (filename, short_filename, path, and so on). Click on OK to close the step. Preview the Select Values step. The data from the SAS Input step should appear in a data grid, as shown in the following screenshot: How it works... The SAS Input step takes advantage of Kasper Sørensen's Sassy Reader project (http://sassyreader.eobjects.org). Sassy is a Java library used to read datasets in the sas7bdat format and is derived from the R package created by Matt Shotwell (https://github.com/BioStatMatt/sas7bdat). Before those projects, it was not possible to read the proprietary file format outside of SAS' own tools. The SAS Input step requires the processed filenames to be provided from another step (like the Get File Names step). Also of note, while the sas7bdat format only has two format types (strings and numbers), PDI is able to convert fields to any of the built-in formats (dates, integers, and so on).
Read more
  • 0
  • 0
  • 2837

article-image-openshift-java-developers
Packt
29 Oct 2014
21 min read
Save for later

OpenShift for Java Developers

Packt
29 Oct 2014
21 min read
This article written by Shekhar Gulati, the author of OpenShift Cookbook, covers how Java developers can openly use OpenShift to develop and deploy Java applications. It also teaches us how to deploy Java EE 6 and Spring applications on OpenShift. (For more resources related to this topic, see here.) Creating and deploying Java EE 6 applications using the JBoss EAP and PostgreSQL 9.2 cartridges Gone are the days when Java EE or J2EE (as it was called in the olden days) was considered evil. Java EE now provides a very productive environment to build web applications. Java EE has embraced convention over configuration and annotations, which means that you are no longer required to maintain XML to configure each and every component. In this article, you will learn how to build a Java EE 6 application and deploy it on OpenShift. This article assumes that you have basic knowledge of Java and Java EE 6. If you are not comfortable with Java EE 6, please read the official tutorial at http://docs.oracle.com/javaee/6/tutorial/doc/. In this article, you will build a simple job portal that will allow users to post job openings and view a list of all the persisted jobs in the system. These two functionalities will be exposed using two REST endpoints. The source code for the application created in this article is on GitHub at https://github.com/OpenShift-Cookbook/chapter7-jobstore-javaee6-simple. The example application that you will build in this article is a simple version of the jobstore application with only a single domain class and without any application interface. You can get the complete jobstore application source code on GitHub as well at https://github.com/OpenShift-Cookbook/chapter7-jobstore-javaee6. Getting ready To complete this article, you will need the rhc command-line client installed on your machine. Also, you will need an IDE to work with the application code. The recommended IDE to work with OpenShift is Eclipse Luna, but you can also work with other IDEs, such as IntelliJ Idea and NetBeans. Download and install the Eclipse IDE for Java EE developers from the official website at https://www.eclipse.org/downloads/. How to do it… Perform the following steps to create the jobstore application: Open a new command-line terminal, and go to a convenient location. Create a new JBoss EAP application by executing the following command: $ rhc create-app jobstore jbosseap-6 The preceding command will create a Maven project and clone it to your local machine. Change the directory to jobstore, and execute the following command to add the PostgreSQL 9.2 cartridge to the application: $ rhc cartridge-add postgresql-9.2 Open Eclipse and navigate to the project workspace. Then, import the application created in step 1 as a Maven project. To import an existing Maven project, navigate to File|Import|Maven|Existing Maven Projects. Then, navigate to the location of your OpenShift Maven application created in step 1. Next, update pom.xml to use Java 7. The Maven project created by OpenShift is configured to use JDK 6. Replace the properties with the one shown in the following code: <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> Update the Maven project to allow the changes to take effect. You can update the Maven project by right-clicking on the project and navigating to Maven|Update Project. Now, let us write the domain classes for our application. Java EE uses JPA to define the data model and manage entities. The application has one domain class: Job. Create a new package called org.osbook.jobstore.domain, and then create a new Java class called Job inside it. Have a look at the following code: @Entity public class Job {   @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;   @NotNull private String title;   @NotNull @Size(max = 4000) private String description;   @Column(updatable = false) @Temporal(TemporalType.DATE) @NotNull private Date postedAt = new Date();   @NotNull private String company;   //setters and getters removed for brevity   } Create a META-INF folder at src/main/resources, and then create a persistence.xml file with the following code: <?xml version="1.0" encoding="UTF-8"?> <persistence xsi_schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0" >   <persistence-unit name="jobstore" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data- source>java:jboss/datasources/PostgreSQLDS</jta-data- source>   <exclude-unlisted-classes>false</exclude-unlisted- classes>   <properties> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="update" /> </properties> </persistence-unit>   </persistence> Now, we will create the JobService class that will use the JPA EntityManager API to work with the database. Create a new package called org.osbook.jobstore.services, and create a new Java class as shown in the following code. It defines the save and findAll operations on the Job entity. @Stateless public class JobService {   @PersistenceContext(unitName = "jobstore") private EntityManager entityManager;   public Job save(Job job) { entityManager.persist(job); return job; }   public List<Job> findAll() { return entityManager .createQuery("SELECT j from org.osbook.jobstore.domain.Job j order by j.postedAt desc", Job.class) .getResultList(); } } Next, enable Contexts and Dependency Injection (CDI) in the jobstore application by creating a file with the name beans.xml in the src/main/webapp/WEB-INF directory as follows: <?xml version="1.0"?> <beans xsi_schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd"/> The jobstore application will expose the REST JSON web service. Before you can write the JAX-RS resources, you have to configure JAX-RS in your application. Create a new package called org.osbook.jobstore.rest and a new class called RestConfig, as shown in the following code: @ApplicationPath("/api/v1") public class RestConfig extends Application { } Create a JAX-RS resource to expose the create and findAll operations of JobService as REST endpoints as follows: @Path("/jobs") public class JobResource {   @Inject private JobService jobService;   @POST @Consumes(MediaType.APPLICATION_JSON) public Response createNewJob(@Valid Job job) { job = jobService.save(job); return Response.status(Status.CREATED).build(); }   @GET @Produces(MediaType.APPLICATION_JSON) public List<Job> showAll() { return jobService.findAll(); } } Commit the code, and push it to the OpenShift application as shown in the following commands: $ git add . $ git commit -am "jobstore application created" $ git push After the build finishes successfully, the application will be accessible at http://jobstore-{domain-name}.rhcloud.com. Please replace domain-name with your own domain name. To test the REST endpoints, you can use curl. curl is a command-line tool for transferring data across various protocols. We will use it to test our REST endpoints. To create a new job, you will run the following curl command: $ curl -i -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"title":"OpenShift Evangelist","description":"OpenShift Evangelist","company":"Red Hat"}'http://jobstore-{domain- name}.rhcloud.com/api/v1/jobs To view all the jobs, you can run the following curl command: $ curl http://jobstore-{domain-name}.rhcloud.com/api/v1/jobs How it works… In the preceding steps, we created a Java EE application and deployed it on OpenShift. In step 1, you used the rhc create-app command to create a JBoss EAP web cartridge application. The rhc command-line tool makes a request to the OpenShift broker and asks it to create a new application using the JBoss EAP cartridge. Every OpenShift web cartridge specifies a template application that will be used as the default source code of the application. For Java web cartridges (JBoss EAP, JBoss AS7, Tomcat 6, and Tomcat 7), the template is a Maven-based application. After the application is created, it is cloned to the local machine using Git. The directory structure of the application is shown in the following command: $ ls -a .git .openshift README.md pom.xml deployments src As you can see in the preceding command, apart from the .git and .openshift directories, this looks like a standard Maven project. OpenShift uses Maven to manage application dependencies and build your Java applications. Let us take a look at what's inside the jobstore directory to better understand the layout of the application: The src directory: This directory contains the source code for the template application generated by OpenShift. You need to add your application source code here. The src folder helps in achieving source code deployment when following the standard Maven directory conventions. The pom.xml file: The Java applications created by OpenShift are Maven-based projects. So, a pom.xml file is required when you do source code deployment on OpenShift. This pom.xml file has a profile called openshift, which will be executed when you push code to OpenShift as shown in the following code. This profile will create a ROOT WAR file based upon your application source code. <profiles> <profile> <id>openshift</id> <build> <finalName>jobstore</finalName> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <outputDirectory>deployments</outputDirectory> <warName>ROOT</warName> </configuration> </plugin> </plugins> </build> </profile> </profiles> The deployments directory: You should use this directory if you want to do binary deployments on OpenShift, that is, you want to deploy a WAR or EAR file directly instead of pushing the source code. The .git directory: This is a local Git repository. This directory contains the complete history of the repository. The config file in.git/ contains the configuration for the repository. It defines a Git remote origin that points to the OpenShift application gear SSH URL. This makes sure that when you do git push, the source code is pushed to the remote Git repository hosted on your application gear. You can view the details of the origin Git remote by executing the following command: $ git remote show origin The .openshift directory: This is an OpenShift-specific directory, which can be used for the following purposes: The files under the action_hooks subdirectory allow you to hook onto the application lifecycle. The files under the config subdirectory allow you to make changes to the JBoss EAP configuration. The directory contains the standalone.xml JBoss EAP-specific configuration file. The files under the cron subdirectory are used when you add the cron cartridge to your application. This allows you to run scripts or jobs on a periodic basis. The files under the markers subdirectory allow you to specify whether you want to use Java 6 or Java 7 or you want to do hot deploy or debug the application running in the Cloud, and so on. In step 2, you added the PostgreSQL 9.2 cartridge to the application using the rhc cartridge-add command. We will use the PostgreSQL database to store the jobstore application data. Then, in step 3, you imported the project in the Eclipse IDE as a Maven project. Eclipse Kepler has inbuilt support for Maven applications, which makes it easier to work with Maven-based applications. From step 3 through step 5, you updated the project to use JDK 1.7 for the Maven compiler plugin. All the OpenShift Java applications use OpenJDK 7, so it makes sense to update the application to also use JDK 1.7 for compilation. In step 6, you created the job domain class and annotated it with JPA annotations. The @Entity annotation marks the class as a JPA entity. An entity represents a table in the relational database, and each entity instance corresponds to a row in the table. Entity class fields represent the persistent state of the entity. You can learn more about JPA by reading the official documentation at http://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html. The @NotNull and @Size annotation marks are Bean Validation annotations. Bean Validation is a new validation model available as a part of the Java EE 6 platform. The @NotNull annotation adds a constraint that the value of the field must not be null. If the value is null, an exception will be raised. The @Size annotation adds a constraint that the value must match the specified minimum and maximum boundaries. You can learn more about Bean Validation by reading the official documentation at http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html. In JPA, entities are managed within a persistence context. Within the persistence context, the entity manager manages the entities. The configuration of the entity manager is defined in a standard configuration XML file called persitence.xml. In step 7, you created the persistence.xml file. The most important configuration option is the jta-datasource-source configuration tag. It points to java:jboss/datasources/PostgreSQLDS. When a user creates a JBoss EAP 6 application, then OpenShift defines a PostgreSQL datasource in the standalone.xml file. The standalone.xml file is a JBoss configuration file, which includes the technologies required by the Java EE 6 full profile specification plus Java Connector 1.6 architecture, Java XML API for RESTful web services, and OSGi. Developers can override the configuration by making changes to the standalone.xml file in the .openshift/config location of your application directory. So, if you open the standalone.xml file in .openshift/config/ in your favorite editor, you will find the following PostgreSQL datasource configuration: <datasource jndi-name="java:jboss/datasources/PostgreSQLDS" enabled="${postgresql.enabled}" use-java-context="true" pool- name="PostgreSQLDS" use-ccm="true"> <connection- url>jdbc:postgresql://${env.OPENSHIFT_POSTGRESQL_DB_HOST}:${env.OP ENSHIFT_POSTGRESQL_DB_PORT}/${env.OPENSHIFT_APP_NAME} </connection-url> <driver>postgresql</driver> <security> <user-name>${env.OPENSHIFT_POSTGRESQL_DB_USERNAME}</user-name> <password>${env.OPENSHIFT_POSTGRESQL_DB_PASSWORD}</password> </security> <validation> <check-valid-connection-sql>SELECT 1</check-valid-connection- sql> <background-validation>true</background-validation> <background-validation-millis>60000</background-validation- millis> <!--<validate-on-match>true</validate-on-match> --> </validation> <pool> <flush-strategy>IdleConnections</flush-strategy> <allow-multiple-users /> </pool> </datasource> In step 8, you created stateless Enterprise JavaBeans (EJBs) for our application service layer. The service classes work with the EntityManager API to perform operations on the Job entity. In step 9, you configured CDI by creating the beans.xml file in the src/main/webapp/WEB-INF directory. We are using CDI in our application so that we can use dependency injection instead of manually creating the objects ourselves. The CDI container will manage the bean life cycle, and the developer just has to write the business logic. To let the JBoss application server know that we are using CDI, we need to create a file called beans.xml in our WEB-INF directory. The file can be completely blank, but its presence tells the container that the CDI framework needs to be loaded. In step 10 and step 11, you configured JAX-RS and defined the REST resources for the Job entity. You activated JAX-RS by creating a class that extends javax.ws.rs.ApplicationPath. You need to specify the base URL under which your web service will be available. This is done by annotating the RestConfig class with the ApplicationPath annotation. You used /api/v1 as the application path. In step 12, you added and committed the changes to the local repository and then pushed the changes to the application gear. After the bits are pushed, OpenShift will stop all the cartridges and then invoke the mvn -e clean package -Popenshift -DskipTests command to build the project. Maven will build a ROOT.war file, which will be copied to the JBoss EAP deployments folder. After the build successfully finishes, all the cartridges are started. Then the new updated ROOT.war file will be deployed. You can view the running application at http://jobstore-{domain-name}.rhcloud.com. Please replace {domain-name} with your account domain name. Finally, you tested the REST endpoints using curl in step 14. There's more… You can perform all the aforementioned steps with just a single command as follows: $ rhc create-app jobstore jbosseap postgresql-9.2 --from-code https://github.com/OpenShift-Cookbook/chapter7-jobstore-javaee6-simple.git --timeout 180 Configuring application security by defining the database login module in standalone.xml The application allows you to create company entities and then assign jobs to them. The problem with the application is that it is not secured. The Java EE specification defines a simple, role-based security model for EJBs and web components. JBoss security is an extension to the application server and is included by default with your OpenShift JBoss applications. You can view the extension in the JBoss standalone.xml configuration file. The standalone.xml file exists in the .openshift/config location. The following code shows the extension: <extension module="org.jboss.as.security" /> OpenShift allows developers to update the standalone.xml configuration file to meet their application needs. You make a change to the standalone.xml configuration file, commit the change to the local Git repository, then push the changes to the OpenShift application gear. Then, after the successful build, OpenShift will replace the existing standalone.xml file with your updated configuration file and then finally start the server. But please make sure that your changes are valid; otherwise, the application will fail to start. In this article, you will learn how to define the database login module in standalone.xml to authenticate users before they can perform any operation with the application. The source code for the application created in this article is on GitHub at https://github.com/OpenShift-Cookbook/chapter7-jobstore-security. Getting ready This article builds on the Java EE 6 application. How to do it… Perform the following steps to add security to your web application: Create the OpenShift application using the following command: $ rhc create-app jobstore jbosseap postgresql-9.2 --from-code https://github.com/OpenShift-Cookbook/chapter7-jobstore- javaee6-simple.git --timeout 180 After the application creation, SSH into the application gear, and connect with the PostgreSQL database using the psql client. Then, create the following tables and insert the test data: $ rhc ssh $ psql jobstore=# CREATE TABLE USERS(email VARCHAR(64) PRIMARY KEY, password VARCHAR(64)); jobstore=# CREATE TABLE USER_ROLES(email VARCHAR(64), role VARCHAR(32)); jobstore=# INSERT into USERS values('admin@jobstore.com', 'ISMvKXpXpadDiUoOSoAfww=='); jobstore=# INSERT into USER_ROLES values('admin@jobstore.com', 'admin'); Exit from the SSH shell, and open the standalone.xml file in the.openshift/config directory. Update the security domain with the following code: <security-domain name="other" cache-type="default"> <authentication> <login-module code="Remoting" flag="optional"> <module-option name="password-stacking" value="useFirstPass" /> </login-module> <login-module code="Database" flag="required"> <module-option name="dsJndiName" value="java:jboss/datasources/PostgreSQLDS" /> <module-option name="principalsQuery" value="select password from USERS where email=?" /> <module-option name="rolesQuery" value="select role, 'Roles' from USER_ROLES where email=?" /> <module-option name="hashAlgorithm" value="MD5" /> <module-option name="hashEncoding" value="base64" /> </login-module> </authentication> </security-domain> Create the web deployment descriptor (that is, web.xml) in the src/main/webapp/WEB-INF folder. Add the following content to it: <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xsi_schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">   <security-constraint> <web-resource-collection> <web-resource-name>WebAuth</web-resource-name> <description>application security constraints </description> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <realm-name>jdbcRealm</realm-name> <form-login-config> <form-login-page>/login.html</form-login- page> <form-error-page>/error.html</form-error- page> </form-login-config> </login-config> <security-role> <role-name>admin</role-name> </security-role>   </web-app> Create the login.html file in the src/main/webapp directory. The login.html page will be used for user authentication. The following code shows the contents of this file: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Login</title> <link href="//cdnjs.cloudflare.com/ajax/libs/twitter- bootstrap/3.1.1/css/bootstrap.css" rel="stylesheet"> </head> <body> <div class="container"> <form class="form-signin" role="form" method="post" action="j_security_check"> <h2 class="form-signin-heading">Please sign in</h2> <input type="text" id="j_username" name="j_username" class="form-control" placeholder="Email address" required autofocus> <input type="password" id="j_password" name="j_password" class="form-control" placeholder="Password" required> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </div> </body> </html> Create an error.html file in the src/main/webapp directory. The error.html page will be shown after unsuccessful authentication. The following code shows the contents of this file: <!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <title>Error page</title> </head> <body> <h2>Incorrect username/password</h2> </body> </html> Commit the changes, and push them to the OpenShift application gear: $ git add . $ git commit –am "enabled security" $ git push Go to the application page at http://jobstore-{domain-name}.rhcloud.com, and you will be asked to log in before you can view the application. Use admin@jobstore.com/admin as the username-password combination to log in to the application. How it works… Let's now understand what you did in the preceding steps. In step 1, you recreated the jobstore application we developed previously. Next, in step 2, you performed an SSH into the application gear and created the USERS and USER_ROLES tables. These tables will be used by the JBoss database login module to authenticate users. As our application does not have the user registration functionality, we created a default user for the application. Storing the password as a clear text string is a bad practice, so we have stored the MD5 hash of the password. The MD5 hash of the admin password is ISMvKXpXpadDiUoOSoAfww==. If you want to generate the hashed password in your application, I have included a simple Java class, which uses org.jboss.crypto.CryptoUtil to generate the MD5 hash of any string. The CryptoUtil class is part of the picketbox library. The following code depicts this: import org.jboss.crypto.CryptoUtil;   public class PasswordHash {   public static String getPasswordHash(String password) { return CryptoUtil.createPasswordHash("MD5", CryptoUtil.BASE64_ENCODING, null, null, password); }   public static void main(String[] args) throws Exception { System.out.println(getPasswordHash("admin")); } } In step 3, you logged out of the SSH session and updated the standalone.xml JBoss configuration file with the database login module configuration. There are several login module implementations available out of the box. This article will only talk about the database login module, as discussing all the modules is outside the scope of this article. You can read about all the login modules at https://docs.jboss.org/author/display/AS7/Security+subsystem+configuration. The database login module checks the user credentials against a relational database. To configure the database login module, you have to specify a few configuration options. The dsJndiName option is used to specify the application datasource. As we are using a configured PostgreSQL datasource for our application, you specified the same dsJndiName option value. Next, you have to specify the SQL queries to fetch the user and its roles. Then, you have specified that the password will be hashed against an MD5 hash algorithm by specifying the hashAlgorithm configuration. In step 4, you applied the database login module to the jobstore application by defining the security constraints in web.xml. This configuration will add a security constraint on all the web resources of the application that will restrict access to authenticated users with role admin. You have also configured your application to use FORM-based authentication. This will make sure that when unauthenticated users visit the website, they will be redirected to the login.html page created in step 5. If the user enters a wrong e-mail/password combination, then they will be redirected to the error.html page created in step 6. Finally, in step 7, you committed the changes to the local Git repository and pushed the changes to the application gear. OpenShift will make sure that the JBoss EAP application server uses the updated standalone.xml configuration file. Now, the user will be asked to authenticate before they can work with the application. Summary This article showed us how to configure application security. In, this article, we also learned about the different ways in which Java applications can be developed on OpenShift. The article explained the database login module. Further resources on this subject: Using OpenShift [Article] Troubleshooting [Article] Schemas and Models [Article]
Read more
  • 0
  • 0
  • 2837

article-image-building-your-first-liferay-site
Packt
09 Dec 2011
10 min read
Save for later

Building your First Liferay Site

Packt
09 Dec 2011
10 min read
(For more resources on Liferay, see here.) Designing the site—Painting the full picture Whenever we get the requirements of the portal, we should first ask ourselves, how would the portal look when it goes live? This one small question will open a door to a lot of other questions and a lot of information which will be required to implement the portal. You should always think of the end state of the portal and then start painting the complete picture of the portal. This approach is known as Beginning with the end! Now, as we have just discussed the approach we should take while starting the site design, let's understand what all information we can retrieve or get when we ask this question to ourselves. We will discuss about four main components of the Liferay Portal design. Users No matter what the size of the portal is—small, medium, or large—it will always be designed for the users. Users are in the centre of any portal application. Let's think of a very common portal . Just imagine that you have to implement this portal. You are sitting in your nice comfy rocking chair and thinking of the users of this portal. What all questions would you ask yourself (or the client, if he/she is with you) about the users of this portal? Who will be using this portal? Will all the users of this portal have same privileges? Or some of the users will have more privileges than the others? Can we collect the users into any specific group? Are these users organized in any kind of hierarchy? Can the users leave or join this portal at a will? and so on. Let's discuss about the answers of the preceding questions and the information we can retrieve from the answers. If we want to provide an answer to the first question in only one word, then it can be—Users. It is quite obvious that the portal will be used by the users. However, if we go into the detail of this question we can get an answer like—all the employees of CNN, users from all over the world, John Walter who will be responsible for administration of the entire portal, and so on. So answer to this question will give us the information about all the players associated with the portal. If all the users of the portal have the same privileges, then it's pretty obvious that every user will be able to perform the same function in the portal. However, this may not be the case with our portal, http://www.cnn.com. Here, we have end users who will come to the portal and will access the information; we can also have a group of internal users who can create the content. Similarly, there can be a group who can approve the content and other group can publish the content; there can also be users who are responsible for user management and one or more users who are responsible for portal management. So this kind of question will give us the information about different roles and permissions associated with each role in our portal. It will also give us the information that which user should be associated with what kind of role. This information is very useful when we are working on the security piece of the portal. If the answer of the third question is Yes then we can either create a user group in our portal or group the users into a specific role. This question will give us the information about user grouping in the portal. Now, answer to the last two questions will be discussed in more detail in this article. However, these questions will help us to determine if we should use an organization or community or mix of both in our portal. Every Liferay portal must have at least one organization or community. Once we get the information about the users in our portal, we should think of the content next. Content No portal can exist without any kind of content. The content can be a static HTML, a static article, a video, an audio, podcast, document, image, and so on. To make the portal more effective, we have to manage the content properly. We will continue with our example of : What kind of questions can we ask to ourselves or to the client about the content? What are the types of the content we will use in the portal? Where are we going to store the content? To Liferay database or some other external database? Do we have to create the content from scratch? Do we need to migrate existing content to Liferay? And a few more… I am sure you must be visiting many portals every day. Can you think of the possible answers to the preceding questions? Also, think of the information which can be retrieved from those answers. The answer to the first question can be—document, images, web content, video, podcast, and so on. This answer will give us an idea about the content types which need to be implemented in the portal. Some of the contents like web content and documents can be available out of the box in Liferay; while other content types like video and podcast may not be available out of the box. This information will enable us to determine the types of content and effort required to create the new content. The second question is a bit tricky. By default, Liferay stores the content into database. However, if the client wants we can store the content in some shared location as well. For example, storing documents into file system. Also, it requires high availability. You can refer to wiki about this on . An answer to this question will give us information like if we need to add any additional hardware or not, if we should do any specific configuration to meet the requirements, and so on. Answers to third and fourth questions will be mainly used for the effort estimation purpose. If a portal like site is using a huge amount of content and all of the content has to be built from scratch then it may take a good amount of time. If the content is already available, we need to consider the effort of migrating and organizing the content. We can also think of other questions like requirement of workflow, type of the flow, if staging is required for the content or not, and so on. All of the preceding questions will give us a good picture about the content to be created or used in our portal. Once we are thorough with this section, it becomes easy to appoint someone to do content management design for your portal. Applications We have discussed about the user and the content of the portal. However, what about the applications which will be hosted on the portal? We cannot imagine a portal without any application nowadays. A portal can contain different applications like forum, wiki, blog, document viewer, video player, and so on. Let's get a list of questions which you should ask yourself while designing the portal: What are the different applications required for this portal? Do we have any application available out of the box in Liferay? Do we need to modify/extend any existing application available in Liferay? Do we have to create any new application for the portal? If yes, what is the complexity of this application? Liferay ships with more than 60 applications pre-bundled known as portlets. These applications are very commonly used in many of the portals. When we ask the preceding questions to ourselves, we get the following information: Number of applications required in the portal How many applications are available out of the box? If we have more applications available OOTB (out of the box) then it can reduce the development time Scope of the project. Do we need any integration with external system or not? Kind of applications we have to develop from scratch Once we go through the preceding set of questions, it becomes very easy to get a picture of the applications required in our portal. It will also provide us information about the efforts required to implement the portal. So, if you are managing a portal development project, you should definitely have this information ready before starting the project. Security Security is one of the most important aspects of any portal. When we think about security of any portal, we must consider access to the content and the applications on the portal. You must have come across many of the portals so far. Some of the portals allow you to create your account freely; some of the portals require an authorization before creating the account while some other portals don't allow creating an account at all. Also, there are some portals which display the same information to all the users while other portals display certain information to members only. To put this in a nutshell, each of the portal has its own security policy and it operates based on that policy only. Once we have discussed/thought about users, content, and application in portal design, we should consider the security aspect of the portal. Now, let's think of some of the questions which we should normally ask to ourselves/client while considering security aspect of the portal. Can everyone freely create an account with the portal? Do we need to migrate the users from an existing user base like LDAP? Can everyone access the same information or we need to display certain information to portal members only? Can all the members of the portal access all the applications with the same privileges or certain users have more privileges over other members? And so on… When we get answers to the preceding questions, we get following information about the portal security: If everyone can create an account with the portal, then in that case we need to provide the create account functionality to the users. Also, we don't need to do any authorization on account creation. If the answer to the first question is NO, then we need to either provide some kind of authorization on account creation or we need to remove the account creation facility completely If we have to migrate the users from an existing user base, then we have to think about the migration and integration. Liferay provides integration with LDAP. If the user base is not present, then we will end up creating all the accounts manually or provide users an ability to create their accounts If all the users (including non-logged in users) can access the same information then we will keep all the information on public pages but if we want certain information to be accessed by members only, then we might consider putting that information on the pages which can be accessed only after logging in If all the members of the portal can access the same information with same privileges then we will not need any special role in the portal. However, if a certain group or a set of users have more privileges over the other—like only content creators can create content—then we will have to create a role and provide the required permission to the role When we think of the security of the portal, we are thinking about users and their access, applications and access to those applications, and other security-related aspects of the portal. It is very important to think about the security before implementing the portal to prevent unauthorized and unauthenticated access to the portal and applications within the portal.
Read more
  • 0
  • 0
  • 2837

article-image-managing-azure-hosted-services-service-management-api
Packt
08 Aug 2011
11 min read
Save for later

Managing Azure Hosted Services with the Service Management API

Packt
08 Aug 2011
11 min read
  Microsoft Windows Azure Development Cookbook Over 80 advanced recipes for developing scalable services with the Windows Azure platform         Read more about this book       (For more resources on this subject, see here.) Introduction The Windows Azure Portal provides a convenient and easy-to-use way of managing the hosted services and storage account in a Windows Azure subscription, as well as any deployments into these hosted services. The Windows Azure Service Management REST API provides a programmatic way of managing the hosted services and storage accounts in a Windows Azure subscription, as well as any deployments into these hosted services. These techniques are complementary and, indeed, it is possible to use the Service Management API to develop an application that provides nearly all the features of the Windows Azure Portal. The Service Management API provides almost complete control over the hosted services and storage accounts contained in a Windows Azure subscription. All operations using this API must be authenticated using an X.509 management certificate. We see how to do this in the Authenticating against the Windows Azure Service Management REST API recipe in Controlling Access in the Windows Azure Platform. In Windows Azure, a hosted service is an administrative and security boundary for an application. A hosted service specifies a name for the application, as well as specifying a Windows Azure datacenter or affinity group into which the application is deployed. In the Creating a Windows Azure hosted service recipe, we see how to use the Service Management API to create a hosted service. A hosted service has no features or functions until an application is deployed into it. An application is deployed by specifying a deployment slot, either production or staging, and by providing the application package containing the code, as well as the service configuration file used to configure the application. We see how to do this using the Service Management API in the Deploying an application into a hosted service recipe. Once an application has been deployed, it probably has to be upgraded occasionally. This requires the provision of a new application package and service configuration file. We see how to do this using the Service Management API in the Upgrading an application deployed to a hosted service recipe. A hosted service has various properties defining it as do the applications deployed into it. There could, after all, be separate applications deployed into each of the production and staging slots. In the Retrieving the properties of a hosted service recipe, we see how to use the Service Management API to get these properties. An application deployed as a hosted service in Windows Azure can use the Service Management API to modify itself while running. Specifically, an application can autoscale by varying the number of role instances to match anticipated demand. We see how to do this in the Autoscaling with the Windows Azure Service Management REST API recipe. We can use the Service Management API to develop our own management applications. Alternatively, we can use one of the PowerShell cmdlets libraries that have already been developed using the API. Both the Windows Azure team and Cerebrata have developed such libraries. We see how to use them in the Using the Windows Azure Platform PowerShell Cmdlets recipe. Creating a Windows Azure hosted service A hosted service is the administrative and security boundary for an application deployed to Windows Azure. The hosted service specifies the service name, a label, and either the Windows Azure datacenter location or the affinity group into which the application is to be deployed. These cannot be changed once the hosted service is created. The service name is the subdomain under cloudapp.net used by the application, and the label is a humanreadable name used to identify the hosted service on the Windows Azure Portal. The Windows Azure Service Management REST API exposes a create hosted service operation. The REST endpoint for the create hosted service operation specifies the subscription ID under which the hosted service is to be created. The request requires a payload comprising an XML document containing the properties needed to define the hosted service, as well as various optional properties. The service name provided must be unique across all hosted services in Windows Azure, so there is a possibility that a valid create hosted service operation will fail with a 409 Conflict error if the provided service name is already in use. As the create hosted service operation is asynchronous, the response contains a request ID that can be passed into a get operation status operation to check the current status of the operation. In this recipe, we will learn how to use the Service Management API to create a Windows Azure hosted service. Getting ready The recipes in this article use the ServiceManagementOperation utility class to invoke operations against the Windows Azure Service Management REST API. We implement this class as follows: Add a class named ServiceManagementOperation to the project. Add the following assembly reference to the project: System.Xml.Linq.dll Add the following using statements to the top of the class file: using System.Security.Cryptography.X509Certificates;using System.Net;using System.Xml.Linq;using System.IO; Add the following private members to the class: String thumbprint;String versionId = "2011-02-25"; Add the following constructor to the class: public ServiceManagementOperation(String thumbprint){ this.thumbprint = thumbprint;} Add the following method, retrieving an X.509 certificate from the certificate store, to the class: private X509Certificate2 GetX509Certificate2( String thumbprint){ X509Certificate2 x509Certificate2 = null; X509Store store = new X509Store("My", StoreLocation.LocalMachine); try { store.Open(OpenFlags.ReadOnly); X509Certificate2Collection x509Certificate2Collection = store.Certificates.Find( X509FindType.FindByThumbprint, thumbprint, false); x509Certificate2 = x509Certificate2Collection[0]; } finally { store.Close(); } return x509Certificate2;} Add the following method, creating an HttpWebRequest, to the class: private HttpWebRequest CreateHttpWebRequest( Uri uri, String httpWebRequestMethod){ X509Certificate2 x509Certificate2 = GetX509Certificate2(thumbprint); HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri); httpWebRequest.Method = httpWebRequestMethod; httpWebRequest.Headers.Add("x-ms-version", versionId); httpWebRequest.ClientCertificates.Add(x509Certificate2); httpWebRequest.ContentType = "application/xml"; return httpWebRequest;} Add the following method, invoking a GET operation on the Service Management API, to the class: public XDocument Invoke(String uri){ XDocument responsePayload; Uri operationUri = new Uri(uri); HttpWebRequest httpWebRequest = CreateHttpWebRequest(operationUri, "GET"); using (HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse()) { Stream responseStream = response.GetResponseStream(); responsePayload = XDocument.Load(responseStream); } return responsePayload;} Add the following method, invoking a POST operation on the Service Management API, to the class: public String Invoke(String uri, XDocument payload){ Uri operationUri = new Uri(uri); HttpWebRequest httpWebRequest = CreateHttpWebRequest(operationUri, "POST"); using (Stream requestStream = httpWebRequest.GetRequestStream()) { using (StreamWriter streamWriter = new StreamWriter(requestStream, System.Text.UTF8Encoding.UTF8)) { payload.Save(streamWriter, SaveOptions.DisableFormatting); } } String requestId; using (HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse()) { requestId = response.Headers["x-ms-request-id"]; } return requestId;} How it works... In steps 1 through 3, we set up the class. In step 4, we add a version ID for service management operations. Note that Microsoft periodically releases new operations for which it provides a new version ID, which is usually applicable for operations added earlier. In step 4, we also add a private member for the X.509 certificate thumbprint that we initialize in the constructor we add in step 5. In step 6, we open the Personal (My) certificate store on the local machine level and retrieve an X.509 certificate identified by thumbprint. If necessary, we can specify the current user level, instead of the local machine level, by using StoreLocation.CurrentUser instead of StoreLocation.LocalMachine. In step 7, we create an HttpWebRequest with the desired HTTP method type, and add the X.509 certificate to it. We also add various headers including the required x-ms-version. In step 8, we invoke a GET request against the Service Management API and load the response into an XML document which we then return. In step 9, we write an XML document, containing the payload, into the request stream for an HttpWebRequest and then invoke a POST request against the Service Management API. We extract the request ID from the response and return it. How to do it... We are now going to construct the payload required for the create hosted service operation, and then use it when we invoke the operation against the Windows Azure Service Management REST API. We do this as follows: Add a new class named CreateHostedServiceExample to the WPF project. If necessary, add the following assembly reference to the project: System.Xml.Linq.dll Add the following using statement to the top of the class file: using System.Xml.Linq; Add the following private members to the class: XNamespace wa = "http://schemas.microsoft.com/windowsazure";String createHostedServiceFormat ="https://management.core.windows.net/{0}/services/hostedservices"; Add the following method, creating a base-64 encoded string, to the class: private String ConvertToBase64String(String value){ Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value); String base64String = Convert.ToBase64String(bytes); return base64String;} Add the following method, creating the payload, to the class: private XDocument CreatePayload( String serviceName, String label, String description, String location, String affinityGroup){ String base64LabelName = ConvertToBase64String(label); XElement xServiceName = new XElement(wa + "ServiceName", serviceName); XElement xLabel = new XElement(wa + "Label", base64LabelName); XElement xDescription = new XElement(wa + "Description", description); XElement xLocation = new XElement(wa + "Location", location); XElement xAffinityGroup = new XElement(wa + "AffinityGroup", affinityGroup); XElement createHostedService = new XElement(wa +"CreateHostedService"); createHostedService.Add(xServiceName); createHostedService.Add(xLabel); createHostedService.Add(xDescription); createHostedService.Add(xLocation); //createHostedService.Add(xAffinityGroup); XDocument payload = new XDocument(); payload.Add(createHostedService); payload.Declaration = new XDeclaration("1.0", "UTF-8", "no"); return payload;} Add the following method, invoking the create hosted service operation, to the class: private String CreateHostedService(String subscriptionId, String thumbprint, String serviceName, String label, String description, String location, String affinityGroup){ String uri = String.Format(createHostedServiceFormat, subscriptionId); XDocument payload = CreatePayload(serviceName, label, description, location, affinityGroup); ServiceManagementOperation operation = new ServiceManagementOperation(thumbprint); String requestId = operation.Invoke(uri, payload); return requestId;} Add the following method, invoking the methods added earlier, to the class: public static void UseCreateHostedServiceExample(){ String subscriptionId = "{SUBSCRIPTION_ID}"; String thumbprint = "{THUMBPRINT}"; String serviceName = "{SERVICE_NAME}"; String label = "{LABEL}"; String description = "Newly created service"; String location = "{LOCATION}"; String affinityGroup = "{AFFINITY_GROUP}"; CreateHostedServiceExample example = new CreateHostedServiceExample(); String requestId = example.CreateHostedService( subscriptionId, thumbprint, serviceName, label, description, location, affinityGroup);} How it works... In steps 1 through 3, we set up the class. In step 4, we add private members to define the XML namespace used in creating the payload and the String format used in generating the endpoint for the create hosted service operation. In step 5, we add a helper method to create a base-64 encoded copy of a String. We create the payload in step 6 by creating an XElement instance for each of the required and optional properties, as well as the root element. We add each of these elements to the root element and then add this to an XML document. Note that we do not add an AffinityGroup element because we provide a Location element and only one of them should be provided. In step 7, we use the ServiceManagementOperation utility class , described in the Getting ready section, to invoke the create hosted service operation on the Service Management API. The Invoke() method creates an HttpWebRequest, adds the required X.509 certificate and the payload, and then sends the request to the create hosted services endpoint. It then parses the response to retrieve the request ID which can be used to check the status of the asynchronous create hosted services operation. In step 8, we add a method that invokes the methods added earlier. We need to provide the subscription ID for the Windows Azure subscription, a globally unique service name for the hosted service, and a label used to identify the hosted service in the Windows Azure Portal. The location must be one of the official location names for a Windows Azure datacenter, such as North Central US. Alternatively, we can provide the GUID identifier of an existing affinity group and swap the commenting out in the code, adding the Location and AffinityGroup elements in step 6. We see how to retrieve the list of locations and affinity groups in the Locations and affinity groups section of this recipe. There's more... Each Windows Azure subscription can create six hosted services. This is a soft limit that can be raised by requesting a quota increase from Windows Azure Support at the following URL: http://www.microsoft.com/windowsazure/support/ There are also soft limits on the number of cores per subscription (20) and the number of Windows Azure storage accounts per subscription (5). These limits can also be increased by request to Windows Azure Support. Locations and affinity groups The list of locations and affinity groups can be retrieved using the list locations and list affinity groups operations respectively in the Service Management API. We see how to do this in the Using the Windows Azure Platform PowerShell Cmdlets recipe. As of this writing, the locations are: Anywhere US South Central US North Central US Anywhere Europe North Europe West Europe Anywhere Asia Southeast Asia East Asia The affinity groups are specific to a subscription.
Read more
  • 0
  • 0
  • 2837
article-image-installing-drupal-themes
Packt
21 Oct 2009
5 min read
Save for later

Installing Drupal Themes

Packt
21 Oct 2009
5 min read
The large and active community of developers that has formed around Drupal guarantees a steady flow of themes for this popular CMS. The diversity of that community also assures that there will be a wide variety of themes produced. Add into the equation the existence of a growing number of commercial and open source web designs and you can be certain that somewhere out there is a design that is close to what you want. The issue becomes identifying the sources of themes and designs, and determining how much work you want to do yourself. You can find both design ideas and complete themes on the Web. You need to decide whether you want to work with an existing theme, or convert a design into a theme, or whether you want to start from scratch, unburdened by any preliminary constraints or alien code. For purposes of this article, we will be dealing with finding, installing, and then uninstalling an existing and current Drupal theme. This article assumes you have a working Drupal installation, and that you have access to the files on your server. Finding Additional Themes There are several factors to consider when determining the suitability of an existing theme. The first issue is compatibility. Due to changes made to Drupal in the 5.x series, older themes will not work properly with Drupal 5.x. Accordingly, your first step is to determine which version of Drupal you are running. To find the version information for your installation, go to Administer | Logs | Status Report. The first line of the Status Report tabular data will show your version number. If you do not see the Status Report option, then you are probably using a Drupal version earlier than 5.x. We suggest you upgrade as this book is for Drupal 5.x. If you know your Drupal version, you can confirm whether the theme you are considering is usable on your system. If the theme you are looking at doesn't provide versioning information, assume the worst and make sure you back up your site before you install the questionable theme. Once you're past the compatibility hurdle, your next concern is system requirements; does the theme require any additional extensions to work properly? Some themes are ready to run with no additional extensions required. Many themes require that your Drupal installation include a particular templating engine. The most commonly required templating engine is PHPTemplate. If you are running a recent instance of Drupal, you will find that the PHPTemplate engine is installed by default. You can also download a variety of other popular templating engines, including Smarty and PHPTal from http://drupal.org/project/Theme+engines.Check carefully whether the theme you've chosen requires you to download and install other extensions. If so, track down the additional extensions and install them first, before you install your theme. A good place to start looking for a complete Drupal theme is, perhaps not surprisingly, the official Drupal site. At Drupal.org, you can find a variety of downloads, including both themes and template engines. Go to http://drupal.org/project/Themes to find a listing of the current collection of themes. All the themes state very clearly the version compatibility and whether there are any prerequisites to run the theme. In addition to the resources on the official Drupal site, there is an assortment of fan sites providing themes. Some sites are open source, others commercial, and a fair number are running unusual licenses (most frequently asking that footers be left intact with links back to their sites). Some of the themes available are great; most are average. If your firm is brand sensitive, or your design idiosyncratic, you will probably find yourself working from scratch. Regardless of your particular needs, the theme repositories are a good place to start gathering ideas. Even if you cannot find exactly what you need, you sometimes find something with which you can work. An existing set of properly formed theme files can jump start your efforts and save you a ton of time. If you wish to use an existing theme, pay attention to the terms of usage. You can save yourself (or your clients) major headaches by catching any unusual licensing provisions early in the process. There's nothing worse than spending hours on a theme only to discover its use is somehow restricted. One source for designs with livable usage policies is the Open Source Web Design site, http://www.oswd.org, which includes a repository of designs, all governed by open source licensing terms. The down side of this resource is that all you get is the design—not the code, not a ready-made theme. You will need to convert the design into a usable theme. For this article, let's search out a completed theme and for the sake of simplicity, let's take one from the official Drupal site. I am going to download the Gagarin theme from Drupal.org. I'll refer to this theme as a working example of some ofthe steps below. You can either grab a copy of the same theme or you can use another—the principles are the same regardless. Gagarin is an elegant little theme from Garamond of the Russian Drupal community. Gagarin is set up for a two-column site (though it can be run in three columns) and works particularly well for a blog site.
Read more
  • 0
  • 0
  • 2836

article-image-hadoop-monitoring-and-its-aspects
Packt
04 May 2015
8 min read
Save for later

Hadoop Monitoring and its aspects

Packt
04 May 2015
8 min read
In this article by Gurmukh Singh, the author of the book Monitoring Hadoop, tells us the importance of monitoring Hadoop and its importance. It also explains various other concepts of Hadoop, such as its architecture, Ganglia (a tool used to monitor Hadoop), and so on. (For more resources related to this topic, see here.) In any enterprise, how big or small it could be, it is very important to monitor the health of all its components like servers, network devices, databases, and many more and make sure things are working as intended. Monitoring is a critical part for any business dependent upon infrastructure, by giving signals to enable necessary actions incase of any failures. Monitoring can be very complex with many components and configurations in a real production environment. There might be different security zones; different ways in which servers are setup or a same database might be used in many different ways with servers listening on various service ports. Before diving into setting up Monitoring and logging for Hadoop, it is very important to understand the basics of monitoring, how it works and some commonly used tools in the market. In Hadoop, we can do monitoring of the resources, services and also do metrics collection of various Hadoop counters. There are many tools available in the market and one of them is Nagios, which is widely used. Nagios is a powerful monitoring system that provides you with instant awareness of your organization's mission-critical IT infrastructure. By using Nagios, you can: Plan release cycle and rollouts, before things get outdated Early detection, before it causes an outage Have automation and a better response across the organization Nagios Architecture It is based on a simple server client architecture, in which the server has the capability to execute checks remotely using NRPE agents on the Linux clients. The results of execution are captured by the server and accordingly alerted by the system. The checks could be for memory, disk, CPU utilization, network, database connection and many more. It provides the flexibility to use either active or passive checks. Ganglia Ganglia, it is a beautiful tool for aggregating the stats and plotting them nicely. Nagios, give the events and alerts, Ganglia aggregates and presents it in a meaningful way. What if you want to look for total CPU, memory per cluster of 2000 nodes or total free disk space on 1000 nodes. Some of the key feature of Ganglia. View historical and real time metrics of a single node or for the entire cluster Use the data to make decision on cluster sizing and performance Ganglia Components Ganglia Monitoring Daemon (gmond): This runs on the nodes that need to be monitored, captures state change and sends updates using XDR to a central daemon. Ganglia Meta Daemon (gmetad): This collects data from gmond and other gmetad daemons. The data is indexed and stored to disk in round robin fashion. There is also a Ganglia front-end for meaningful display of information collected. All these tools can be integrated with Hadoop, to monitor it and capture its metrics. Integration with Hadoop There are many important components in Hadoop that needs to be monitored, like NameNode uptime, disk space, memory utilization, and heap size. Similarly, on DataNode we need to monitor disk usage, memory utilization or job execution flow status across the MapReduce components. To know what to monitor, we must understand how Hadoop daemons communicate with each other. There are lots of ports used in Hadoop, some are for internal communication like scheduling jobs, and replication, while others are for user interactions. They may be exposed using TCP or HTTP. Hadoop daemons provide information over HTTP about logs, stacks, metrics that could be used for troubleshooting. NameNode can expose information about the file system, live or dead nodes or block reports by the DataNode or JobTracker for tracking the running jobs. Hadoop uses TCP, HTTP, IPC or socket for communication among the nodes or daemons. YARN Framework The YARN (Yet Another resource Negotiator) is the new MapReduce framework. It is designed to scale for large clusters and performs much better as compared to the old framework. There are new sets of daemons in the new framework and it is good to understand how to communicate with each other. The diagram that follows, explains the daemons and ports on which they talk. Logging in Hadoop In Hadoop, each daemon writes its own logs and the severity of logging is configurable. The logs in Hadoop can be related to the daemons or the jobs submitted. Useful to troubleshoot slowness, issue with map reduce tasks, connectivity issues and platforms bugs. The logs generated can be user level like task tracker logs on each node or can be related to master daemons like NameNode and JobTracker. In the newer YARN platform, there is a feature to move the logs to HDFS after the initial logging. In Hadoop 1.x the user log management is done using UserLogManager, which cleans and truncates logs according to retention and size parameters like mapred.userlog.retain.hours and mapreduce.cluster.map.userlog.retain-size respectively. The tasks standard out and error are piped to Unix tail program, so it retains the require size only. The following are some of the challenges of log management in Hadoop: Excessive logging: The truncation of logs is not done till the tasks finish, this for many jobs could cause disk space issues as the amount of data written is quite large. Truncation: We cannot always say what to log and how much is good enough. For some users 500KB of logs might be good but for some 10MB might not suffice. Retention: How long to retain logs, 1 or 6 months?. There is no rule, but there are best practices or governance issues. In many countries there is regulation in place to keep data for 1 year. Best practice for any organization is to keep it for at least 6 months. Analysis: What if we want to look at historical data, how to aggregate logs onto a central system and do analyses. In Hadoop logs are served over HTTP for a single node by default. Some of the above stated issues have been addressed in the YARN framework. Rather then truncating logs and that to on individual nodes, the logs can be moved to HDFS and processed using other tools. The logs are written at the per application level into directories per application. The user can access these logs through command line or web UI. For example, $HADOOP_YARN_HOME/bin/yarn logs. Hadoop metrics In Hadoop there are many daemons running like DataNode, NameNode, JobTracker, and so on, each of these daemons captures a lot of information about the components they work on. Similarly, in YARN framework we have ResourceManager, NodeManager, and Application Manager, each of which exposes metrics, explained in the following sections under Metrics2. For example, DataNode collects metrics like number of blocks it has for advertising to the NameNode, the number of replicated blocks, metrics about the various read or writes from clients. In addition to this there could be metrics related to events, and so on. Hence, it is very important to gather it for the working of the Hadoop cluster and also helps in debugging, if something goes wrong. For this, Hadoop has a metrics system, for collecting all this information. There are two versions of the metrics system, Metrics and Metrics2 for Hadoop 1.x and Hadoop 2.x respectively. The file hadoop-metrics.properties and hadoop-metrics2.properties for each Hadoop version can be configured respectively. Configuring Metrics2 For Hadoop version 2, which uses YARN framework, the metrics can be configured using hadoop-metrics2.properties, under the $HADOOP_HOME directory. *.sink.file.class=org.apache.hadoop.metrics2.sink.FileSink *.period=10 namenode.sink.file.filename=namenode-metrics.out datanode.sink.file.filename=datanode-metrics.out jobtracker.sink.file.filename=jobtracker-metrics.out tasktracker.sink.file.filename=tasktracker-metrics.out maptask.sink.file.filename=maptask-metrics.out reducetask.sink.file.filename=reducetask-metrics.out Hadoop metrics Configuration for Ganglia Firstly, we need to define a sink class, as per Ganglia. *.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31 Secondly, we need to define the frequency of how often the source showed be polled for data. We are polling every 30 seconds: *.sink.ganglia.period=30 Define retention for the metrics: *.sink.ganglia.dmax=jvm.metrics.threadsBlocked=70,jvm.metrics.memHeapUsedM=40 Summary In this article, we learned about Hadoop monitoring and its importance, and also the various concepts of Hadoop. Resources for Article: Further resources on this subject: Hadoop and MapReduce [article] YARN and Hadoop [article] Hive in Hadoop [article]
Read more
  • 0
  • 0
  • 2832

article-image-aspnet-35-cms-adding-security-and-membership-part-1
Packt
27 Oct 2009
10 min read
Save for later

ASP.NET 3.5 CMS: Adding Security and Membership (Part 1)

Packt
27 Oct 2009
10 min read
Security is a concern in any web application, but the security this article deals with is that of user accounts, membership, and roles. We'll be using the ASP.NET membership and roles functions to allow certain users such as administrators to perform specific tasks. These tasks may include managing the application, while other users such as content editors, may be restricted to the specific tasks we want them to manage such as adding or changing content. User account management can be handled either by the application (in our case, our Content Management System) or by Windows itself, using standard Windows authentication functions, as well as file and folder permissions. The advantage of an application-based user authentication system is primarily in cost. To use Windows authentication, we need to purchase Client Access Licenses (CALs) for each user that will access our application. This is practical in an intranet, where users would have these licenses to perform other functions in the network. However, for an Internet application, with potentially thousands of users, licensing could be extremely expensive. The drawback to an application-based system is that there is a lot more work to do in designing and using it. The Windows authentication process has been around for years, continually improved by Microsoft with each Windows release. It scales extremely well, and with Active Directory, can be extended to manage just about anything you can think of. ASP.NET membership Fortunately, Microsoft has provided relief for application-based authentication drawbacks in the 2.0 version of the ASP.NET framework, with the ASP.NET membership functions, and in our case, the SqlMembershipProvider. The membership API makes it simple for us to use forms authentication in our application, retrieving authentication and membership information from a membership provider. The membership provider abstracts the membership details from the membership storage source. Microsoft provides two providers—the ActiveDirectoryMembershipProvider that uses Active Directory and the SqlMembershipProvider that uses an SQL server database for the user data store. By default, ASP.NET authentication uses cookies—small text files stored on the user's system—to maintain authentication status throughout the application. These cookies normally have an expiration time and date, which requires users to log in again after the cookie has expired. It is possible to use cookies to allow the client system to authenticate the application without a user login, commonly seen as a "Remember Me" checkbox in many web site login pages. There is naturally a downside to cookies in that a client system may not accept cookies. ASP.NET can encode the authentication information into the URL to bypass this restriction on cookies. Although in the case of our application, we will stick with the cookie method. Forms authentication secures only ASP.NET pages. Unless you are using IIS7, and the integrated pipeline, where ASP.NET processes all file requests, the ASP.NET DLL won't be called for non-ASP.NET pages. This means that you cannot easily secure HTML pages, PDF files, or anything other than ASP.NET through forms authentication. Configuring and using forms authentication Let's start learning ASP.NET forms authentication by walking through a brand new application. We'll then add it to our Content Management System application. Forms authentication is actually quite simple, both in concept and execution, and a simple application can explain it better than adopting our current CMS application. Of course, we eventually need to integrate authentication into our CMS application, but this is also easier once you understand the principles and techniques we'll be using. Creating a new application Start by opening Visual Web Developer 2008 Express and creating a new web site by clicking on File | New Web Site. Use the ASP.NET Website template, choose File System, and name the folder FormsDemo. When the site is created, you are presented with a Default.aspx page created with generic code. We will use this as our home page for the new site, although we need to modify it for our needs. Creating the home page Visual Web Developer 2008 Express creates a generic Default.aspx file whenever you create a new site. Unfortunately, the generic file is not what we want and will need modification. The first thing we want to do is make sure our site uses a Master Page, just as our Content Management System application will. To do this, we could delete the page, create our Master Page, and then add a new Default.aspx page that uses our Master Page. In the case of a brand new site, it's pretty easy, but what if you have developed an extensive site that you want to convert to Master Pages? You would want to add a Master Page to an existing site, so let's go ahead and do that. Create the Master Page To create a Master Page, leave the Default.aspx file open and press Ctrl+Shift+A to add a new item to the solution. Choose the Master Page template and leave the name as MasterPage.Master. Place the code in a separate file and click Add to create the Master Page. You will notice that this creates the same generic code as in the previous chapter. Unfortunately, our Default.aspx file is not a content page and won't use the MasterPage.Master we just created, unless we tell it to. To tell our Default.aspx page to use the MasterPage.Master, we need to add the MasterPageFile declaration, in the @ Page declaration, at the top of the file. Add the following code between the Language and AutoEventWireup declarations: MasterPageFile="~/MasterPage.master" This adds the Master Page to our Default.aspx page. However, content pages include only those Content controls that match the Master Page, not the full page code as our Default.aspx page currently does. To fix this, replace the remaining code outside the @ Page declaration with the following two Content controls: <asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server"></asp:Content><asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server"> <h1>This is where the content goes.</h1></asp:Content> We've left the Content1 control empty for the moment, and we've added a simple text statement to the Content2 control so that it can be tested. If you view the Default.aspx page in a browser, you should see the relatively uninteresting web page below: Enabling forms authentication Okay, we have a boring home page for our new site. Let's leave it for a moment and enable forms authentication for the site, so we can restrict who can access our home page. The process of enabling forms authentication is simply adding a few lines to our web.config file. Or in the case of the generic web.config file, which we created while creating our new site, we simply need to alter a single line. Open the web.config file in the new site and look for the line that says: <authentication mode="Windows" /> Edit it to read: <authentication mode="Forms" /> Save the web.config file and you have now enabled forms authentication for this site. The default authentication mode for ASP.NET applications is Windows, which is fine if you're working in an intranet environment where every user probably has a Windows login for use in the corporate network anyway. Using Windows authentication, Windows itself handles all the security and authentication, and you can use the myriad of Windows utilities and functions such as Active Directory, to manage your users. On the other hand, with forms authentication, ASP.NET is expected to handle all the details of authentication and security. While ASP.NET 2.0 and later have sophisticated membership and profile capabilities, there is no ASP.NET mechanism for protecting files and folders from direct access, outside of the application. You will still need to secure the physical server and operating system from outside of your application. Creating the membership database To use forms authentication and the SqlMembershipProvider, we need to create a database to authenticate against. This database will hold our user information, as well as membership information, so we can both authenticate the user and provide access based on membership in specific roles. For our demonstration, we will create a new database for this function. We'll create a database with SQL Server ManagementExpress, so open it and right-click Databases in the Object Explorer pane. Choose New Database and name it FormsDemo. Change the location of the database path to the App_Data folder of your FormsDemo web application—the default is C:InetpubFormsDemoApp_Data as shown below. Click OK and the new database will be created. If you look at this database, you will see that it is empty. We haven't added any tables to it, and we haven't set up any fields in those non-existent tables. The database is pretty much useless at this stage. We need to create the database layout, or schema, to hold all the authentication and membership details. Fortunately, Microsoft provides a simple utility to accomplish this task for the 2.0 version of the ASP.NET framework – aspnet_regsql.exe. We'll use this too, in order to create the schema for us, and make our database ready for authentication and membership in our application. To use aspnet_regsql.exe, we need to provide the SQL Server name and login information. This is the same information as shown in the login dialog when we open the database in SQL Server Management Studio Express, as shown below: Note the server name, it will usually be {SystemName}/SQLEXPRESS, but it may be different depending on how you set it up. We use SQL Server Authentication with the sa account and a password of SimpleCMS when we set up SQL Server Express 2005, and that's what we'll use when we run the aspnet_regsql.exe tool. To run aspnet_regsql.exe, you may browse to it in Windows Explorer, or enter the path into the Run dialog when you click on Start and then Run. The default path is C:WINDOWSMicrosoft.NETFrameworkv2.0.50727aspnet_regsql.exe. The utility may be run with command-line arguments, useful when scripting the tool or using it in a batch file, but simply running it with no parameters brings it up in a GUI mode. When the ASP.NET SQL Server Setup Wizard launches, click Next. Make sure that the Configure SQL Server for application services is selected and click on Next. The ASP.NET SQL Server Setup Wizard will ask for the server, authentication, and database. You should enter these according to the information from above. Click Next to confirm the settings. Click Next again to configure the database with the ASP.NET users and membership schema. Continue and exit the wizard, and the database is ready for us to use for authentication. If you were to open the FormsDemo database in SQL Server Management Studio Express, you would find that new tables, views, and stored procedures have been added to the database during this configuration process.
Read more
  • 0
  • 0
  • 2832
article-image-fine-tune-view-layer-your-fusion-web-application
Packt
08 Jul 2010
5 min read
Save for later

Fine Tune the View layer of your Fusion Web Application

Packt
08 Jul 2010
5 min read
(For more resources on Oracle, see here.) The following diadram illustrates the roles of each layer. Use AJAX to boost up the performance of your web pages Asynchronous JavaScript and XML (AJAX) avoid the full page refresh and minimize the data that being transferred between client and server during each round trip. ADF Faces is packaged with 150+ AJAX enabled components which adds AJAX capability to your applications with zero effort. Certain events on an ADF Faces component trigger Partial Page Rendering (PPR) by default. However action components, by default, triggers full page refresh which is quite expensive and may not be required in most of the cases. Make sure that you set partialSubmit attribute to true whenever possible to optimize the page lifecycle. When partialSubmit is set to true, then only the components that have values for their partialTriggers attribute will be processed through the lifecycle. Avoid mixing of html tags with ADF Faces components Don’t mix html tags and ADF Faces components though JSF design let you to do so using <f:verbatim> tag. Mixing row html contents with ADF Faces components may produce undesired output, especially when you have complex layout design for your page. It's highly discouraged to use <f:verbatim> to embed JavaScript or CSS, instead you can use <af:resource> which adds the resource to the document element and optimizes further processing during tree rendering. Avoid long Ids for User Interface components It's always recommended to use short Ids for your User Interface (UI) components. Your JSF page finally boils down to html contents, whose size decides the network bandwidth usage for your web application. If you use long Ids for UI (User Interface) component, that increases the size of the generated html content. This becomes even worse, if you have many UI elements with long Ids. If there's no Id is set for a UI component explicitly, ADF Faces runtime auto generates Ids for you. ADF Faces let you to control the auto generation of component ids by setting context parameter 'oracle.adf.view.rich.SUPPRESS_IDS' in the web.xml file. The <context-param> entry in the web.xml file may look like as shown below. <context-param> <param-name> oracle.adf.view.rich.SUPPRESS_IDS </param-name> <param-value>auto or explicit </param-value></context-param> Possible values for oracle.adf.view.rich.SUPPRESS_IDS are listed below. auto: Components can suppress auto generated IDs, but explicitly set ID will be honored. explicit: This is the default value for oracle.adf.view.rich.SUPPRESS_IDS parameter. In this case both auto generated IDs and explicitly set IDs would get suppressed. Avoid inline usage of JavaScript/Cascading Style Sheets (CSS) whenever possible If you need to use custom JavaScript functions or CSS in your application, try using external files to hold the same. Avoid inline usage of JavaScripts/CSS as much as possible. A better idea is to logically group them in external files and embed the required one in the candidate page using <af:resource> tag. If you keep JavaScript and CSS in external files, they are cached by the browser. Apparently, subsequent requests for these resources are served from the cache. This in turn reduces the network usage and improves the performance. Avoid mixing JSF/ADF Faces and JavaServer Pages Standard Tag Library (JSTL) tags Stick on JSF/ADF Faces components for building your UI as much as you can. JSF component may not work properly with some JSTL tags as they are not designed to co-exist. Relying on JSF/ADF Faces components may give you better extensibility and portability for your application as bonus. Don't generate client component unless it's really needed ADF Faces runtime generates the client components only when they are really required on the client. However you can override this behavior by setting the attribute clientComponent to true, as shown in the following code snippet. <af:commandButton text="DoSomething" clientComponent="true"> <af:clientListener method="doSomething" type="action"/></af:commandButton> Set clientComponent to true only if you need to access the component on the client side using JavaScript. Otherwise this may result in increased Document Object Model (DOM) size at the client side and may affect the performance of your web page. The following diagram shows the runtime coordination between client side and server side component trees. In the above diagram, you can see that no client side component is generated for the server component whose clientComponent attribute is set to false. Prefer not to render the components over hiding components from DOM tree If you need to hide UI components conditionally on a page, try achieving this with rendered property of the component instead of using visible property. Because the later creates the component instance and then hides the same from client side DOM tree, where as the first approach skips the component creation at the server side itself and client side DOM does not have this element added. Apparently setting rendered to false, reduces the client content size and gives better performance as bonus.
Read more
  • 0
  • 0
  • 2831

article-image-drools-jboss-rules-50-flow-part-1
Packt
16 Oct 2009
10 min read
Save for later

Drools JBoss Rules 5.0 Flow (Part 1)

Packt
16 Oct 2009
10 min read
Loan approval service Loan approval is a complex process starting with customer requesting a loan. This request comes with information such as amount to be borrowed, duration of the loan, and destination account where the borrowed amount will be transferred. Only the existing customers can apply for a loan. The process starts with validating the request. Upon successful validation, a customer rating is calculated. Only customers with a certain rating are allowed to have loans. The loan is processed by a bank employee. As soon as an approved event is received from a supervisor, the loan is approved and money can be transferred to the destination account. An email is sent to inform the customer about the outcome. Model If we look at this process from the domain modeling perspective, in addition to the model that we already have, we'll need a Loan class. An instance of this class will be a part of the context of this process. The screenshot above shows Java Bean, Loan, for holding loan-related information. The Loan bean defines three properties. amount (which is of type BigDecimal), destinationAccount (which is of type Account; if the loan is approved, the amount will be transferred to this account), and durationYears (which represents a period for which the customer will be repaying this loan). Loan approval ruleflow We'll now represent this process as a ruleflow. It is shown in the following figure. Try to remember this figure because we'll be referring back to it throughout this article. The preceding figure shows the loan approval process—loanApproval.rf file. You can use the Ruleflow Editor that comes with the Drools Eclipse plugin to create this ruleflow. The rest of the article will be a walk through this ruleflow explaining each node in more detail. The process starts with Validate Loan ruleflow group. Rules in this group will check the loan for missing required values and do other more complex validation. Each validation rule simply inserts Message into the knowledge session. The next node called Validated? is an XOR type split node. The ruleflow will continue through the no errors branch if there are no error or warning messages in the knowledge session—the split node constraint for this branch says: not Message() Code listing 1: Validated? split node no errors branch constraint (loanApproval.rf file). For this to work, we need to import the Message type into the ruleflow. This can be done from the Constraint editor, just click on the Imports... button. The import statements are common for the whole ruleflow. Whenever we use a new type in the ruleflow (constraints, actions, and so on), it needs to be imported. The otherwise branch is a "catch all" type branch (it is set to 'always true'). It has higher priority number, which means that it will be checked after the no errors branch. The .rf files are pure XML files that conform with a well formed XSD schema. They can be edited with any XML editor. Invalid loan application form If the validation didn't pass, an email is sent to the customer and the loan approval process finishes as Not Valid. This can be seen in the otherwise branch. There are two nodes-Email and Not Valid. Email is a special ruleflow node called work item. Email work item Work item is a node that encapsulates some piece of work. This can be an interaction with another system or some logic that is easier to write using standard Java. Each work item represents a piece of logic that can be reused in many systems. We can also look at work items as a ruleflow alternative to DSLs. By default, Drools Flow comes with various generic work items, for example, Email (for sending emails), Log (for logging messages), Finder (for finding files on a file system), Archive (for archiving files), and Exec (for executing programs/system commands). In a real application, you'd probably want to use a different work item than a generic one for sending an email. For example, a custom work item that inserts a record into your loan repository. Each work item can take multiple parameters. In case of email, these are: From, To, Subject, Text, and others. Values for these parameters can be specified at ruleflow creation time or at runtime. By double-clicking on the Email node in the ruleflow, Custom Work Editor is opened (see the following screenshot). Please note that not all work items have a custom editor. In the first tab (not visible), we can specify recipients and the source email address. In the second tab (visible), we can specify the email's subject and body. If you look closer at the body of the email, you'll notice two placeholders. They have the following syntax: #{placeholder}. A placeholder can contain any mvel code and has access to all of the ruleflow variables (we'll learn more about ruleflow variables later in this article). This allows us to customize the work item parameters based on runtime conditions. As can be seen from the screenshot above, we use two placeholders: customer.firstName and errorList. customer and errorList are ruleflow variables. The first one represents the current Customer object and the second one is ValidationReport. When the ruleflow execution reaches this email work item, these placeholders are evaluated and replaced with the actual values (by calling the toString method on the result). Fault node The second node in the otherwise branch in the loan approval process ruleflow is a fault node. Fault node is similar to an end node. It accepts one incoming connection and has no outgoing connections. When the execution reaches this node, a fault is thrown with the given name. We could, for example, register a fault handler that will generate a record in our reporting database. However, we won't register a fault handler, and in that case, it will simply indicate that this ruleflow finished with an error. Test setup We'll now write a test for the otherwise branch. First, let's set up the test environment. Then a new session is created in the setup method along with some test data. A valid Customer with one Account is requesting a Loan. The setup method will create a valid loan configuration and the individual tests can then change this configuration in order to test various exceptional cases. @Before public void setUp() throws Exception { session = knowledgeBase.newStatefulKnowledgeSession(); trackingProcessEventListener = new TrackingProcessEventListener(); session.addEventListener(trackingProcessEventListener); session.getWorkItemManager().registerWorkItemHandler( "Email", new SystemOutWorkItemHandler()); loanSourceAccount = new Account(); customer = new Customer(); customer.setFirstName("Bob"); customer.setLastName("Green"); customer.setEmail("bob.green@mail.com"); Account account = new Account(); account.setNumber(123456789l); customer.addAccount(account); account.setOwner(customer); loan = new Loan(); loan.setDestinationAccount(account); loan.setAmount(BigDecimal.valueOf(4000.0)); loan.setDurationYears(2); Code listing 2: Test setup method called before every test execution (DefaulLoanApprovalServiceTest.java file). A tracking ruleflow event listener is created and added to the knowledge session. This event listener will record the execution path of a ruleflow—store all of the executed ruleflow nodes in a list. TrackingProcessEventListener overrides the beforeNodeTriggered method and gets the node to be executed by calling event.getNodeInstance(). loanSourceAccount represents the bank's account for sourcing loans. The setup method also registers an Email work item handler. A work item handler is responsible for execution of the work item (in this case, connecting to the mail server and sending out emails). However, the SystemOutWorkItemHandler implementation that we've used is only a dummy implementation that writes some information to the console. It is useful for our testing purposes. Testing the 'otherwise' branch of 'Validated?' node We'll now test the otherwise branch, which sends an email informing the applicant about missing data and ends with a fault. Our test (the following code) will set up a loan request that will fail the validation. It will then verify that the fault node was executed and that the ruleflow process was aborted. @Test public void notValid() { session.insert(new DefaultMessage()); startProcess(); assertTrue(trackingProcessEventListener.isNodeTriggered( PROCESS_LOAN_APPROVAL, NODE_FAULT_NOT_VALID)); assertEquals(ProcessInstance.STATE_ABORTED, processInstance.getState()); } Code listing 3: Test method for testing Validated? node's otherwise branch (DefaultLoanApprovalServiceTest.java file). By inserting a message into the session, we're simulating a validation error. The ruleflow should end up in the otherwise branch. Next, the test above calls the startProcess method. It's implementation is as follows: private void startProcess() { Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("loanSourceAccount", loanSourceAccount); parameterMap.put("customer", customer); parameterMap.put("loan", loan); processInstance = session.startProcess( PROCESS_LOAN_APPROVAL, parameterMap); session.insert(processInstance); session.fireAllRules(); } Code listing 4: Utility method for starting the ruleflow (DefaultLoanApprovalServiceTest.java file). The startProcess method starts the loan approval process. It also sets loanSourceAccount, loan, and customer as ruleflow variables. The resulting process instance is, in turn, inserted into the knowledge session. This will enable our rules to make more sophisticated decisions based on the state of the current process instance. Finally, all of the rules are fired. We're already supplying three variables to the ruleflow; however, we haven't declared them yet. Let's fix this. Ruleflow variables can be added through Eclipse's Properties editor as can be seen in the following screenshot (just click on the ruleflow canvas, this should give the focus to the ruleflow itself). Each variable needs a name type and, optionally, a value. The preceding screenshot shows how to set the loan ruleflow variable. Its Type is set to Object and ClassName is set to the full type name droolsbook.bank.model.Loan. The other two variables are set in a similar manner. Now back to the test from code listing 3. It verifies that the correct nodes were triggered and that the process ended in aborted state. The isNodeTriggered method takes the process ID, which is stored in a constant called PROCESS_LOAN_APPROVAL. The method also takes the node ID as second argument. This node ID can be found in the properties view after clicking on the fault node. The node ID—NODE_FAULT_NOT_VALID—is a constant of type long defined as a property of this test class. static final long NODE_FAULT_NOT_VALID = 21;static final long NODE_SPLIT_VALIDATED = 20; Code listing 5: Constants that holds fault and Validated? node's IDs (DefaultLoanApprovalServiceTest.java file). By using the node ID, we can change node's name and other properties without breaking this test (node ID is least likely to change). Also, if we're performing bigger re-factorings involving node ID changes, we have only one place to update—the test's constants. Ruleflow unit testingDrools Flow support for unit testing isn't the best. With every test, we have to run the full process from start to the end. We'll make it easier with some helper methods that will set up a state that will utilize different parts of the flow. For example, a loan with high amount to borrow or a customer with low rating.Ideally we should be able to test each node in isolation. Simply start the ruleflow in a particular node. Just set the necessary parameters needed for a particular test and verify that the node executed as expected.Drools support for snapshots may resolve some of these issues; however, we'd have to first create all snapshots that we need before executing the individual test methods. Another alternative is to dig deeper into Drools internal API, but this is not recommended. The internal API can change in the next release without any notice.
Read more
  • 0
  • 0
  • 2831
Modal Close icon
Modal Close icon