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

How-To Tutorials

7018 Articles
article-image-avro-source-sink
Packt
19 Jul 2013
3 min read
Save for later

Avro Source Sink

Packt
19 Jul 2013
3 min read
(For more resources related to this topic, see here.) A typical configuration might look something as follows: To use the Avro Source, you specify the type property with a value of avro. You need to provide a bind address and port number to listen on: collector.sources=av1collector.sources.av1.type=avrocollector.sources.av1.bind=0.0.0.0collector.sources.av1.port=42424collector.sources.av1.channels=ch1collector.channels=ch1collector.channels.ch1.type=memorycollector.sinks=k1collector.sinks.k1.type=hdfscollector.sinks.k1.channel=ch1collector.sinks.k1.hdfs.path=/path/in/hdfs Here we have configured the agent on the right that listens on port 42424, uses a memory channel, and writes to HDFS. Here I've used the memory channel for brevity of this example configuration. Also, note that I've given this agent a different name, collector, just to avoid confusion. The agents on the left—feeding the collector tier—might have a configuration similar to this. I have left the sources off this configuration for brevity: client.channels=ch1client.channels.ch1.type=memoryclient.sinks=k1client.sinks.k1.type=avroclient.sinks.k1.channel=ch1client.sinks.k1.hostname=collector.example.comclient.sinks.k1.port=42424 The hostname value, collector.example.com, has nothing to do with the agent name on that machine, it is the host name (or you can use an IP) of the target machine with the receiving Avro Source. This configuration, named client, would be applied to both agents on the left assuming both had similar source configurations. Since I don't like single points of failure, I would configure two collector agents with the preceding configuration and instead set each client agent to round robin between the two using a sink group. Again, I've left off the sources for brevity: client.channels=ch1client.channels.ch1.type=memoryclient.sinks=k1 k2client.sinks.k1.type=avroclient.sinks.k1.channel=ch1client.sinks.k1.hostname=collectorA.example.comclient.sinks.k1.port=42424client.sinks.k2.type=avroclient.sinks.k2.channel=ch1client.sinks.k2.hostname=collectorB.example.comclient.sinks.k2.port=42424client.sinkgroups=g1client.sinkgroups.g1=k1 k2client.sinkgroups.g1.processor.type=load_balanceclient.sinkgroups.g1.processor.selector=round_robinclient.sinkgroups.g1.processor.backoff=true Summary In this article, we covered tiering data flows using the Avro Source and Sink. More information on this topic can be found in the book Apache Flume: Distributed Log Collection for Hadoop. Resources for Article : Further resources on this subject: Supporting hypervisors by OpenNebula [Article] Integration with System Center Operations Manager 2012 SP1 [Article] VMware View 5 Desktop Virtualization [Article]
Read more
  • 0
  • 0
  • 3125

article-image-java-development
Packt
18 Jul 2013
16 min read
Save for later

Java Development

Packt
18 Jul 2013
16 min read
(For more resources related to this topic, see here.) Creating a Java project To create a new Java project, navigate to File | New | Project . You will be presented with the New Project wizard window that is shown in the following screenshot: Choose the Java Project option, and click on Next . The next page of the wizard contains the basic configuration of the project that you will create. The JRE section allows you to use a specific JRE to compile and run your project. The Project layout section allows you to choose if both source and binary files are created in the project's root folder or if they are to be separated into different folders (src and bin by default). The latter is the default option. You can create your project inside a working set. This is a good idea if you have too many projects in your workspace and want to keep them organized. Check the Creating working sets section of this article for more information on how to use and manage working sets. The next page of the wizard contains build path options. In the Managing the project build path section of this article , we will talk more about these options. You can leave everything as the default for now, and make the necessary changes after the project is created. Creating a Java class To create a new Java class, right-click on the project in the Package Explorer view and navigate to New | Class . You will be presented with the New Java Class window, where you will input information about your class. You can change the class's superclass, and add interfaces that it implements, as well as add stubs for abstract methods inherited from interfaces and abstract superclasses, add constructors from superclasses, and add the main method. To create your class inside a package, simply enter its name in the appropriate field, or click on the Browse button beside it and select the package. If you input a package name that doesn't exist, Eclipse will create it for you. New packages can also be created by right-clicking on the project in the Package Explorer and navigating to New | Package . Right-clicking on a package instead of a project in the Project Explorer and navigating to New | Class will cause the class to be created inside that package. Creating working sets Working sets provide a way to organize your workspace's projects into subsets. When you have too many projects in your workspace, it gets hard to find the project you're looking for in the Package Explorer view. Projects you are not currently working on, for example, can be kept in separate working sets. They won't get in the way of your current work but will be there in case you need them. To create a new working set, open the Package Explorer's view menu (white triangle in the top-right corner of the view), and choose Select Working Set . Click on New and select the type of projects that the working set will contain (Java , in this case). On the next page, insert the name of the working set, and choose which projects it will contain. Once the working set is created, choose the Selected Working Sets option, and mark your working set. Click on OK , and the Package Explorer will only display the projects inside the working set you've just created. Once your working sets are created, they are listed in the Package Explorer's view menu. Selecting one of them will make it the only working set visible in the Package Explorer. To view more than one working set at once, choose the Select Working Set option and mark the ones you want to show. To view the whole workspace again, choose Deselect Workspace in the view menu. You can also view all the working sets with their nested projects by selecting working sets as the top-level element of the Package Explorer view. To do this, navigate to Top Level Elements | Working Sets in the view menu. Although you don't see projects that belong to other working sets when a working set is selected, they are still loaded in your workspace, and therefore utilize resources of your machine. To avoid wasting these resources, you can close unrelated projects by right-clicking on them and selecting Close Project . You can select all the projects in a working set by using the Ctrl + A keyboard shortcut. If you have a big number of projects, but you never work with all of them at the same time (personal/business projects, different clients' projects, and so on), you can also create a specific workspace for each project set. To create a new workspace, navigate to File | Switch Workspace | Other in the menu, enter the folder name of your new workspace, and click on OK . You can choose to copy the current workspace's layout and working sets in the Copy Settings section. Importing a Java project If you are going to work on an existing project, there are a number of different ways you can import it into Eclipse, depending on how you have obtained the project's source code. To open the Import wizard, navigate to File | Import . Let's go through the options under the General category: Archive file : Select this option if the project you are working on already exists in your workspace, and you just want to import an archive file containing new resources to it. The Import wizard will list all the resources inside the archive file so that you can select the ones you wish to import. To select to which project the resources will be imported click on the Browse button. You can also select in which folder the resources are to be included. Click on Finish when you are done. The imported resources will be decompressed and copied into the project's folder. Existing Projects into Workspace : If you want to import a new project, select this option from the Import wizard. If the project's source file has been compressed into an archive file (the .zip, .tar, .jar, or .tgz format), there's no need to decompress it; just mark the Select archive file option on the following page of the wizard, and point to the archive file. If you have already decompressed the code, mark Select root directory and point to the project. The wizard will list all the Eclipse projects found in the folder or archive file. Select the ones you wish to import and click on Finish . You can add the imported projects to a specific working set and choose whether the projects are to be copied into your workspace folder or not. It's highly recommended to do so for both simplicity and portability; you know where all your Eclipse projects are, and it's easy to backup or move all of them to a different machine. File System : Use this wizard if you already have a project in your workspace and want to add new existing resources in your filesystem. On the next page, select the resources you wish to import by checking them. Click on the Browse button to select the project and the folder where the resources will be imported. When you click on the Finish button, the resources will be copied to the project's folder inside your workspace. Preferences : You can import Eclipse preferences files to your workspace by selecting this option. Preferences file contains code style and compiler preferences, the list of installed JREs, and the Problems view configurations. You can choose which of these preferences you wish to import from the selected configuration file. Importing a project from Version Control Servers Projects that are stored in Version Control Servers can be imported directly into Eclipse. There's a number of version control softwares, each with its pros and cons, and most of them are supported by Eclipse via plugins. GIT is one of the most used softwares for version control. CVS is the only version control system supported by default. To import a project managed by it, navigate to CVS | Projects from CVS in the Import wizard. Fill in the server information on the following page, and click on Finish . Introducing Java views Eclipse's user interface consists of elements called views. The following sections will introduce the main views related to Java development. The Package Explorer view The Package Explorer view is the default view used to display a project's contents. As the name implies, it uses the package hierarchy of the project to display its classes, regardless of the actual file hierarchy. This view also displays the project's build path. The following screenshot shows how the Package Explorer view looks: The Java Editor view The Java Editor is the Eclipse component that will be used to edit Java source files. It is the main view in the Java perspective and is located in the middle of the screen. The following screenshot shows the Java Editor view: The Java Editor is much more than an ordinary text editor. It contains a number of features that makes it easy for newcomers to start writing Java code and increases the productivity of experienced Java programmers. Let's talk about some of these features. Compiling errors and warnings annotations As you will see in the Building and running section with more details, Eclipse builds your code automatically after every saved modification by default. This allows Eclipse to get the Java Compiler output and mark errors and warnings through the code, making it easier to spot and correct them. Warnings are underlined in yellow and errors in red. Content assist This is probably the most used Java Editor feature both by novice and experienced Java programmers. It allows you to list all the methods callable by a given instance, along with their documentation. This feature will work by default for all Java classes and for the ones in your workspace. To enable it for external libraries, you will have to configure the build path for your project. We'll talk more about build paths further in this article in the Managing the project build path section. To see this feature in action, open a Java Editor, and create a new String instance. String s = new String(); Now add a reference to this String instance, followed by a dot, and press Ctrl + Space bar . You will see a list of all the String() and Object() methods. This is way more practical than searching for the class's API in the Java documentation or memorizing it. The following screenshot shows the content assist feature in action: This list can be filtered by typing the beginning of the method's name after the dot. Let's suppose you want to replace some characters in this String instance. As a novice Java programmer, you are not sure if there's a method for that; and if there is, you are not sure which parameters it receives. It's a fair guess that the method's name probably starts with replace, right? So go ahead and type: s.replace When you press Ctrl along with the space bar, you will get a list of all the String() methods whose name starts with replace. By choosing one of them and pressing Enter , the editor completes the code with the rest of the method's name and its parameters. It will even suggest some variables in your code that you might want to use as parameters, as shown in the following screenshot: Content assist will work with all classes in the project's classpath. You can disable content assist's automatic activation by unmarking Enable auto activation inside the Preferences window and navigating to Java | Editor | Content Assist . Code navigation When the project you are working on is big enough, finding a class in the Package Explorer can be a pain. You will frequently find yourself asking, "In which package is that class again?". You can leave the source code of the classes you are working on open in different tabs, but soon enough you will have more open tabs than you would like to have. Eclipse has an easy solution for this. In the toolbar, select Navigate | Open Type . Now, just type in the class's name, and click on OK . If you don't remember the full name of the class, you can use the wildcard characters, ? (matches one character) and * (matches any number of characters). You can also use only the uppercase letters for the CamelCase names (for example, SIOOBE for StringIndexOutOfBoundsException). The shortcut for the Open Type dialog is Ctrl + Shift + T . There's also an equivalent feature for finding and opening resources other than Java classes, such as HTML files, images, and plain text files. The shortcut for the Open Resource dialog is Ctrl + Shift + R . You can also navigate to a class' source file by holding Ctrl and clicking on a reference to that class in the code. To navigate to a method's implementation or definition directly, hold Ctrl and click on the method's call. Another useful feature that makes it easy to browse through your project's source files is the Link With Editor feature in the Package Explorer view, as shown in the following screenshot: By enabling it, the selected resource in the Package Explorer will always be the one that's open in the editor. Using this feature together with OpenType is certainly the easiest way of finding a resource in the Package Explorer. Quick fix Whenever there's an error or warning marker in your code, Eclipse might have some suggestions on how to get rid of it. To open the Quick Fix menu containing the suggestions, place the caret on the marked piece of code related to the error or warning, right-click on it, and choose Quick Fix . You can also use the shortcut by pressing Ctrl + 1 with the caret placed on the marked piece of code. The following screenshot shows the quick fix feature suggesting you to either get rid of the unused variable, create getters and setters for it, or add a SuppressWarnings annotation: Let's see some of the most used quick fixes provided by Eclipse. You can take advantage of these quick fixes to speed up your code writing. You can for example, deliberately call a method that throws an exception without the try/catch block, and use the quick fix to generate it instead of writing the try/catch block yourself. Unhandled exceptions : When a method that throws an exception is called, and the exception is not caught or thrown, Eclipse will mark the call with an error. You can use the quick fix feature to surround the code with a proper try/catch block automatically. Just open the Quick Fix menu, and choose Surround with Try/Catch . It will generate a catch block that will then call the printStackTrace() method of the thrown exception. If the method is already inside a try block, you can also choose the Add catch clause to the surrounding try option. If the exception shouldn't be handled in the current method, you can also use the Add throws declaration quick fix. References to nonexisting methods and variables : Eclipse can create a stub for methods referenced through the code that doesn't exist with quick fix. To illustrate this feature's usefulness, let's suppose you are working on a class's code, and you realize that you will need a method that performs some specific operation with two integers, returning another integer value. You can simply use the method, pretending that it exists: int b = 4; int c = 5; int a = performOperation(b,c); The method call will be marked with an error that says performOperation is undefined. To create a stub for this method, place the caret over the method's name, open the Quick Fix menu, and choose create method performOperation(int, int) . A private method will be created with the correct parameters and return type as well as a TODO marker inside it, reminding you that you have to implement the method. You can also use a quick fix to create methods in other classes. Using the same previous example, you can create the performOperation() method in a different class, such as the following: OperationPerformer op = new OperationPerformer(); int a = op.performOperation(b,c); Speaking of classes, quick fix can also create one if you add a call to a non-existing class constructor. Non-existing variables can also be created with quick fix. Like with the method creation, just refer to a variable that still doesn't exist, place the caret over it, and open the Quick Fix menu. You can create the variable either as a local variable, a field, or a parameter. Remove dead code : Unused methods, constructors and fields with private visibility are all marked with warnings. While the quick fix provided for unused methods and constructors is the most evident one (remove the dead code), it's also possible to generate getters and setters for unused private fields with a quick fix. Customizing the editor Like almost everything in Eclipse, you can customize the Java Editor's appearance and behavior. There are plenty of configurations in the Preferences window (Window | Preferences ) that will certainly allow you to tailor the editor to suit your needs. Appearance-related configurations are mostly found in General | Appearance | Colors and Fonts and behavior and feature configurations are mostly under General | Editors | Text Editors . Since there are lots of different categories and configurations, the filter text in the Preferences window might help you find what you want. A short list of the preferences you will most likely want to change is as follows: Colors and fonts : Navigate to General | Appearance . In the Colors and Fonts configuration screen, you can see that options are organized by categories. The ones inside the Basic and Java categories will affect the Java Editor. Enable/Disable spell checking : The Eclipse editor comes with a spellchecker. While in some cases it can be useful, in many others you won't find much use for it. To disable or configure it, navigate to General | Editors | Text Editors | Spelling . Annotations : You can edit the way annotations (warnings and errors, among others) are shown in the editor by navigating to General | Editors | Text Editors | Annotations inside the Preferences window. You can change colors, the way annotations are highlighted in the code (underline, squiggly line, box, among others), and whether they are shown in the vertical bar before the code. Show Line Numbers : To show line numbers on the left-hand side of the editor, mark the corresponding checkbox by navigating to General | Editors | Text Editors . Right-clicking on the bar on the editor's left-hand side brings a dialog in which you can also enable/disable line numbers.
Read more
  • 0
  • 0
  • 2072

article-image-implementing-log-screen-using-ext-js
Packt
18 Jul 2013
31 min read
Save for later

Implementing a Log-in screen using Ext JS

Packt
18 Jul 2013
31 min read
In this article Loiane Groner, author of Mastering Ext JS, talks about developing a login page for an application using Ext JS. It is very common to have a login page for an application, which we can use to control access to the system by identifying and authenticating the user through the credentials presented by him/her. Once the user is logged in, we can track the actions performed by the user. We can also restrain access of some features and screens of the system that we do not want a particular user or even a specific group of users to have access to. In this article, we will cover: Creating the login page Handling the login page on the server Adding the Caps Lock warning message in the Password field Submitting the form by pressing the Enter key Encrypting the password before sending to the server (For more resources related to this topic, see here.) The Login screen The Login window will be the first view we are going to implement in this project. We are going to build it step by step and it will have the following capabilities: User will enter the username and password to log in Client-side validation (username and password required to log in) Submit the Login form by pressing Enter Encrypt the password before sending to the server Password Caps Lock warning (similar to Windows OS) Multilingual capability Except for the multilingual capability, we will implement all the other features throughout this topic. So at the end of the implementation, we will have a Login window that looks like the following: So let's get started! Creating the Login screen Under the app/view directory, we will create a new file named Login.js.In this file, we will implement all the code that the user is going to see on the screen. Inside the Login.js file, we will implement the following code: Ext.define('Packt.view.Login', { // #1 extend: 'Ext.window.Window', // #2 alias: 'widget.login', // #3 autoShow: true, // #4 height: 170, // #5 width: 360, // #6 layout: { type: 'fit' // #7 }, iconCls: 'key', // #8 title: "Login", // #9 closeAction: 'hide', // #10 closable: false // #11 }); On the first line (#1) we have the definition of the class. To define a class we use Ext.define, followed by parentheses (()), and inside the parentheses we first declare the name of the class, followed by a comma (") and curly brackets ({}), and at the end a semicolon. All the configurations and properties (#2 to #11) go inside curly brackets. We also need to pay attention to the name of the class. This is the formula suggested by Sencha in Ext JS MVC projects: App Namespace + package name + name of the JS file. we defined the namespace as Packt (configuration name inside the app.js file). We are creating a View for this project, so we will create the JS file under the view package/directory. And then, the name of the file we created is Login.js; therefore, we will lose the .js part and use only Login as the name of the View. Putting all together, we have Packt.view.Login and this will be the name of our class. Then, we are saying that the Login class will extend from the Window class (#2), because we want it to be displayed inside a window, and not on any other component. We are also assigning this class an alias (#3). The alias for a class that extends from a component always starts with widget., followed by the alias we want to assign. The naming convention for an alias is lowercase . It is also important to remember that the alias must be unique in an application. In this case we want to assign login as alias to this class so later we can instantiate this same class using its alias (that is the same as xtype). For example, we can instantiate the Login class using four different options: Using the complete name of the class, which is the most used one: Ext.create('Packt.view.Login'); Using the alias in the Ext.create method: Ext.create('widget.login'); Using the Ext.widget, which is a shorthand way of using Ext.ClassManager.instantiateByAlias: Ext.widget('login'); Using the xtype as an item of another component: items: [ { xtype: 'login' } ] In this book we will use the first, third, and fourth options most of the time. Then we have autoShow configured to true (#4). What happens with the window is that instantiating the component is not enough for displaying it. When we instantiate the window we will have its reference, but it will not be displayed on the screen. If we want it to be displayed we need to call the method show() manually. Another option is to have the autoShow configuration set to true. This way the window will be automatically displayed when we instantiate it. We also have height (#5) and width (#6) of the window. We set the layout as fit (#7) because we want to add a form inside this window that will contain the username and password fields. And using the fit layout the form will occupy all the body space of the window. Remember that when using the fit layout we can only have one item as a child component. We are setting an iconCls (#8) property to the window; this way we will have an icon of a key in the header of the window. We can also give a title for the window (#9), and in this case we chose Login. Following is the declaration of the key style used by the iconCls property: .key { background-image:url('../icons/key.png') !important; } All the styles we will create to use as iconCls have a format like the preceding one. And at last we have the closeAction (#10) and closable (#11) configurations. The closeAction configuration will tell if we want to destroy the window when we close it. In this case, we do not want to destroy it; we only want to hide it. The closable configuration tells if we want to display the X icon on the top-right corner of the window. As this is a Login window, we do not want to give this option for the user. If you would like to, you can also add the resizable and draggable options as false. This will prevent the user to drag the Login window around and also to resize it. So far, this will be the output we have. A single window with an icon at the top-left corner with a title Login : The next step is to add the form with the username and password fields. We are going to add the following code to the Login class: items: [ { xtype: 'form', // #12 frame: false, // #13 bodyPadding: 15, // #14 defaults: { // #15 xtype: 'textfield', // #16 anchor: '100%', // #17 labelWidth: 60 // #18 }, items: [ { name: 'user', fieldLabel: "User" }, { inputType: 'password', // #19 name: 'password', fieldLabel: "Password" } ] } ] As we are using the fit layout, we can only declare one child item in this class. So we are going to add a form (#12) and to make the form to look prettier, we are going to remove the frame property (#13) and also add padding to the form body (#14). The form's frame property is by default set to false. But by default, there is a blue border that appears if we to do not explicitly add this property set to false. As we are going to add two fields to the form, we probably want to avoid repeating some code. That is why we are going to declare some field configurations inside the defaults configuration of the form (#15); this way the configuration we declare inside defaults will be applied to all items of the form, and we will need to declare only the configurations we want to customize. As we are going to declare two fields, both of them will be of type textfield. The default layout of the form is the anchor layout, so we do not need to make this declaration explicit. However, we want both fields can occupy all the horizontal available space of the body of the form. That is why we are declaring anchor as 100% (#17). By default, the width attribute of the label of the TextField class is 100 pixels. It is too much space for a label User and Password, so we are going to decrease this value to 60 pixels (#18). And finally, we have the user text field and the password text field. The configuration name is what we are going to use to identify each field when we submit the form to the server. But there is only one detail missing: when the user types the password into the field the system cannot display its value, we need to mask it somehow. That is why inputType is 'password' (#19) for the password field, as we want to display bullets instead of the original value, and the user will not be able to see the password value. Now we have improved our Login window a little more. This is the output so far: Client-side validations The field component in Ext JS provides some client-side validation capability. This can save time and also bandwidth (the system will only make a server request when it is sure the information has passed the basic validation). It also helps to point out to the user where they have gone wrong in filling out the form. Of course, it is also good to validate the information again on the server side for security reasons, but for now we will focus on the validations we can apply to the form of our Login window. Let's brainstorm some validations we can apply to the username and password fields: The username and password must be mandatory—how are going to authenticate the user without a username and password? The user can only enter alphanumeric characters (A-Z, a-z, and 0-9) in both the fields. The user can only type between 3 and 25 chars in the username field. The user can only type between 3 and 15 chars in the password field. So let's add into the code the ones that are common to both fields: allowBlank: false, // #20 vtype: 'alphanum', // #21 minLength: 3, // #22 msgTarget: 'under' // #23 We are going to add the preceding configurations inside the defaults configuration of the form, as they all apply to both the fields we have. First, both need to be mandatory (#20), we can only allow to enter alphanumeric characters (#21) and the minimum number of characters the user needs to input is three (#22). Then, a last common configuration is that we want to display any validation error message under the field (#23). And the only validation customized for each field is that we can enter a maximum of 25 characters in the User field: name: 'user', fieldLabel: "User", maxLength : 25 And a maximum of 15 characters in the Password field: inputType: 'password', name: 'password', fieldLabel: "Password", maxLength : 15 After we apply the client validations, we will have the following output in case the user went wrong in filling out the Login window: If you do not like it, we can change the place where the error message appears. We just need to change the msgTarget value. The available options are: title, under, side, and none. We can also show the error message as a tooltip (qtip) or display it in a specific target (inner HTML of a specific component). Creating custom VTypes Many systems have a special format for passwords. Let's say we need the password to have at least one digit (0-9), one letter lowercase, one letter uppercase, one special character (@, #, $, %, and so on) and its length between 6 and 20 characters. We can create a regular expression to validate that the password is entering into the app. And to do this, we can create a custom VType to do the validation for us. Creating a custom VType is simple. For our case, we can create a custom VType called passRegex: Ext.apply(Ext.form.field.VTypes, { customPass: function(val, field) { return /^((?=.*d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})/.test(val); }, customPassText: 'Not a valid password. Length must be at least 6 characters and maximum of 20Password must contain one digit, one letter lowercase, one letter uppercase, onse special symbol @#$% and between 6 and 20 characters.', }); customPass is the name of our custom VType, and we need to declare a function that will validate our regular expression. customPassText is the message that will be displayed to the user in case the incorrect password format is entered. The preceding code can be added anywhere on the code, inside the init function of a controller, inside the launch function of the app.js, or even in a separate JavaScript file (recommended) where you can put all your custom VTypes. To use it, we simply need to add vtype: 'customPass' to our Password field. To learn more about regular expressions, please visit http://www.regular-expressions.info/. Adding the toolbar with buttons So far we have created the Login window, which contains a form with two fields and it is already being validated as well. The only thing missing is to add the two buttons: cancel and submit . We are going to add the buttons as items of a toolbar and the toolbar will be added on the form as a docked item. The docked items can be docked to either on the top, right, left, or bottom of a panel (both form and window components are subclasses of panel). In this case we will dock the toolbar to the bottom of the form. Add the following code right after the items configuration of the form: dockedItems: [ { xtype: 'toolbar', dock: 'bottom', items: [ { xtype: 'tbfill' //#24 }, { xtype: 'button', // #25 itemId: 'cancel', iconCls: 'cancel', text: 'Cancel' }, { xtype: 'button', // #26 itemId: 'submit', formBind: true, // #27 iconCls: 'key-go', text: "Submit" } ] } ] If we take a look back to the screenshot of the Login screen we first presented at the beginning of this article, we will notice that there is a component for the translation/multilingual capability. And after this component there is a space and then we have the Cancel and Submit buttons. As we do not have the multilingual component yet, we can only implement the two buttons, but they need to be at the right end of the form and we need to leave that space. That is why we first need to add a toolbar fill component (#24), which is going to instruct the toolbar's layout to begin using the right-justified button container. Then we will add the Cancel button (#25) and then the Submit button (#26). We are going to add icons to both buttons (iconCls) and later, when we implement the controller class, we will need a way to identify the buttons. This is why we assigned itemId to both of them. We already have the client validations, but even with the validations, the user can click on the Submit button and we want to avoid this behavior. That is why we are binding the Submit button to the form (#27); this way the button will only be enabled if the form has no error from the client validation. In the following screenshot, we can see the current output of the Login form (after we added the toolbar) and also verify the behavior of the Submit button: Running the code To execute the code we have created so far, we need to make a few changes in the app.js file. First, we need to declare views we are using (only one in this case). Also, as we are going to instantiate using the Login class' xtype, we need to declare this class in the requires declaration: requires: [ 'Packt.view.Login' ], views: [ 'Login' ], And the last change is inside the launch function. now we only need to replace the console.log message with the Login instance (#1): splashscreen.next().fadeOut({ duration: 1000, remove:true, listeners: { afteranimate: function(el, startTime, eOpts ){ Ext.widget('login'); // #1 } } }); Now the app.js is OK and we can execute what we have implemented so far! Using itemId versus id Ext.Cmp is bad! Before we create the controller, we will need to have some knowledge about Ext.ComponentQuery selectors. And in this topic we will discuss a subject to help us to understand better why we took some decisions while creating the Login window and why we are going to take some other decisions on the controller topic. Whenever we can, we will always try to use the itemId configuration instead of id to uniquely identify a component. And here comes the question, why? When using id, we need to make sure that id is unique, and none of all the other components of the application has the same id. Now imagine the situation where you are working with other developers of the same team and it is a big application. How can you make sure that id is going to be unique? Pretty difficult, don't you think? And this can be a hard task to achieve. Components created with an id may be accessed globally using Ext.getCmp, which is a short-hand reference for Ext.ComponentManager.get. Just to mention one example, when using Ext.getCmp to retrieve a component by its id, it is going to return the last component declared with the given id. And if the id is not unique, it can return the component that you are not expecting and this can lead into an error of the application. Do not panic! There is an elegant solution, which is using itemId instead of id. The itemId can be used as an alternative way to get a reference of a component. The itemId is an index to the container's internal MixedCollection, and that is why the itemId is scoped locally to the container. This is the biggest advantage of the itemId. For example, we can have a class named MyWindow1, extending from window and inside this class we can have a button with item ID submit. Then we can have another class named MyWindow2, also extending from window, and also with a button with item ID submit. Having two item IDs with the same value is not an issue. We only need to be careful when we use Ext.ComponentQuery to retrieve the component we want. For example, if we have a Login window whose alias is login and another screen called the Registration window whose alias is registration. Both the windows have a button Save whose itemId is save. If we simply use Ext.ComponentQuery.query('button#save'), the result will be an array with two results. However, if we narrow down the selector even more, let's say we want the Login window's Save button, and not the Registration window's Save button, we need to use Ext.ComponentQuery.query('login button#save'), and the result will be a single item, which is exactly we expect. You will notice that we will not use Ext.getCmp in the code of our project. Because it is not a good practice; especially for Ext JS 4 and also because we can use itemId and Ext.ComponentQuery instead. We will understand Ext.ComponentQuery better during the next topic. Creating the login controller We have created the view for the Login screen so far. As we are following the MVC architecture, we are not implementing the user interaction on the View class. If we click on the buttons on the Login class, nothing will happen because we have not yet implemented this logic. We are going to implement this logic now on the controller class. Under the app/controller directory, we will create a new file named Login.js. In this file we will implement all the code related to the events management of the Login screen. Inside the Login.js file we will implement the following code, which is only a base of the controller class we are going to implement: Ext.define('Packt.controller.Login', { // #1 extend: 'Ext.app.Controller', // #2 views: [ 'Login' // #3 ], init: function(application) { // #4 this.control({ // #5 }); } }); As usual, on the first line of the class we have its name (#1). Following the same formula we used for the view/Login.js we will have Packt (app namespace) + controller (name of the package) + Login (which is the name of the file), resulting in Packt.controller.Login. Note that that the controller JS file (controller/Login.js) has the same name as view/Login.js, but that is OK because they are in a different package. It is good to use a similar name for the views, models, stores and controllers because it is going to be easier to maintain the project later. For example, let's say that after the project is in production, we need to add a new button on the Login screen. With only this information (and a little bit of MVC concept knowledge) we know we will need to add the button code on the view/Login.js file and listen to any events that might be fired by this button on the controller/Login.js. Easier maintainability is also a great pro of using the MVC architecture. The controller classes need to extend from Ext.app.Controller (#2), so we will always use this parent class for our controllers. Then we have the views declaration (#3), which is where we are going to declare all the views that this controller will care about. In this case, we only have the Login view so far. We will add more views later on this article. Next, we have the init method declaration (#4). The init method is called before the application boots, before the launch function of Ext.application (app.js). The controller will also load the views, models, and stores declared inside its class. Then we have the control method configured (#5). This is where we are going to listen to all events we want the controller to react. And as we are coding the events fired by the Login window and its child components, this will be our scope in this controller. Adding the controller to app.js Now that we already have a base of the login controller, we need to add it to the app.js file. We can remove this code, since the controller will be responsible for loading the view/Login.js file for us: requires: [ 'Packt.view.Login' ], views: [ 'Login' ], And add the controllers declaration: controllers: [ 'Login' ], And as our project is only starting, declaring the views on the controller classes will help us to have a code more organized, as we do not need to declare all the application's views in the app.js file. Listening to the button click event Our next step now is to start listening to the Login window events. First, we are going to listen to the Submit and Cancel buttons. We already know that we are going to add the listeners inside the this.control declaration. The format that we need to use is the following: 'Ext.ComponentQuery selector': { eventWeWantToListenTo: functionOrMethodWeWantToExecute } First, we need to pass the selector that is going to be used by the Ext.ComponentQuery class to find the component. Then we need to list the event that we want to listen to. And then, we need to declare the function that is going to be executed when the event we are listening to is fired, or declare the name of the controller method that is going to be executed when the event is fired. In our case, we are going to declare the method only for code organization purposes. Now let's focus on finding the correct selector for the Submit and Cancel buttons. According to Ext.ComponentQuery API documentation, we can retrieve components by using their xtype (if you are already familiar with jQuery, you will notice that Ext.ComponentQuery selectors are very similar to jQuery selectors' behavior). Well, we are trying to retrieve two buttons, and their xtype is button. We try then the selector button. But before we start coding, let's make sure that this is the correct selector to avoid us to change the code all the time when trying to figure out the correct selector. There is one very useful tip we can try: open the browser console (command editor), type the following command, and click on Run : Ext.ComponentQuery.query('button'); As we can see in the screenshot, it returned an array of the buttons that were found by the selector we used, and the array contains six buttons; too many buttons and it is not what we want. We want to narrow down to the Submit and Cancel buttons. Let's try to draw a path of the Login window using the components xtype we used: We have a Login window (xtype: login or window), inside the window we have a form (xtype: form), inside the form we have a toolbar (xtype: toolbar), and inside the toolbar we have two buttons (xtype: button). Therefore, we have login-form-toolbar-button. However, if we use login-form-button we will have the same result, because we do not have any other buttons inside the form. So we can try the following command: Ext.ComponentQuery.query('login form button'); So let's try this last selector on the command editor: Now the result is an array of two buttons and these are the buttons that we are looking for! There is still one detail missing: if we use the login form button selector, it will listen to the click event (which is the event we want to listen to) of both buttons. When we click on the Cancel button one thing should happen (reset the form) and when we click on the Submit button, another thing should happen (submit the form to the server to validate the login). So we still want to narrow down the selector even more, until it returns the Cancel button and another selector that will return the Submit button. Going back to the view/Login code, notice that we declared a configuration named itemId to both buttons. We can use these itemId configurations to identify the buttons in a unique way. According to the Ext.ComponentQuery API docs, we can use # as a prefix of itemId. So let's try the following command on the command editor to get the Submit button reference: Ext.ComponentQuery.query('login form button#submit'); The output will be only one button as we expect: Now let's try the following command to retrieve the Cancel button reference: Ext.ComponentQuery.query('login form button#cancel'); The output will be only one button as we expect: So now we have the selectors that we were looking for! Console command editor is a great tool and using it can save us a lot of time when trying to find the exact selector that we want, instead of coding, testing, not the selector we want, code again, test again, and so on. Could we use only button#submit or button#cancel as selectors? Yes, we could use a shorter selector. However, it would work perfectly for now. As the application grows and we declare many more classes and buttons, the event would be fired for all buttons that have the itemId named submit or cancel and this could lead to an error in the application. We always need to remember that itemId is scoped locally to the container. By using login form button as the selector, we make sure that the event will come from the button from the Login window. So let's implement the code inside the controller class: init: function(application) { this.control({ "login form button#submit": { // #1 click: this.onButtonClickSubmit // #2 }, "login form button#cancel": { // #3 click: this.onButtonClickCancel // #4 } }); }, onButtonClickSubmit: function(button, e, options) { console.log('login submit'); // #5 }, onButtonClickCancel: function(button, e, options) { console.log('login cancel'); // #6 } In the preceding code, we have first the listener to the Submit button (#1), and on the following line we say that we want to listen to the click event, and then, when the click event of the Submit button is fired, the onButtonClickSubmit method should be executed (#2). Then we have the same for the Cancel button: we have the listener to the Cancel button (#3), and on the following line we say that we want to listen to the click event, and then, when the click event of the Cancel button is fired, the onButtonClickCancel method should be executed (#4). Next, we have the declaration of the methods onButtonClickSubmit and onButtonClickCancel. For now, we are only going to output a message on the console to make sure that our code is working. So we are going to output login submit (#5) in case the user clicks on the Submit button, and login cancel (#6) in case the user clicks on the Cancel button. But how do you know which are the parameters the event method can receive? You can find the answer to this question in the documentation. If we take a look at the click event in the documentation, this is what we will find: This is exactly what we declared. For all the other event listeners, we will go to the docs and see which are the parameters the event accepts, and then list them as parameters in our code. This is also a very good practice. We should always list out all the arguments from the docs, even if we are only interested in the first one. This way we always know that we have the full collection of the parameters, and this can come very handy when we are doing maintenance of the application. Let's go ahead and try it. Click on the Cancel button and then on the Submit button. This should be the output: Cancel button listener implementation Let's remove the console.log messages and add the code we actually want the methods to execute. First, let's work on the onButtonClickCancel method. When we execute this method, we want it to reset the form. So this is the logic sequence we want to program: Get the Login form reference. Call the method getForm, which is going to return the form basic class. Call the reset method to reset the form. The form basic class provides input field management, validation, submission, and form loading services. The Ext.form.Panel class (xtype: form) works as the container, and it is automatically hooked up with an instance of Ext.form.Basic. That is why we need to get the form basic reference to call the reset method. If we take a look at the parameters we have available on the onButtonClickCancel method, we have: button, e, and options, and none of them provides us the form reference. So what can we do about it? We can use the up method from the Button class (inherited from the AbstractComponent class). With this method, we can use a selector to try to retrieve the form. The up method navigates up the component hierarchy, searching from an ancestor container that matches the passed selector. As the button is inside a toolbar that is inside the form we are looking for, if we use button.up('form'), it will retrieve exactly what we want. Ext JS will see what is the first ancestor in the hierarchy of the button and will find a toolbar. Not what we are looking for. So it goes up again and it will find a form, which is what we are looking for. So this is the code that we are going to implement inside the onButtonClickCancel method: button.up('form').getForm().reset(); Some people like to implement the toolbar inside the window instead of the form. No problem at all, it is only a matter of how you like to implement it. In this case, if the toolbar that contains the Submit button is inside the Window class we can use: button.up('window').down('form').getForm().reset() And we will have the same result! Submit button listener implementation Now we need to implement the onButtonClickSubmit method. Inside this method, we want to program the logic to send the username and password values to the server so that the user can be authenticated. We can implement two programming logics inside this method: the first one is to use the submit method that is provided by the form basic class and the second one is to use an Ajax call to submit the values to the server. Either way we will achieve what we want to do. However, there is one detail that we need to know prior to making this decision: if using the submit method of the form basic class, we will not be able to encrypt the password before we send it to the server, and if we take a look at the parameters sent to the server, the password will be a plain text, and this is not good. Using the Ajax request will result the same; however, we can encrypt the password value before sending to the server. So apparently, the second option seems better and that is the one that we will implement. So to summarize, following are the steps we need to perform in this method: Get the Login form reference Get the Login window reference (so that we can close it once the user has been authenticated) Get the username and password values from the form Encrypt the password Send login information to the server Handle the server response If user is authenticated display application If not, display an error message First, let's get the references that we need: var formPanel = button.up('form'), login = button.up('login'), user = formPanel.down('textfield[name=user]').getValue(), pass = formPanel.down('textfield[name=password]').getValue(); To get the form reference, we can use the button.up('form') code that we already used in the onButtonClickCancel method; to get the Login window reference we can do the same thing, only changing the selector to login or window. Then to get the values from the User and Password fields we can use the down method, but this time the scope will start from the form reference. For the selector we will use the text field xtype, and to make sure we are retrieving the text field we want, we can create an itemId attribute, but there is no need for it. We can use the name attribute since the user and password fields have different names and they are unique within the Login window. To use attributes within a selector we must wrap it in brackets. The next step is to submit the values to the server: if (formPanel.getForm().isValid()) { Ext.Ajax.request({ url: 'php/login.php', params: { user: user, password: pass } }); } If we try to run this code, the application will send the request to the server, but we will get an error as the response because we do not have the login.php page implemented yet. That's OK because we are interested in other details right now. With Firebug or Chrome Developer Tools enabled, open the Net tab and filter by the XHR requests. Make sure to enter a username and password (any valid value so that we can click on the Submit button). This will be the output: We still do not have the password encrypted. The original value is still being displayed and this is not good. We need to encrypt the password. Under the app directory, we will create a new folder named util where we are going to create all the utility classes. We will also create a new file named MD5.js; therefore, we will have a new class named Packt.util.MD5. This class contains a static method called encode and this method encodes the given value using the MD5 algorithm. To understand more about the MD5 algorithm go to http://en.wikipedia.org/wiki/MD5. As Packt.util.MD5 is big, we will not list its code here, but you can download the source code of this book from http://www.packtpub.com/mastering-ext-javascript/book or get the latest version at https://github.com/loiane/masteringextjs). If you would like to make it even more secure, you can also use SSL and ask for a random salt string from the server, salt the password and hash it. You can learn more about it at one the following URLs: http://en.wikipedia.org/wiki/Transport_Layer_Security and http://en.wikipedia.org/wiki/Salt_(cryptography). A static method does not require an instance of the class to be able to be called. In Ext JS, we can declare static attributes and methods inside the static configuration. As the encode method from Packt.util.MD5 class is static, we can call it like Packt.util.MD5.encode(value);. So before Ext.Ajax.request, we will add the following code: pass = Packt.util.MD5.encode(pass); We must not forget to add the Packt.util.MD5 class on the controller's requires declaration (the requires declaration is right after the extend declaration): requires: [ 'Packt.util.MD5' ], Now, if we try to run the code again, and check the XHR requests on the Net tab, we will have the following output: The password is encrypted and it is much safer now.
Read more
  • 0
  • 0
  • 10515

article-image-detailing-environments
Packt
17 Jul 2013
4 min read
Save for later

Detailing Environments

Packt
17 Jul 2013
4 min read
(For more resources related to this topic, see here.) Applying materials As it stands, our current level looks rather... well, bland. I'd say it's missing something in order to really make it realistic... the walls are all the same! Thankfully, we can use textures to make the walls come to life in a very simple way, bringing us one step closer to that AAA quality that we're going for! Applying materials to our walls in Unreal Development Kit (UDK) is actually very simple once we know how to do it, which is what we're going to look at now: First, go to the menu bar at the top and access the Actor Classes window by going to the top menu and navigating to View | Browser Windows | Content Browser. Once in the Content Browser window, make sure that Packages are sorted by folder by clicking on the left-hand side button. Once this is done, click on the UDK Game folder in the Packages window. Then type in floor master in the top search bar menu. Click on the M_LT_Floors_BSP_Master material. Close the Content Browser window and then left-click on the floor of our level; if you look closely, you should see. With the floor selected, right-click and select Apply Material : M_LT_Floors_BSP_Master. Now that we have given the floor a material, let's give it a platform as well. Select each of the faces by holding down Ctrl and left-clicking on them individually. Once selected, right-click and select Apply Material : M_LT_Floors_BSP_Master. Another way to select all of the faces would be to rightclick on the floor and navigate to Select Surfaces | Adjacent Floors. Now our floor is placed; but if you play the game, you may notice the texture being repeated over and over again and the texture on the platform being stretched strangely. One of the ways we can rectify this problem is by scaling the texture to fit our needs. With all of the floor and the pieces of the platform selected, navigate to View| Surface Properties. From there, change the Simple field under Scaling to 2.0 and click on the Apply button to its right that will double the size of our textures. After that, go to Alignment and select Box; click on the Apply button placed below it to align our textures as if the faces that we selected were like a box. This works very well for objects consisting of box-like objects (our brushes, for instance). Close the Surface Properties window and open up the Content Browser window. Now search for floors organic. Select M_LT_Floors_BSP_ Organic15b and close the Content Browser window. Now select one of the floors on the edges with the default texture on them. Then right-click and go to Select Surfaces | Matching Texture. After that, right-click and select Apply Material : M_LT_Floors_BSP_Organic15b. We build our project by navigating to Build | Build All, save our game by going to the Save option within the File menu, and run our game by navigating to Play | In Editor. And with that, we now have a nicely textured world, and it is quite a good start towards getting our levels looking as refined as possible. Summary This article discusses the role of an environment artist doing a texture pass on the environment. After that, we will place meshes to make our level pop with added details. Finally, we will add a few more things to make the experience as nice looking as possible. Resources for Article : Further resources on this subject: Getting Started on UDK with iOS [Article] Configuration and Handy Tweaks for UDK [Article] Creating Virtual Landscapes [Article]
Read more
  • 0
  • 0
  • 8319

article-image-implementing-document-management
Packt
17 Jul 2013
19 min read
Save for later

Implementing Document Management

Packt
17 Jul 2013
19 min read
(For more resources related to this topic, see here.) Managing spaces A space in Alfresco is nothing but a folder, which contains content as well as sub-spaces. Space users are the users invited to a space to perform specific actions, such as editing content, adding content, discussing a particular document, and so on. The exact capability a given user has within a space is a function of their role or rights. Consider the capability of creating a sub-space. By default, to create a sub-space, one of the following must apply: The user is the administrator of the system The user has been granted the Contributor role. The user has been granted the Coordinator role. The user has been granted the Collaborator role. Similarly, to edit space properties, a user will need to be the administrator or be granted a role that gives them rights to edit the space. These roles include Editor, Collaborator, and Coordinator.  Space is a smart folder Space is a folder with additional features such as security, business rules, workflow, notifications, local search, and special views. These additional features which make a space a smart folder are explained as follows: Space security: You can define security at the space level. You can specify a user or a group of users, who may perform certain actions on content in a space. For example, on the Marketing Communications space in intranet, you can specify that only users of the marketing group can add the content and others can only see the content. Space business rules: Business rules, such as transforming content from Microsoft Word to Adobe PDF and sending notifications when content gets into a space can be defined at space level. Space workflow: You can define and manage content workflow on a space. Typically, you will create a space for the content to be reviewed, and a space for approved content. You will create various spaces for dealing with the different stages the work flows through, and Alfresco will manage the movement of the content between those spaces. Space events: Alfresco triggers events when content gets into a space, or when content goes out of a space, or when content is modified within a space. You can capture such events at space level and trigger certain actions, such as sending e-mail notifications to certain users. Space aspects: Aspects are additional properties and behavior, which could be added to the content, based on the space in which it resides. For example, you can define a business rule to add customer details to all the customer contract documents in your intranet's Sales space. Space search: Alfresco search can be limited to a space. For example, if you create a space called Marketing, you can limit the search for documents within the Marketing space, instead of searching the entire site. Space syndication: Space content can be syndicated by applying RSS feed scripts on a space. You can apply RSS feeds on your News space, so that other applications and websites can subscribe for news updates. Space content: Content in a space can be versioned, locked, checked-in and checked-out, and managed. You can specify certain documents in a space to be versioned and others not. Space network folder: Space can be mapped to a network drive on your local machine, enabling you to work with the content locally. For example, using CIFS interface, space can be mapped to the Windows network folder. Space dashboard view: Content in a space can be aggregated and presented using special dashboard views. For example, the Company Policies space can list all the latest policy documents which are updated for the past one month or so. You can create different views for Sales, Marketing and Finance departmental spaces. Importance of space hierarchy Like regular folders, a space can have child spaces (called sub-spaces) and sub-spaces can further have sub-spaces of their own. There is no limitation on the number of hierarchical levels. However, the space hierarchy is very important for all the reasons specified above in the previous section. Any business rule and security defined at a space is applicable to all the content and sub-spaces underlying that space by default. Use the created system users, groups, and spaces for various departments as per the example. Your space hierarchy should look similar to the following screenshot: A space in Alfresco enables you to define various business rules, a dashboard view, properties, workflow, and security for the content belonging to each department. You can decentralize the management of your content by giving access to departments at individual space levels. The example of the intranet space should contain sub-spaces, as shown in the preceding screenshot. If you have not already created spaces, you must do it now by logging in as administrator. Also, it is very important to set security (by inviting groups of users to these spaces). Editing a space Using a web client, you can edit the spaces you have added previously. Note that you need to have edit permissions on the spaces to edit them. Editing space properties Every space listed will have clickable actions, as shown in the following screenshot: These clickable actions will be dynamically generated for each space based on the current user's permissions on that space. If you have copy permission on a space, you will notice the copy icon as a clickable action for that space. On clicking the View Details action icon, the detailed view of a space will be displayed, as shown in the next screenshot: The detailed view page of a space allows you to select a dashboard view for viewing and editing existing space properties, to categorize the space, to set business rules, and to run various actions on the space, as shown in the preceding screenshot. To edit space properties, click on the Edit Space Properties icon, shown in the preceding screenshot. You can change the name of the space and other properties as needed. Deleting space and its contents From the list of space actions, you can click on the Delete action to delete the space. You need to be very careful while deleting a space as all the business rules, sub-spaces, and the entire content within the space will also be deleted. Moving or copying space by using the clipboard From the list of space actions, you can click on the Cut action to move a space to the clipboard. Then you can navigate to any space hierarchy, assuming that you have the required permissions to do so, and paste this particular space, as required. Similarly, you can use the Copy action to copy the space to some other space hierarchy. This is useful if you have an existing space structure (such as a marketing project or engineering project), and you would like to replicate it along with the data it contains. The copied or moved space will be identical in all aspects to the original (source) space. When you copy a space, the space properties, categorization, business rules, space users, entire content within the space, and all sub-spaces along with their content will also be copied. Creating a shortcut to a space for quick access If you need to frequently access a space, you can create a shortcut (similar to the Favorite option in Internet browsers) to that space, in order to reach the space in just one click. From the list of space actions, you can click on the Create Shortcut action to create a shortcut to the existing space. Shortcuts are listed in the left-hand side shelf. Consider a scenario where after creating the shortcut, the source space is deleted. The shortcuts are not automatically removed as there is a possibility for the user to retrieve the deleted space. What will happen when you click on that shortcut link in the Shelf? If the source space is not found (deleted by user), then the shortcut will be removed with an appropriate error message. Choosing a default view for your space There are four different out-of-the-box options available (as shown in the screenshot overleaf). These options support the display of the space's information: Details View: This option provides listings of sub-spaces and content, in horizontal rows. Icon View: This option provides a title, description, timestamp, and action menus for each sub-space and content item present in the current space. Browse View: Similar to the preceding option, this option provides title, description, and list of sub-spaces for each space. Dashboard View: This option is disabled and appears in gray. This is because you have not enabled the dashboard view for this space. In order to enable dashboard view for a space, you need to select a dashboard view (Refer to the icon shown in the preceding screenshot). Sample space structure for a marketing project Let us say you are launching a new marketing project called Product XYZ Launch. Go to the Company Home | Intranet | Marketing Communications space and create a new space called Product XYZ Launch and create various sub-spaces as needed. You can create your own space structure within the marketing project space to manage content. For example, you can have a space called 02_Drafts to keep all the draft marketing documents and so on. Managing content Content could be of any type, as mentioned at the start of this article. By using the Alfresco web client application, you can add and modify content and its properties. You can categorize content, lock content for safe editing, and can maintain several versions of the content. You can delete content, and you can also recover the deleted content. This section uses the space you have already created as a part of your Intranet sample application. As a part of sample application, you will manage content in the Intranet | Marketing Communications space. Because you have secured this space earlier, only the administrator (admin) and users belonging to the Marketing group (Peter Marketing and Harish Marketing) can add content in this space. You can log in as Peter Marketing to manage content in this space. Creating content A web client provides two different interfaces for adding content. One can be used to create inline editable content, such as HTML, text, and XML, and the other can be used to add binary content, such Microsoft office files and scanned images. You need to have either administrator, contributor, collaborator, or coordinator roles on a space to create content within that space.  Creating text documents HTML, text, and XML To create an HTML file in a space, follow these steps: Ensure that you are in the Intranet | Marketing Communications | Product XYZ Launch | 02_Drafts space. On the header, click on Create | Create Content. The first pane of the Create Content wizard appears. You can track your progress through the wizard from the list of steps at the left of the pane. Provide name of the HTML file, select HTML as Content Type and click on the Next button. The Enter Content pane of the wizard appears, as shown in the next screenshot. Note that Enter Content is now highlighted in the list of steps at the left of the pane:   You can see that there is a comprehensive set of tools to help you format your HTML document. Enter some text, using some of the formatting features. If you know HTML, you can also use the HTML editor by clicking on the HTML icon. The HTML source editor is displayed. Once you update the HTML content, click on the Update button to return to the Enter Content pane in the wizard, with the contents updated. After the content is entered and edited in the Enter Content pane, click on Finish. You will see the Modify Content Properties screen, which can used to update the metadata associated with the content. Give some filename with .html as extension. Also, you will notice that then Inline Editing checkbox is selected by default. Once you are done with editing the properties, click on the OK button to return to the 02_Drafts space, with your newly created file inserted. You can launch the newly created HTML file by clicking on it. Your browser launches most of the common files, such as HTML, text, and PDF. If the browser could not recognize the file, you will be prompted with the windows dialog box containing the list of applications, from which you must choose an application. This is the normal behavior if you try to launch a file on any Internet page. Uploading binary files – Word, PDF, Flash, Image, and Media Using a web client, you can upload content from your hard drive. Choose a file from your hard disk that is not an HTML or text file. I chose Alfresco_CIGNEX.docx from my hard disk for the sample application. Ensure that you are in the Intranet | Marketing Communications | Product XYZ Launch | 02_Drafts space. To upload a binary file in a space, follow these steps: In the space header, click on the Add Content link. The Add Content dialog appears. To specify the file that you want to upload, click Browse. In the File Upload dialog box, browse to the file that you want to upload. Click Open. Alfresco inserts the full path name of the selected file in the Location textbox. Click on the Upload button to upload the file from your hard disk to the Alfresco repository. A message informs you that your upload was successful, as shown in the following screenshot. Click OK to confirm. Modify the Content Properties dialog appears. Verify the pre-populated properties and add information in the textboxes. Click OK to save and return to the 02_Drafts space. The file that you uploaded appears in the Content Items pane. Alfresco extracts the file size from the properties of the disk file, and includes the value in the size row. Editing content You can edit the content in Alfresco in three different ways: by using the Edit Online, Edit Offline, and Update actions. Note that you need to have edit permissions on the content to edit them. Online editing of HTML, text, and XML HTML files and plain text files can be created and edited online. If you have edit access to a file, you will notice a small pencil (Edit Online) icon, as shown in the following screenshot: Clicking on the pencil icon will open the file in its editor. Each file type is edited in its own WYSIWYG editor. Once you select to edit online, a working copy of the file will be created for editing, whereas the original file will be locked, as shown in the next screenshot. The working copy can be edited further as needed by clicking on the Edit Online button. Once you are done with editing, you can commit all the changes to the original document by clicking on the Done Editing icon. For some reason, if you decided to cancel editing of a document and discard any changes, you can do that by clicking on the Cancel Editing button given below. If you cancel editing of a document, the associated working copy will be deleted and all changes to it since it was checked out will be lost. The working copy can be edited by any user who has edit access to the document or the folder containing the document. For example, if user1 created the working copy and user2 has edit access to the document, and then both user1 and user2 can edit the working copy. Consider a scenario where user1 and user2 are editing the working copy simultaneously. If user1 commits the changes first, then the edits done by user2 will be lost. Hence, it is important to follow best practices in editing the working copy. Some of these best practices are listed here for your reference: Securing the edit access to the working copy to avoid multiple users simultaneously editing the file Saving the working copy after each edit to avoid losing the work done Following the process of allowing only the owner of the document edit the working copy. If others need to edit, they can claim the ownership Triggering the workflow on working copy to confirm the changes before committing Offline editing of files If you wish to download the files to your local machine, edit it locally, and then upload the updated version to Alfresco, then you might consider using the Edit Offline option (pencil icon). Once you click on the Edit Offline button, the original file will be locked automatically and a working copy of the file will be created for download. Then you will get an option to save the working copy of the document locally on your laptop or personal computer. If you don't want to automatically download the files for offline editing, you can turn off this feature. In order to achieve this, click on the User Profile icon in the top menu, and uncheck the option for Offline Editing, as shown here: The working copy can be updated by clicking on the Upload New Version button. Once you have finished editing the file, you can commit all the changes to the original document by clicking on the Done Editing icon. Or you can cancel all the changes by clicking on the Cancel Editing button. Uploading updated content If you have edit access to a binary file, you will notice the Update action icon in the drop-down list for the More actions link. Upon clicking on the Update icon, the Update pane opens. Click on the Browse button to upload the updated version of the document from your hard disk. It is always a good practice to check out the document and update the working copy rather than directly updating the document. Checking the file out avoids conflicting updates by locking the document, as explained in the previous section. Content actions Content will have clickable actions, as shown in the upcoming screenshot. These clickable actions (icons) will be dynamically generated for a content based on the current user's permissions for that content. For example, if you have copy permission for the content, you will notice the Copy icon as a clickable action for that content. Deleting content Click on the Delete action, from the list of content actions, to delete the content. Please note that when content is deleted, all the previous versions of that content will also be deleted. Moving or copying content using the clipboard From the list of content actions, as shown in the preceding screenshot, you can click on the Cut action to move content to the clipboard. Then, you can navigate to any space hierarchy and paste this particular content as required. Similarly, you can use the Copy action to copy the content to another space. Creating a shortcut to the content for quick access If you have to access a particular content very frequently, you can create a shortcut (similar to the way you can with Internet and Windows browser's Favorite option) to that content, in order to reach the content in one click. From the list of content actions, as shown in the preceding screenshot, you can click on the Create Shortcut action to create a shortcut to the existing content. Shortcuts are listed in the left-hand side Shelf. Managing content properties Every content item in Alfresco will have properties associated with it. Refer to the preceding screenshot to see the list of properties, such as Title, Description, Author, Size, and Creation Date. These properties are associated with the actual content file, named Alfresco_CIGNEX.docx. The content properties are stored in a relational database and are searchable using Advanced Search options. What is Content Metadata? Content properties are also known as Content Metadata. Metadata is structured data, which describes the characteristics of the content. It shares many similar characteristics with the cataloguing that takes place in libraries. The term "Meta" derives from the Greek word denoting a nature of a higher order or more fundamental kind. A metadata record consists of a number of predefined elements representing specific attributes of content, and each element can have one or more values. Metadata is a systematic method for describing resources, and thereby improving access to them. If access to the content will be required, then it should be described using metadata, so as to maximize the ability to locate it. Metadata provides the essential link between the information creator and the information user. While the primary aim of metadata is to improve resource discovery, metadata sets are also being developed for other reasons, including: Administrative control Security Management information Content rating Rights management Metadata extractors Typically, in most of the content management systems, once you upload the content file, you need to add the metadata (properties), such as title, description, and keywords to the content manually. Most of the content, such as Microsoft Office documents, media files, and PDF documents contain properties within the file itself. Hence, it is double the effort, having to enter those values again in the content management system along with the document. Alfresco provides built-in metadata extractors for popular document types to extract the standard metadata values from a document and populate the values automatically. This is very useful if you are uploading the documents through FTP, CIFS, or WebDAV interface, where you do not have to enter the properties manually, as Alfresco will transfer the document properties automatically. Editing metadata To edit metadata, you need to click the Edit Metadata icon () in content details view. Refer the Edit Metadata icon shown in the screenshot, which shows a detailed view of the Alfresco_CIGNEX.docx file. You can update the metadata values, such as Name and Description for your content items. However, certain metadata values, such as Creator, Created Date, Modifier, and Modified Date are read-only and you cannot change them. Certain properties, such as Modifier and Modified Date will be updated by Alfresco automatically, whenever the content is updated. Adding additional properties Additional properties can be added to the content in two ways. One way is to extend the data model and define more properties in a content type.  The other way is to dynamically attach the properties and behavior through Aspects. By using aspects, you can add additional properties, such as Effectivity, Dublin Core Metadata, and Thumbnailable, to the content. 
Read more
  • 0
  • 0
  • 2063

article-image-dpm-non-aware-windows-workload-protection
Packt
16 Jul 2013
18 min read
Save for later

DPM Non-aware Windows Workload Protection

Packt
16 Jul 2013
18 min read
(For more resources related to this topic, see here.) Protecting DFS with DPM DFS stands for Distributed File System . It was introduced in Windows Server 2003, and is a set of services available as a role on Windows Server operating systems that allow you to group file shares held in different locations (different servers) under one folder known as DFS root . The actual locations of the file shares are transparent to the end user. DFS is also often used for redundancy of file shares. For more information on DFS Windows Server 2008: http://technet.microsoft.com/en-us/library/cc753479%28v=ws.10%29.aspx Windows Server 2008 R2 and Windows Server 2012: http://technet.microsoft.com/en-us/library/cc732006.aspx Before DFS can be protected it is important to know how it is structured. DFS consists of both data and configuration information: The configuration for DFS is stored in the registry of each server, and in either the DFS tree during standalone DFS deployments, or in Active Directory when domain-based DFS is deployed. DFS data is stored on each server in the DFS tree. The data consists of the multiple shares that make up the DFS root. Protecting DFS with DPM is fairly straightforward. It is recommended to protect the actual file shares directly on each of the servers in the DFS root. When you have a standalone DFS deployment you should protect the system state on the servers in the DFS root, and when you have a domain-based DFS deployment we recommend you protect your Active Directory of the domain controller that hosts the DFS root. If you are using DFS replication it is also recommended to protect the shadow copy components on servers that host the replication data, in addition to the previously mentioned items. These methods would allow you to restore DFS by restoring the data and either system state or Active Directory depending on your deployment type. Another option is to use the DfsUtil tool to export/import your DFS configuration. This is a command-line utility that comes with Windows Server that can export the namespace configuration to a file. The configuration can then be imported back into a DFS server to restore a DFS namespace. DPM can be set up to protect the DFS export. You would still need to protect the actual data directly. An example of using the DfsUtil tool would be: Run DfsUtil root export domainnamerootname dfsrootname.xml to export the DFS configuration to an XML file, then run DfsUtil root import to import the DFS configuration back in. For more information on the DfsUtil tool, visit the following URL: http://blogs.technet.com/b/josebda/archive/2009/05/01/using-the-windows-server-2008-dfsutil-exe-command-line-to-manage-dfs-namespaces.aspx That covers the backing up of DFS with DPM. Protecting Dynamics CRM with DPM Microsoft Dynamics CRM is Microsoft's customer relationship management (CRM) software in the CRM market. Microsoft Dynamics CRM Version 1.0 was released in 2003. It then progressed to Version 4.0 and the latest one is 2011. CRM is a part of the Microsoft Dynamics product family. In this section we will cover protecting Versions 4.0 and 2011. Note that when protecting Microsoft Dynamics CRM on either Version 4.0 or 2011, you should keep a note of your update-rollup level some place safe, so that you can install CRM back to that level in the event of a restore. You will need to restore the CRM database and this could lead to an error if CRM is not at the correct update level. To protect Microsoft Dynamics CRM 4.0, back up the following components: Microsoft CRM Server database This is straightforward; you simply need to protect the SQL CRM databases. The two databases you want to protect are the following: The configuration database: MSCRM_CONFIG The organization database: OrganizationName_MSCRM Microsoft CRM Server program files By default, these files will be located at C:Program FilesMicrosoft CRM. Microsoft CRM website By default the CRM website files are located in the C:Inetpubwwwroot directory. The web.config file can be protected. It only needs protecting if it has been changed from the default settings. Microsoft CRM registry subkey Back up the HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSCRM key. Microsoft CRM customizations To protect customizations or any third-party add-ons you will need to understand the specific components to back up and protect. Other components to back up for protecting Microsoft CRM include the following: System state of your domain controller. Exchange server if the CRM's e-mail router is used. To protect Microsoft Dynamics CRM 2011, back up the following components: Microsoft CRM 2011 databases This is straightforward, you simply need to protect the SQL CRM databases. The two databases you want to protect are: The configuration database: MSCRM_CONFIG The organization database: OrganizationName_MSCRM Microsoft CRM 2011 program files By default, these files will be located at C:Program FilesMicrosoft CRM. Microsoft CRM 2011 website By default the CRM website files are located in the C:Program FilesMicrosoft CRMCRMWeb directory. The web.config file can be protected. It only needs protecting if it has been changed from the default settings. Microsoft CRM 2011 registry subkey Back up the HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSCRM subkey. Microsoft CRM 2011 customizations To protect customizations or any third-party add-ons you will need to understand the specific components to back up and protect. Other components to back up for protecting Microsoft CRM 2011 include: System state of your domain controller. Exchange server if the CRM's e-mail router is used. SharePoint if CRM and SharePoint integration is in use. Note that for both CRM 4.0 and CRM 2011, you could have more than one OrganizationName_MSCRM database if you have more than one organization in CRM. Be sure to protect all of the OrganizationName_MSCRM databases that may exist. That wraps up the Microsoft Dynamics CRM protection for both 4.0 and 2011. You simply need to configure protection of the mentioned components with DPM. Now let's look at what it will take to protect another product from the Dynamics family. Protecting Dynamics GP with DPM Dynamics GP is Microsoft's ERP and accounting software package for mid-market businesses. GP has standard accounting functions but it can do more such as Sales Order Processing, Order Management, Inventory Management, and Demand Planner for forecasting, thus making it usable as a full-blown ERP. GP was once known as Great Plains Software before acquisition by Microsoft. The most recent versions of GP are Microsoft Dynamics GP 10.0 and Dynamics GP 2010 R2. GP holds your organization's financial data. If you use it as an ERP solution, it holds even more critical data, and losing this data could be devastating to an organization. Yes, there is a built-in backup utility in GP but this does not cover all bases in protecting your GP. In fact, the built-in backup process only backs up the SQL database, and does not cover items like: Customized forms Reports Financial statement formats The sysdata folder These are the GP components you should protect with DPM: SQL administrative databases: Master, TempDB, and Model Microsoft Dynamics GP system database (DYNAMICS) Each of your company databases If you use SQL Server Agent to schedule automatic tasks, back up the msdb database forms.dic (for customized forms) can be found in %systemdrive%Program Files (x86)Microsoft DynamicsGP2010 reports.dic (for reports) can be found in %systemdrive%Program Files (x86)Microsoft DynamicsGP2010 Backing up these components with DPM should be sufficient protection in the event a restore is needed. Protecting TMG 2010 with DPM Threat Management Gateway (TMG ) is a part of the Forefront product family. The predecessor to TMG is Internet Security and Acceleration Server (ISA Server ). TMG is fundamentally a firewall, but a very powerful one with features such as VPN, web caching, reverse proxy, advanced stateful packet, WAN failover, malware protection, routing, load balancer, and much more. There have been several forum threads on the Microsoft DPM TechNet forums asking about DPM protecting TMG, which sparked the inclusion of this section in the book. TMG is a critical part of networks and should have high priority in regards to backup, right up there with your other critical business applications. In many environments, if TMG is down, there are a good amount of users that cannot access certain business applications which causes downtime. Let's take a look at how and what to protect in regards to TMG. The first step is to allow DPM traffic on TMG so that the agent can communicate with DPM. You will need to install the DPM agent on TMG and then start protecting it from there. Follow the ensuing steps to protect your TMG server: On the TMG server, go to Start | All Programs | Microsoft TMG Server . Open the TMG Server Management MMC. Expand Arrays and then TMG Server computer , then click on Firewall Policy . On the View menu, click on Show System Policy Rules . Right-click on the Allow remote management from selected computers using MMC system policy rule. Select Edit System Policy . In the System Policy Editor dialog box, click to clear the Enable this configuration group checkbox, and then click on OK . Click on Apply to update the firewall configuration, and then click on OK . Right-click on the Allow RPC from TMG server to trusted servers system policy rule. Select Edit System Policy . In the System Policy Editor dialog box, click to clear the Enforce strict RPC compliance checkbox, and then click on OK . Click on Apply to update the firewall configuration, and then click on OK . On the View menu, click on Hide System Policy Rules . Right-click on Firewall Policy . Select New and then Access Rule . In the New Access Rule Wizard window, type a name in the Access rule name box. Click on Next . Check the Allow checkbox and then click on Next . In the This rule applies to list, select All outbound traffic from the drop-down menu and click on Next . On the Access Rule Sources page, click on Add . In the Add Network Entities dialog window, click on New and select Computer from the drop-down list. Now type the name of your DPM server and type the DPM server's IP address in the Computer IP Address field. Click on OK when you are done. You will then see your DPM server listed under the Computers folder in the Add Network Entities window. Select it and click on Add . This will bring the DPM computer into your access rule wizard. Click on Next . In the Add Rule Destinations window click on Add . The Add Network Entities window will come up again. In this window expand the Networks folder, and then select Local Host and click on Add . Now click on Next . Your rule should have both the DPM server and Local Host listed for both incoming and outgoing. Click on Next , leave the default All Users entry in the This rule applies to requests from the following user sets box, click on Next again. Click on Finish . Right-click on the new rule (DPM2010 in this example), and then click on Move Up . Right-click on the new rule, and select Properties . In the rule name properties dialog box (DPM2010 Properties ), click on the Protocols tab, then click on Filtering . Now select Configure RPC Protocol . In the Configure RPC protocol policy dialog box, check the Enforce strict RPC compliance checkbox, and then click on OK twice. Click on Apply to update the firewall policy, and then click on OK . Now you will need to attach the DPM agent for the TMG server. Follow the ensuing steps to complete this task: Open the DPM Administrator Console. Click on the Management tab on the navigation bar. Now click on the Agents tab. On the Actions pane, click on Install . Now the Protection Agent Install Wizard window should pop up. Choose the Attach agents checkbox. Choose Computer on trusted domain , and click on Next . Select the TMG server from the list and click on Add and then click on Next . Enter credentials for the domain account. The account that is used here needs to have administrative rights on the computer you are going to protect. Click on Next to continue. You will receive a warning that DPM cannot tell if the TMG server is clustered or not. Click on OK for this. On the next screen click on Attach to continue. Next you have to install the agent on the TMG firewall and point it to the correct DPM server. Follow the ensuing steps to complete this task: From the TMG server that you will be protecting, access the DPM server over the network and copy the folder with the agent installed in it down to the local machine. Use this path DPMSERVERNAME%systemdrive%program filesMicrosoft DPMDPMProtectionAgentsRA3.03.0.7696.0i386. Then from the local folder on the protected computer, run dpmra.msi to install the agent. Open a command prompt (make sure you have elevated privileges), change directory to C:Program FilesMicrosoft Data Protection ManagerDPMbin then run the following: SetDpmServer.exe -dpmServerName <serverName> userName <userName> Following is the example of the previous command: SetDpmServer.exe -dpmServerName buchdpm Now restart the TMG server. Once your TMG server comes back, check the Windows services to make sure that the DPMRA service is set to automatic, and then start it. That is it for configuring DPM to start protecting TMG, but there are a few more things that we still need to cover on this topic. With TMG backup you can choose to back up certain components of TMG, depending on your recovery needs. With DPM you can back up the TMG hard drive, TMG logs that are stored in SQL, TMG's system state, or BMR of TMG. Following is the list of components you should back up depending on your circumstances: What can be included in TMG server backup: TMG configuration settings (exported through TMG) TMG firewall settings (exported through TMG) TMG logfiles (stored in SQL databases) TMG install directory (only needed if you have custom forms for things such as an Outlook Web Access login screen TMG server system state TMG BMR None of the previous components are required for protection of TMG. In fact, protecting the SQL logfiles tends to cause more issues than it helps, as they change so often. These SQL log databases change so often that DPM will send an error when the old SQL databases no longer shown under protection. The logfiles are not required to restore your TMG. For a standard TMG restore, you will need to reinstall TMG, reconfigure NIC settings, import any certificates, and restore TMG configuration and firewall settings. For more information on backing up TMG 2010, visit the following page: http://technet.microsoft.com/en-us/library/cc984454.aspx. DPM cannot back up the TMG configuration and firewall settings natively. This needs to be scripted and scheduled through Windows Task Scheduler, and then placed on the local hard drive. DPM can back up the .XML settings for TMG export from there. You can find the TMG server's export script at http://msdn.microsoft.com/en-us/library/ms812627.aspx. Place this script into a .VBS file, and then set up a scheduled task to call this file to run. This automates the export of your TMG server settings. There is another way to back up the entire TMG server. This is a new type of protection, specific to TMG 2010. This protection is BMR and is available because TMG is now installed on top of Windows Server 2008 and Windows Server 2008 R2. Protecting the BMR of your TMG gives you the ability to restore your entire TMG in the event that it fails-configuration and firewall settings included. BMR will also bring back certificates and NIC card settings. Note that BMR of TMG restored on a virtual machine can't use its NIC card settings. It only on the same hardware. Well that covers how to protect TMG with DPM. As you can see that there are some improvements through BMR, and if you do not employ BMR protection you can still automate the process of protecting TMG. How to protect IIS Internet Information Services (IIS ) is Microsoft's web server platform. It is included for free with Windows Server operating systems. Its modular nature makes it scalable for different organization web server need. The latest version is IIS 8. It can be used for more than standard web hosting, for example as an FTP server or for media delivery . Knowing what to protect when it comes to IIS will come in handy in almost any environment you may work in. Backing up IIS is one thing but you need to ensure that you understand the websites or web applications you are running, so that you know how to back them up too. In this section, we are going to look at the protection of IIS. To protect IIS, you should backup the following components: IIS configuration files Website or web applications data SSL certificates Registry (only needed if website or web application required modifications of the registry) Metabase The IIS configuration files are located in the %systemdrive%windowssystem32inetsrvconfig directory (and subdirectories). The website or web application files are typically found in C:inetpubwwwroot. Now this is the default location but the website or web application files can be located anywhere on an IIS server. To export SSL certificates directly from IIS, follow the ensuing steps: Open the Microsoft IIS 7 console. In the left-hand pane, select the server name. In the center pane click on the server certificates icon. Right-click on the certificate you wish to export and select export . Enter a file path, name the certificate file, and give it a password. Click on OK and your certificate will be exported as a .pfx file in the path you specified. Metabase is an internal database that holds IIS configuration data. It is made up of two files: MBSchema.xml and MetaBase.xml. These can be found in %SystemRoot%system32inetsrv. A good thing to know is that if you protect the system state of a server, then IIS configuration will be included in this backup. This does not include the website or web application files, so you will still need to protect these in addition to a system state backup. That covers the items you will need to protect IIS with DPM backup. Protecting Lync 2010 with DPM Lync 2010 is Microsoft's Unified Communication platform complete with IM, presence, conferencing, enterprise video and voice, and more. Lync was formerly known as Office Communicator. Lync is quickly becoming an integral part of business communications. With Lync being a critical application to organizations, it important to ensure this platform is backed up. Lync is a massive product with many moving parts. We are not going to cover all of Lync's architecture as this would need its own book. We are going to focus on what should be backed up to ensure protection of your Lync deployment. Overall, we want to protect Lync's settings and configuration data. The majority of this data is stored in the Lync Central Management store. The following are the components that needs to be protected in order to back up Lync: Settings and configuration data Topology configuration (Xds.mdf) Location information (Lis.mdf) Response group configuration (RgsConfig.mdf) Data stored in databases User data (Rtc.mdf) Archiving data (LcsLog.mdf) Monitoring data (csCDR.mdf and QoeMetrics.mdf) File stores Lync server file store Archiving file store These stores will be file shares on the Lync server, named in the format lyncservernamesharename. To track down these file shares if you don't know where they are, go to the Lync Topology Builder and look in the File stores node. Note the files named Meeting.Active should not be backed up. These files are in use and locked while a meeting takes place. Other components as follows: Active Directory (User SIP data, a pointer to the Central Management store, and objects for Response Group and Conferencing Attendant) Certification authority (CA) and certificates (if you use an internal CA) Microsoft Exchange and Exchange Unified Messaging (UM) if you are using UM with your Exchange Domain Name System (DNS) records and IP addresses IIS on Lync Server DHCP Configuration Group Chat (if used) XMPP gateways if you are using XMPP gateway Public switched telephone network (PSTN) gateway configuration, if your Lync is connected to one Firewall and Load Balancer (if used) configurations Summary Now that we had a chance to look at several Microsoft workloads that are used in organizations today and how to protect them with DPM, you should have a good understanding what it takes to back them up. These workloads included Lync 2010, IIS, CRM, GP, DFS, and TMG. Note there are many more Microsoft workloads that DPM cannot protect natively, which we were unable to cover in this article. Resources for Article : Further resources on this subject: Overview of Microsoft Dynamics CRM 2011 [Article] Deploying .NET-based Applications on to Microsoft Windows CE Enabled Smart Devices [Article] Working with Dashboards in Dynamics CRM [Article]
Read more
  • 0
  • 0
  • 2135
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-article-authorizations-in-sap-hana
Packt
16 Jul 2013
28 min read
Save for later

Authorizations in SAP HANA

Packt
16 Jul 2013
28 min read
(For more resources related to this topic, see here.) Roles In SAP HANA, as in most of SAP's software, authorizations are grouped into roles. A role is a collection of authorization objects, with their associated privileges. It allows us, as developers, to define self-contained units of authorization. In the same way that at the start of this book we created an attribute view allowing us to have a coherent view of our customer data which we could reuse at will in more advanced developments, authorization roles allow us to create coherent developments of authorization data which we can then assign to users at will, making sure that users who are supposed to have the same rights always have the same rights. If we had to assign individual authorization objects to users, we could be fairly sure that sooner or later, we would forget someone in a department, and they would not be able to access the data they needed to do their everyday work. Worse, we might not give quite the same authorizations to one person, and have to spend valuable time correcting our error when they couldn't see the data they needed (or worse, more dangerous and less obvious to us as developers, if the user could see more data than was intended). It is always a much better idea to group authorizations into a role and then assign the role to users, than assign authorizations directly to users. Assigning a role to a user means that when the user changes jobs and needs a new set of privileges; we can just remove the first role, and assign a second one. Since, we're just starting out using authorizations in SAP HANA, let's get into this good habit right from the start. It really will make our lives easier later on. Creating a role Role creation is done, like all other SAP HANA development, in the Studio. If your Studio is currently closed, please open it, and then select the Modeler perspective. In order to create roles, privileges, and users, you will yourself need privileges. Your SAP HANA user will need the ROLE ADMIN, USER ADMIN, and CREATE STRUCTURED PRIVILEGE system privileges in order to do the development work in this article. You will see in the Navigator panel we have a Security folder, as we can see here: Please find the Security folder and then expand this folder. You will see a subfolder called Roles. Right-click on the Roles folder and select New Role to start creating a role. On the screen which will open, you will see a number of tabs representing the different authorization objects we can create, as we can see here: We'll be looking at each of these in turn, in the following sections, so for the moment just give your role Name (BOOKUSER might be appropriate, if not very original). Granted roles Like many other object types in SAP HANA, once you have created a role, you can then use it inside another role. This onion-like arrangement makes authorizations a lot easier to manage. If we had, for example, a company with two teams: Sales   Purchasing   And two countries, say: France   Germany   We could create a role giving access to sales analytic views, one giving purchasing analytic views, one giving access to data for France, and one giving access to data for Germany. We could then create new roles, say Sales-France, which don't actually contain any authorization objects themselves, but contain only the Sales and the France roles. The role definition is much simpler to understand and to maintain than if we had directly created the Sales-France role and a Sales-Germany role with all the underlying objects. Once again, as with other development objects, creating small self-contained roles and reusing them when possible will make your (maintenance) life easier. In the Granted Roles tab we can see the list of subroles this main role contains. Note that this list is only a pointer, you cannot modify the actual authorizations and the other roles given here, you would need to open the individual role and make changes there. Part of roles The Part of Roles tab in the role definition screen is exactly the opposite of the Granted Roles tab. This tab lists all other roles of which this role is a subrole. It is very useful to track authorizations, especially when you find yourself in a situation where a user seems to have too many authorizations and can see data they shouldn't be able to see. You cannot manipulate this list as such, it exists for information only. If you want to make changes, you need to modify the main role of which this role is a subrole. SQL privileges An SQL privilege is the lowest level at which we can define restrictions for using database objects. SQL privileges apply to the simplest objects in the database such as schemas, tables and so on. No attribute, analytical, or calculation view can be seen by SQL privileges. This is not strictly true, though you can consider it so. What we have seen as an analytical view, for example, the graphical definition, the drag and drop, the checkboxes, has been transformed into a real database object in the _SYS_BIC schema upon activation. We could therefore define SQL privileges on this database object if we wanted, but this is not recommended and indeed limits the control we can have over the view. We'll see a little later that SAP HANA has much finer-grained authorizations for views than this. An important thing to note about SQL privileges is that they apply to the object on which they are defined. They restrict access to a given object itself, but do not at any point have any impact on the object's contents. For example, we can decide that one of our users can have access to the CUSTOMER table, but we couldn't restrict their access to only CUSTOMER values from the COUNTRY USA. SQL privileges can control access to any object under the Catalog node in the Navigator panel. Let's add some authorizations to our BOOK schema and its contents. At the top of the SQL Privileges tab is a green plus sign button. Now click on this button to get the Select Catalog Object dialog, shown here: As you can see in the screenshot, we have entered the two letters bo into the filter box at the top of the dialog. As soon as you enter at least two letters into this box, the Studio will attempt to find and then list all database objects whose name contains the two letters you typed. If you continue to type, the search will be refined further. The first item in the list shown is the BOOK schema we created right back at the start of the book in the Chapter 2, SAP HANA Studio - Installation and First Look . Please select the BOOK item, and then click on OK to add it to our new role: The first thing to notice is the warning icon on the SQL Privileges tab itself: This means that your role definition is incomplete, and the role cannot be activated and used as yet. On the right of the screen, a list of checkbox options has appeared. These are the individual authorizations appropriate to the SQL object you have selected. In order to grant rights to a user via a role, you need to decide which of these options to include in the role. The individual authorization names are self-explicit. For example, the CREATE ANY authorization allows creation of new objects inside a schema. The INSERT or SELECT authorization might at first seem unusual for a schema, as it's not an object which can support such instructions. However, the usage is actually quite elegant. If a user has INSERT rights on the schema BOOK, then they have INSERT rights on all objects inside the schema BOOK. Granting rights on the schema itself avoids having to specify the names of all objects inside the schema. It also future-proofs your authorization concept, since new objects created in the schema will automatically inherit from the existing authorizations you have defined. On the far right of the screen, alongside each authorization is a radio button which gives an additional privilege, the possibility for a given user to, in turn, give the rights to a second user. This is an option which should not be given to all users, and so should not be present in all roles you create; the right to attribute privileges to users should be limited to your administrators. If you give just any user the right to pass on their authorizations further, you will soon find that you are no longer able to determine who can do what in your database. For the moment we are creating a simple role to show the working of the authorization concept in SAP HANA, so we will check all the checkboxes, and leave the radio buttons at No : There are some SQL privileges which are necessary for any user to be able to do work in SAP HANA. These are listed below. They give access to the system objects describing the development models we create in SAP HANA, and if a user does not have these privileges, nothing will work at all, the user will not be authorized to do anything. The SQL privileges you will need to add to the role in order to give access to basic SAP HANA system objects are: The SELECT privilege on the _SYS_BI schema   The SELECT privilege on the _SYS_REPO schema   The EXECUTE privilege on the REPOSITORY_REST procedure   Please add these SQL privileges to your role now, in order to obtain the following result: As you can see with the configuration we have just done, SQL privileges allow a user to access a given object and allow specific actions on the object. They do not however allow us to specify particular authorizations to the contents of the object. In order to use such fine-grained rights, we need to create an analytic privilege, and then add it to our role, so let's do that now. Analytic privileges An analytic privilege is an artifact unique to SAP HANA, it is not part of the standard SQL authorization concept. Analytic privileges allow us to restrict access to certain values of a given attribute, analytic, or calculation view. This means that we can create one view, which by default shows all available data, and then restrict what is actually visible to different users. We could restrict visible data by company code, by country, or by region. For example, our users in Europe would be allowed to see and work with data from our customers in Europe, but not those in the USA. An analytic privilege is created through the Quick Launch panel of Modeler , so please open that view now (or switch to the Quick Launch tab if it's already open). You don't need to close the role definition tab that's already open, we can leave it for now, create our analytic privilege, and then come back to the role definition later. From the Quick Launch panel, select Analytic Privilege , and then Create . As usual with SAP HANA, we are asked to give Name , Description , and select a package for our object. We'll call it AP_EU (for analytic privilege, Europe), use the name as the description, and put it into our book package alongside our other developments. As is common in SAP HANA, we have the option of creating an analytic privilege from scratch (Create New ) or copying an existing privilege (Copy From ). We don't currently have any other analytic privileges in our development, so leave Create New selected, then click on Next to go to the second screen of the wizard, shown here: On this page of the dialog, we are prompted to add development models to the analytic privilege. This will then allow us to restrict access to given values of these models. In the previous screenshot, we have added the CUST_REV analytic view to the analytic privilege. This will allow us to restrict access to any value we specify of any of the fields visible in the view. To add a view to the analytic privilege, just find it in the left panel, click on its name and then click on the Add button. Once you have added the views you require for your authorizations, click on the Finish button at the bottom of the window to go to the next step. You will be presented with the analytic privilege development panel, reproduced here: This page allows us to define our analytic privilege completely. On the left we have the list of database views we have included in the analytic privilege. We can add more, or remove one, using the Add and Remove buttons. To the right, we can see the Associated Attributes Restrictions and Assign Restrictions boxes. These are where we define the restrictions to individual values, or sets of values. In the top box, Associated Attributes Restrictions , we define on which attributes we want to restrict access (country code or region, maybe). In the bottom box, Assign Restrictions , we define the individual values on which to restrict (for example, for company code, we could restrict to value 0001, or US22; for region, we could limit access to EU or USA). Let's add a restriction to the REGION field of our CUST_REV view now. Click on the Add button next to the Associated Attributes Restrictions box, to see the Select Object dialog: As can be expected, this dialog lists all the attributes in our analytic view. We just need to select the appropriate attribute and then click on OK to add it to the analytic privilege. Measures in the view are not listed in the dialog. We cannot restrict access to a view according to numeric values. We cannot therefore, make restrictions to customers with a revenue over 1 million Euros, for example. Please add the REGION field to the analytic privilege now. Once the appropriate fields have been added, we can define the restrictions to be applied to them. Click on the REGION field in the Associated Attributes Restrictions box, then on the Add button next to the Assign Restrictions box, to define the restrictions we want to apply. As we can see, restrictions can be defined according to the usual list of comparison operators. These are the same operators we used earlier to define a restricted column in our analytic views. In our example, we'll be restricting access to those lines with a REGION column equal to EU, so we'll select Equal . In the Value column, we can either type the appropriate value directly, or use the value help button, and the familiar Value Help Dialog which will appear, to select the value from those available in the view. Please add the EU value, either by typing it or by having SAP HANA find it for us, now. There is one more field which needs to be added to our analytic privilege, and the reason behind might seem at first a little strange. This point is valid for SAP HANA SP5, up to and including (at least) release 50 of the software. If this point turns out to be a bug, then it might not be necessary in later versions of the software. The field on which we want to restrict user actions (REGION) is not actually part of the analytic view itself. REGION, if you recall, is a field which is present in CUST_REV , thanks to the included attribute view CUST_ATTR . In its current state, the analytic privilege will not work, because no fields from the analytic view are actually present in the analytic privilege. We therefore need to add at least one of the native fields of the analytic view to the analytic privilege. We don't need to do any restriction on the field; however it needs to be in the privilege for everything to work as expected. This is hinted at in SAP Note 1809199, SAP HANA DB: debugging user authorization errors. Only if a view is included in one of the cube restrictions and at least one of its attribute is employed by one of the dimension restrictions, access to the view is granted by this analytical privilege. Not an explicit description of the workings of the authorization concept, but close. Our analytic view CUST_REV contains two native fields, CURRENCY and YEAR. You can add either of these to the analytic privilege. You do not need to assign any restrictions to the field; it just needs to be in the privilege. Here is the state of the analytic privilege when development work on it is finished: The Count column lists the number of restrictions in effect for the associated field. For the CURRENCY field, no restrictions are defined. We just need (as always) to activate our analytic privilege in order to be able to use it. The activation button is the same one as we have used up until now to activate the modeling views, the round green button with the right-facing white arrow at the top-right of the panel, which you can see on the preceding screenshot. Please activate the analytic privilege now. Once that has been done, we can add it to our role. Return to the Role tab (if you left it open) or reopen the role now. If you closed the role definition tab earlier, you can get back to our role by opening the Security node in the Navigator panel, then opening Roles, and double-clicking on the BOOKUSER role. In the Analytic Privileges tab of the role definition screen, click on the green plus sign at the top, to add an analytic privilege to our role. The analytic privilege we have just created is called AP_EU, so type ap_eu into the search box at the top of the dialog window which will open. As soon as you have typed at least two characters, SAP HANA will start searching for matching analytic privileges, and your AP_EU privilege will be listed, as we can see here: Click on OK to add the privilege to the role. We will see in a minute the effect our analytic privilege has on the rights of a particular user, but for the moment we can take a look at the second-to-last tab in the role definition screen, System Privileges . System privileges As its name suggests, system privileges gives to a particular user the right to perform specific actions on the SAP HANA system itself, not just on a given table or view. These are particular rights which should not be given to just any user, but should be reserved to those users who need to perform a particular task. We'll not be adding any of these privileges to our role, however we'll take a look at the available options and what they are used for. Click on the green plus-sign button at the top of the System Privileges tab to see a list of the available privileges. By default the dialog will do a search on all available values; there are only fifteen or so, but you can as usual filter them down if you require using the filter box at the top of the dialog: For a full list of the system privileges available and their uses, please refer to the SAP HANA SQL Reference, available on the help.sap.com website at http://help.sap.com/hana/html/sql_grant.html. Package privileges The last tab in the role definition screen concerns Package Privileges . These allow a given user to access those objects in a package. In our example, the package is called book, so if we add the book package to our role in the Package Privileges tab, we will see the following result: Assigning package privileges is similar to assigning SQL privileges we saw earlier. We first add the required object (here our book package), then we need to indicate exactly which rights we give to the role. As we can see in the preceding screenshot, we have a series of checkboxes on the right-hand side of the window. At least one of these checkboxes must be checked in order to save the role. The individual rights have names which are fairly self-explanatory. REPO.READ gives access to read the package, whereas REPO.EDIT_NATIVE_OBJECTS allows modification of objects, for example. The role we are creating is destined for an end user who will need to see the data in a role, but should not need to modify the data models in any way (and in fact we really don't want them to modify our data models, do we?). We'll just add the REPO.READ privilege, on our book package, to our role. Again we can decide whether the end user can in turn assign this privilege to others. And again, we don't need this feature in our role. At this point, our role is finished. We have given access to the SQL objects in the BOOK schema, created an analytic privilege which limits access to the Europe region in our CUST_REV model, and given read-only access to our book package. After activation (always) we'll be able to assign our role to a test user, and then see the effect our authorizations have on what the user can do and see. Please activate the role now. Users Users are probably the most important part of the authorization concept. They are where all our problems begin, and their attempts to do and see things they shouldn't are the main reason we have to spend valuable time defining authorizations in the first place. In technical terms, a user is just another database object. They are created, modified, and deleted in the same way a modeling view is. They have properties (their name and password, for example), and it is by modifying these properties that we influence the actions that the person who connects using the user can perform. Up until now we have been using the SYSTEM user (or the user that your database administrator assigned to you). This user is defined by SAP, and has basically the authorizations to do anything with the database. Use of this user is discouraged by SAP, and the author really would like to insist that you don't use it for your developments. Accidents happen, and one of the great things about authorizations is that they help to prevent accidents. If you try to delete an important object with the SYSTEM user, you will delete it, and getting it back might involve a database restore. If however you use a development user with less authorization, then you wouldn't have been allowed to do the deletion, saving a lot of tears. Of course, the question then arises, why have you been using the SYSTEM user for the last couple of hundred pages of development. The answer is simple: if the author had started the book with the authorizations article, not many readers would have gotten past page 10. Let's create a new user now, and assign the role we have just created. From the Navigator panel, open the Security node, right-click on User , and select New User from the menu to obtain the user creation screen as shown in the following screenshot: Defining a user requires remarkably little information: User Name : The login that the user will use. Your company might have a naming convention for users. Users might even already have a standard login they use to connect to other systems in your enterprise. In our example, we'll create a user with the (once again rather unimaginative) name of BOOKU.   Authentication : How will SAP HANA know that the user connecting with the name of ANNE really is Anne? There are three (currently) ways of authenticating a user with SAP HANA. Password : This is the most common authentication system, SAP HANA will ask Anne for her password when she connects to the system. Since Anne is the only person who knows her password, we can be sure that Anne really is ANNE, and let her connect and do anything the user ANNE is allowed to do. Passwords in SAP HANA have to respect a certain format. By default this format is one capital, one lowercase, one number, and at least eight characters. You can see and change the password policy in the system configuration. Double-click on the system name in the Navigator panel, click on the Configuration tab, type the word pass into the filter box at the top of the tab, and scroll down to indexserver.ini and then password policy . The password format in force on your system is listed as password_layout . By default this is A1a, meaning capitals, numbers, and lowercase letters are allowed. The value can also contain the # character, meaning that special characters must also be contained in the password. The only special characters allowed by SAP HANA are currently the underscore, dollar sign, and the hash character. Other password policy defaults are also listed on this screen, such as maximum_password_lifetime (the time after which SAP HANA will force you to change your password).   Kerberos and SAML : These authentication systems need to be set up by your network administrator and allow single sign-on in your enterprise. This means that SAP HANA will be able to see the Windows username that is connecting to the system. The database will assume that the authentication part (deciding whether Anne really is ANNE) has already been done by Windows, and let the user connect.     Session Client : As we saw when we created attribute and analytic views back at the start of the book, SAP HANA understands the notion of client, referring to a partition system of the SAP ERP database. In the SAP ERP, different users can work in different Clients. In our development, we filtered on Client 100. A much better way of handling filtering is to define the default client for a user when we define their account. The Session Client field can be filled with the ERP Client in which the user works. In this way we do not need to filter on the analytic models, we can leave their client value at Dynamic in the view, and the actual value to use will be taken from the user record. Once again this means maintenance of our developments is a lot simpler. If you like, you can take a few minutes at the end of this article to create a user with a session client value of 100, then go back and reset our attribute and analytic views' default client value to Dynamic, reactivate everything, and then do a data preview with your test user. The result should be identical to that obtained when the view was filtered on client 100. However, if you then create a second user with a session client of 200, this second user will see different data.   We'll create a user with a password login, so type a password for your user now. Remember to adhere to the password policy in force on your system. Also note that the user will be required to change their password on first login. At the bottom of the user definition screen, as we can see from the preceding screenshot, we have a series of tabs corresponding to the different authorizations we can assign to our user. These are the same tabs we saw earlier when defining a role. As explained at the beginning of this article, it is considered best practice to assign authorizations to a role and then the role to a user, rather than assign authorizations directly to a user; this makes maintenance easier. For this reason we will not be looking at the different tabs for assigning authorizations to our user, other than the first one, Granted Roles . The Granted Roles tab lists, and allows adding and removing roles from the list assigned to the user. By default when we create a user, they have no roles assigned, and hence have no authorizations at all in the system. They will be able to log in to SAP HANA but will be able to do no development work, and will see no data from the system. Please click on the green plus sign button in the Granted Roles tab of the user definition screen, to add a role to the user account. You will be provided with the Select Role dialog, shown in part here: This dialog has the familiar search box at the top, so typing the first few letters of a role name will bring up a list of matching roles. Here our role was called BOOKUSER, so please do a search for it, then select it in the list and click on OK to add it to the user account. Once that is done, we can test our user to verify that we can perform the necessary actions with the role and user we have just created. We just need, as with all objects in SAP HANA, to activate the user object first. As usual, this is done with the round green button with the right-facing white arrow at the top-right of the screen. Please do this now. Testing our user and role The only real way to check if the authorizations we have defined are appropriate to the business requirements is to create a user and then try out the role to see what the user can and cannot see and do in the system. The first thing to do is to add our new user to the Studio so we can connect to SAP HANA using this new user. To do this, in the Navigator panel, right click on the SAP HANA system name, and select Add Additional User from the menu which appears. This will give you the Add additional user dialog, shown in the following screenshot:     Enter the name of the user you just created (BOOKU) and the password you assigned to the user. You will be required to change the password immediately: Click on Finish to add the user to the Studio. You will see immediately in the Navigator panel that we can now work with either our SYSTEM user, or our BOOKU user: We can also see straight away that BOOKU is missing the privileges to perform or manage data backups; the Backup node is missing from the list for the BOOKU user. Let's try to do something with our BOOKU user and see how the system reacts. The way the Studio lets you handle multiple users is very elegant, since the tree structure of database objects is duplicated, one per user, you can see immediately how the different authorization profiles affect the different users. Additionally, if you request a data preview from the CUST_REV analytic view in the book package under the BOOKU user's node in the Navigator panel, you will see the data according to the BOOKU user's authorizations. Requesting the same data preview from the SYSTEM user's node will see the data according to SYSTEM's authorizations. Let's do a data preview on the CUST_REV view with the SYSTEM user, for reference: As we can see, there are 12 rows of data retrieved, and we have data from the EU and NAR regions. If we ask for the same data preview using our BOOKU user, we can see much less data: BOOKU can only see nine of the 12 data rows in our view, as no data from the NAR region is visible to the BOOKU user. This is exactly the result we aimed to achieve using our analytic privilege, in our role, assigned to our user. Summary In this article, we have taken a look at the different aspects of the authorization concept in SAP HANA. We examined the different authorization levels available in the system, from SQL privileges, analytic privileges, system privileges, and package privileges. We saw how to add these different authorization concepts to a role, a reusable group of authorizations. We went on to create a new user in our SAP HANA system, examining the different types of authentications available, and the assignment of roles to users. Finally, we logged into the Studio with our new user account, and found out the first-hand effect our authorizations had on what the user could see and do. In the next article, we will be working with hierarchical data, seeing what hierarchies can bring to our reporting applications, and how to make the best use of them. Resources for Article : Further resources on this subject: SAP Netweaver: Accessing the MDM System [Article] SAP HANA integration with Microsoft Excel [Article] Exporting SAP BusinessObjects Dashboards into Different Environments [Article]
Read more
  • 0
  • 2
  • 13915

article-image-understanding-passbook
Packt
15 Jul 2013
5 min read
Save for later

Understanding Passbook

Packt
15 Jul 2013
5 min read
(For more resources related to this topic, see here.) Getting ready With iOS 6, Apple introduced the Passbook app as a central digital wallet for all the store cards, coupons, boarding passes, and event tickets that have become a popular feature of apps. A company wishing to take advantage of this digital wallet and the extra functionality it provides, can use Apple's developer platform to create a Pass for their users. How to do it... To understand Passbook, we need to see a Pass in action. Download the example Pass from: http://passkit.pro/example-generic-pkpass If you open this link within Mobile Safari on an iPhone or iPod Touch running iOS 6, you will be presented with the Pass and the option to add it to your Passbook. Alternatively, you can download the Pass on a Mac or PC and e-mail it to yourself, and then open the e-mail within the Mail app on an iPhone or iPod Touch. Tapping the Pass attachment link will present the Pass. If you choose to add the Pass to your Passbook app, the displayed Pass will disappear, having been filed away within your Passbook. Now click on the home button to return to the home screen and launch the Passbook app. In the app you will now see the Pass that was just added. It contains information specified by the app creator and can be presented when interacting with the company providing the service. Additional information can be placed on the back of the Pass. Tap the i button in the top-right hand corner of the Pass, to reveal the this information. How it works… The following diagram describes how Passes are delivered to a Passbook, and how these can be updated: The process of creating a Pass involves cryptographically signing the Pass using a certificate and key generated from your iOS developer account. For this reason, the generation of the Pass needs to take place on a server, and then be delivered to Passbook either via your own app, as an e-mail attachment, or by embedding it in a website. It's important to note that Apple does not provide any system for the Pass providers to authenticate, validate, or invalidate Passes. The Pass can contain barcode information, but it is up to the Pass provider to provide the infrastructure for reading and processing these barcodes. Instead of just sitting in the Passbook app, waiting to be used, a Pass can contain location and time triggers, that proactively present the Pass to the user, serving as both a reminder and providing convenient access. For example, an event Pass could be set to appear 15 mins before the start time, at the time when a user likely wants to present their event Pass to an attendant. Alternatively, a coupon Pass could be presented as a user approaches their local store where the coupon can be redeemed. Passes that have been added to Passbook can also be updated dynamically. For example, if the Pass is for a store card, a change to the card balance may require an update to the Pass. In the case of, for an airline ticket Pass, a departure gate change should trigger a Pass update. When a Pass needs to be updated, your server sends a push notification to the Passbook app on the user's device. This push notification is not displayed to the user. Upon receiving this Push Notification, the Passbook app then makes a request to your server for the updated Pass information. Your server would then respond to the relevant request, and provide the updated information in the expected format. When the Passbook App on the user's device receives the updated information, it silently updates the Pass. The next time the user looks at the Pass contained in the Passbook app, the updated information is displayed. There's more… Support for Passbook is also built into OSX Mountain Lion (10.8.2). Pass files with the -pkpass file extension will open in a preview window: Clicking on the Add to Passbook button will place the Pass in the Passbook associated with the iCloud account set up in OSX system preferences. The OSX Mail app and Safari also support embedded Passes. When building a Pass, you can specify a relevant time and up to 10 relevant locations that will trigger a message to be displayed on the lock screen. The message looks similar to a push notification, however a Pass notification is less intrusive. When it is relevant to display, it doesn't vibrate the iPhone and it doesn't wake up the screen. The notification only becomes visible when the phone wakes up from sleep. The option to specify relevant time and locations, and how far from the location the notification is triggered, is determined by the Pass type, as we will see later. Apps using Passbook Some of the apps in the App Store using Passbook are as follows : Hotels.com: This uses Passbook for room reservation details. It can be downloaded from http://appstore.com/hotelscom/hotelscom. Starbucks: This uses Passbook for a store card. It can be downloaded from http://appstore.com/starbuckscoffeecompany. Ticketmaster: This uses Passbook for event tickets. It can be downloaded from http://appstore.com/ticketmaster/ticketmaster. United Airlines: This uses Passbook for boarding passes. It can be downloaded from http://appstore.com/unitedairlines. Summary This article introduced you to Passbook. Apple's Passbook feature is a collection of technologies that come together to provide digital wallet functionality to the user. We understood what Passbook consists of, from both the user and Pass creator perspective. Resources for Article : Further resources on this subject: Development of iPhone Applications [Article] iPhone Applications Tune-Up: Design for Performance [Article] New iPad Features in iOS 6 [Article]
Read more
  • 0
  • 0
  • 11159

article-image-rules-and-events
Packt
12 Jul 2013
10 min read
Save for later

Rules and Events

Packt
12 Jul 2013
10 min read
(For more resources related to this topic, see here.) Handling specifc events is something everybody expects from an application. While JavaScript has its own event handling model, working with Dynamics CRM offers a different set of events that we can take advantage of. The JavaScript event model, while it might work, is not supported, and defnitely not the approach you want to take when working within the context of Dynamics CRM. Some of the most notable events and their counterparts in JavaScript are described in the following table: Dynamics CRM 2011 JavaScript Description OnLoad onload This is a form event. Executes when a form is loaded. Most common use is to filter and hide elements on the form. OnSave onsubmit This is a form event. It executes when a form is saved. Most common use is to stop an operation from executing, as a result of a failed validation procedure. TabStateChange n/a This is a form event. It executes when the DisplayState of the tab changes. OnChange onchange This is a field specific event. It executes when tabbing out of a field where you've changed the value. Please note that there is no equivalent for onfocus and onblur. OnReadyStateComplete n/a This event indicates that the content of an IFrame has completed loading. Additional details on Dynamics CRM 2011 specifc events can be found on MSDN at http://msdn.microsoft.com/en-us/library/gg334481.aspx. Form load event usage In this recipe, we will focus on executing a few operations triggered by the form load event. We can check the value of a specifc field on the form, and based on that we can decide to hide a tab, hide a field, and prepopulate a text field with a predefned value. Getting ready... Just as with any of the previous recipes, you will need access to an environment, and permissions to make customizations. You should be a system administrator, a system customizer, or a custom role configured to allow you to perform the following operations. How to do it... For the purpose of this exercise, we will add to the Contact entity a new tab called "Special Customer", with some additional custom fields. We will also add an option set that we will check to determine if we hide or not the fields, as well as two new fields: one text field and one lookup field. So let's get started! Open the contact's main form for editing. Add a new tab by going to Insert | Tab | One Column. Double-click on the newly added tab to open the Tab Properties window. Change the Label field of the tab to Special Customer. Make sure the show label is expanded by default and visible checkboxes are checked. Click on OK. Add a few additional text fields on this tab. We will be hiding the tab along with the content within the tab. Add a new field, named Is Special Customer (new_IsSpecialCustomer). Leave the default yes/no values. Add the newly created field to the general form for the contact. Add another new text field, named Customer Classifcation (new_ CustomerClassifcation). Leave the Format as Text, and the default Maximum Length to 100, as shown in the following screenshot: Add the newly created text field to the general form, under the previously added field. Add a new lookup field, called Partner (new_Partner). Make it a lookup for a contact, as shown in the following screenshot: Add this new field to the general form, under the other two fields. Save and Publish the Contact form. Your form should look similar to the following screenshot: Observe the fact that I have ordered the three fields one on top of the other. The reason for this is because the default tab order in CRM is vertical and across. This way, when all the fields are visible, I can tab right from one to another. In your solution where you made the previous changes, add a new web resource named FormLoader (new_FormLoader). Set the Type to JScript. Click on the Text Editor button and insert the following function: function IsSpecialCustomer(){var _isSpecialSelection = null;var _isSpecial = Xrm.Page.getAttribute("new_isspecialcustomer");if(_isSpecial != null){_isSpecialSelection = _isSpecial.getValue();}if(_isSpecialSelection == false){// hide the Special Customer tabXrm.Page.ui.tabs.get("tab_5").setVisible(false);// hide the Customer Classification fieldXrm.Page.ui.controls.get("new_customerclassification").setVisible(false);// hide the Partner fieldXrm.Page.ui.controls.get("new_partner").setVisible(false);}} Save and Publish the web resource. Go back to the Contact form, and on the ribbon select Form Properties. On the Events tab, add the library created as web resource in the Forms Libraries section, and in the Event Handlers area, on the Form OnLoad add the function we created: Click on the Text Editor button and insert the following function: Click on OK, then click on Save andPublish the form Test your configuration by opening a new contact, setting the Is Special Customer field to No. Save and close the contact. Open it again, and the tab and fields should be hidden. How it works... The whole idea of this script is not much different from what we have demonstrated in some of the previous recipes. Based on a set form value, we hide a tab and some fields. Where we capture the difference is where we set the script to execute. Working with scripts executing when the form loads gives us a whole new way of handling various scenarios. There's more... In many scenarios, working with the form load events in conjunction with the other field events can potentially result in a very complex solution. When debugging, always pay close attention to the type of event you associate your script function with. See also See the Combining events recipe towards the end of this article for a more complex recipe detailing how to work with multiple events to achieve the expected result. Form save event usage While working with the Form OnLoad event can help us format and arrange the user interface, working with the Form OnSave opens up a new door towards validation of user input and execution of business process amongst others. Getting ready Using the same solution we have worked on in the previous recipe, we will continue to demonstrate a few other aspects of working with the forms in Dynamics CRM 2011. In this recipe the focus is on the handling the Form OnSave event. How to do it... First off, in order to kick off this, we might want to verify a set of fields for a condition, or perform a calculation based on a formula. In order to simplify this process, we can just check a simple yes/no condition on a form. How it works... Using the previously customized solution, we will be taking advantage of the Contact entity and the fields that we have already customized on that form. If you are starting with this recipe fresh, take the following step before delving into this recipe: Add a new two-options field, named Is Special Customer (new_IsSpecialCustomer). Leave the default yes/no values. Using this field, if the answer is No, we will stop the save process. In your solution add a new web resource. I have named it new_ch4rcp2. Set its type to JScript. Enter the following function in your resource: function StopSave(context){var _isSpecialSelection = null;var _isSpecial = Xrm.Page.getAttribute("new_isspecialcustomer");if(_isSpecial != null){_isSpecialSelection = _isSpecial.getValue();}if(_isSpecialSelection == false){alert("You cannot save your record while the Customer is not afriend!");context.getEventArgs().preventDefault();}} The function basically checks for the value in our Is Special Customer. If a value is retrieved, and that value is No, we can bring up an alert and stop the Save and Close event. Now, back on to the contact's main form, we attach this new function to the form's OnSave event. Save and Publish your solution. In order to test this functionality, we will create a new contact, populate all the required fields, and set the Is Special Customer field to No. Now try to click on Save and Close. You will get an alert as seen in the following screenshot, and the form will not close nor be saved. Changing the Is Special Customer selection to Yes and saving the form will now save and close the form. There's more... While this recipe only describes in a very simplistic manner the way to stop a form from saving and closing, the possibilities here are immense. Think about what you can do on form save, and what you can achieve if a condition should be met in order to allow the form to be saved. Starting a process instead of saving the form Another good use for blocking the save and close form is to take a different path. Let's say we want to kick off a workfow when we block the save form. We can call from the previous function a new function as follows: function launchWorkflow(dialogID, typeName, recordId){var serverUri = Mscrm.CrmUri.create('/cs/dialog/rundialog.aspx');window.showModalDialog(serverUri + '?DialogId=' + dialogID +'&EntityName=' + typeName +'&ObjectId=' + recordId, null, 'width=615,height=480,resizable=1,status=1,scrollbars=1');// Reload formwindow.location.reload(true);} We pass to this function the following three parameters: GUID of the Workfow or Dialog The type name of the entity The ID of the record See also For more details on parameters see the following article on MSDN: http://msdn.microsoft.com/en-us/library/gg309332.aspx Field change event usage In this recipe we will drill down to a lower level. We have handled form events, and now it is time to handle field events. The following recipe will show you how to bring all these together and achieve exactly the result you need. Getting ready For the purpose of this recipe, let's focus on reusing the previous solution. We will check the value of a field, and act upon it. How to do it... In order to walkthrough this recipe, follow these steps:> Create a new form field called new_changeevent, with a label of Change Event, and a Type of Two Options. Leave the default values of No and Yes. Leave the Default Value as No. Add this field to your main Contact form. Add the following script to a new JScript web resource: function ChangeEvent(){var _changeEventSelection = null;var _isChanged = Xrm.Page.getAttribute("new_changeevent");if(_isChanged != null){_changeEventSelection = _isChanged.getValue();}if(_changeEventSelection == true){alert("Change event is set to True");// perform other actions here}else{alert("Change event is set to False");}} This function, as seen in the previous recipes, checks the value of the Two Options field, and performs and action based on the user selection. The action in this example is simply bringing an alert message up. Add the new web resource to the form libraries. Associate this new function to the OnChange event of the field we have just created. Save and Publish your solution. Create a new contact, and try changing the Change Event value from No to Yes and back. Every time the selection is changed, a different message comes up in the alert. How it works... Handling events at the field level, specifcally the OnSave event, allows us to dynamically execute various other functions. We can easily take advantage of this functionality to modify the form displayed to a user dynamically, based on a selection. Based on a field value, we can defne areas or field on the form to be hidden and shown.
Read more
  • 0
  • 0
  • 1629

Packt
12 Jul 2013
7 min read
Save for later

Kinect in Motion – An Overview

Packt
12 Jul 2013
7 min read
Motion control computing has been establishing itself as one of the most relevant techniques for designing and implementing a Natural User Interface (NUI). NUIs are human-machine interfaces that enable the user to interact in a natural way with software systems. The goals of NUIs are to be natural and intuitive. NUIs are built on the following two main principles: The NUI has to be imperceptible, thanks to its intuitive characteristics: (a sensor able to capture our gestures, a microphone able to capture our voice, and a touch screen able to capture our hands' movements). All these interfaces are imperceptible to us because their use is intuitive. The interface is not distracting us from the core functionalities of our software system. The NUI is based on nature or natural elements. (the slide gesture, the touch, the body movements, the voice commands—all these actions are natural and not diverting from our normal behavior). NUIs are becoming crucial for increasing and enhancing the user accessibility for software solution. Programming a NUI is very important nowadays and it will continue to evolve in the future. Kinect embraces the NUIs principle and provides a powerful multimodal interface to the user. We can interact with complex software applications and/or video games simply by using our voice and our natural gestures. Kinect can detect our body position, velocity of our movements, and our voice commands. It can detect objects' position too. Microsoft started to develop Kinect as a secret project in 2006 within the Xbox division as a competitive Wii killer. In 2008, Microsoft started Project Natal, named after the Microsoft General Manager of Incubation Alex Kipman's hometown in Brazil. The project's goal was to develop a device including depth recognition, motion tracking, facial recognition, and speech recognition based on the video recognition technology developed by PrimeSense. Kinect for Xbox was launched in November 2010 and its launch was indeed a success: it was and it is still a break-through in the gaming world and it holds the Guinness World Record for being the "fastest selling consumer electronics device" ahead of the iPhone and the iPad. In December 2010, PrimeSense (primesense.com) released a set of open source drivers and APIs for Kinect that enabled software developers to develop Windows applications using the Kinect sensor. Finally, on June 17 2011 Microsoft launched the Kinect SDK beta, which is a set of libraries and APIs that enable us to design and develop software applications on Microsoft platforms using the Kinect sensor as a multimodal interface. With the launch of the Kinect for Windows device and the Kinect SDK, motion control computing is now a discipline that we can shape in our garages, writing simple and powerful software applications ourselves. This article is written for all of us who want to develop market-ready software applications using Kinect for Windows that can track audio and video and control motion based on NUI. In an area where Kinect established itself in such a short span of time, there is the need to consolidate all the technical resources and develop them in an appropriate way: this is our zero-to-hero Kinect in motion journey. This is what this book is about. The aim of this article is to understand the steps for capturing data from the color stream, depth stream, and IR stream data. The key learning tools and steps for mastering all these streams are: color camera: data stream, event driven and polling techniques to manage color frames, image editing, color image tuning, and color image formats depth image: data stream, depth image ranges, and mapping between color image and depth image All the examples we will develop in this book are built on Visual Studio 2010 or 2012. In this introduction, we want to include the key steps for getting started. From Visual Studio, select File | New | Project. In the New Project window, do the following: Select the WPF Application Visual C# template. Select the .Net Framework 4.0 as the framework for the project (it works in .Net Framework 4.5 too). Assign a name to the project Choose a location for the project. Leave all the other settings with the default value. Click on the OK button. In the Solution Explorer window, please locate the references of the project. Right-click on References and select Add Reference to invoke the Reference Manager window. Select the Microsoft.Kinect Version 1.6.0.0 assembly and click on the OK button. Skeletal tracking allows applications to recognize people and follow their actions. Skeletal tracking combined with gesture-based programming enables applications to provide a natural interface and increase the usability and ease of the application itself. In this article we will learn how to enable and handle the skeleton data stream. For instance, we will address the following: Tracking users by analyzing the skeleton data streamed by Kinect and mapping them to the color stream Understanding what joints are and which joints are tracked in the near and seated mode Observing the movements of the tracked users to detect simple actions Mastering the skeleton data stream enables us to implement an application by tracking the user's actions and to recognize the user's gestures. The Kinect sensor, thanks to the IR camera, can recognize up to six users in its field of view. Of these, only up to two users can be fully tracked, while the others are tracked from one single point only, as demonstrated in the following image: In this article we will explicate how to use the Kinect sensor's speech recognition capability as an additional natural interface modality in our applications. Speech recognition is a powerful interface that increases the adoption of software solutions by users with disabilities. Speech recognition can be used in working environments where the user can perform his/her job or task away from a traditional workstation. The Microsoft Kinect SDK setup process includes the installation of the speech recognition components. The Kinect sensor is equipped with one array of four microphone devices. The array of microphones can be handled using the code libraries released by Microsoft since Windows Vista. These libraries include Voice Capture DirectX Media Object (DMO) and the Speech Recognition API (SAPI). In managed code, Kinect SDK v1.6 provides a wrapper extending the Voice Capture DMO. Thanks to the Voice Capture DMO, Kinect provides capabilities such as: Acoustic echo cancellation (AEC) Automatic gain control (AGC) Noise suppression The Speech Recognition API is the development library that allows us to use the built-in speech recognition capabilities of the operating system while developing our custom application. These APIs can be used with or without the Kinect sensor and its SDK. We walked through the journey on how to manage and master all the data streamed out from the Kinect sensor, starting from managing the depth and color stream to implementing natural user interface enabled applications based on gestures and speech recognition. While implementing the proposed examples, we have been standing up, walking in our room or office, and letting our colleagues or friends wonder what we were doing! Of course, we would never like to discourage doing physical exercises and talking to our Kinect sensor, but having said so, there are in fact scenarios where we need to be close to the keyboard. For instance, when things go wrong and we cry for a passionate look through the source code flow (does that sound like a romantic way to explain debugging?). Moving back and forward from our keyboard limits our ability to spot issues. What about when we have to process the same stream of data over and over again, or, in a development team type of scenario, when we have to unit test the application in a repetitive manner? In this article we will learn how we can save time coding and testing on Kinect enabled applications by: Recording all the video data coming into an application from a Kinect sensor with Kinect Studio Injecting the recorded video in an application allowing us to test our code without getting out of our chair over and over again Saving and playing back voice commands with a simple custom tool for enforcing quality on our application’s speech recognition capabilities Summary In this Article, we touched upon the essential concepts of Kinect and its varied operations. Resources for Article : Further resources on this subject: Getting started with Kinect for Windows SDK Programming [Article] Active Directory migration [Article] Atmosfall – Managing Game Progress with Coroutines [Article]
Read more
  • 0
  • 0
  • 1842
article-image-getting-started-adobe-premiere-pro-cs6-hotshot
Packt
11 Jul 2013
14 min read
Save for later

Getting Started with Adobe Premiere Pro CS6 Hotshot

Packt
11 Jul 2013
14 min read
(For more resources related to this topic, see here.) Getting the story right! This is basic housekeeping and ignoring it is like making your own editing life much more frustrating. So take a deep breath, think of calm blue oceans, and begin by getting this project organized. First you need to set the Timeline correctly and then you will create a short storyboard of the interview; again you will do this by focusing on the beginning, middle, and end of the story. Always start this way as a good story needs these elements to make sense. For frame-accurate editing it's advisable to use the keyboard as much as possible, although some actions will need to be performed with the mouse. Towards the end of this task you will cover some new ground as you add and expand Timeline tracks in preparation for the tasks ahead. Prepare for Lift Off Once you have completed all the preparations detailed in the Mission Checklist section, you are ready to go. Launch Premiere Pro CS6 in the usual way and then proceed to the first task. Engage Thrusters First you will open the project template, save it as a new file, and then create a three-clip sequence; the rough assembly of your story. Once done, perform the following steps: When the Recent Projects splash screen appears, select Hotshots Template – Montage. Wait for the project to finish loading and save this as Hotshots – Interview Project. Close any sequences open on the Timeline. Select Editing Optimized Workspace. Select the Project panel and open the Video bin without creating a separate window. If you would like Premiere Pro to always open a bin without creating a separate window, select Edit | Preferences | General from the menu. When the General Options window displays, locate the Bins option area and change the Double-Click option to Open in Place. Import all eight video files into the Video folder inside the Project 3 folder. Create a new sequence. Pick any settings at random, you will correct this in the next step. Rename the sequence as Project 3. Match the Timeline settings with any clip from the Video bin, and then delete the clip from the Timeline. Set the Project panel as the active panel and switch to List View if it is not already displayed. Create the basic elements of a short story for this scene using only three of the available clips in the Video bin. To do this, hold down the Ctrl or command key and click on the clips named ahead. Make sure you click on them in the same order as they are presented here: Intro_Shot.avi Two_Shot.avi Exit_Shot.avi Ensure the Timeline indicator is at the start of the Timeline and then click on the Automate to Sequence icon. When the Automate To Sequence window appears, change Ordering to Selection Order and leave Placement as the default (Sequentially). Uncheck the Apply Default Audio Transition , Apply Default Video Transition, and Ignore Audio checkboxes. Click on OK or press Enter on the keyboard to complete this action. Right-click on the Video 1 track and select Add Tracks from the context menu. When the Add Tracks window appears, set the number of video tracks to be added as 2 and the number of audio tracks to be added as 0. Click on OK or press Enter to confirm these changes. Dial open the Audio 1 track (hint – small triangle next to Audio 1), then expand the Audio 1 track by placing the cursor at the bottom of the Audio 1 area, and clicking on it, and dragging it downwards. Stop before the Master audio track disappears below the bottom of the Timeline panel. The Master audio track is used to control the output of all the audio tracks present on the Timeline; this is especially useful when you come to prepare your timeline for exporting to DVD. The Master audio track also allows you to view the left and right audio channels of your project. More details on the use of the Master audio track can be found in the Premiere Pro reference guide, which can be downloaded from http://helpx.adobe.com/pdf/premiere_pro_reference.pdf. Make sure the Timeline panel is active and zoom in to show all the clips present (hint – press backslash). You should end this section with a Timeline that looks something like the following screenshot. Save your project (Press Ctrl + S or command + S) before moving on to the next task. Objective Complete - Mini Debriefing How did you do? Review the shortcuts listed next. Did you remember them all? In this task you should have automatically matched up the Timeline to the clips with one drag-and-drop, plus a delete. You should have then sent three clips from the Project panel to the Timeline using the Automate to Sequence function. Finally you should have added two new video tracks and expanded the Audio 1 track. Keyboard shortcuts covered in this task are as follows: (backslash): Zoom the Timeline to show all populated clips Ctrl or command + double-click: Open bin without creating a separate Project panel (also see the tip after step 3 in the Engage Thrusters section) Ctrl or command + N: Create a new sequence Ctrl or command + (backslash): Create new bin in the Project panel Ctrl or command + I: Open the Import window Shift + 1: Set the Project panel as active Shift + 3: Set Timeline as active Classified Intel In this project, the Automate to Timeline function is being used to create a rough assembly of three clips. These are placed on the Timeline in the order that you clicked on them in the project bin. This is known as the selection order and allows the Automate to Timeline function to ignore the clips-relative location in the project bin. This is not a practical work flow if you have too many clips in your Project panel (how would you remember the selection order of twenty clips?). However, for a small number of clips, this is a practical workflow to quickly and easily send a rough draft of your story to the Timeline in just a few clicks. If you remember nothing else from this book, always remember how to correctly use Automate To Timeline! Extracting audio fat Raw material from every interview ever filmed will have lulls and pauses, and some stuttering. People aren't perfect and time spent trying to get lines and timing just right can lead to an unfortunate waste of filming time. As this performance is not live, you, the all-seeing editor, have the power to cut those distracting lulls and pauses, keeping the pace on beat and audience's attention on track. In this task you will move through the Timeline, cutting out some of the audio fat using Premiere Pro's Extract function, and to get this frame accurate, you will use as many keyboard shortcuts as possible. Engage Thrusters You will now use the Extract function to remove "dead" audio areas from the Timeline. Perform the following steps: Set the Timeline panel as active then play the timeline back by pressing the L key once. Make a mental note of the silences that occur in the first clip (Intro_Shot.avi). Return the Timeline indicator to the start of the Timeline using the Home key. Zoom in on the Timeline by pressing the + (plus) key on the main keyboard area. Do this until your Timeline looks something like the screenshot just after the following tip: To zoom in and out of the Timeline use the + (plus) and - (minus) keys in the main keyboard area, not the ones in the number pad area. Pressing the plus or minus key in the number area allows you to enter an exact number of frames into whichever tool is currently active. You should be able to clearly see the first area of silence starting at around 06;09 on the Timeline. Use the J, K, and L keyboard shortcuts to place the Timeline indicator at this point. Press the I key to set an In point here, then move the Timeline indicator to the end of the silence (around 08;17), and press the O key to set an Out point. Press the # (hash) key on your keyboard to remove the marked section of silence using Premiere Pro's Extract function. Important information on Sync Locking tracks The above step will only work if you have the Sync Lock icons toggled on for both the Video 1 and Audio 1 tracks. The Sync Lock icon controls which Timeline tracks will be altered when using a function such as Extract. For example; if the Sync Lock icon was toggled off for the Audio 1 track, then only the video would be extracted, which is counterproductive to what you are trying to achieve in this task! By default each new project should open with the Sync Lock icon toggled on for all video and audio tracks that already exist on the Timeline, and those added at a later point in the project. More information on Sync Lock can be found in the Premiere Pro reference guide (tinyurl.com/cz5fvh9). Repeat steps 5 and 6 to remove silences from the following Timeline areas (you should judge these points for yourself rather than slavishly following the suggestions given next): i. Set In point at 07;11 and Out point at 08;10. ii.Press # (hash). iii.Set In point at 11;05 and Out point at 12;13. iv.Press # (hash). Play back the Timeline to make sure you haven't extracted away too much audio and clipped the end of a sentence. Use the Trim tool to restore the full sentence. You may have spotted other silences on the Timeline; for the moment leave them alone. You will deal with these using other methods later in this project. Save the project before moving on to the next section. Objective Complete - Mini Debriefing At the end of this section you should have successfully removed three areas of silence from the Intro_Shot.avi clip. You did this using the Extract function, an elegant way of removing unwanted areas from your clips. You may also have refreshed your working knowledge of the Trim tool. If this still feels a lit le alien to you, don't worry, you will have a chance to practice trimming skills later in this project. Classified Intel Extract is another cunningly simple function that does exactly what it says; it extracts a section of footage from the Timeline, and then closes the gap created by this ac i on. In one step it replicates the razor cut and ripple delete. Creating a J-cut (away) One of the most common video techniques used in interviews and documentaries (not to mention a number of films) is called a J-cut. This describes cutting away some of the video, while leaving the audio beneath intact. The deleted video area is then replaced with alternative footage. This creates a voice-over effect that allows for a seamless transfer between the alternative viewpoints and the original speaker. In this task you will create a J-cut by replacing the video at the start of Intro_Shot.avi, leaving the voice of the newsperson and replacing his image with cutaway shots of what he is describing. You will make full use of four-point edits. Engage Thrusters Create J-cuts and cutaway shots using work flows you should now be familiar with. Perform the following steps to do so: Send the Cutaways_1.avi clip from the Project panel to the Source Monitor. In the Source Monitor, create an In point at 00;00 and an Out point just before the shot changes (around 04;24). Switch to the Timeline and send the Timeline indicator to the start of the Timeline (00;00). Create an In point here. Use a keyboard shortcut of your choice to identify the point just before the newsperson mentions the "Local village shop". (hint – roughly at 06;09). Create an Out point here. You want to create a J-cut, which means protecting the audio track that is already on the Timeline. To do this click once on the Audio 1 track header so it turns dark gray. Switch back to the Source Monitor and send the marked Cutaways_1.avi clip to the Timeline using the Overwrite function (hint – press the '.' (period) key). When the Fit Clip window appears, select Change Clip Speed (Fit to Fill), and click on OK or press Enter on the keyboard. The village scene cutaway shot should now appear on Video 1, but Audio 1 should retain the newsperson's dialog. His inserted village scene clip will have also slowed slightly to match what's being said by the newsperson. Repeat steps 2 to 7 to place the Cutaways_1.avi clip that shows the shot of the village shop, to match the village church and the village pub on the Timeline with the newsperson's dialog. The following are some suggestions on times, but try to do this step first of all without looking too closely at them: For the village shop cutaway, set the Source Monitor In point at 05;00 and Out point at 09;24. Set the Timeline In point at 06;10 and Out point at 07;13. Switch back to Source Monitor. Send the clip in the Overwrite mode and set Change Clip Speed to Fit to Fill. For the village church cutaway, set the Source Monitor In point at 10;00 and Out point at 14;24. Set the Timeline In point at 07;14 and Out point at 09;03. Switch back to Source Monitor. Send the clip in the Overwrite mode and set Change Clip Speed to Fit to Fill. For the pub cutaway, send Reconstruction_1.avi to the Source Monitor. Set the Source Monitor In point at 04;11 and Out point at 04;17. Set the Timeline In point at 09;04 and Out point at 12;00. Switch back to Source Monitor. Send the clip in the Overwrite mode and set Change Clip Speed to Fit to Fill. The last cutaway shot here is part of the reconstruction reel and has been used because your camera person was unable (or forgot) to film a cutaway shot of the pub. This does sometimes happen and then it's down to you, the editor in charge, to get the piece on air with as few errors as possible. To do this you may find yourself scavenging footage from any of the other clips. In this case you have used just seven frames of Reconstruction_1.avi, but using the Premiere Pro feature, Fit to Fill , you are able to match the clip to the duration of the dialogue, saving your camera person from a production meeting dressing down! Review your edit decisions and use the Trim tool or the Undo command to alter edit points that you feel need adjustments. As always, being an editor is about experimentation, so don't be afraid to try something out of the box, you never know where it might lead. Once you are happy with your edit decisions, render any clips on the Timeline that display a red line above them. You should end up with a Timeline that looks something like the following screenshot; save your project before moving on to the next section. Objective Complete - Mini Debriefing In this task you have learned how to piece together cutaway shots to match the voice-over, creating an effective J-cut, as seen in the way the dialog seamlessly blends between the pub cutaway shot and the news reporter finishing his last sentence. You also learned how to scavenge source material from other reels in order to find the necessary shot to match the dialog. Classified Intel The last set of time suggestions given in this task allow the pub cutaway shot to run over the top of the newsperson saying "And now, much to the surprise…". This is an editorial decision that you can make on whether or not this cutaway should run over the dialog. It is simply a matter of taste, but you are the editor and the final decision is yours! In this article, we learned how to extract audio fat and create a J-cut. Resources for Article : Further resources on this subject: Responsive Design with Media Queries [Article] Creating a Custom HUD [Article] Top features you'll want to know about [Article]
Read more
  • 0
  • 0
  • 2684

article-image-transition-readshift
Packt
11 Jul 2013
25 min read
Save for later

Transition to Readshift

Packt
11 Jul 2013
25 min read
(For more resources related to this topic, see here.) Cluster configurations You will find that for most things that you deal with on Redshift, the default mode is one of no access(default security group, VPC access, database access, objects in the database, and so on). Due to the fact that you need to deal with that on a consistent basis, you will find that it will not be an issue for you; it will simply be part of the process. Creating objects will require granting permissions as well as granting permissions to access cluster management. Depending on the environment that you are coming from, this may be frustrating sometimes; however, considering the fact that you are remotely hosting your data, I for one am happy with the extra steps necessary to access things. The importance of data security, as a general statement, cannot be overstated. You are responsible for your company's data as well as its image and reputation. Hardly a week goes by without news of companies that have had to make public announcements of data being improperly accessed. The fact that data has been improperly accessed has little to do with the location of the data (remote or local) if you use Amazon or some other provider, but rather it depends on the rules that have been set up to allow access to the data. Do not take your security group's configuration lightly. Only open access to the things you really need and continue to maintain strict database rules on access. Honestly, this should be something you are already doing (regardless of where your data is physically located); however, if you are not, take this as the opportunity to enforce the necessary security to safeguard your data. You will need to add your IP ranges to allow access from the machine(s) that you will be using to access your cluster. In addition, you should add your EC2 security group that contains the EC2 instances (if there are any) that you will be connecting from, as shown in the next screenshot. Later in this article, we will cover installation and configuration of the command-line interface using a connection from an EC2 instance. If you don't have an EC2 instance, don't worry, you can still add it later if you find it necessary. Don't get hung up on that, but if you already have the security group, add it now. You will also need to have a parameter group. A parameter group applies to every database within the cluster, so whatever options you choose, think of them as global settings. If there are things that you would like to adjust in these settings, you need to create your own parameter group (you may not edit the default). The creation of the new group may be done before you create your cluster. You will see where you associate the parameter group to the cluster in the next section. If you don't need to change anything about the default values, feel free to simply use the parameter group that is already created, as shown in the following screenshot: Cluster creation In this section, we will go through the steps necessary to actually create your cluster. You have already made the "hard" decisions about the kinds of nodes, your initial number of nodes, and whether you are going to use encryption or not. Really, you only have a couple of other things to decide, such as what you want to name your cluster. In addition to the cluster name, you will need to pick your master username and password. Once you have those things decided, you are (quite literally) four simple pages away from having provisioned your first cluster. Don't forget, you can resize to a different number of nodes and even a different cluster type later. Launch the cluster creation wizard by selecting the Launch Cluster option from the Amazon Redshift Management console: This will bring you to the first screen, CLUSTER DETAILS, as shown in the following screenshot. Here you will name your cluster, the primary database, your username, and password. As you can see, there are clear onscreen instructions for what is required in each field. The NODE CONFIGURATION screen, shown as follows, will allow you to pick the size of the nodes. You can also select the type of cluster (Single Node or Multi Node). For this example, I chose Single Node. The additional configuration screen, as shown in the next screenshot, is where you will select your parameter group, encryption option, VPC if you choose, as well as the availability zone. A Virtual Private Cloud (VPC) is a networking configuration that will enable isolation of your network within the public portion of the cloud. Amazon allows you to manage your own IP ranges. A Virtual Private Network (VPN) connection to your VPC is used to essentially extend your own internal network to the resources you have allocated in the cloud. How to set up your VPC goes beyond Redshift as a topic; however, do understand that Redshift will run inside your VPC if you so choose. Believe it or not, that really is everything. On the REVIEW screen, as shown in the next screenshot, you can now confirm your selections and actually start the cluster. Once you select the Launch Cluster button here, it will take a few minutes for your cluster to initialize. Once initialization is complete, your cluster is ready for you to use. Cluster details We will take a look at some of the options you have to manage the cluster you have just created in ways other than using the Redshift Management console; however, since we just used the console to create the cluster, we will continue on with that tool for now. Before we go much further into the details, take a quick look around at the Redshift Management console. You will be quickly comfortable with the options you have available to manage and run your cluster. Once you have your cluster running, the following initial screen giving you the "at a glance" health status is displayed:   Along the left-hand side of the screen, as shown in the following screenshot, you can see some of the high-level management functions related to backups, security groups, and so on.   Once you have selected your cluster, there are some tabs across the top. For now, you can familiarize yourself with these, particularly the Configuration screen that you can access from the tab shown in the next screenshot. There is a wealth of information there. Most important (for now), because surely you want to get connected, is the endpoint information. From the main AWS console, you can drag any of the AWS services you wish up into your own menu bar (see the EC2 and Redshift icons in the preceding screenshot), making it easy to get to the different console views. Before we go too far and you jump the gun and start connecting tools and loading data, there are a few things to be aware of. I will go into greater detail on the configuration, layout, table creation, and so on as we go further along; so, let's just start with a few high-level things to keep in mind. Although you will be using PostgreSQL drivers, the core of the database is Postgres. There are certain things that have, for performance reasons, been removed. We will shortly take a closer look at the kinds of things that have not been implemented. So, as you mentally prepare the choices for the first tables you will be loading to test with, depending on what environment you are coming from, partitioning, subpartitioning, and range partitioning are the things you will leave on the table. I will explain the concept of distribution keys, which is similar to partitioning but not altogether the same. As a database professional, there are some other core features that you are used to maintaining, thinking about, and optimizing, such as indexing, clustering of data, primary keys, as well as unique constraints on columns. In the traditional sense, none of the clustering options are supported, nor are indexes. I will discuss sort keys and the considerations around what it means to select sort keys later. As far as primary key assignment is concerned, you can, and (depending on the table) maybe should, assign the primary key; however, it does nothing to enforce uniqueness on the table. It will simply be used by the optimizer to make informed decisions as to how to access the data. It tells the optimizer what you, as the user, expect to be unique. If you are not familiar with data warehouse design, you might be thinking "Oh my gosh, what were they thinking?". Those of you familiar with warehouse implementations of large tables are probably already running without primary keys on your largest tables. Load processes are designed to look up keys in dimensions, manage those keys based on the business values, and so on. I am not going to go too far off the topic on dimensional modeling here; that is not really what we are trying to learn. It should be suffcient to say that when you are loading the fact table, by the time you hit the insert statement into the fact table, you should have fully-populated dimension keys. Null values would be handled and all of the heavy lifting would be done by the load process. Logically, the overhead incurred by the database's revalidation of all of the things that you just assigned in the load is a very expensive operation when you are dealing with a 100-million row table (Redshift is about eliminating I/O). The same logic applies to the constraints at the column level. You can set the not null constraints but do nothing to actually ensure the data matches that expectation. There are a couple of maintenance commands (similar to a statistics update you are likely to be familiar with) after you manipulate large quantities of data that are more important to the optimization process than the application of constraints on the columns. I will get into the details about those commands after we get some data loaded SQL Workbench and other query tools Since you are able to connect to the database with native or ODBC PostgreSQL drivers,your choice of query tools is really and exactly that, your choice. It is recommended that you use the PostgreSQL 8.x JDBC and ODBC drivers. Amazon makes a recommendation for a SQL Workbench tool, which for the (free) price will certainly work, having come from environments that have more fully-featured query tools. I was a little frustrated by that product. It left me wanting for more functionalities than is provided in that product.  I know it sounds counterintuitive to the discussion we just had about all the features that are not needed or are not supported; so, there are clearly going to be some things in the query tool that you simply will never use. You are after all not managing a traditional PostgreSQL database. However, the ability to have multiple connections, script objects, doc windows, to run explain plans, and to manage the results with the "pivot" type functionality is a great beneft. So, now that I have talked you out of the SQL Workbench tool and into the EMS tool, go and download that. Just to limit the confusion and to translate between tools, the screenshots, descriptions, and query examples from this point forward in this book will be using the EMS tool. Once you have the SQL tool of your choice installed, you will need some connection information from your configuration screen, as shown in the next screenshot. There is a unique endpoint name and a port number. You will also need the master user ID and password. This is your sysadmin account that we will be using to create other users, schemas, and so on. Now that you have everything you need, select the option to create a database connection, plug in the info, and you are now connected to your cluster. If this is really your first remote database, it may be a bit early to declare total victory; however, you have now joined the ranks of the companies that talk about their cloud computing capabilities! I will go into greater detail about schemas and permissions when we discuss the management of your data. Before we get into loading your data, we will talk about accessing your data with third-party products. There are a variety of ETL tools, and depending on which product you are currently using, you may simply be able to continue with the product you are using. Additionally, there have been some recent partnership announcements from Informatica about providing an Amazon service-based option to use their PowerCenter product. If your native expertise is in SSIS, it is possible to connect using ODBC database connections; however, you will have performance issues with large quantities of data without using some data copy options from files. There are other options, such as Pentaho, that also have a tremendous amount of promise as well. As you start to think about your transition to this new environment, you will have a variety of decisions to make that will be unique to the current location of your source data and the in-house expertise that you have for ETL products. The good news is that most of the types of processes you currently support will translate well to Amazon Redshift. There are certain functions in SQL that are not supported; however, for the most part, the insert, update, and delete functionality, right down to creating temp tables in your queries are supported and will translate without major changes. Unsupported features There are a few things to keep in mind that you might be accustomed to using that are different than you might expect or that are simply not available to you. I am not really going back on the statement that there is little change to your SQL. You will find that the majority of your queries will work with little or no modifcation. This section will highlight what I think will be most important to you as you review the kinds of reporting and analytical processes you have running in your current environment. In addition to the changes in Postgres' functionality, there are a series of Redshift-specifc system tables that augment, and in some cases, replace the functionality found in the Postgres system tables. We will look at the system tables specifcally as we discuss management and querying later in the book. Just understand that if you are familiar with Postgres' system tables, there are going to be some things that you will need to be aware of. Create table:This is a standard SQL statement that will allow you to build objects in the database. Tablespaces: These are not supported. Table partitioning: This is not supported; however, there is the distribution key, which is different from traditional partitioning. Inheritance: This is not supported. Unique constraint: This constraint can be created; however, it is used only to inform the optimizer when creating the query plan to access the data. We will review these constraints later as we discuss loading data. Exclusion constraint: This is not supported. Foreign Key: This is informational to the optimizer only. Primary Key: This is informational to the optimizer only. Alter table: This is a standard SQL statement that will allow you to change the structure of tables. ALTER COLUMN: This is not supported. Copy: This feature is highly optimized for load purposes and will connect to an Amazon S3 bucket or an Amazon DynamoDB database. Order by: This standard SQL keyword will affect the order in which the data is output by a query. Nulls: (order by nulls) is not supported. First/Last: (order by first/last) is not supported. VACUUM: If you are familiar with the Postgres VACUUM function to reorganize tables, this is similar, but with new options to support the Redshift functionality. Insert/Update/Delete: No worries, these are supported! However, the WITH syntax is not supported. Indexes: You will find this as one of the management (particularly space management) items that you will not miss. My only concern early on, which as of yet has not been a problem, is the ability to affect a particularly poorly performing query with the help of a well-placed index. This will remain on my "watch list" of things to look out for, but as I said, so far it has not posed any problems. Collations: Locale-specifc or user-defned collation sequences are not supported. Array and Row constructor value expressions:These are not supported. User-defned functions: This seems to be a limitation that at some point will need to be addressed. I don't see a technical reason why this was eliminated (but then again, I am not one of the engineers that built Redshift). Stored procedures: Why this was eliminated is also not clear to me. Building stored procedures, thus incorporating complicated logic into a centralized stored process, seems like it would be something that an analytical database would be able to do Triggers: These are not supported Table functions: These are not supported and are one of those items based on the column store functionality that may not prove to be necessary. Keep this item in mind as you review your queries for what will be impacted as you move to Redshift. Sequences:These are not supported. Full text search: This is not supported. Without getting too bogged down with the specifcs, there are certain SQL related datatypes that are supported on the leader node, where that part of the query will not be passed to the data nodes. There are also datatypes that fall into the category of "unsupported features" that we are discussing here. Some of the unsupported datatypes are slightly more obscure datatypes, such as object identifer types and network address types. Those and a few others, for the sake of clarity, I am leaving off this list. For our purposes here, we will review those datatypes that are simply not available. No caveats here, so you do need to review your SQL. For these, the create table statements are not available to you in Redshift. Arrays Bit/Bit Varying(ok, so there is a caveat, Boolean works fne) Bytea(Postgres' binary datatype) Composite types Date/Time types: Interval Time Timestamp with timezone Timezone_hour Enumerated types Geometric types JSON XML Numeric types: Serial, Bigserial, Smallserial Money (careful here! You will likely have something somewhere in your database defined as money) Now that we have looked at some of the things that are different as well as the unsupported datatypes that you need to look at in your tables and SQL, there is just one remaining section of unsupported features and those are functions. Similar to the other parts of this section, this is not a complete listing. There are quite a number of these unsupported functions. Please don't be discouraged at this point. Most of these are not likely to impact much of your SQL, particularly standard end user queries. I am simply trying to paint an accurate picture of the things you need to consider. Access privilege inquiry functions Aggregate functions:Don't worry; since this could be a hot-button issue for some, I have listed all of them here. As you will see in the following list, I am sure you will find that most of the queries that you have already written do not use these functions: string_agg() array_agg() every() xml_agg() corr() covar_pop() covar_samp() regr_avgx() regr_avgy() regr_count() regr_intercept() regr_r2() regr_slope() regr_sxx() regr_sxy() regr_syy() variance() Database management functions: Backup/Restore (these are handled by Redshift snapshots) Database object location functions Database object size functions Date/Time functions:These are mostly related to the lack of timestamp support that we have already discussed: clock_timestamp() justify_days()/hours()/interval() transaction_timestamp() to_timestamp() Greatest() Least() JSON functions(as the JSON datatype is not supported) XML functions(as the XML datatype is not supported) Mathematical functions: div() setseed() Range functions and operators Sequence manipulation functions String functions: There are really only a couple of string functions that you will likely come across with any kind of regularity. Please note that convert() and substr() are on the list of unsupported functions: bit_length() overlay() convert() convert_from() convert_to() encode() format() quote_nullable() regexp_matches() regexp_replace() regexp_split_to_array() regexp_split_to_table() split_part() substr() translate() Trigger functions (as triggers themselves are not supported) Window functions(depending on the types of queries you currently have, the following may be found in your SQL): row_number() percent_rank() cume_dist() Text search functions(as text search is not supported) System Catalog Functions:As I have already mentioned, we will cover the use of system tables shortly. I have tried to give you a sense of the kinds of things that are different. Review the complete listings in the Amazon documentation before you formulate a migration plan for your environment. Command line There are a variety of cluster management options that are available to you in addition to the online Redshift Management console. Something many of you will be very quickly comfortable with is the command line. The command-line interface (CLI) is currently a developer preview product as a GitHub project. Just as with the other options, I am not going to try to replace the available Amazon documentation here. This is just to serve as a highlight of the steps needed to get you going and to show you some of the kinds of things you can do with the help of some basic examples. The Amazon command line utilizes Python (2.6 or greater) and will run on any operating system that supports Python. If you need assistance with Python, there are many great resources at www.python.org. To install the command-line interface, detailed instructions can be found at http://aws.amazon.com/cli. I will describe the basic steps if you are installing on an existing Amazon EC2 instance. First of all, if you are running on an Amazon EC2 instance, you already have Python installed. To get the command-line packages, run the installation with the following command from an account that has permissions to install software on the server: easy_install awscli1 Next, you will need to create a file with your Amazon credentials on the EC2 server. Make this file read-only to the user that is executing the commands, as it will contain your private and public Amazon keys. For this example, I called the file cliconfig. txt; however, you may call it anything you wish. [default] is for the profile. If you use [default], you do not need to specify the profile on the command line. This will allow you different configurations within the same file and you can then specify which profile you wish to use. Keep it simple for now and just use [default]. [default]aws_access_key_id = <Your Access Key>aws_secret_access_key = <Your Secret Key>region = us-east-12 As we noted earlier when we looked at security, you will need your own credentials to be able to fill in the necessary parts here, and you will also need to pick the region that you have your cluster running in. Once you have that file, export the environmental variable necessary for the command line to understand where the configuration file is (add this to your profile as well, so the next time you connect to the host, this will be set for you already). export AWS_CONFIG_FILE=/home/user/cliconfig.txt3 Once you have the command-line interface installed, your configuration file created,and the environmental variable set, the following command will confirm whether the command line has been properly installed: asw help To verify that you have everything working for your cluster's connectivity, run the following command: aws redshift help Now that you have the technical parts of the command-line interface working, the basic syntax of the command line for Redshift is as follows: aws redshift operation The following are optional arguments: --output output_format--region region_name--debug yes--profile profile_name--endpoint-url endpoint_url Note that the default output type is JSON. You can also specify text or CSV. aws redshift describe-cluster Again, this is not intended as a replacement for the available documentation. There are currently over 30 command line operations, each with extensive documentation. Clearly, each of these commands will have a unique set of options that are both required and optional. For our purposes here, I just want you to get a feel of the kinds of things that are possible. create-cluster delete-cluster modify-cluster describe-clusters reboot-cluster create-cluster-snapshot delete-cluster-snapshot describe-cluster-snapshot restore-from-cluster-snapshot describe-resize As you can see in the preceding list, the create-cluster option will allow you to execute the creation of the cluster from the command line. This would produce the exact same result as having gone through the Launch Cluster button from the Redshift Management console that we looked at in the beginning of this article. The output from describe-clusters of a single-node xlarge cluster from the previous command is shown in the following screenshot: The same output can be produced as text by adding the –output text to the command line. I am confident that if you have a Unix-scripting background,you will be up and running very quickly with the functionality you find in the command-line interface. The PSQL command line If you are interested in running commands other than those available in the CLI interface, you can install the standard Postgres PSQL command-line tools. The Amazon CLI tool is clearly focused on management functionality and not on the execution of queries. To connect using the psql command line, you need three values: -h (hostname), -p (port), and -U (user). You will then be prompted for the password as shown in the following command: # psql -h <Endpoint> -p 5439 -U <user> There are many other options to pass in files, how you wish to have the output formatted, or setting variables as described here. Connection options The following are the connection options: -h, --host=HOSTNAME: This is the database server host or socket directory (default is local socket) -p, --port=PORT: This is the database server port (default is 5432) -U, --username=USERNAME: This is the database username (default is root) -w, --no-password: This never prompts for a password -W, --password: This forces a password prompt (this should happen automatically) Output format options The following are the output format options: -A, --no-align: Denotes the unaligned table output mode -F, --field-separator=STRING: Sets the field separator (default is |) -H, --html: Denotes the HTML table output mode -P, --pset=VAR[=ARG]: Sets the printing option VAR to ARG (see the pset command) -R, --record-separator=STRING: Sets the record separator (default is newline) -t, --tuples-only: Prints only rows -T, --table-attr=TEXT: Sets the HTML table tag attributes (for example width and border) -x, --expanded: Turns on the expanded table's output General options The following are general options: -c, --command=COMMAND: Runs only a single command (SQL or internal) and exits -d, --dbname=DBNAME: Denotes the database name to connect to (default is root) -f, --file=FILENAME: Executes the commands from a file and then exits -l, --list: Lists the available databases and then exits -v, --set=, --variable=NAME=VALUE: Sets the psql variable NAME to VALUE -X, --no-psqlrc: Prevents the startup file from being read -1, --single-transaction: Executes the command file as a single transaction API Along the same lines as the command-line interface, there is a rich list of over 70 API calls. Just like the command line options, the API functions have a well-defined section in the Amazon documentation. As I noted with the command line, you can see that the same ability to create a cluster exists within the API functions as well as the other cluster management tools you would expect to find. CreateCluster ModifyCluster DescribeClusters DeleteCluster RebootCluster DescribeClusterParameters DescribeClusterSecurityGroups DescribeEvents DescribeResize Snapshot RestoreClusterFromSnapshot The thing to understand at this point is one of fexibility. You have choices on how to connect to the cluster as well as what kinds of tools you wish to use to manage that cluster. Summary This article has started to bring together the things you will need to consider as you bring your data and processes to the Redshift environment. We have looked at quite a few things to get the cluster running, getting your query tools installed and connected, and even started to understand some of the management functions that you will be using on a daily basis. By this point, you should feel comfortable navigating through the Redshift Management console, have your own cluster running, and have a general understanding of the overall Redshift capabilities. You should also understand some of the limitations that you will need to consider as you begin thinking more closely about your own environment. You are by no means ready to run your production reporting yet; however, you really are closer than you might think. Resources for Article: Further resources on this subject: Introduction to Cloud Computing with Microsoft Azure [Article] Configuring Clusters in GlassFish [Article] Microsoft SQL Server 2008 High Availability: Understanding Domains, Users, and Security [Article]
Read more
  • 0
  • 0
  • 1279

article-image-database-active-record-and-model-tricks
Packt
11 Jul 2013
14 min read
Save for later

Database, Active Record, and Model Tricks

Packt
11 Jul 2013
14 min read
(For more resources related to this topic, see here.) Getting data from a database Most applications today use databases. Be it a small website or a social network, at least some parts are powered by databases. Yii introduces three ways that allow you to work with databases: Active Record Query builder SQL via DAO We will use all these methods to get data from the film, film_actor, and actor tables and show it in a list. We will measure the execution time and memory usage to determine when to use these methods. Getting ready Create a new application by using yiic webapp as described in the official guide at the following URL:http://www.yiiframework.com/doc/guide/en/quickstart.first-app Download the Sakila database from the following URL:http://dev.mysql.com/doc/index-other.html Execute the downloaded SQLs; first schema then data. Configure the DB connection in protected/config/main.php to use the Sakila database. Use Gii to create models for the actor and film tables. How to do it... We will create protected/controllers/DbController.php as follows: <?php class DbController extends Controller { protected function afterAction($action) { $time = sprintf('%0.5f', Yii::getLogger() ->getExecutionTime()); $memory = round(memory_get_peak_usage()/(1024*1024),2)."MB"; echo "Time: $time, memory: $memory"; parent::afterAction($action); } public function actionAr() { $actors = Actor::model()->findAll(array('with' => 'films', 'order' => 't.first_name, t.last_name, films.title')); echo '<ol>'; foreach($actors as $actor) { echo '<li>'; echo $actor->first_name.' '.$actor->last_name; echo '<ol>'; foreach($actor->films as $film) { echo '<li>'; echo $film->title; echo '</li>'; } echo '</ol>'; echo '</li>'; } echo '</ol>'; } public function actionQueryBuilder() { $rows = Yii::app()->db->createCommand() ->from('actor') ->join('film_actor', 'actor.actor_id=film_actor.actor_id') ->leftJoin('film', 'film.film_id=film_actor.film_id') ->order('actor.first_name, actor.last_name, film.title') ->queryAll(); $this->renderRows($rows); } public function actionSql() { $sql = "SELECT * FROM actor a JOIN film_actor fa ON fa.actor_id = a.actor_id JOIN film f ON fa.film_id = f.film_id ORDER BY a.first_name, a.last_name, f.title"; $rows = Yii::app()->db->createCommand($sql)->queryAll(); $this->renderRows($rows); } public function renderRows($rows) { $lastActorName = null; echo '<ol>'; foreach($rows as $row) { $actorName = $row['first_name'].' '.$row['last_name']; if($actorName!=$lastActorName){ if($lastActorName!==null){ echo '</ol>'; echo '</li>'; } $lastActorName = $actorName; echo '<li>'; echo $actorName; echo '<ol>'; } echo '<li>'; echo $row['title']; echo '</li>'; } echo '</ol>'; } } Here, we have three actions corresponding to three different methods of getting data from a database. After running the preceding db/ar, db/queryBuilder and db/sql actions, you should get a tree showing 200 actors and 1,000 films they have acted in, as shown in the following screenshot: At the bottom there are statistics that give information about the memory usage and execution time. Absolute numbers can be different if you run this code, but the difference between the methods used should be about the same: Method Memory usage (megabytes) Execution time (seconds) Active Record 19.74 1.14109 Query builder 17.98 0.35732 SQL (DAO) 17.74 0.35038 How it works... Let's review the preceding code. The actionAr action method gets model instances by using the Active Record approach. We start with the Actor model generated with Gii to get all the actors and specify 'with' => 'films' to get the corresponding films using a single query or eager loading through relation, which Gii builds for us from InnoDB table foreign keys. We then simply iterate over all the actors and for each actor—over each film. Then for each item, we print its name. The actionQueryBuilder function uses query builder. First, we create a query command for the current DB connection with Yii::app()->db->createCommand(). We then add query parts one by one with from, join, and leftJoin. These methods escape values, tables, and field names automatically. The queryAll function returns an array of raw database rows. Each row is also an array indexed with result field names. We pass the result to renderRows, which renders it. With actionSql, we do the same, except we pass SQL directly instead of adding its parts one by one. It's worth mentioning that we should escape parameter values manually with Yii::app()->db->quoteValue before using them in the query string. The renderRows function renders the query builder. The DAO raw row requires you to add more checks and generally, it feels unnatural compared to rendering an Active Record result. As we can see, all these methods give the same result in the end, but they all have different performance, syntax, and extra features. We will now do a comparison and figure out when to use each method: Method Active Record Query Builder SQL (DAO) Syntax This will do SQL for you. Gii will generate models and relations for you. Works with models, completely OO-style, and very clean API. Produces array of properly nested models as the result. Clean API, suitable for building query on the fly. Produces raw data arrays as the result. Good for complex SQL. Manual values and keywords quoting. Not very suitable for building query on the fly. Produces raw data arrays as results. Performance Higher memory usage and execution time compared to SQL and query builder. Okay. Okay. Extra features Quotes values and names automatically. Behaviors. Before/after hooks. Validation. Quotes values and names automatically. None. Best for Prototyping selects. Update, delete, and create actions for single models (model gives a huge benefit when using with forms). Working with large amounts of data, building queries on the fly. Complex queries you want to do with pure SQL and have maximum possible performance. There's more... In order to learn more about working with databases in Yii, refer to the following resources: http://www.yiiframework.com/doc/guide/en/database.dao http://www.yiiframework.com/doc/guide/en/database.query-builder http://www.yiiframework.com/doc/guide/en/database.ar See also The Using CDbCriteria recipe Defining and using multiple DB connections Multiple database connections are not used very often for new standalone web applications. However, when you are building an add-on application for an existing system, you will most probably need another database connection. From this recipe you will learn how to define multiple DB connections and use them with DAO, query builder, and Active Record models. Getting ready Create a new application by using yiic webapp as described in the official guide at the following URL:http://www.yiiframework.com/doc/guide/en/quickstart.first-app Create two MySQL databases named db1 and db2. Create a table named post in db1 as follows: DROP TABLE IF EXISTS `post`; CREATE TABLE IF NOT EXISTS `post` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) NOT NULL, `text` TEXT NOT NULL, PRIMARY KEY (`id`) ); Create a table named comment in db2 as follows: DROP TABLE IF EXISTS `comment`; CREATE TABLE IF NOT EXISTS `comment` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `text` TEXT NOT NULL, `postId` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`) ); How to do it... We will start with configuring the DB connections. Open protected/config/main.php and define a primary connection as described in the official guide: 'db'=>array( 'connectionString' => 'mysql:host=localhost;dbname=db1', 'emulatePrepare' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8', ), Copy it, rename the db component to db2, and change the connection string accordingly. Also, you need to add the class name as follows: 'db2'=>array( 'class'=>'CDbConnection', 'connectionString' => 'mysql:host=localhost;dbname=db2', 'emulatePrepare' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8', ), That is it. Now you have two database connections and you can use them with DAO and query builder as follows: $db1Rows = Yii::app()->db->createCommand($sql)->queryAll(); $db2Rows = Yii::app()->db2->createCommand($sql)->queryAll(); Now, if we need to use Active Record models, we first need to create Post and Comment models with Gii. Starting from Yii version 1.1.11, you can just select an appropriate connection for each model.Now you can use the Comment model as usual. Create protected/controllers/DbtestController.php as follows: <?php class DbtestController extends CController { public function actionIndex() { $post = new Post(); $post->title = "Post #".rand(1, 1000); $post->text = "text"; $post->save(); echo '<h1>Posts</h1>'; $posts = Post::model()->findAll(); foreach($posts as $post) { echo $post->title."<br />"; } $comment = new Comment(); $comment->postId = $post->id; $comment->text = "comment #".rand(1, 1000); $comment->save(); echo '<h1>Comments</h1>'; $comments = Comment::model()->findAll(); foreach($comments as $comment) { echo $comment->text."<br />"; } } } Run dbtest/index multiple times and you should see records added to both databases, as shown in the following screenshot: How it works... In Yii you can add and configure your own components through the configuration file. For non-standard components, such as db2, you have to specify the component class. Similarly, you can add db3, db4, or any other component, for example, facebookApi. The remaining array key/value pairs are assigned to the component's public properties respectively. There's more... Depending on the RDBMS used, there are additional things we can do to make it easier to use multiple databases. Cross-database relations If you are using MySQL, it is possible to create cross-database relations for your models. In order to do this, you should prefix the Comment model's table name with the database name as follows: class Comment extends CActiveRecord { //… public function tableName() { return 'db2.comment'; } //… } Now, if you have a comments relation defined in the Post model relations method, you can use the following code: $posts = Post::model()->with('comments')->findAll(); Further reading For further information, refer to the following URL: http://www.yiiframework.com/doc/api/CActiveRecord See also The Getting data from a database recipe Using scopes to get models for different languages Internationalizing your application is not an easy task. You need to translate interfaces, translate messages, format dates properly, and so on. Yii helps you to do this by giving you access to the Common Locale Data Repository ( CLDR ) data of Unicode and providing translation and formatting tools. When it comes to applications with data in multiple languages, you have to find your own way. From this recipe, you will learn a possible way to get a handy model function that will help to get blog posts for different languages. Getting ready Create a new application by using yiic webapp as described in the official guide at the following URL:http://www.yiiframework.com/doc/guide/en/quickstart.first-app Set up the database connection and create a table named post as follows: DROP TABLE IF EXISTS `post`; CREATE TABLE IF NOT EXISTS `post` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `lang` VARCHAR(5) NOT NULL DEFAULT 'en', `title` VARCHAR(255) NOT NULL, `text` TEXT NOT NULL, PRIMARY KEY (`id`) ); INSERT INTO `post`(`id`,`lang`,`title`,`text`) VALUES (1,'en_us','Yii news','Text in English'), (2,'de','Yii Nachrichten','Text in Deutsch'); Generate a Post model using Gii. How to do it... Add the following methods to protected/models/Post.php as follows: class Post extends CActiveRecord { public function defaultScope() { return array( 'condition' => "lang=:lang", 'params' => array( ':lang' => Yii::app()->language, ), ); } public function lang($lang){ $this->getDbCriteria()->mergeWith(array( 'condition' => "lang=:lang", 'params' => array( ':lang' => $lang, ), )); return $this; } } That is it. Now, we can use our model. Create protected/controllers/ DbtestController.php as follows: <?php class DbtestController extends CController { public function actionIndex() { // Get posts written in default application language $posts = Post::model()->findAll(); echo '<h1>Default language</h1>'; foreach($posts as $post) { echo '<h2>'.$post->title.'</h2>'; echo $post->text; } // Get posts written in German $posts = Post::model()->lang('de')->findAll(); echo '<h1>German</h1>'; foreach($posts as $post) { echo '<h2>'.$post->title.'</h2>'; echo $post->text; } } } Now, run dbtest/index and you should get an output similar to the one shown in the following screenshot: How it works... We have used Yii's Active Record scopes in the preceding code. The defaultScope function returns the default condition or criteria that will be applied to all the Post model query methods. As we need to specify the language explicitly, we create a scope named lang, which accepts the language name. With $this->getDbCriteria(), we get the model's criteria in its current state and then merge it with the new condition. As the condition is exactly the same as in defaultScope, except for the parameter value, it overrides the default scope. In order to support chained calls, lang returns the model instance by itself. There's more... For further information, refer to the following URLs: http://www.yiiframework.com/doc/guide/en/database.ar http://www.yiiframework.com/doc/api/CDbCriteria/ See also The Getting data from a database recipe The Using CDbCriteria recipe Processing model fields with AR event-like methods Active Record implementation in Yii is very powerful and has many features. One of these features is event-like methods , which you can use to preprocess model fields before putting them into the database or getting them from a database, as well as deleting data related to the model, and so on. In this recipe, we will linkify all URLs in the post text and we will list all existing Active Record event-like methods. Getting ready Create a new application by using yiic webapp as described in the official guide at the following URL:http://www.yiiframework.com/doc/guide/en/quickstart.first-app Set up a database connection and create a table named post as follows: DROP TABLE IF EXISTS `post`; CREATE TABLE IF NOT EXISTS `post` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) NOT NULL, `text` TEXT NOT NULL, PRIMARY KEY (`id`) ); Generate the Post model using Gii How to do it... Add the following method to protected/models/Post.php as follows: protected function beforeSave() { $this->text = preg_replace('~((?:https?|ftps?)://.*?)( |$)~iu', '<a href="1">1</a>2', $this->text); return parent::beforeSave(); } That is it. Now, try saving a post containing a link. Create protected/controllers/TestController.php as follows: <?php class TestController extends CController { function actionIndex() { $post=new Post(); $post->title='links test'; $post->text='test http://www.yiiframework.com/ test'; $post->save(); print_r($post->text); } } Run test/index. You should get the following: How it works... The beforeSave method is implemented in the CActiveRecord class and executed just before saving a model. By using a regular expression, we replace everything that looks like a URL with a link that uses this URL and call the parent implementation, so that real events are raised properly. In order to prevent saving, you can return false. There's more... There are more event-like methods available as shown in the following table: Method name Description afterConstruct Called after a model instance is created by the new operator beforeDelete/afterDelete Called before/after deleting a record beforeFind/afterFind Method is invoked before/after each record is instantiated by a find method beforeSave/afterSave Method is invoked before/after saving a record successfully beforeValidate/afterValidate Method is invoked before/after validation ends Further reading In order to learn more about using event-like methods in Yii, you can refer to the following URLs: http://www.yiiframework.com/doc/api/CActiveRecord/ http://www.yiiframework.com/doc/api/CModel See also The Using Yii events recipe  The Highlighting code with Yii recipe The Automating timestamps recipe The Setting up an author automatically recipe
Read more
  • 0
  • 0
  • 3874
article-image-data-driven-design
Packt
10 Jul 2013
21 min read
Save for later

Data-driven Design

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

article-image-understanding-express-routes
Packt
10 Jul 2013
10 min read
Save for later

Understanding Express Routes

Packt
10 Jul 2013
10 min read
(For more resources related to this topic, see here.) What are Routes? Routes are URL schema, which describe the interfaces for making requests to your web app. Combining an HTTP request method (a.k.a. HTTP verb) and a path pattern, you define URLs in your app. Each route has an associated route handler, which does the job of performing any action in the app and sending the HTTP response. Routes are defined using an HTTP verb and a path pattern. Any request to the server that matches a route definition is routed to the associated route handler. Route handlers are middleware functions, which can send the HTTP response or pass on the request to the next middleware in line. They may be defined in the app file or loaded via a Node module. A quick introduction to HTTP verbs The HTTP protocol recommends various methods of making requests to a Web server. These methods are known as HTTP verbs. You may already be familiar with the GET and the POST methods; there are more of them, about which you will learn in a short while. Express, by default, supports the following HTTP request methods that allow us to define flexible and powerful routes in the app: GET POST PUT DELETE HEAD TRACE OPTIONS CONNECT PATCH M-SEARCH NOTIFY SUBSCRIBE UNSUBSCRIBE GET, POST, PUT, DELETE, HEAD, TRACE, OPTIONS, CONNECT, and PATCH are part of the Hyper Text Transfer Protocol (HTTP) specification as drafted by the Internet Engineering Task Force (IETF) and the World Wide Web Consortium (W3C). M-SEARCH, NOTIFY, SUBSCRIBE, and UNSUBSCRIBE are specified by the UPnP Forum. There are some obscure HTTP verbs such as LINK, UNLINK, and PURGE, which are currently not supported by Express and the underlying Node HTTP library. Routes in Express are defined using methods named after the HTTP verbs, on an instance of an Express application: app.get(), app.post(), app.put(), and so on. We will learn more about defining routes in a later section. Even though a total of 13 HTTP verbs are supported by Express, you need not use all of them in your app. In fact, for a basic website, only GET and POST are likely to be used. Revisiting the router middleware This article would be incomplete without revisiting the router middleware. The router middleware is very special middleware. While other Express middlewares are inherited from Connect, router is implemented by Express itself. This middleware is solely responsible for empowering Express with Sinatra-like routes. Connect-inherited middlewares are referred to in Express from the express object (express.favicon(), express.bodyParser(), and so on). The router middleware is referred to from the instance of the Express app (app.router)  To ensure predictability and stability, we should explicitly add router to the middleware stack: app.use(app.router); The router middleware is a middleware system of its own. The route definitions form the middlewares in this stack. Meaning, a matching route can respond with an HTTP response and end the request flow, or pass on the request to the next middleware in line. This fact will become clearer as we work with some examples in the upcoming sections. Though we won't be directly working with the router middleware, it is responsible for running the whole routing show in the background. Without the router middleware, there can be no routes and routing in Express. Defining routes for the app we know how routes and route handler callback functions look like. Here is an example to refresh your memory: app.get('/', function(req, res) { res.send('welcome'); }); Routes in Express are created using methods named after HTTP verbs. For instance, in the previous example, we created a route to handle GET requests to the root of the website. You have a corresponding method on the app object for all the HTTP verbs listed earlier. Let's create a sample application to see if all the HTTP verbs are actually available as methods in the app object: var http = require('http'); var express = require('express'); var app = express(); // Include the router middleware app.use(app.router); // GET request to the root URL app.get('/', function(req, res) { res.send('/ GET OK'); }); // POST request to the root URL app.post('/', function(req, res) { res.send('/ POST OK'); }); // PUT request to the root URL app.put('/', function(req, res) { res.send('/ PUT OK'); }); // PATCH request to the root URL app.patch('/', function(req, res) { res.send('/ PATCH OK'); }); // DELETE request to the root URL app.delete('/', function(req, res) { res.send('/ DELETE OK'); }); // OPTIONS request to the root URL app.options('/', function(req, res) { res.send('/ OPTIONS OK'); }); // M-SEARCH request to the root URL app['m-search']('/', function(req, res) { res.send('/ M-SEARCH OK'); }); // NOTIFY request to the root URL app.notify('/', function(req, res) { res.send('/ NOTIFY OK'); }); // SUBSCRIBE request to the root URL app.subscribe('/', function(req, res) { res.send('/ SUBSCRIBE OK'); }); // UNSUBSCRIBE request to the root URL app.unsubscribe('/', function(req, res) { res.send('/ UNSUBSCRIBE OK'); }); // Start the server http.createServer(app).listen(3000, function() { console.log('App started'); }); We did not include the HEAD method in this example, because it is best left for the underlying HTTP API to handle it, which it already does. You can always do it if you want to, but it is not recommended to mess with it, because the protocol will be broken unless you implement it as specified. The browser address bar isn't capable of making any type of request, except GET requests. To test these routes we will have to use HTML forms or specialized tools. Let's use Postman, a Google Chrome plugin for making customized requests to the server. We learned that route definition methods are based on HTTP verbs. Actually, that's not completely true, there is a method called app.all() that is not based on an HTTP verb. It is an Express-specific method for listening to requests to a route using any request method: app.all('/', function(req, res, next) { res.set('X-Catch-All', 'true'); next(); }); Place this route at the top of the route definitions in the previous example. Restart the server and load the home page. Using a browser debugger tool, you can examine the HTTP header response added to all the requests made to the home page, as shown in the following screenshot: Something similar can be achieved using a middleware. But the app.all() method makes it a lot easier when the requirement is route specified. Route identifiers So far we have been dealing exclusively with the root URL (/) of the app. Let's find out how to define routes for other parts of the app. Routes are defined only for the request path. GET query parameters are not and cannot be included in route definitions. Route identifiers can be string or regular expression objects. String-based routes are created by passing a string pattern as the first argument of the routing method. They support a limited pattern matching capability. The following example demonstrates how to create string-based routes: // Will match /abcd app.get('/abcd', function(req, res) { res.send('abcd'); }); // Will match /acd app.get('/ab?cd', function(req, res) { res.send('ab?cd'); }); // Will match /abbcd app.get('/ab+cd', function(req, res) { res.send('ab+cd'); }); // Will match /abxyzcd app.get('/ab*cd', function(req, res) { res.send('ab*cd'); }); // Will match /abe and /abcde app.get('/ab(cd)?e', function(req, res) { res.send('ab(cd)?e'); }); The characters ?, +, *, and () are subsets of their Regular Expression counterparts.   The hyphen (-) and the dot (.) are interpreted literally by string-based route identifiers. There is another set of string-based route identifiers, which is used to specify named placeholders in the request path. Take a look at the following example: app.get('/user/:id', function(req, res) { res.send('user id: ' + req.params.id); }); app.get('/country/:country/state/:state', function(req, res) { res.send(req.params.country + ', ' + req.params.state); } The value of the named placeholder is available in the req.params object in a property with a similar name. Named placeholders can also be used with special characters for interesting and useful effects, as shown here: app.get('/route/:from-:to', function(req, res) { res.send(req.params.from + ' to ' + req.params.to); }); app.get('/file/:name.:ext', function(req, res) { res.send(req.params.name + '.' + req.params.ext.toLowerCase()); }); The pattern-matching capability of routes can also be used with named placeholders. In the following example, we define a route that makes the format parameter optional: app.get('/feed/:format?', function(req, res) { if (req.params.format) { res.send('format: ' + req.params.format); } else { res.send('default format'); } }); Routes can be defined as regular expressions too. While not being the most straightforward approach, regular expression routes help you create very flexible and powerful route patterns. Regular expression routes are defined by passing a regular expression object as the first parameter to the routing method. Do not quote the regular expression object, or else you will get unexpected results. Using regular expression to create routes can be best understood by taking a look at some examples. The following route will match pineapple, redapple, redaple, aaple, but not apple, and apples: app.get(/.+app?le$/, function(req, res) { res.send('/.+ap?le$/'); }); The following route will match anything with an a in the route name: app.get(/a/, function(req, res) { res.send('/a/'); }); You will mostly be using string-based routes in a general web app. Use regular expression-based routes only when absolutely necessary; while being powerful, they can often be hard to debug and maintain. Order of route precedence Like in any middleware system, the route that is defined first takes precedence over other matching routes. So the ordering of routes is crucial to the behavior of an app. Let's review this fact via some examples. In the following case, http://localhost:3000/abcd will always print "abc*" , even though the next route also matches the pattern: app.get('/abcd', function(req, res) { res.send('abcd'); }); app.get('/abc*', function(req, res) { res.send('abc*'); }); Reversing the order will make it print "abc*": app.get('/abc*', function(req, res) { res.send('abc*'); }); app.get('/abcd', function(req, res) { res.send('abcd'); }); The earlier matching route need not always gobble up the request. We can make it pass on the request to the next handler, if we want to. In the following example, even though the order remains the same, it will print "abc*" this time, with a little modification in the code. Route handler functions accept a third parameter, commonly named next, which refers to the next middleware in line. We will learn more about it in the next section: app.get('/abc*', function(req, res, next) { // If the request path is /abcd, don't handle it if (req.path == '/abcd') { next(); } else { res.send('abc*'); } }); app.get('/abcd', function(req, res) { res.send('abcd'); }); So bear it in mind that the order of route definition is very important in Express. Forgetting this will cause your app behave unpredictably. We will learn more about this behavior in the examples in the next section.
Read more
  • 0
  • 0
  • 6000
Modal Close icon
Modal Close icon