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-unreal-development-toolkit-level-design-hq
Packt
23 Nov 2011
5 min read
Save for later

Unreal Development Toolkit: Level Design HQ

Packt
23 Nov 2011
5 min read
(For more resources related to this subject, see here.) So let's get on with it. We will first look at downloading the UDK, and install it on your PC. Time for action – UDK download and installation Download the latest version of UDK. Log on to www.udk.com and download the latest version of unreal development kit beta. Once you download the UDK Installer, go ahead and install the UDK. The default directory for installing UDK is C:UDKUDK-VersionRelease. Version Release will be the month and year that the UDK you downloaded was built. UDK folder structure The UDK folder structure looks like the following screenshot: The UDK folder structure consists of the following four folders: Binaries: game/binary executable. Development: source code for UDK. Engine: engine files. UTGame: game files. For level-design and environment creation, the important folder here is the content folder. The packaged environment's assets such as models, textures, materials, sounds, and such are stored here. For environment creation and level design, the most important folder is UTGame | Content | Environments. It contains all the files you need to create your map, as shown in the following screenshot: UDK extension is the UDK package's name. This is how the models and textures are stored in UDK. Think of UDK extension as folders. Inside those folders are stored all the models, animations, textures, materials, and similar assets. You can browse the UDK files through the UDK editor.   UDK is the map file extension.   Time for action – launching the editor To launch the unreal editor, go to the Start Menu | Unreal Development Kit | UDK Version | Editor. Another way to launch the editor is to create a shortcut. To do this, go to the installation folder: UDKUDK-VersionReleaseBinaries, locate UDKLift.exe, right-click and select Send To | Desktop (create shortcut), as shown in the following screenshot: Once on you have created the shortcut on your desktop, right-click the shortcut and select Properties. Then, in the Target box under the Shortcut tab, add editor at the end of the text. It should look something like the following screenshot: Now double-click on the desktop icon and launch the UDK Editor. Autosave When you first launch the editor, you will have Autosave automatically enabled. This will save your map at a chosen timed interval. You can set how often it will automatically save by clicking the Left Mouse Button (LMB) on the arrow on the bottom-right of the Autosave Interval and choosing the time you want, as shown in the following screenshot: You will find the Autosave feature at the bottom right of the editor. If you enable Autosave, there are a few options such as Interval and Type. Save manually by going up to File | Save As. Content browser Content browser is where you will find off the game's assets. Placing static meshes (models), textures, sounds, and game entities such as player starts, weapons, and so on, can all be done through the content browser. You will be using the content browser very often. To open the content browser click on the top menu bar, as shown in the following screenshot: Packages are where you will find specific items contained within the UDK. Things such as static meshes are contained within a package. You can search for a package, or just find the package you want to use and select it as shown in the following screenshot: The top of the content browser contains a search box as well as a filter box. This is very useful. You can sort out the content in the browser by animation sets, material instances, static meshes, sounds, and so on. This helps a lot when looking for items. The next screenshot lists full names of the items within a selected package. You can sort by clicking on the Name, Type, Tags, or Path fields, and it will re-arrange the content's preview: The content browser is one of the most commonly used tools in UDK. Get comfortable using the content browser. Spend some time navigating around it. UDK basics covers the most essential tools and functions you need to know to get started with UDK. You'll be able to quickly jump into UDK and begin feeling comfortable using the most commonly used functions. What just happened? So we know how to launch the editor, how to use the Autosave function, and where to find the content browser. We are now going to look at how to move and rotate around the editor. Time for action – movement and rotation Time to have a look at movement, rotation, and navigating around the editor. Navigation Buttons used to navigate around UDK. UDK These are your primary keys for navigating and rotating using the editor: Left Mouse Button (LMB): pan right/left/forward/backward movements Right Mouse Button (RMB): rotate, look around LMB+RMB: up/down WASD key navigation The following are other forms of primary keys for navigating and rotating around the editor: Click and hold RMB. As you hold it, use the WASD keyboard keys to move around as you would in a first person shooter game. WASD movement is great if you are familiar with hammer source mapping. MAYA users If you are familiar with Maya, the following will be your primary keys for navigating and rotating around the editor. Hold down the U key U+ LMB: rotate, look around U+ RMB: forward/backward movements U+ MMB: right/left/up/down movements What just happened? Now that you have installed UDK and know what the content browser is, you are ready to begin. So let's get started.
Read more
  • 0
  • 0
  • 8363

article-image-microsoft-dynamics-nav-2009-designing-forms
Packt
25 Oct 2010
8 min read
Save for later

Microsoft Dynamics NAV 2009: Designing Forms

Packt
25 Oct 2010
8 min read
Forms are a predominant visual element in Dynamics NAV. There are 937 tables in the base NAV software and 1,820 forms that display information from those tables. Apart from learning how to create a form using the wizard, this article will not discuss the basic elements of form design. That information can be found in the C/SIDE Reference Guide and Development Coursework from Microsoft. If you have not designed a form before, it is highly recommended that you acquaint yourself with forms first. With NAV 2009, Microsoft released the RoleTailored client, or RTC. This was a huge change from the existing NAV product. In this release, Microsoft introduced the RTC as a second client or interface in addition to what is called the Classic client, or more traditional interface. While the future of NAV is definitely with the RTC, it is still important to understand what forms are and how they work, in order to support customers who might not upgrade to the latest version of the product. Obtaining input without a form Sometimes you don't want to use an entire form to get user input. Dialog boxes are not a substitute for forms, but they work just fine for quick input. How to do it... Create a new codeunit from Object Designer. Add the following global variables: Add the following code to the OnRun trigger of the codeunit: Window.OPEN('Customer No: #1####################'); Window.INPUT(1, CustomerNo); Window.CLOSE; IF Customer.GET(CustomerNo) THEN MESSAGE('Customer Name: %1', Customer.Name) ELSE MESSAGE('No customer found!); Save and close the codeunit. How it works... The first line of code opens an input dialog window that looks like one shown in the following screenshot: The next line lets the user input a value and stores it in the CustomerNo variable. The dialog window then closes and the result can be used later in code. There's more... As you can tell from the input window, dialogs are much weaker than forms when it comes to functionality. You can't do lookups, data validation, or anything other than basic text input. From a licensing aspect, forms are one of the cheapest objects to buy. They also don't match the look and feel for the rest of the system. For these reasons it is almost always better to use a form than an input dialog, but it is important to know what you can do using dialogs. Using the Form Generation Wizard You can always create a form manually, but using the Form Generation Wizard is a quick and painless way to create the skeleton. How to do it... With the form selected in Object Designer click the New button. Choose the Customer table. Select Create a form using a wizard. Select Tabular-Type Form. Click OK. Use the arrow buttons between the two lists to add the No. and Name fields. Click on Finish. How it works... The Form Generation Wizard allows you to tell the system what fields you want on the form and the format or order in which you want them to appear. NAV will then automatically place the fields on the form for you. There is no manual positioning of labels or textboxes; no creating tabs or list boxes. It is all done automatically. There's more... The wizard will only create a basic form for you. If you need to create special functions or do any specific data validation, you will have to code that manually. A wizard is only designed to get you started, not to do anything advanced. Changing text appearance A great way to improve the user experience is to change the way text appears on the screen. This recipe will explore several options that are available to you. Getting ready Design the Customer List form and save it as a new object. How to do it... Design the copy of the Customer List form. Create a function named GetColor that returns an integer. Add the following code to the function: IF "Location Code" = 'BLUE' THEN EXIT(16711680) ELSE IF "Location Code" = 'GREEN' THEN EXIT(65280) ELSE IF "Location Code" = 'RED' THEN EXIT(255) ELSE IF "Location Code" = 'YELLOW' THEN EXIT(65535) Create a function named GetBold that returns a boolean value. Add the following code to the function: EXIT("Credit Limit (LCY)" > 1000); In the OnFormat trigger for the name column, add the following code: CurrForm.Name.UPDATEFORECOLOR(GetColor); CurrForm.Name.UPDATEFONTBOLD(GetBold); Save and close the form. How it works... The trigger that controls the appearance of text is the OnFormat trigger. The first function we use is UPDATEFORECOLOR. This method is found on every text field in a form. It takes one parameter—the color we want the text to be. In our example, we pass a function as the parameter and that function returns the color we should use. UPDATEFONTBOLD works in a similar way. It takes a boolean parameter that tells the form whether or not to emphasize the text. The resulting form will look similar to the one shown in the following screenshot: There's more... The look and feel of a system is important for user satisfaction. Finding ways to make the information easier to understand, such as displaying the text in the same color as the warehouse location, can improve user understanding and decrease the time it takes to look up information. That said, don't go overboard. Having a form with multiple colors that have no direct relation to the data can be confusing. You don't want to the user to have a "cheat sheet" of what everything means. If it takes longer than a couple of minutes to explain what certain characteristics mean and you can't remember them an hour later, then you probably have gone too far. It also makes your upgrade-time to the RoleTailored client longer because display colors only have limited support. Preventing editable lookup forms You may want users to only add records when running a form from a setup location. This example will show you how to prevent users from adding or modifying values when only trying to look up a record. Getting ready This example will use the Salesperson/Purchasers form (14). How to do it... Design the Salesperson/Purchasers form from Object Designer. In the OnOpen trigger for the form, add the following code: IF CurrForm.LOOKUPMODE THEN CurrForm.EDITABLE := FALSE; Save and close the form. How it works... The code here is pretty self-explanatory. If the form is in lookup mode, it will not be editable. There's more... The Lookup mode is a special mode in which forms can run. Essentially, when in lookup mode, the OK and Cancel buttons are displayed; when not in lookup mode, they are hidden. When using these buttons you can retrieve the selected value from the form. It is often a good idea to make forms uneditable in lookup mode, although you will find many forms in base NAV where this is not the case. When the purpose of running a form is only to retrieve a value, it is a good idea to make sure that the form is not editable to make sure those values are not accidentally changed. Adding an editable field to a non-editable form Have you ever needed to make a form uneditable rather than just one field? This recipe will show you a quick and easy way to do it. Getting ready Create a list form based on the Customer table that displays the number and name of the customer. The Editable property of the form should be set to No. How to do it... View the code for the Name column in the list form. In the OnActivate trigger, add the following code: CurrForm.EDITABLE := TRUE; In the OnDeactivate trigger add the following code: CurrForm.EDITABLE := FALSE; Save and close the form. How it works... When you click on a textbox its OnActivate trigger is executed. In our form, we have told the system to override the default Editable property when we click on the textbox. We set it to true so that the field becomes editable. In fact, the entire form becomes editable. We must make the entire form editable because that overrides the editable property of the controls on the form. But when we click-off or tab-off of the field the OnDeactivate trigger fires. We then reset the form back to uneditable. Whenever the field is activated you can edit it, otherwise you cannot edit anything. In the RoleTailored client there is no OnActivate or OnDeactivate trigger. You will have to do it the hard way, that is, by setting the Editable property on every field. Further resources on this subject: Microsoft Dynamics NAV 2009: Creating a Matrix Form Microsoft Dynamics NAV 2009: Creating a Wizard-style Form
Read more
  • 0
  • 0
  • 8358

article-image-advanced-aspects-inserting-and-deleting-data-mysql
Packt
01 Oct 2010
10 min read
Save for later

Advanced aspects of Inserting and Deleting data in MySQL

Packt
01 Oct 2010
10 min read
  MySQL Admin Cookbook 99 great recipes for mastering MySQL configuration and administration Set up MySQL to perform administrative tasks such as efficiently managing data and database schema, improving the performance of MySQL servers, and managing user credentials Deal with typical performance bottlenecks and lock-contention problems Restrict access sensibly and regain access to your database in case of loss of administrative user credentials Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible Read more about this book (For more resources on MySQL, see here.) Inserting new data and updating data if it already exists Manipulating data in a database is part of everyday work and the basic SQL means of INSERT, UPDATE, and DELETE make this a pretty straightforward, almost trivial task—but is this always true? When considering data manipulation, most of the time we think of a situation where we know the content of the database. With this information, it is usually pretty easy to find a way of changing the data the way you intend to. But what if you have to change data in circumstances where you do not know the actual database content beforehand? You might answer: "Well, then look at your data before changing it!" Unfortunately, you do not always have this option. Think of distributed installations of any software that includes a database. If you have to design an update option for this software (and the respective databases), you might easily come to a situation where you simply do not know about the actual database content. One example of a problem arising in these cases is the question of whether to insert or to update data: "Does the data in question already (partially) exist?" Let us assume a database table config that stores configuration settings. It holds key-value pairs, with name being the name (and thus the key) of the setting and value its value. This table exists in different database installations, one for every branch office of your company. Your task is to create an update package to set a uniform upper limit of 25% for the price discount that is allowed in your sales software. If no such limit has been defined yet, there is no respective entry in the config table, and you have to insert a new record. If the limit, however, has been set before (for example by the local manager), the entry does already exist, in which case you have to update it to hold the new value. While the update of a potentially existing entry does not pose a problem, an INSERT statement that violates uniqueness constraints will simply cause an error. This is, however, typically not acceptable in an automated update procedure. The following recipe will show you how to solve this problem with only one SQL command. Getting ready Besides a running MySQL server, a SQL client, and an account with appropriate user rights (INSERT, UPDATE), we need a table to update. In the earlier example, we assumed a table named sample.config with two character columns name and value. The name column is defined as the primary key: CREATE TABLE sample.config ( name VARCHAR(64) PRIMARY KEY, value VARCHAR(64)); How to do it... Connect to your database using your SQL client Execute the following command: mysql> INSERT INTO sample.config VALUES ("maxPriceDiscount","25%") ON DUPLICATE KEY UPDATE value='25%';Query OK, 1 row affected (0.05 sec) How it works... This command is easily explained because it simply does what it says: it inserts a new row in the table using the given values, as long as this does not cause a duplicate entry in either the primary key or another unique index. If a duplicate record exists, the existing row is updated according to the clauses defined after ON DUPLICATE KEY UPDATE. While it is sometimes tedious to enter some of the data and columns two times (once for the INSERT and a second time for the UPDATE), this statement allows for a lot of flexibility when it comes to the manipulation of potentially existing data. Please note that when executing the above statement, the result differs slightly with respect to the number of affected rows, depending on the actual data present in the database: When the record does not exist yet, it is inserted, which results in one affected row. But if the record is updated rather than inserted, it reports two affected rows instead, even if only one row gets updated. There's more... The INSERT INTO … ON DUPLICATE UPDATE construct does not work when there is no UNIQUE or PRIMARY KEY defined on the target table. If you have to provide the same semantics without having appropriate key definitions in place, it is recommended to use the techniques discussed in the next recipe. Inserting data based on existing database content In the previous recipe Inserting new data and updating data if it already exists, we discussed a method to either insert or update records depending on whether the records already exist in the database. A similar problem arises when you need to insert data to your database, but the data to insert depends on the data in your database. As an example, consider a situation in which you need to insert a record with a certain message into a table logMsgs, but the message itself should be different depending on the current system language that is stored in a configuration table (config). It is fairly easy to achieve a similar behavior for an UPDATE statement because this supports a WHERE clause that can be used to only perform an update if a certain precondition is met: UPDATE logMsgs SET message= CONCAT('Last update: ', NOW()) WHERE EXISTS (SELECT value FROM config WHERE name='lang' AND value = 'en');UPDATE logMsgs SET message= CONCAT('Letztes Update: ', NOW()) WHERE EXISTS (SELECT value FROM config WHERE name='lang' AND value = 'de');UPDATE logMsgs SET message= CONCAT('Actualisation derniere: ', NOW()) WHERE EXISTS (SELECT value FROM config WHERE name='lang' AND value = 'fr'); Unfortunately, this approach is not applicable to INSERT commands, as these do not support a WHERE clause. Despite this missing option, the following recipe describes a method to make INSERT statements execute only if an appropriate precondition in the database is met. Getting ready As before, we assume a database, a SQL client (mysql), and a MySQL user with sufficient privileges (INSERT and SELECT in this case). Additionally, we need a table to insert data into (here: logMsgs) and a configuration table config (please refer to the previous recipe for details). How to do it... Connect to your database using your SQL client. Execute the following SQL commands: mysql> INSERT INTO sample.logMsgs(message) -> SELECT CONCAT('Last update: ', NOW()) -> FROM sample.config WHERE name='lang' AND value='en';Query OK, 0 rows affected (0.00 sec)Records: 0 Duplicates: 0 Warnings: 0 How it works... Our goal is to have an INSERT statement take into account the present language stored in the database. The trick to do so is to use a SELECT statement as input for the INSERT. The SELECT command provides a WHERE clause, so you can use a condition that only matches for the respective language. One restriction of this solution is that you can only insert one record at a time, so the size of scripts might grow considerably if you have to insert lots of data and/or have to cover many alternatives. There's more... If you have more than just a few values to insert, it is more convenient to have the data in one place rather than distributed over several individual INSERT statements. In this case, it might make sense to consolidate the data by putting it inside a temporary table; the final INSERT statement uses this temporary table to select the appropriate data rows for insertion into the target table. The downside of this approach is that the user needs the CREATE TEMPORARY TABLES privilege, but it typically compensates with much cleaner scripts: After creating the temporary table with the first statement, we insert data into the table with the following INSERT statement. The next statement inserts the appropriate data into the target table sample.logMsgs by selecting the appropriate data from the temporary data that matches the language entry from the config table. The temporary table is then removed again. The final SELECT statement is solely for checking the results of the operation. Deleting all data from large tables Almost everyone who works with databases experiences the constant growth of the data stored in their database and it is typically well beyond the initial estimates. Because of that you often end up with rather large data sets. Another common observation is that in most databases, there are some tables that have a special tendency to grow especially big. If a table's size reaches a virtual threshold (which is hard to define, as it depends heavily on the access patterns and the data structures), it gets harder and harder to maintain and performance degradation might occur. From a certain point on, it is even difficult to get rid of data in the table again, as the sheer number of records makes deletion a pretty expensive task. This particularly holds true for storage engines with Multi-Version Concurrency Control (MVCC): if you order the database to delete data from the table, it must not be deleted right away because you might still roll back the deletion. So even while the deletion was initiated, a concurrent query on the table still has to be able to see all the records (depending on the transaction isolation level). To achieve this, the storage engine will only mark the records as deleted, but the actual deletion takes place after the operation is committed and all other transactions that access this table are closed as well. If you have to deal with large data sets, the most difficult task is to operate on the production system while other processes concurrently work on the data. In these circumstances, you have to keep the duration of your maintenance operations as low as possible in order to minimize the impact on the running system. As the deletion of data from a large table (typically starting at several millions of rows) might take quite some time, the following recipe shows a way of minimizing the duration of this operation in order to reduce side effects (like locking effects or performance degradation). Getting ready Besides a user account with appropriate privileges (DELETE), you need a sufficiently large table to delete data from. For this recipe, we will use the employees database, which is an example database available from MySQL: http://dev.mysql.com/doc/employee/en/employee.html This database provides some tables with sensible data and some pretty large tables, the largest having more than 2.8 million records. We assume that the Employees database was installed with an InnoDB storage engine enabled. To delete all rows of the largest table employees.salaries in a quick way, please read on. How to do it...
Read more
  • 0
  • 0
  • 8356

article-image-speeding-gradle-builds-android
Packt
16 Jul 2015
7 min read
Save for later

Speeding up Gradle builds for Android

Packt
16 Jul 2015
7 min read
In this article by Kevin Pelgrims, the author of the book, Gradle for Android, we will cover a few tips and tricks that will help speed up the Gradle builds. A lot of Android developers that start using Gradle complain about the prolonged compilation time. Builds can take longer than they do with Ant, because Gradle has three phases in the build lifecycle that it goes through every time you execute a task. This makes the whole process very configurable, but also quite slow. Luckily, there are several ways to speed up Gradle builds. Gradle properties One way to tweak the speed of a Gradle build is to change some of the default settings. You can enable parallel builds by setting a property in a gradle.properties file that is placed in the root of a project. All you need to do is add the following line: org.gradle.parallel=true Another easy win is to enable the Gradle daemon, which starts a background process when you run a build the first time. Any subsequent builds will then reuse that background process, thus cutting out the startup cost. The process is kept alive as long as you use Gradle, and is terminated after three hours of idle time. Using the daemon is particularly useful when you use Gradle several times in a short time span. You can enable the daemon in the gradle.properties file like this: org.gradle.daemon=true In Android Studio, the Gradle daemon is enabled by default. This means that after the first build from inside the IDE, the next builds are a bit faster. If you build from the command-line interface; however, the Gradle daemon is disabled, unless you enable it in the properties. To speed up the compilation itself, you can tweak parameters on the Java Virtual Machine (JVM). There is a Gradle property called jvmargs that enables you to set different values for the memory allocation pool for the JVM. The two parameters that have a direct influence on your build speed are Xms and Xmx. The Xms parameter is used to set the initial amount of memory to be used, while the Xmx parameter is used to set a maximum. You can manually set these values in the gradle.properties file like this: org.gradle.jvmargs=-Xms256m -Xmx1024m You need to set the desired amount and a unit, which can be k for kilobytes, m for megabytes, and g for gigabytes. By default, the maximum memory allocation (Xmx) is set to 256 MB, and the starting memory allocation (Xms) is not set at all. The optimal settings depend on the capabilities of your computer. The last property you can configure to influence build speed is org.gradle.configureondemand. This property is particularly useful if you have complex projects with several modules, as it tries to limit the time spent in the configuration phase, by skipping modules that are not required for the task that is being executed. If you set this property to true, Gradle will try to figure out which modules have configuration changes and which ones do not, before it runs the configuration phase. This is a feature that will not be very useful if you only have an Android app and a library in your project. If you have a lot of modules that are loosely coupled, though, this feature can save you a lot of build time. System-wide Gradle properties If you want to apply these properties system-wide to all your Gradle-based projects, you can create a gradle.properties file in the .gradle folder in your home directory. On Microsoft Windows, the full path to this directory is %UserProfile%.gradle, on Linux and Mac OS X it is ~/.gradle. It is a good practice to set these properties in your home directory, rather than on the project level. The reason for this is that you usually want to keep memory consumption down on build servers, and the build time is of less importance. Android Studio The Gradle properties you can change to speed up the compilation process are also configurable in the Android Studio settings. To find the compiler settings, open the Settings dialog, and then navigate to Build, Execution, Deployment | Compiler. On that screen, you can find settings for parallel builds, JVM options, configure on demand, and so on. These settings only show up for Gradle-based Android modules. Have a look at the following screenshot: Configuring these settings from Android Studio is easier than configuring them manually in the build configuration file, and the settings dialog makes it easy to find properties that influence the build process. Profiling If you want to find out which parts of the build are slowing the process down, you can profile the entire build process. You can do this by adding the --profile flag whenever you execute a Gradle task. When you provide this flag, Gradle creates a profiling report, which can tell you which parts of the build process are the most time consuming. Once you know where the bottlenecks are, you can make the necessary changes. The report is saved as an HTML file in your module in build/reports/profile. This is the report generated after executing the build task on a multimodule project: The profiling report shows an overview of the time spent in each phase while executing the task. Below that summary is an overview of how much time Gradle spent on the configuration phase for each module. There are two more sections in the report that are not shown in the screenshot. The Dependency Resolution section shows how long it took to resolve dependencies, per module. Lastly, the Task Execution section contains an extremely detailed task execution overview. This overview has the timing for every single task, ordered by execution time from high to low. Jack and Jill If you are willing to use experimental tools, you can enable Jack and Jill to speed up builds. Jack (Java Android Compiler Kit) is a new Android build toolchain that compiles Java source code directly to the Android Dalvik executable (dex) format. It has its own .jack library format and takes care of packaging and shrinking as well. Jill (Jack Intermediate Library Linker) is a tool that can convert .aar and .jar files to .jack libraries. These tools are still quite experimental, but they were made to improve build times and to simplify the Android build process. It is not recommended to start using Jack and Jill for production versions of your projects, but they are made available so that you can try them out. To be able to use Jack and Jill, you need to use build tools version 21.1.1 or higher, and the Android plugin for Gradle version 1.0.0 or higher. Enabling Jack and Jill is as easy as setting one property in the defaultConfig block: android {   buildToolsRevision '22.0.1'   defaultConfig {     useJack = true   }} You can also enable Jack and Jill on a certain build type or product flavor. This way, you can continue using the regular build toolchain, and have an experimental build on the side: android {   productFlavors {       regular {           useJack = false       }        experimental {           useJack = true       }   }} As soon as you set useJack to true, minification and obfuscation will not go through ProGuard anymore, but you can still use the ProGuard rules syntax to specify certain rules and exceptions. Use the same proguardFiles method that we mentioned before, when talking about ProGuard. Summary This article helped us lean different ways to speed up builds; we first saw how we can tweak the settings, configure Gradle and the JVM, we saw how to detect parts that are slowing down the process, and then we learned Jack and Jill tool. Resources for Article: Further resources on this subject: Android Tech Page Android Virtual Device Manager Apache Maven and m2eclipse  Saying Hello to Unity and Android
Read more
  • 0
  • 0
  • 8354

article-image-aspnet-site-performance-reducing-long-wait-times
Packt
12 Oct 2010
8 min read
Save for later

ASP.NET site performance: reducing long wait times

Packt
12 Oct 2010
8 min read
Measuring wait times We can use a number of ways to find out which external requests are most frequent and how long the site has to wait for a response: Run the code in the debugger with breakpoints around each external request. This will give you a quick hint of which external request is the likely culprit. However, you wouldn't do this in a production environment, as it only gives you information for a few requests. Use the Trace class (in the namespace System.Diagnostics) to trace how long each request takes. This will give you a lot of detailed information. However, the overhead incurred by processing all the trace messages may be too high to use in a production environment, and you would have to somehow aggregate the trace data to find which requests are the most frequent and take the longest. Build performance counters into your code that record the frequency of each request and the average wait time. These counters are light-weight, and hence, can be used in a production environment. Also, you can readily access them via perfmon, along with the counters provided by ASP.NET, SQL Server, and so on that you have already come across. The remainder of this section focuses on performance counters. Also, performance counters are a convenient way to keep an eye on off-box requests on a day-to-day basis instead of as a one-off. Windows offers you 28 types of performance counters to choose from. Some of these are esoteric, others extremely useful. For example, you can measure the rate per second that a request is made, and the average time in milliseconds that the site waits for a response. Adding your own custom counters is easy, and you can see their real-time values in perfmon, along with that of the built-in counters. The runtime overhead of counters is minimal. You have already come across some of the hundreds of counters published by ASP.NET, SQL Server, and Windows itself. Even if you add a lot of counters, CPU overhead would be well under one percent. This section describes only three commonly used counters: simple number, rate per second, and time. A list of all types of counters with examples of their use is available at http://msdn.microsoft.com/en-us/library/system.diagnostics.performancecountertype.aspx?ppud=4. To use the counters, you need to follow these three steps: Create custom counters. Update them in your code. See their values in perfmon. Creating custom counters In this example, we'll put counters on a page that simply waits for one second to simulate waiting for an external resource. Windows allows you to group counters into categories. We'll create a new category "Test Counters" for the new counters. Counter NameCounter TypeDescriptionNbr Page HitsNumberOfItems6464 bit counter, counting the total number of hits on the page since the website started.Hits/secondRateOfCountsPerSecond32Hits per secondAverage WaitAverageTimer32Time taken by the resource. Inspite of the name, it is used here to simply measure an interval, not an average.Average Wait Base*AverageBaseUtility counter required by Average Wait. *The text says there are three counters, but the table lists four. Why? The last counter, Average Wait Base, doesn't provide information on its own, but helps to compute the value of counter Average Wait. Later on, we'll see how this works. There are two ways to create the "Test Counters" category and the counters themselves: Using Visual Studio: This is relatively quick, but if you want to apply the same counters to for example your development and production environments, you'll have to enter the counters separately in each environment Programmatically: Because this involves writing code, it takes a bit longer upfront, but makes it easier to apply the same counters to multiple environments and to place the counters under source control Creating counters with Visual Studio To create the counters in Visual Studio: Make sure you have administrative privileges or are a member of the Performance Monitor Users group. Open Visual Studio. Click on the Server Explorer tab. Expand Servers. Expand your machine. Right-click on Performance Counters and choose Create New Category. Enter Test Counters in the Category Name field. Click on the New button for each of the four counters to add, as listed in the table you saw earlier. Be sure to add the Average Wait Base counter right after Average Wait, to properly associate the two counters. Click on OK when you're done. This technique is easy. However, you'll need to remember to add the same counters to the production machine when you release new code with new custom counters. Writing a program to create the counters is more work initially, but gives you easier maintenance in the long run. Let's see how to do this. Creating counters programmatically From a maintenance point of view, it would be best to create the counters when the web application starts, in the Global.asax file. However, you would then have to make the account under which the application pool runs part of the Performance Monitor Users group. An alternative is to create the counters in a separate console program. An administrator can then run the program to create the counters on the server. Here is the code. using System; using System.Diagnostics; namespace CreateCounters { class Program { static void Main(string[] args) { To create a group of counters, you create each one in turn, and add them to a CounterCreationDataCollection object: CounterCreationDataCollection ccdc = new CounterCreationDataCollection(); Create the first counter, Nbr Page Hits. Give it a short help message and the counter type. Now, add it to the CounterCreationDataCollection object: CounterCreationData ccd = new CounterCreationData ("Nbr Page Hits", "Total number of page hits", PerformanceCounterType.NumberOfItems64); ccdc.Add(ccd); Add the second, third, and fourth counters along the same lines: ccd = new CounterCreationData("Hits / second", "Total number of page hits / sec", PerformanceCounterType.RateOfCountsPerSecond32); ccdc.Add(ccd); ccd = new CounterCreationData("Average Wait", "Average wait in seconds", PerformanceCounterType.AverageTimer32); ccdc.Add(ccd); ccd = new CounterCreationData("Average Wait Base", "", PerformanceCounterType.AverageBase); ccdc.Add(ccd); Now, it's time to take the CounterCreationDataCollection object and make it into a category. Because you'll get an exception when you try to create a category that already exists if there already is a category with the same name, delete it now. Because you can't add new counters to an existing category, there is no simple work-around for this: if (PerformanceCounterCategory.Exists("Test Counters")) { PerformanceCounterCategory.Delete("Test Counters"); } Finally, create the Test Counters category. Give it a short help message, and make it a single instance. You can also make a category multi-instance, which allows you to split the category into instances. Also, pass in the CounterCreationDataCollection object with all the counters. This creates the complete category with all your counters in one go, as shown in the following code: PerformanceCounterCategory.Create("Test Counters", "Counters for test site",PerformanceCounterCategoryType. SingleInstance,ccdc); } } } Now that you know how to create the counters, let's see how to update them in your code Updating counters in your code To keep things simple, this example uses the counters in a page that simply waits for a second to simulate waiting for an external resource: using System; using System.Diagnostics; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { First, increment the nbrPageHits counter. To do this, create a PerformanceCounter object, attaching it to the nbrPageHits counter in the Test Counters category. Then, increment the PerformanceCounter object: PerformanceCounter nbrPageHitsCounter = new PerformanceCounter("Test Counters", "Nbr Page Hits", false); nbrPageHitsCounter.Increment(); Now, do the same with the Hits/second counter. Because you set its type to RateOfCountsPerSecond32 when you generated it in the console program, the counter will automatically give you a rate per second when viewed in perfmon: PerformanceCounter nbrPageHitsPerSecCounter = new PerformanceCounter("Test Counters", "Hits / second", false); nbrPageHitsPerSecCounter.Increment(); To measure how long the actual operation takes, create a Stopwatch object, and start it: Stopwatch sw = new Stopwatch(); sw.Start(); Execute the simulated operation: // Simulate actual operation System.Threading.Thread.Sleep(1000); Stop the stopwatch: sw.Stop(); Update the Average Wait counter and the associated Average Wait Base counter to record the elapsed time in the stopwatch. PerformanceCounter waitTimeCounter = new PerformanceCounter("Test Counters", "Average Wait", false); waitTimeCounter.IncrementBy(sw.ElapsedTicks); PerformanceCounter waitTimeBaseCounter = new PerformanceCounter("Test Counters", "Average Wait Base", false); waitTimeBaseCounter.Increment(); } } Now that we've seen how to create and use the most commonly used counters, it's time to retrieve their values. Viewing custom counters in perfmon Accessing your custom counters goes the following way: On the server, run perfmon from the command prompt. To open the command prompt on Vista, click on Start | All Programs | Accessories | Command Prompt. This opens the monitor window. Expand Monitoring Tools and click on Performance Monitor. Click on the green "plus" sign. In the Add Counters dialog, scroll down to your new Test Counters category. Expand that category and add your new counters. Click on OK. To see the counters in action, run a load test. If you use WCAT, you could use files runwcat_testcounters.bat and testcounters_scenario.ubr from the downloaded code bundle.
Read more
  • 0
  • 2
  • 8350

article-image-building-etl-pipelines-in-no-time-using-chatgpt
Sagar Lad
11 Jun 2023
5 min read
Save for later

Building ETL Pipelines in no time using ChatGPT

Sagar Lad
11 Jun 2023
5 min read
Given the volume, velocity, and diversity of data expanding at an exponential rate in the modern era, it is crucial to utilize this data for data analytics and machine learning projects to generate business insights. Let's discuss in this post how to utilize ChatGPT to develop ETL pipelines considering the growing popularity of ChatGPT in recent years.What is an ETL (Extract, Transform, and Load) Pipeline Data must first be fetched from one or more sources, processed, or transformed in accordance with the requirements, and then loaded into a storage system that can be used directly by end users without the need for data validation or poor data quality. ETL Pipeline also known as a ‘Data Pipeline’ sometimes known as three phases. Image 1: ETL Process Flow (Extract, Transformation, and Load) During the ETL process, first, we fetch the data, and we perform data quality and validation checks on the extracted data. Once data is extracted then data pre and post-processing should be done to transform the data into a usable format. Once data processing is done, the last step is to store the data from where the end user can access this data. Let’s ask ChatGPT to build an ETL pipeline for data engineering.Problem StatementUsing Databricks and Pyspark build an ETL pipeline using 3 layered approaches: Raw, Bronze, and Gold Layers.  Data should be ingested incrementally automatically, and data should be stored in Azure SQL Database. Business Analysts can use this data to derive business insights.1. Input to ChatGPT with the programming language, and file information (location, format, delimiter, headers, output) Image 2 : Input to chatGPT to create ETL Pipeline Here, the input has been given to the ChatGPT to build an ETL pipeline with all the required input. 2. Prerequisites suggested by ChatGPT before using the code are as follows:  Image 3 : Prerequisite to setup ETL Flow Here, ChatGPT first lists down the prerequisites to set up the blob storage, azure data lake storage gen2, and Databricks workspace.3. Importing the necessary libraries and configuring the ADLS Gen2 storage credentials as shown in the preceding figure:Image 4 : ADLS Gen2 configuration for Pyspark NotebookThis code configures ADLS gen2 using the pyspark notebook to connect and use the data using the storage account key. Here, you should replace the storage account name and key with your storage account name and key details. 4. In this step, pyspark notebook creates the schema for the pyspark data frame based on the file information and we must replace adls container name with the actual container name of ADLS Gen2. Image 5 : Create Structured Schema for pyspark dataframe5. This piece of code renames the columns and once the data frame is ready, it will write the content of the dataframe to the delta format. Image 6 : Pyspark Notebook - Data Transformation Logic 6.  Finally, ChatGPT provides guidance on where to run this code and how to set up an automated pipeline using Databricks. Image 7 : Final ChatGPT Output  7. At a first glance, it looks like it worked like a charm. Generated code can be directly used in the Databricks workspace to build an ETL pipeline. But the limitation of this solution is that it is hard coding the file path so it is not a generic code.Image 8 : Pyspark Code Optimization - ChatGPT input  8.     ChatGPT creates a generic code that can be used to trigger the ETL pipeline whenever there is a new source file in the ADLS Gen2 container. Image 9 : ADLS Gen2 configuration for Pyspark Notebook9.     Next step is to configure the ADLS Gen2 to connect to Databricks using the storage account key.Image 10 : Schema Definition for CSV File 10.  Next step is to create a structured schema to use it while creating the pyspark dataframe in the next step: Image 11: Setting up a loop to check new files 11.  As a final step, we will work towards optimizing the PySpark code: Image 12: Optimised Pyspark Code for incremental data loadTo process any file using the ETL pipeline code, the suggestion is to loop the code to continuously poll the storage container location to check if there is any new file and execute the code to process the new file if any.So, we can use this code and set up Databricks notebooks in a couple of minutes to set up an automated data pipeline for the incremental data load.ConclusionIt is getting much easier and more efficient to build ETL data pipelines using the ChatGPT. ChatGPT can also assist to create a generic and optimized code as per our requirements promptly without spending development efforts. Author BioSagar Lad is a Cloud Data Solution Architect with a leading organization and has deep expertise in designing and building Enterprise-grade Intelligent Azure Data and Analytics Solutions. He is a published author, content writer, Microsoft Certified Trainer, and C# Corner MVP. Link - Medium , Amazon , LinkedIn 
Read more
  • 0
  • 0
  • 8350
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-integrating-lotus-quickplacequickr-ibm-lotus-notes-and-domino-851
Packt
05 Feb 2010
7 min read
Save for later

Integrating Lotus QuickPlace/Quickr with IBM Lotus Notes and Domino 8.5.1

Packt
05 Feb 2010
7 min read
The Lotus brand within the IBM Software group represents the "people facing" side of the overall IBM product family. This does not necessarily mean the end user uses no other IBM products, but the Lotus brand is the front end of the IBM SOA  Interaction Services component. The following diagram shows the interaction between these components. Each can be deployed as a separate infrastructure piece and provide value. However, when  they are leveraged as an integrated solution, the possibilities are nearly endless. WebSphere Portal Server provides an on-the-glass integration solution for your enterprise. It allows you to create composite role-based applications from different data sources. For more information, visit http://www.ibm.com/websphere/portal Web Content Management is now an integrated piece of the WebSphere Portal Server. It provides an end user driven content authoring and content delivery system. Visit http://www.ibm.com/websphere/portal Lotus Notes/Domino is IBM's fagship messaging and collaboration product, deployed to over 100 million users. It provides a foundation on which many of the other value-added products build. Visit http://www.ibm.com/software/sw-lotus/products/product4.nsf/wdocs/dominohomepage Lotus QuickPlace/Quickr is IBM's team collaboration product. It provides template-based services and a set of content connectors, allowing end users to quickly create interactive team places, all without the need for administrator intervention. Visit http://www.ibm.com/software/sw-lotus/products/product3.nsf/wdocs/ltwhome Lotus Sametime is IBM's unified communications and collaboration product. It provides enterprise-class instant messaging and web conferencing services, as well as a platform for many other capabilities. This includes telephony and voice integration services. Visit http://www.ibm.com/software/sw-lotus/products/product3.nsf/wdocs/st75home Lotus Connections is IBM's "social networking" product. It provides a new category of integrated application services covering the following areas: activities, dogear, blogs, communities, and profles. Visit http://www.ibm.com/software/sw-lotus/products/product3.nsf/wdocs/connections This article does not cover all the products available under the Lotus brand. For additional information on these, visit http://www.lotus.com Lotus QuickPlace/Quickr The Lotus QuickPlace product has been available for several years. QuickPlace has become recognized as a leader in web-based team collaboration, and it is used in many large corporations throughout the world. In early 2007, IBM announced two important changes to the QuickPlace product family: The new version of Lotus QuickPlace will be renamed Lotus Quickr A new J2EE-based version of QuickPlace will be introduced, also called Lotus Quickr These two versions of Quickr (one based on Domino and one based on WebSphere Portal) form a single product. They do, however, have very different deployment architectures. From an end user's standpoint, they should be viewed as collaboration appliances. They will be presented with a consolidated list of places where they  are involved so that the end users can easily navigate. This will be the case regardless of the backend deployment architecture. The goal of these changes to QuickPlace/Quickr is simplification. IBM wants the product to be so simple that an end user can, with a few clicks, generate productive services. There is also a focus on server deployment simplification. One of the key components of Quickr is the connector technology that it introduces. These connectors will allow for direct and programmatic interaction with the data stored in the Quickr places. Connectors that will be shipping with Quickr 8.0 are the following: Notes Sametime File System/Windows Explorer RSS/ATOM In this section, we will cover each edition and discuss how it integrates with Domino. We will not be covering how to install the product itself; this is covered in detail within the product documentation. Quickr with services for Domino The Domino-based edition of Quickr should be thought of as a signifcant upgrade to the existing QuickPlace product line. This product has been maturing over many years, and this new release provides many new capabilities. These include native support for wikis and blogs "out of the box". Installing Lotus Quickr The basic installation process for Quickr with service for Domino is very simple. It involves installing a base Domino server v7.0.2 FP1. The Quickr product components are then installed on top of this server. Directory integration After installation, there is some additional configuration required, depending on the intended usage. They are: User/group directory configuration is used to control the authorization and authentication of the environment Sametime integration is used to enable presence awareness within the places QPServlet confguration is used for WebSphere Portal integration The user/group directory configuration allows you to select one of three directory types: Internal place level directory allows for registration of users at a place level. The users are independent of any corporate directory that may be in place. Generally this is used to support external user access. Lotus Domino Directory allows users and groups to be stored in the names.nsf database (public address book) and corresponding directories surfaced via directory assistance. LDAP directory allows for users in a supported native LDAP directory (for example, Domino, IBM Tivoli Directory Server, Microsoft Active Directory, Novelle Directory, or Sun One Directory). This configuration allows for the refinement of the settings used to interact with the LDAP source. The directory can be configured by first logging as an administrative user. In the following screenshot, we have logged in as user in Admin from the Domino Directory. Next, select the User Directory option from Site Administration. The following screen shows the default value of No Directory selected. This is the base setup where the contacts.nsf database for each place will be used as a user directory. It is possible to change the user directory by clicking the Change Directory button. This lets you choose between No Directory, LDAP Server, and Domino Server. If LDAP Server is selected, then options for connecting to the directory server will be displayed, as shown in the following screenshot: This screen contains the following felds: Name provides the host name of the LDAP Directory Server. Port number is the LDAP TCP/IP port used to communicate with the server. Check for SSL connection with LDAP User Directory enables SSL  encryption of the LDAP traffic. Note that this is an "all or nothing" selection. It is not possible to only encrypt parts of the conversation with the  LDAP server. Search base controls determines where in the LDAP tree to search for users and groups. For a Domino LDAP directory this value is generally left blank. This is because groups in Domino, as seen through LDAP, have no organizational component (for example, cn=Sales Users). Narrow searches to the place name further restricts the LDAP search to users that contain the Quickr place name (for instance, Sales). Check to use credentials specified below when searching the directory controls whether or not anonymous access is used for the LDAP directory. It is very common to have read-only binding credentials to search the directory. Username provides the user's distinguished Name for the LDAP server (for example, cn=quickrbind, ou=Admin, o=Acme). Password is the password for the username above. Authentication Timeout controls the time in seconds for the login operation to timeout. The default is 120 seconds. Search Timeout controls the time in seconds for LDAP searches to timeout. The default is 120 seconds.
Read more
  • 0
  • 0
  • 8345

article-image-working-forms-dynamics-ax-part-1
Packt
07 Jan 2010
12 min read
Save for later

Working with Forms in Dynamics AX: Part 1

Packt
07 Jan 2010
12 min read
Introduction Normally, forms are created using AOT by creating a form object and adding form controls like tabs, tab pages, grids, groups, data fields, images, and other. Form behavior is controlled by its properties or the code in its member methods. The behavior and layout of form controls are also controlled by their properties and the code in their member methods. Although it is very rare, forms can also be created dynamically from code. In this article, we will cover various aspects of using Dynamics AX forms. We start with building Dynamics AX dialogs, which actually are dynamic forms, and explain how to handle their events. The article will also show how to add dynamic controls to existing forms, how to build dynamic forms from scratch, how to make forms modal, and how to change the appearance of all application forms with a few lines of code. This article also discusses the usage of splitters, tree controls, creating checklists, saving last user selections, modifying application version, and other things. Creating Dialogs Dialogs are a way to present users with a simple input form. They are commonly used for small user tasks like filling in report values, running batch jobs, presenting only the most important fields to the user when creating a new record, etc. Dialogs are normally created from X++ code without storing actual layout in AOT. The application class Dialog is used to build dialogs. Other application classes like DialogField, DialogGroup , DialogTabPage, and so on, are used to create dialog controls. One of the common ways is to use dialogs within RunBase framework classes that need user input. In this example, we will see how to build a dialog from code using the RunBase framework class. The dialog will contain customer table fields shown in different groups and tabs for creating a new record. There will be two tab pages, General and Details. The first page will have Customer account and Name input controls. The second page will be divided into two groups, Setup and Payment, with relevant fields. The actual record will not be created, as it is out of scope of this example. However, for demonstration purposes, the information specified by the user will be displayed in the Infolog. How to do it... Open AOT, and create a new class CustCreate with the following code: class CustCreate extends RunBase{ DialogField fieldAccount; DialogField fieldName; DialogField fieldGroup; DialogField fieldCurrency; DialogField fieldPaymTermId; DialogField fieldPaymMode; CustAccount custAccount; CustName custName; CustGroupId custGroupId; CurrencyCode currencyCode; CustPaymTermId paymTermId; CustPaymMode paymMode;}public container pack(){ return connull();}public boolean unpack(container packedClass){ return true;}protected Object dialog(){ Dialog dialog; DialogTabPage tabGeneral; DialogTabPage tabDetails; DialogGroup groupCustomer; DialogGroup groupPayment; ; dialog = super(); dialog.caption("Customer information"); tabGeneral = dialog.addTabPage("General"); fieldAccount = dialog.addField( typeid(CustVendAC), "Customer account"); fieldName = dialog.addField(typeid(CustName)); tabDetails = dialog.addTabPage("Details"); groupCustomer = dialog.addGroup("Setup"); fieldGroup = dialog.addField(typeid(CustGroupId)); fieldCurrency = dialog.addField(typeid(CurrencyCode)); groupPayment = dialog.addGroup("Payment"); fieldPaymTermId = dialog.addField(typeid(CustPaymTermId)); fieldPaymMode = dialog.addField(typeid(CustPaymMode)); return dialog;}public boolean getFromDialog(){; custAccount = fieldAccount.value(); custName = fieldName.value(); custGroupId = fieldGroup.value(); currencyCode = fieldCurrency.value(); paymTermId = fieldPaymTermId.value(); paymMode = fieldPaymMode.value(); return true;}public void run(){; info("You have entered customer information:"); info(strfmt("Account: %1", custAccount)); info(strfmt("Name: %1", custName)); info(strfmt("Group: %1", custGroupId)); info(strfmt("Currency: %1", currencyCode)); info(strfmt("Terms of payment: %1", paymTermId)); info(strfmt("Method of payment: %1", paymMode));}static void main(Args _args){ CustCreate custCreate = new CustCreate(); ; if (custCreate.prompt()) { custCreate.run(); }} To test the dialog, run the class. The following form should appear with the General tab page open initially: When you click on the Details tab page, you will see the following screen: Enter some information into the fields, and click OK. The results are displayed in the Infolog: How it works... Firstly, we create a new class CustCreate. By extending it from RunBase, we utilize the standard approach of running such kinds of dialogs. RunBase will also automatically add the required buttons to the dialog. Then we declare class member variables, which will be used later. DialogField type variables are actual user input fields. The rest are used to store the values returned from user input. The pack() and unpack() methods are normally used to convert an object to a container, which is a format to store an object in the user cache (SysLastValue) or to transfer it between Server and Client tiers. RunBase requires those two methods to be present in all its subclasses. In this example, we are not using any of the pack()/unpack() features, but because those methods are mandatory, we return an empty container from pack() and true from unpack(). The layout of the actual dialog is constructed in the dialog() member method. Here, we define local variables for the dialog itself, tab pages, and groups. Those variables, as opposed to the dialog fields, do not store any value to be processed further. The super() of the RunBase framework creates the initial dialog object for us. The object is created using the Dialog application class. The class actually uses the Dynamics AX form as a base, automatically adds the relevant controls, including OK and Cancel buttons, and presents it to the user as a dialog. Additional dialog controls are added to the dialog by using the addField(), addGroup(), and addTabPage() methods . There are more methods to add different types of controls like addText(), addImage(), addMenuItemButton(), and others. All controls have to be added to the dialog object directly. Adding an input control to groups or tabs is done by calling addField() right after addGroup() or addTabPage(). In the example above, we add tab pages, groups, and fields in logical sequence, so every control appears in the right position. The method returns a prepared dialog object for further processing. Values from the dialog controls are assigned to variables by calling the value() member method of DialogField. If a dialog is used within the RunBase framework, as in this example, the best place to assign dialog control values to variables is the getFormDialog() member method. RunBase calls this method right after the user clicks OK. The main processing is done in run(). For demonstration purposes, this example contains only variable output to Infolog. In order to make this class runable, the static method main() has to be created. Here, we create a new CustCreate object, invoke user dialog by calling prompt(), and once the user finishes entering customer details by clicking OK, we call run() to process the data. Handling dialog events Sometimes, the user interface requires us to change the status of a field, depending on the status of another field. For example, if the user marks the Show filter checkbox, another field, Filter, appears or becomes enabled. In standard Dynamics AX forms, this can be done using input control event modified() . But sometimes such features are required on dialogs where handling events is not that straightforward. Very often, I find myself in a situation where existing dialogs need to be adjusted to support events. The easiest way of doing that is of course to build a form in AOT, which will replace the original dialog. But in cases when the existing dialog is complex enough, probably a more cost effective solution would be to implement dialog event handling. It is not as flexible as AOT forms, but in most cases it does the job. In this recipe, we will create a dialog very similar to the previous one, but instead of entering the customer number, we will be able to select it from the list. Once the customer is selected, the rest of the fields will be filled automatically by the system from the customer record. How to do it... In AOT, create a new class named CustSelect with the following code: class CustSelect extends RunBase{ DialogField fieldAccount; DialogField fieldName; DialogField fieldGroup; DialogField fieldCurrency; DialogField fieldPaymTermId; DialogField fieldPaymMode;}public container pack(){ return connull();}public boolean unpack(container packedClass){ return true;}protected Object dialog(){ Dialog dialog; DialogTabPage tabGeneral; DialogTabPage tabDetails; DialogGroup groupCustomer; DialogGroup groupPayment; ; dialog = super(); dialog.caption("Customer information"); dialog.allowUpdateOnSelectCtrl(true); tabGeneral = dialog.addTabPage("General"); fieldAccount = dialog.addField( typeid(CustAccount), "Customer account"); fieldName = dialog.addField(typeid(CustName)); fieldName.enabled(false); tabDetails = dialog.addTabPage("Details"); groupCustomer = dialog.addGroup("Setup"); fieldGroup = dialog.addField(typeid(CustGroupId)); fieldCurrency = dialog.addField(typeid(CurrencyCode)); fieldGroup.enabled(false); fieldCurrency.enabled(false); groupPayment = dialog.addGroup("Payment"); fieldPaymTermId = dialog.addField(typeid(CustPaymTermId)); fieldPaymMode = dialog.addField(typeid(CustPaymMode)); fieldPaymTermId.enabled(false); fieldPaymMode.enabled(false); return dialog;}public void dialogSelectCtrl(){ CustTable custTable; ; custTable = CustTable::find(fieldAccount.value()); fieldName.value(custTable.Name); fieldGroup.value(custTable.CustGroup); fieldCurrency.value(custTable.Currency); fieldPaymTermId.value(custTable.PaymTermId); fieldPaymMode.value(custTable.PaymMode);}static void main(Args _args){ CustSelect custSelect = new CustSelect(); ; if (CustSelect.prompt()) { CustSelect.run(); }} Run the class, select any customer from the list, and move the cursor to the next control. Notice how the rest of the fields were populated automatically with the customer information: When you click on Details tab page, you will see the details as in following screenshot: How it works... The new class CustSelect is a copy of CustCreate from the previous recipe with few changes. In its declaration, we leave all DialogField declarations. We remove all other variables apart from Customer account. The Customer account input control is the only editable field on the dialog, so we have to keep it for storing its value. The methods pack()/unpack() remain the same as we are not using any of their features. In the dialog() member method, we call allowUpdateOnSelect() with the argument true to enable input control event handling. We also disable all fields apart from Customer account by calling enable() with parameter false for every field. The member method dialogSelectCtrl() of the RunBase class is called every time the user modifies any input control in the dialog. It is the place where we have to add all the required code to ensure that, in our case, all controls are populated with the correct data from the customer record, once the Customer account is chosen. Static main() method ensures that we can run this class. There's more... Usage of dialogSelectCtrl() sometimes might appear a bit limited as this method is only invoked when the dialog control loses its focus. No other events can be controlled, and it can become messy if more controls needs to be processed. Actually, this method is called from the selectControl() of the form, which is used as a base for the dialog. As mentioned earlier, dialogs created using the Dialog class are actually forms, which are dynamically created during runtime. So in order to extend event handling functionality on dialogs, we should utilize form event handling features. The Dialog class does not provide direct access to form event handling functions, but we can easily access the form object within the dialog. Although we cannot create the usual event handling methods on runtime form controls, we can override this behavior. Let's modify the previous example to include more events. We will add an event on the second tab page, which is triggered once the page is activated. First, we have to override the dialogPostRun() method on the CustSelect class: public void dialogPostRun(DialogRunbase dialog){; dialog.formRun().controlMethodOverload(true); dialog.formRun().controlMethodOverloadObject(this); super(dialog);} Here, we enable event overriding on the form after it is fully created and is ready for displaying on the screen. We also pass the CustSelect object as argument for the controlMethodOverloadObject() to make sure that form "knows" where overridden events are located. Next, we have to create the method that overrides the tab page event: void TabPg_2_pageActivated(){; info('Tab page activated');} The method name consists of the control name and event name joined with an underscore. But before creating such methods, we first have to get the name of the runtime control. This is because the dialog form is created dynamically, and Dynamics AX defines control names automatically without allowing the user to choose them. In this example, I have temporary added the following code to the bottom of dialog(), which displayed the name of the Details tab page control when the class was executed: info(tabDetails.name()); Now, run the class again, and select the Details tab page. The message should be displayed in the Infolog. Creating dynamic menu buttons Normally, Dynamics AX forms are created in AOT by adding various controls to the form's design and do not change during runtime. But besides that, Dynamics AX allows developers to add controls dynamically during form runtime. Probably, you have already noticed that the Document handling form in the standard Dynamics AX application has a nice option to create a new record by clicking the New button and selecting the desired document type from the list. This feature does not add any new functionality to the application, but it provides an alternative way of quickly creating a new record and it makes the form more user-friendly. The content of this button is actually generated dynamically during the initialization of the form and may vary depending on the document handling setup. There might be other cases when such features can be used. For example, dynamic menu buttons could be used to display a list of statuses, which depends on the type of the selected record. In this recipe, we will explore the code behind this feature. As an example, we will modify the Ledger budget button on the Chart of accounts form to display a list of available budget models relevant only for the selected ledger account. That means the list is going to be generated dynamically and may be different for different accounts.
Read more
  • 0
  • 0
  • 8339

article-image-nginx-web-services-configuration-and-implementation
Packt
08 Jul 2011
6 min read
Save for later

Nginx Web Services: Configuration and Implementation

Packt
08 Jul 2011
6 min read
Nginx 1 Web Server Implementation Cookbook Installing new modules and compiling Nginx Today, most softwares are designed to be modular and extensible. Nginx, with its great community, has an amazing set of modules out there that lets it do some pretty interesting things. Although most operating system distributions have Nginx binaries in their repositories, it is a necessary skill to be able to compile new, bleeding edge modules, and try them out. Now we will outline how one can go about compiling and installing Nginx with its numerous third-party modules. How to do it... The first step is to get the latest Nginx distribution, so that you are in sync with the security and performance patches (http://sysoev.ru/nginx/nginx-0.7.67.tar.gz). Do note that you will require sudo or root access to do some of the installation steps going ahead. Un-tar the Nginx source code. This is simple, you will need to enter the following command: tar -xvzf nginx-0.7.67.tar.gz Go into the directory and configure it. This is essential, as here you can enable and disable the core modules that already come with Nginx. Following is a sample configure command: ./configure --with-debug --with-http_ssl_module --with-http_realip_module --with-http_ssl_module --with-http_perl_module --with-http_stub_status_module You can figure out more about what other modules and configuration flags use: ./configure --help If you get an error, then you will need to install the build dependencies, depending on your system. For example, if you are running a Debian based system, you can enter the following command: apt-get build-dep nginx This will install all the required build dependencies, like PCRE and TLS libraries. After this, you can simply go ahead and build it: sudo make install This was the plain vanilla installation! If you want to install some new modules, we take the example of the HTTP subscribe-publish module. Download your module (http://pushmodule.slact.net/downloads/nginx_http_push_module-0.692.tar.gz). Un-tar it at a certain location:/path/to/module. Reconfigure Nginx installation: ./configure ..... --add-module=/path/to/module The important part is to point the –add-module flag to the right module path. The rest is handled by the Nginx configuration script. You can continue to build and install Nginx as shown in step 5. sudo make install If you have followed steps 1 to 10, it will be really easy for you to install any Nginx module. There's more... If you want to check that the module is installed correctly, you can enter the following command: nginx -V A sample output is something as shown in the following screenshot: This basically gives you the compilation flags that were used to install this particular binary of Nginx, indirectly listing the various modules that were compiled into it. Running Nginx in debug mode Nginx is a fairly stable piece of software which has been running in production for over a decade and has built a very strong developer community around it. But, like all software there are issues and bugs which crop up under the most critical of situations. When that happens, it's usually best to reload Nginx with higher levels of error logging and if possible, in the debug mode. How to do it... If you want the debug mode, then you will need to compile Nginx with the debug flag (--with-debug). In most cases, most of the distributions have packages where Nginx is precompiled with debug flag. Here are the various levels of debugging that you can utilize: error_log LOGFILE [debug | info | notice | warn | error | crit | debug_core | debug_alloc | debug_mutex | debug_event | debug_http | debug_imap]; Downloading the example code You can download the example code files here If you do not set the error log location, it will log to a compiled-in default log location. This logging is in addition to the normal error logging that you can do per site. Here is what the various specific debug flags do: There's more... Nginx allows us to log errors for specific IP addresses. Here is a sample configuration that will log errors from 192.168.1.1 and the IP range of 192.168.10.0/24: error_log logs/error.log; events { debug_connection 192.168.1.1; debug_connection 192.168.10.0/24; } This is extremely useful when you want to debug in the production environment, as logging for all cases has unnecessary performance overheads. This feature allows you to not set a global debug on the error_log, while being able to see the debug output for specific matched IP blocks based on the user's IP address. Easy reloading of Nginx using the CLI Depending on the system that you have, it will offer one clean way of reloading your Nginx setup Debian based: /etc/init.d/Nginx reload Fedora based: service Nginx reload FreeBSD/BSD: service Nginx reload Windows: Nginx -s reload All the preceding commands reload Nginx; they send a HUP signal to the main Nginx process. You can send quite a few control signals to the Nginx master process, as outlined in the following table. These let you manage some of the basic administrative tasks: How to do it... Let me run you through the simple steps of how you can reload Nginx from the command line. Open a terminal on your system. Most UNIX-based systems already have fairly powerful terminals, while you can use PuTTY on Windows systems. Type in ps auxww | grep nginx. This will output something as shown in the following screenshot: If nothing comes, then it means that Nginx is not running on your system. If you get the preceding output, then you can see the master process and the two worker processes (it may be more, depending on your worker_processes configuration). The important number is 3322, which is basically the PID of the master process. To reload Nginx, you can issue the command kill -HUP <PID of the nginx master process>. In this case, the PID of the master process is 3322. This will basically read the configurations again, gracefully close your current connections, and start new worker processes. You can issue another ps auxww | grep nginx to see new PIDs for the worker processes (4582,4583): If the worker PIDs do not change it means that you may have a problem while reloading the configuration files. Go ahead and check the Nginx error log. This is very useful while writing scripts, which control Nginx configuration. A good example is when you are deploying code on production; you will temporarily point the site to a static landing page.  
Read more
  • 0
  • 0
  • 8339

article-image-ninject-patterns-and-anti-patterns
Packt
30 Sep 2013
7 min read
Save for later

Ninject Patterns and Anti-patterns

Packt
30 Sep 2013
7 min read
(For more resources related to this topic, see here.) Dependencies can be injected in a consumer class using different patterns and injecting them into a constructor is just one of them. While there are some patterns that can be followed for injecting dependencies, there are also some patterns that are recommended to be avoided, as they usually lead to undesirable results. In this article, we will examine only those patterns and antipatterns that are somehow relevant to Ninject features. Constructor Injection Constructor Injection is the most common and recommended pattern for injecting dependencies in a class. Generally this pattern should always be used as the primary injection pattern unless we have to use other ones. In this pattern, a list of all class dependencies should be introduced in the constructor. The question is what if the class has more than one constructor. Although Ninject's strategy for selecting constructor is customizable, its default behavior is selecting the constructor with more parameters, provided all of them are resolvable by Ninject. So, although in the following code the second constructor introduces more parameters, Ninject will select the first one if it cannot resolve IService2 and it will even use the default constructor if IService1 is not registered either. But if both dependencies are registered and resolvable, Ninject will select the second constructor because it has more parameters: public class Consumer { private readonly IService1 dependency1; private readonly IService2 dependency2; public Consumer(IService1 dependency1) { this.dependency1 = dependency1; } public Consumer(IService1 dependency1, IService2 dependency2) { this.dependency1 = dependency1; this.dependency2 = dependency2; } } If the preceding class had another constructor with two resolvable parameters, Ninject would throw an ActivationException exception notifying that several constructors had the same priority. There are two approaches to override this default behavior and explicitly select a constructor. The first approach is to indicate the desired constructor in a binding as follows: Bind<Consumer>().ToConstructor(arg => new Consumer(arg.Inject<IService1>())); In the preceding example, we explicitly selected the first constructor. Using the Inject<T> method that the arg argument provides, we requested Ninject to resolve IService1 in order to be injected into the specified constructor. The second method is to indicate the desired constructor using the [Inject] attribute: [Inject] public Consumer(IService1 dependency1) { this.dependency1 = dependency1; } In the preceding example, we applied the Ninject's [Inject] attribute on the first constructor to explicitly specify that we need to initialize the class by injecting dependencies into this constructor; even though the second constructor has more parameters and the default strategy of Ninject would be to select the second one. Note that applying this attribute on more than one constructor will result in the ActivationException. Ninject is highly customizable and it is even possible to substitute the default [Inject] attribute with another one, so that we don't need to add reference to the Ninject library from our consumer classes just because of an attribute: kernel.Settings.Set("InjectAttribute",typeof(MyAttribute)); Initializer methods and properties Apart from constructor injection, Ninject supports the injection of dependencies using initializer methods and property setters. We can specify as many methods and properties as required using the [Inject] attribute to inject dependencies. Although the dependencies will be injected to them as soon as the class is constructed, it is not possible to predict in which order they will receive their dependencies. The following code shows how to specify a property for injection: [Inject]public IService Service{ get { return dependency; } set { dependency = value; }} Here is an example of injecting dependencies using an injector method: [Inject]public void Setup(IService dependency){ this.dependency = dependency;} Note that only public members and constructors will be injected and even the internals will be ignored unless Ninject is configured to inject nonpublic members. In Constructor Injection, the constructor is a single point where we can consume all of the dependencies as soon as the class is activated. But when we use initializer methods the dependencies will be injected via multiple points in an unpredictable order, so we cannot decide in which method all of the dependencies will be ready to consume. In order to solve this problem, Ninject offers the IInitializable interface. This interface has an Initialize method which will be called once all of the dependencies have been injected: public class Consumer:IInitializable{ private IService1 dependency1; private IService2 dependency2; [Inject] public IService Service1 { get { return dependency1; } set { dependency1 = value; } } [Inject] public IService Service2 { get { return dependency2; } set { dependency2 = value; } } public void Initialize() { // Consume all dependencies here }} Although Ninject supports injection using properties and methods, Constructor Injection should be the superior approach. First of all, Constructor Injection makes the class more reusable, because a list of all class dependencies are visible, while in the initializer property or method the user of the class should investigate all of the class members or go through the class documentations (if any), to discover its dependencies. Initialization of the class is easier while using Constructor Injection because all the dependencies get injected at the same time and we can easily consume them at the same place where the constructor initializes the class. As we have seen in the preceding examples the only case where the backing fields could be readonly was in the Constructor Injection scenario. As the readonly fields are initializable only in the constructor, we need to make them writable to be able to use initializer methods and properties. This can lead to potential mutation of backing fields. Service Locator Service Locator is a design pattern introduced by Martin Fowler regarding which there have been some controversies. Although it can be useful in particular circumstances, it is generally considered as an antipattern and preferably should be avoided. Ninject can easily be misused as a Service Locator if we are not familiar to this pattern. The following example demonstrates misusing the Ninject kernel as a Service Locator rather than a DI container: public class Consumer{ public void Consume() { var kernel = new StandardKernel(); var depenency1 = kernel.Get<IService1>(); var depenency2 = kernel.Get<IService2>(); ... }} There are two significant downsides with the preceding code. The first one is that although we are using a DI container, we are not at all implementing DI. The class is tied to the Ninject kernel while it is not really a dependency of this class. This class and all of its prospective consumers will always have to drag their unnecessary dependency on the kernel object and Ninject library. On the other hand, the real dependencies of class (IService1 and IService2) are invisible from the consumers, and this reduces its reusability. Even if we change the design of this class to the following one, the problems still exist: public class Consumer{ private readonly IKernel kernel; public Consumer(IKernel kernel) { this.kernel = kernel; } public void Consume() { var depenency1 = kernel.Get<IService1>(); var depenency2 = kernel.Get<IService2>(); ... }} The preceding class still depends on the Ninject library while it doesn't have to and its actual dependencies are still invisible to its consumers. It can easily be refactored using the Constructor Injection pattern: public Consumer(IService1 dependency1, IService2 dependency2){ this.dependency1 = dependency1; this.dependency2 = dependency2;} Summary In this article we studied the most common DI patterns and anti-patterns related to Ninject. Resources for Article: Further resources on this subject: Introduction to JBoss Clustering [Article] Configuring Clusters in GlassFish [Article] Designing Secure Java EE Applications in GlassFish [Article]
Read more
  • 0
  • 0
  • 8336
article-image-using-pvr-raspbmc
Packt
18 Feb 2013
9 min read
Save for later

Using PVR with Raspbmc

Packt
18 Feb 2013
9 min read
(For more resources related to this topic, see here.) What is PVR? Personal Video Recording (PVR), with a TV tuner, allows you to record as well as watch Live TV. Recordings can be scheduled manually based on a time, or with the help of the TV guide, which can be downloaded from the TV provider (by satellite/aerial or cable), or from a content information provider, such as Radio Times via the Internet. Not only does PVR allow you to watch Live TV, but on capable backends (we'll look at what a backend is in a moment), it allows you to rewind and pause live TV. A single tuner allows you to tune into one channel at once, while two tuners would allow you to tune into two. As such, it's important to note that the capabilities listed earlier are not mutually exclusive, that is, with enough tuners it is possible to record one channel while watching another. This may, depending on the software you use as a backend, be possible on one tuner, if the two channels are on the same multiplexer. Raspbmc's role in PVR Raspbmc can function as both a PVR backend and a frontend. For PVR support in XBMC, it is necessary to have both a backend and one or more frontends. Let's see what a backend and frontend is: Backend: A backend is the part that tunes the channel, records your scheduled programs, and serves those channels and recorded television to the frontends. One backend can serve multiple frontends, if it is sufficiently powerful enough and there are enough tuners available. Frontend: A frontend is the part that receives content from the backend and plays back live television and recorded programs to the user. In the case of Raspbmc, XBMC serves as the frontend and allows us to play back the content. Multiple frontends can connect to one or more backends. This means that we can have several installations of Raspbmc play broadcast content from even a single tuner. As we've now learned, Raspbmc has a built-in PVR frontend in the form of XBMC. However, it also has a built-in backend. This backend is TVHeadend, and we'll look at getting that up and running shortly. Standalone backend versus built-in backend There are cases when it is more favorable to use an external, or standalone, backend rather than the one that ships with Raspbmc itself. Outlined as follows is a comparison: Standalone backend Raspbmc backend (TVHeadend) A better choice if you do not find TVHeadend feature-rich or prefer another backend. If you only intend to have one frontend, it makes sense to run everything off the same device, rather than relying on an external system. If you have a pre-existing backend, it is easier to configure Raspbmc to use that, rather than reconfiguring it completely. The process may be simplified for you as, generally, one can just connect their device and it will be detected in TVHeadend. If you are planning on having multiple frontends, it is more sensible to have a standalone backend. This is to ensure that the computer has enough horsepower, and also, you can serve from the same computer you are serving files from, and thus, only need one device on, rather than two (the streaming machine and the Pi). Raspbmc's auto-update system covers the backend that is included as well. This means you will always have a reliable and stable version of TVHeadend bundled with Raspbmc and you need not worry about having to update it to get new features. If you need to use a PCI or PCI expressbased tuner, you will need to use an external backend due to limitations of the Pi's connectivity. Better for wireless media centers. If you have low bandwidth throughput, then running the tuner locally on the Raspberry Pi makes more sense as it does not rely on any transfers between the network (unless using HDHomeRun). Setting up PVR We will now look at how to set up a PVR. This will include configuring the backend as well as getting it running in XBMC. An external backend The purpose of this title is to focus on the Raspberry Pi, and as there is a great variety of PVR software available, it would be implausible to cover the many options. If you are planning on using an external backend, it is recommended that you thoroughly search for information on the Internet. There are even books for popular and comprehensive PVR packages, such as MythTV. TVHeadend was chosen for Raspbmc because it is lightweight and easy to manage. Raspbmc's XBMC build will support the following backends at the time of writing: MythTV TVHeadend ForTheRecord/Argus TV MediaPortal Njoy N7 NextPVR VU+/Enigma DVBViewer VDR Setting up TVHeadend in Raspbmc It should be noted that not all TV tuners will work on your device. Due to the fact that the list changes frequently, it is not possible to list here the devices that work on the Raspberry Pi. However, the most popular tuners used with Raspbmc are AF90015 based. HDHomerun tuners by SilliconDust are supported as well (note that these tuners do not connect to your Pi directly, but are accessed through the network). With the right kernel modules, TVHeadend can support DVB-T (digital terrestrial), DVB-S (satellite), and DVB-C (cable) based tuners. By default, the TVHeadend service is disabled in Raspbmc. We'll need to enable it as follows: Go to Raspbmc Settings—we did this before by selecting it from the Programs menu. Under the System Configuration tab, check the TVHeadend server radio button found under the Service Management category. Click on OK to save your settings. Now that the TVHeadend is running, we can now access its management page by going to http://192.168.1.5:9981. You should substitute the preceding IP address, 192.168.1.5, with the actual IP address of the Raspberry Pi. You will be greeted with an interface much akin to the following screenshot: In the preceding screenshot we see that there are three main tabs available. They are as follows: Electronic Program Guide: This shows us what is being broadcast on each channel. It is empty in the preceding screenshot because we've not scanned and added any channels. Digital Video Recorder: This will allow you to schedule recordings of TV channels as well as use the Automatic recorder functionality, which can allow you to create powerful rules for automatic recording. You can also schedule recordings in XBMC; however, doing so via the web interface is probably more flexible. Configuration: This is where you can configure the EPG source, choose where recordings are saved, manage access to the backend, and manage tuners. The Electronic Program Guide and Digital Video Recorder tabs are intuitive and simple so we will instead look at the Configuration section. Our first step in configuring a tuner is to head over to TV Adapters: As shown in the preceding screenshot, TV tuners should automatically be detected and selectable in the drop-down menu (highlighted here). On the right, a box entitled Adapter Configuration can be used for adjusting the tuner's parameters. Now, we need to select the Add DVB Network by location option. The following dialog box will appear: Once we have defined the region we are in, TVHeadend will automatically begin scanning for new services on the correct frequencies. These services can be mapped to channels by selecting the Map DVB services to channels button as shown earlier. We are now ready to connect to the backend in XBMC. Connecting to our backend in XBMC Regardless of whether we have used Raspbmc's built-in backend or an external one, the process for connecting to it in XBMC is very much the same. We need to do the following: In XBMC, go to System | Settings | Add-ons | Disabled Add-ons | PVR clients. You will now see the following screenshot: Select the type of backend that you would like to connect to. You will then see a dialog allowing you to configure or enable the add-on. Select Configure and fill out the necessary connection details. Note that if you are connecting to the Raspbmc built-in backend, select the TVHeadend client to configure. The default settings will suffice: Click on OK to save these settings and select Enable. Note that the add-on is now located in System | Settings | Add-ons | Enabled add-ons | PVR clients rather than Disabled add-ons | PVR clients. Now, we need to go into Settings | System | Live TV. This allows you to configure a host of options related to Live TV. The most important one is the Enable Live TV option—be sure to check this box! Now, if we go back to the main menu, we'll see a Live TV option. Your channel information will be there already, although, like the instance shown as follows, it may need a bit of renaming: The following screenshot shows us a sample electronic program guide: Simply select a channel and press Play! The functionality that PVR offers is controlled in a similar manner to XBMC, so this won't be covered in this article. If you have got this far, you've done the hard part already. Summary We've now covered what PVR can do for us, the differences between a frontend and backend, and where a remote backend may be more suitable than the one Raspbmc has built in. We then covered how to connect to that backend in XBMC and play back content from it. Resources for Article : Further resources on this subject: Adding Pages, Image Gallery, and Plugins to a WordPress Blog [Article] Building a CRUD Application with the ZK Framework [Article] Playback Audio with Video and Create a Media Playback Component Using JavaFX [Article]
Read more
  • 0
  • 0
  • 8329

article-image-organizing-backbone-applications-structure-optimize-and-deploy
Packt
21 Jan 2014
9 min read
Save for later

Organizing Backbone Applications - Structure, Optimize, and Deploy

Packt
21 Jan 2014
9 min read
(For more resources related to this topic, see here.) Creating application architecture The essential premise at the heart of Backbone has always been to try and discover the minimal set of data-structuring (Models and Collections) and user interface (Views and URLs) primitives that are useful when building web applications with JavaScript. Jeremy Ashkenas, creator of Backbone.js, Underscore.js, and CoffeeScript As Jeremy mentioned, Backbone.js has no intention, at least in the near future, to raise its bar to provide application architecture. Backbone will continue to be a lightweight tool to produce the minimal features required for web development. So, should we blame Backbone.js for not including such functionality even though there is a huge demand for this in the developer community? Certainly not! Backbone.js only yields the set of components that are necessary to create the backbone of an application and gives us complete freedom to build the app architecture in whichever way we want. If working on a significantly large JavaScript application, remember to dedicate sufficient time to planning the underlying architecture that makes the most sense. It's often more complex than you may initially imagine. Addy Osmani, author of Patterns For Large-Scale JavaScript Application Architecture So, as we start digging into more detail on creating an application architecture, we are not going to talk about trivial applications or something similar to a to-do-list app. Rather, we will investigate how to structure a medium- or large-level application. After discussions with a number of developers, we found that the main issue they face here is that there are several methodologies the online blog posts and tutorials offer to structure an application. While most of these tutorials talk about good practices, it becomes difficult to choose exactly one from them. Keeping that in mind, we will explore a number of steps that you should follow to make your app robust and maintainable in the long run. Managing a project directory This is the first step towards creating a solid app architecture. We have already discussed this in detail in the previous sections. If you are comfortable using another directory layout, go ahead with it. The directory structure will not matter much if the rest of your application is organized properly. Organizing code with AMD We will use RequireJS for our project. As discussed earlier, it comes with a bunch of facilities such as the following: Adding a lot of script tags in one HTML file and managing all of the dependencies on your own may work for a medium-level project, but will gradually fail for a large-level project. Such a project may have thousands of lines of code; managing a code base of that size requires small modules to be defined in each individual file. With RequireJS, you do not need to worry about how many files you have—you just know that if the standard is followed properly, it is bound to work. The global namespace is never touched and you can freely give the best names to something that matches with it the most. Debugging the RequireJS modules is a lot easier than other approaches because you know what the dependencies and path to each of them are in every module definition. You can use r.js, an optimization tool for RequireJS that minifies all the JavaScript and CSS files, to create the production-ready build. Setting up an application For a Backbone app, there must be a centralized object that will hold together all the components of the application. In a simple application, most people generally just make the main router work as the central object. But that will surely not work for a large application and you need an Application object that should work as the parent component. This object should have a method (mostly init()) that will work as the entry point to your application and initialize the main router along with the Backbone history. In addition, either your Application class should extend Backbone.Events or it should include a property that points to an instance of the Backbone.Events class. The benefit of doing this is that the app or Backbone.Events instance can act as a central event aggregator, and you can trigger application-level events on it. A very basic Application class will look like the following code snippet: // File: application.js define([ 'underscore', 'backbone', 'router' ], function (_, Backbone, Router) { // the event aggregator var PubSub = _.extend({}, Backbone.Events); var Application = function () { // Do useful stuff here } _.extend(Application.prototype, { pubsub: new PubSub(), init: function () { Backbone.history.start(); } }); return Application; }); Application is a simple class with an init() method and a PubSub instance. The init() method acts as the starting point of the application and PubSub works as the application-level event manager. You can add more functionality to the Application class, such as starting and stopping modules and adding a region manager for view layout management. It is advisable to keep this class as short as you can. Using the module pattern We often see that intermediate-level developers find it a bit confusing to initially use a module-based architecture. It can be a little difficult for them to make the transition from a simple MVC architecture to a modular MVC architecture. While the points we are discussing in this article are valid for both these architectures, we should always prefer to use a modular concept in nontrivial applications for better maintainability and organization. In the directory structure section, we saw how the module consists of a main.js file, its views, models, and collections all together. The main.js file will define the module and have different methods to manage the other components of that module. It works as the starting point of the module. A simple main.js file will look like the following code: // File: main.js define([ 'app/modules/user/views/userlist', 'app/modules/user/views/userdetails' ], function (UserList, UserDetails) { var myVar; return { initialize: function () { this.showUserList(); }, showUsersList: function () { var userList = new UserList(); userList.show(); }, showUserDetails: function (userModel) { var userDetails = new UserDetails({ model: userModel }); userDetails.show(); } }; }); As you can see, the responsibility of this file is to initiate the module and manage the components of that module. We have to make sure that it handles only parent-level tasks; it shouldn't contain a method that one of its views should ideally have. The concept is not very complex, but you need to set it up properly in order to use it for a large application. You can even go for an existing app and module setup and integrate it with your Backbone app. For instance, Marionette provides an application infrastructure for Backbone apps. You can use its inbuilt Application and Module classes to structure your application. It also provides a general-purpose Controller class—something that doesn't come with the Backbone library but can be used as a mediator to provide generic methods and work as a common medium among the modules. You can also use AuraJS (https://github.com/aurajs/aura), a framework-agonistic event-driven architecture developed by Addy Osmani (http://addyosmani.com) and many others; it works quite well with Backbone.js. A thorough discussion on AuraJS is beyond the scope of this book, but you can grab a lot of useful information about it from its documentation and examples (https://github.com/aurajs/todomvc). It is an excellent boilerplate tool that gives your app a kick-start and we highly recommend it, especially if you are not using the Marionette application infrastructure. The following are a few benefits of using AuraJS ; they may help you choose this framework for your application: AuraJS is framework-agnostic. Though it works great with Backbone.js, you can use it for your JavaScript module architecture even if you aren't using Backbone.js. It utilizes the module pattern, application-level and module-level communication using the facade (sandbox) and mediator patterns. It abstracts away the utility libraries that you use (such as templating and DOM manipulation) so you can swap alternatives anytime you want. Managing objects and module communication One of the most important ways to keep the application code maintainable is to reduce the tight coupling between modules and objects. If you are following the module pattern, you should never let one module communicate with another directly. Loose coupling adds a level of restriction in your code, and a change in one module will never enforce a change in the rest of the application. Moreover, it lets you re-use the same modules elsewhere. But how can we communicate if there is no direct relationship? The two important patterns we use in this case are the observer and mediator patterns. Using the observer/PubSub pattern The PubSub pattern is nothing but the event dispatcher. It works as a messaging channel between the object (publisher) that fires the event and another object (subscriber) that receives the notification. We mentioned earlier that we can have an application-level event aggregator as a property of the Application object. This event aggregator can work as the common channel via which the other modules can communicate, and that too without interacting directly. Even at the module-level, you may need a common event dispatcher only for that module; the views, models, and collections of that module can use it to communicate with each other. However, publishing too many events via a dispatcher sometimes makes it difficult to manage them and you must be careful enough to understand which events you should publish via a generic dispatcher and which ones you should fire on a certain component only. Anyhow, this pattern is one of the best tools to design a decoupled system, and you should always have one ready for use in your module-based application. Summary This article dealt with one of the most important topics of Backbone.js-based application development. At the framework level, learning Backbone is quite easy and developers get a complete grasp over it in a very short period of time. Resources for Article: Further resources on this subject: Building an app using Backbone.js [article] Testing Backbone.js Application [article] Understanding Backbone [article]
Read more
  • 0
  • 0
  • 8321

article-image-digging-windows-azure-diagnostics
Packt
11 Aug 2011
14 min read
Save for later

Digging into Windows Azure Diagnostics

Packt
11 Aug 2011
14 min read
Diagnostic data can be used to identify problems with a hosted service. The ability to view the data from several sources and across different instances eases the task of identifying a problem. Diagnostic data can be used to identify when service capacity is either too high or too low for the expected workload. This can guide capacity decisions such as whether to scale up or down the number of instances. The configuration of Windows Azure Diagnostics is performed at the instance level. The code to do that configuration is at the role level, but the diagnostics configuration for each instance is stored in individual blobs in a container named wad-control-container located in the storage service account configured for Windows Azure Diagnostics. Read more: Windows Azure Diagnostics: Initializing the Configuration and Using a Configuration File There is no need for application data and diagnostics data to be located in the same storage service account. Indeed, a best practice from both security and performance perspectives would be to host application data and diagnostic data in separate storage service accounts. The configuration of Windows Azure Diagnostics is centered on the concept of data buffers with each data buffer representing a specific type of diagnostic information. Some of the data buffers have associated data sources which represent a further refining of the data captured and persisted. For example, the performance counter data buffer has individual data sources for each configured performance counter. Windows Azure Diagnostics supports record-based data buffers that are persisted to Windows Azure tables and file-based data buffers that are persisted to Windows Azure blobs. In the Accessing data persisted to Windows Azure Storage recipe we see that we can access the diagnostic data in the same way we access other data in Windows Azure storage. Windows Azure Diagnostics supports the following record-based data buffers: Windows Azure basic logs Performance counters Windows Event Logs Windows Azure Diagnostic infrastructure logs The Windows Azure basic logs data buffer captures information written to a Windows Azure trace listener. In the Using the Windows Azure Diagnostics trace listener recipe, we see how to configure and use the basic logs data buffer. The performance counters data buffer captures the data of any configured performance counters. The Windows Event Logs data buffer captures the events form any configured Windows Event Log. The Windows Azure Diagnostic infrastructure logs data buffer captures diagnostic data produced by the Windows Azure Diagnostics process. Windows Azure Diagnostics supports the following file-based data sources for the Directories data buffer: IIS logs IIS Failed Request Logs Crash dumps Custom directories The Directories data buffer copies new files in a specified directory to blobs in a specified container in the Windows Azure Blob Service. The data captured by IIS Logs, IIS Failed Request Logs, and crash dumps is self-evident. With the custom directories data source, Windows Azure Diagnostics supports the association of any directory on the instance with a specified container in Windows Azure storage. This allows for the coherent integration of third-party logs into Windows Azure Diagnostics. We see how to do this in the Implementing custom logging recipe. The implementation of Windows Azure Diagnostics was changed in Windows Azure SDK v1.3 and it is now one of the pluggable modules that have to be explicitly imported into a role in the service definition file. As Windows Azure Diagnostics persists both its configuration and data to Windows Azure storage, it is necessary to specify a storage service account for diagnostics in the service configuration file. The default configuration for Windows Azure Diagnostics captures some data but does not persist it. Consequently, the diagnostics configuration should be modified at role startup. In the Initializing the configuration of Windows Azure Diagnostics recipe, we see how to do this programmatically, which is the normal way to do it. In the Using a configuration file with Windows Azure Diagnostics recipe, we see how to use a configuration file to do this, which is necessary in a VM role. In normal use, diagnostics data is captured all the time and is then persisted to the storage service according to some schedule. In the event of a problem, it may be necessary to persist diagnostics data before the next scheduled transfer time. We see how to do this in the Performing an on-demand transfer recipe. Both Microsoft and Cerebrata have released PowerShell cmdlets that facilitate the remote administration of Windows Azure Diagnostics. We see how to do this in the Using the Windows Azure Platform PowerShell cmdlets to configure Windows Azure Diagnostics recipe. There are times, especially early in the development process, when non-intrusive diagnostics monitoring is not sufficient. In the Using IntelliTrace to Diagnose Problems with a Hosted Service recipe, we see the benefits of intrusive monitoring of a Windows Azure role instance. Using the Windows Azure Diagnostics trace listener Windows Azure Diagnostics supports the use of Trace to log messages. The Windows Azure SDK provides the DiagnosticMonitorTraceListener trace listener to capture the messages. The Windows Azure Diagnostics basic logs data buffer is used to configure their persistence to the Windows Azure Table Service. The trace listener must be added to the Listeners collection for the Windows Azure hosted service. This is typically done through configuration in the appropriate app.config or web.config file, but it can also be done in code. When it creates a worker or web role, the Windows Azure tooling for Visual Studio adds the DiagnosticMonitorTraceListener to the list of trace listeners specified in the Configuration section of the relevant configuration file. Methods of the System.Diagnostics.Trace class can be used to write error, warning and informational messages. When persisting the messages to the storage service, the Diagnostics Agent can filter the messages if a LogLevel filter is configured for the BasicLogsBufferConfiguration. The Compute Emulator in the development environment adds an additional trace listener, so that trace messages can be displayed in the Compute Emulator UI. In this recipe, we will learn how to trace messages using the Windows Azure trace listener. How to do it... We are going to see how to use the trace listener provided in the Windows Azure SDK to trace messages and persist them to the storage service. We do this as follows: Ensure that the DiagnosticMonitorTraceListener has been added to the appropriate configuration file: app.config for a worker role and web.config for a web role. If necessary, add the following to the Configuration section of app.config or web.config file: <system.diagnostics> <trace> <listeners> <add type="Microsoft.WindowsAzure.Diagnostics. DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="AzureDiagnostics"> <filter type="" /> </add> </listeners> </trace> </system.diagnostics> Use the following to write an informational message: System.Diagnostics.Trace.TraceInformation("Information"); Use the following to write a warning message: System.Diagnostics.Trace.Warning("Warning "); Use the following to write an error message: System.Diagnostics.Trace.TraceError("Error"); Ensure that the DiagnosticMonitorConfiguration.Logs property is configured with an appropriate ScheduledTransferPeriod and ScheduledTransferLogLevelFilter when DiagnosticMonitor.Start() is invoked. How it works... In steps 1 and 2, we ensure that the DiagnosticMonitorTraceListener is added to the collection of trace listeners for the web role or worker role. In steps 3 through 5, we see how to write messages to the trace listener. In step 6, we ensure that the Diagnostic Agent has been configured to persist the messages to the storage service. Note that they can also be persisted through an on-demand transfer. This configuration is described in the recipe Initializing the configuration of Windows Azure Diagnostics. There's more... The Windows Azure SDK v1.3 introduced full IIS in place of the hosted web core used previously for web roles. With full IIS, the web role entry point and IIS are hosted in separate processes. Consequently, the trace listener must be configured separately for each process. The configuration using web.config configures the trace listener for IIS, not the web role entry point. Note that Windows Azure Diagnostics needs to be configured only once in each role, even though the trace listener is configured separately in both the web role entry point and in IIS. The web role entry point runs under a process named WaIISHost.exe. Consequently, one solution is to create a special configuration file for this process named WaIISHost.exe.config and add the trace listener configuration to it. A more convenient solution is to add the DiagnosticMonitorTraceListener trace listener programmatically to the list of trace listeners for the web role entry point. The following demonstrates an overridden OnStart() method in a web role entry point modified to add the trace listener and write an informational message: public override bool OnStart() { System.Diagnostics.Trace.Listeners.Add(new Microsoft. WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener()); System.Diagnostics.Trace.AutoFlush = true; System.Diagnostics.Trace.TraceInformation("Information"); return base.OnStart(); } The AutoFlush property is set to true to indicate that messages should be flushed through the trace listener as soon as they are written. Performing an on-demand transfer The Windows Azure Diagnostics configuration file specifies a schedule in which the various data buffers are persisted to the Windows Azure Storage Service. The on-demand transfer capability in Windows Azure Diagnostics allows a transfer to be requested outside this schedule. This is useful if a problem occurs with an instance and it becomes necessary to look at the captured logs before the next scheduled transfer. An on-demand transfer is requested for a specific data buffer in a specific instance. This request is inserted into the diagnostics configuration for the instance stored in a blob in wad-control-container. This is an asynchronous operation whose completion is indicated by the insertion of a message in a specified notification queue. The on-demand transfer is configured using an OnDemandTransferOptions instance that specifies the DateTime range for the transfer, a LogLevelFilter that filters the data to be transferred, and the name of the notification queue. The RoleInstanceDiagnosticeManager.BeginOnDemandTransfer() method is used to request the on-demand transfer with the configured options for the specified data buffer. Following the completion of an on-demand transfer, the request must be removed from the diagnostics configuration for the instance by using the RoleInstanceDiagnosticManager.EndOnDemandTransfer() method. The completion message in the notification queue should also be removed. The GetActiveTransfers() and CancelOnDemandTransfers() methods of the RoleInstanceDiagnosticManager class can be used to enumerate and cancel active on-demand transfers. Note that it is not possible to modify the diagnostics configuration for the instance if there is a current request for an on-demand transfer, even if the transfer has completed. Note that requesting an on-demand transfer does not require a direct connection with the hosted service. The request merely modifies the diagnostic configuration for the instance. This change is then picked up when the Diagnostic Agent on the instance next polls the diagnostic configuration for the instance. The default value for this polling interval is 1 minute. This means that a request for an on-demand transfer needs to be authenticated only against the storage service account containing the diagnostic configuration for the hosted service. In this recipe, we will learn how to request an on-demand transfer and clean up after it completes. How to do it... We are going to see how to request an on-demand transfer and clean up after it completes. We do this as follows: Use Visual Studio to create a WPF project. Add the following assembly references to the project: Microsoft.WindowsAzure.Diagnostics.dll Microsoft.WindowsAzure.ServiceRuntime.dll Microsoft.WindowsAzure.StorageClient.dll System.configuration.dll Add a class named OnDemandTransferExample to the project. Add the following using statements to the class: using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.Diagnostics; using Microsoft.WindowsAzure.Diagnostics.Management; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.StorageClient; using System.Configuration; Add the following private member to the class: String wadNotificationQueueName = "wad-transfer-queue"; Add the following method, requesting an on-demand transfer, to the class: public void RequestOnDemandTransfer( String deploymentId, String roleName, String roleInstanceId) { CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse( ConfigurationManager.AppSettings[ "DiagnosticsConnectionString"]); OnDemandTransferOptions onDemandTransferOptions = new OnDemandTransferOptions() { From = DateTime.UtcNow.AddHours(-1), To = DateTime.UtcNow, LogLevelFilter = Microsoft.WindowsAzure.Diagnostics.LogLevel.Verbose, NotificationQueueName = wadNotificationQueueName }; RoleInstanceDiagnosticManager ridm = cloudStorageAccount.CreateRoleInstanceDiagnosticManager( deploymentId, roleName, roleInstanceId); IDictionary<DataBufferName, OnDemandTransferInfo> activeTransfers = ridm.GetActiveTransfers(); if (activeTransfers.Count == 0) { Guid onDemandTransferId = ridm.BeginOnDemandTransfer( DataBufferName.PerformanceCounters, onDemandTransferOptions); } } Add the following method, cleaning up after an on-demand transfer, to the class: public void CleanupOnDemandTransfers() { CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse( ConfigurationManager.AppSettings[ "DiagnosticsConnectionString"]); CloudQueueClient cloudQueueClient = cloudStorageAccount.CreateCloudQueueClient(); CloudQueue cloudQueue = cloudQueueClient.GetQueueReference( wadNotificationQueueName); CloudQueueMessage cloudQueueMessage; while ((cloudQueueMessage = cloudQueue.GetMessage()) != null) { OnDemandTransferInfo onDemandTransferInfo = OnDemandTransferInfo.FromQueueMessage( cloudQueueMessage); String deploymentId = onDemandTransferInfo.DeploymentId; String roleName = onDemandTransferInfo.RoleName; String roleInstanceId = onDemandTransferInfo.RoleInstanceId; Guid requestId = onDemandTransferInfo.RequestId; RoleInstanceDiagnosticManager ridm = cloudStorageAccount.CreateRoleInstanceDiagnosticManager( deploymentId, roleName, roleInstanceId); Boolean result = ridm.EndOnDemandTransfer(requestId); cloudQueue.DeleteMessage(cloudQueueMessage); } } Add the following Grid declaration to the Window element of MainWindow.xaml: <Grid> <Label Content="DeploymentId:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="30,60,0,0" Name="label1" /> <Label Content="Role name:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="30,110,0,0" Name="label2" /> <Label Content="Instance Id:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="30,160,0,0" Name="label3" /> <TextBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,60,0,0" Name="DeploymentId" Height="23" Width="120" Text="24447326eed3475ca58d01c223efb778" /> <TextBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,110,0,0" Width="120" Name="RoleName" Text="WebRole1" /> <TextBox Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,160,0,0" Width="120" Name="InstanceId" Text="WebRole1_IN_0" /> <Button Content="Request On-Demand Transfer" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="60,220,0,0" Width="175" Name="RequestTransfer" Click="RequestTransfer_Click" /> <Button Content="Cleanup On-Demand Transfers" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="300,220,0,0" Width="175" Name="CleanupTransfers" Click="CleanupTransfers_Click" /> </Grid> Add the following event handler to MainWindow.xaml.cs: private void RequestTransfer_Click( object sender, RoutedEventArgs e) { String deploymentId = DeploymentId.Text; String roleName = RoleName.Text; String roleInstanceId = InstanceId.Text; OnDemandTransferExample example = new OnDemandTransferExample(); example.RequestOnDemandTransfer( deploymentId, roleName, roleInstanceId); } Add the following event handler to MainWindow.xaml.cs: private void CleanupTransfers_Click( object sender, RoutedEventArgs e) { OnDemandTransferExample example = new OnDemandTransferExample(); example.CleanupOnDemandTransfers(); } Add the following to the configuration element of app.config: <appSettings> <add key="DiagnosticsConnectionString" value="DefaultEndpointsProtocol=https;AccountName={ ACCOUNT_NAME};AccountKey={ACCESS_KEY}"/> </appSettings> How it works... We create a WPF project in step 1 and add the required assembly references in step 2. We set up the OnDemandTransferExample class in steps 3 and 4. We add a private member to hold the name of the Windows Azure Diagnostics notification queue in step 5. In step 6, we add a method requesting an on-demand transfer. We create an OnDemandTransferOptions object configuring an on-demand transfer for data captured in the last hour. We provide the name of the notification queue Windows Azure Diagnostics inserts a message indicating the completion of the transfer. We use the deployment information captured in the UI to create a RoleInstanceDiagnosticManager instance. If there are no active on-demand transfers, then we request an on-demand transfer for the performance counters data buffer. In step 7, we add a method cleaning up after an on-demand transfer. We create a CloudStorageAccount object that we use to create the CloudQueueClient object with which we access to the notification queue. We then retrieve the transfer-completion messages in the notification queue. For each transfer-completion message found, we create an OnDemandTransferInfo object describing the deploymentID, roleName, instanceId, and requestId of a completed on-demand transfer. We use the requestId to end the transfer and remove it from the diagnostics configuration for the instance allowing on-demand transfers to be requested. Finally, we remove the notification message from the notification queue. In step 8, we add the UI used to capture the deployment ID, role name, and instance ID used to request the on-demand transfer. We can get this information from the Windows Azure Portal or the Compute Emulator UI. This information is not needed for cleaning up on-demand transfers, which uses the transfer-completion messages in the notification queue. In steps 9 and 10, we add the event handlers for the Request On-Demand Transfer and Cleanup On-Demand Transfers buttons in the UI. These methods forward the requests to the methods we added in steps 6 and 7. In step 11, we add the DiagnosticsConnectionString to the app.config file. This contains the connection string used to interact with the Windows Azure Diagnostics configuration. We must replace {ACCOUNT_NAME} and {ACCESS_KEY} with the storage service account name and access key for the storage account in which the Windows Azure Diagnostics configuration is located.
Read more
  • 0
  • 0
  • 8320
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-trunks-freepbx-25
Packt
26 Oct 2009
5 min read
Save for later

Trunks in FreePBX 2.5

Packt
26 Oct 2009
5 min read
A trunk in the simplest of terms is a pathway into or out of a telephone system. A trunk connects a PBX to outside resources, such as PSTN telephone lines, or additional PBX systems to perform inter-system transfers. Trunks can be physical, such as a PRI or PSTN line, or they can be virtual by routing calls to another endpoint using Internet Protocol (IP) links. Trunk types FreePBX allows the creation of six different types of trunks as follows: Zap IAX2 SIP ENUM DUNDi Custom Zap, IAX2, and SIP trunks utilize the technologies of their namesake. These trunks have the same highlights and pitfalls that extensions and devices using the same technology do. Zap trunks require physical hardware cards for incoming lines to plug into. SIP trunks are the most widely adopted and compatible, but have difficulties traversing firewalls. IAX2 trunks are able to traverse most firewalls easily, but are limited to adoption mainly on Asterisk-based systems. In terms of VoIP, ENUM(E.164 NUmber Mapping) is a method for unifying E.164 (the international telecommunication numbering plan) with VoIP routing. The ENUM system can be considered very similar to the way that the Internet DNS system works. In the DNS system, when a domain name is looked up an IP address is returned. The IP address allows a PC to traverse the Internet and find the server that belongs to that IP address. The ENUM system provides VoIP routes back when queried for a phone number. The route that is returned is usually a SIP or IAX2 route. An ENUM trunk allows FreePBX to send the dialed phone number to the publice164.orgENUM server. If the called party has listed their phone number in the e164.org directory, a VoIP route will be returned and the call will be connected using that route. A VoIP route contains the VoIP protocol, the server name or IP address, the port, and the extension to use in order to contact the dialed phone number. For example, a SIP route for dialing the number 555-555-1234 might appear as SIP:1234@pbx.example.com:5060. This is advantageous in several ways. It is important to note that indirect routes to another telephony system are often costly. Calling a PSTN telephone number typically requires that call to route through a third-party provider's phone lines and switching equipment (a service they will happily charge for). If a number is listed in the ENUM directory, the returned route will bridge the call directly to the called party (or their provider), bypassing the cost of routing through a third party. ENUM also benefits the called party, allowing them to redirect inbound calls to wherever they would like. Service disruptions that would otherwise render a particular phone number useless can be bypassed by directing the phone number to a different VoIP route in the ENUM system. More information on ENUM can be found at the following web sites: The ENUM home page The e164.org home page: The Internet Engineering Task Force ENUM charter DUNDi (Distributed Universal Number Discovery) is a routing protocol technology similar to ENUM. In order to query another Asterisk system using DUNDi, that system must be "peered" with your own Asterisk system. Peering requires generating and exchanging key files with the other peer. DUNDi is a decentralized way of accomplishing ENUM-style lookups. By peering with one system you are effectively peering with any other system that your peer is connected to. If system A peers with system B, and system B peers with system C, then system C will be able to see the routes provided by system A. In peer-topeer fashion, system B will simply pass the request along to system A, even though system C has no direct connection to system A. DUNDi is not limited to E.164 numbering schemes like ENUM and it allows a PBX to advertise individual extensions, or route patterns, instead of whole phone numbers. Therefore, it is a good candidate for distributed office setups, where a central PBX can be peered with several satellite PBX systems. The extensions on each system will be able to call one another directly without having to statically set up routes on each individual PBX. More information on DUNDi can be found at the following web sites: DUNDi home page Example DUNDi SIP configuration Example DUNDi IAX2 configuration Custom trunks work in the same fashion as custom extensions do. Any valid Asterisk Dial command can be used as a custom trunk by FreePBX. Custom trunks typically use additional VoIP protocols such as H.323 and MGCP. Setting up a new trunk Setting up a trunk in FreePBX is very similar to setting up an extension. All of the trunks share eight common setup fields, followed by fields that are specific to the technology that trunk will be using. In order to begin setting up a trunk, click on Trunks in the left side navigation menu as shown in the following screenshot: From the Add a Trunk screen, click on the name of the technology that the trunk will be using (for example, if a SIP trunk will be used, click on Add SIP Trunk) as shown in the following screenshot:
Read more
  • 0
  • 0
  • 8319
Modal Close icon
Modal Close icon