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

How-To Tutorials

7019 Articles
article-image-datagrid-api-ibm-websphere-extreme-scale-6-part-1
Packt
18 Nov 2009
19 min read
Save for later

The DataGrid API with IBM WebSphere eXtreme Scale 6: Part 1

Packt
18 Nov 2009
19 min read
In a client-server ObjectGrid interaction, local ObjectGrid instances run in the same memory process as the business application. Access to objects stored in the grid is extremely fast, and there are no network hops or routing done on ObjectGrid operations. The disadvantage with a local ObjectGrid instance is that all objects stored in the grid must fit into the heap space of one JVM. The client-server distributed ObjectGrid instances overcomes that single heap space disadvantage by combining the resources of multiple JVMs on multiple servers. These combined resources hide behind the façade of an ObjectGrid instance. The ObjectGrid instance has far more CPU, memory, and network I/O available to it than the resources available to any single client. In this article, we'll learn how to use those resources held by the ObjectGrid instance to co-locate data and business logic on a single JVM. The client-server model relies on a client pulling objects across a network from an ObjectGrid shard. The client performs some operations on those objects. Any object whose state has changed must be sent back across the network to the appropriate shard. The client-server programming model co-locates data and code by moving data to the code. The data grid programming model does the opposite by moving code to the data. Rather than dragging megabytes of objects from an ObjectGrid shard to a client, only to send it right back to the ObjectGrid, we instead send our much smaller application code to an ObjectGrid shard to operate on the data in place. The end result is the same: code and data are co-located. We now have the resources of an entire data grid available to run that code instead of one client process. What does DataGrid do for me? The DataGrid API provides encapsulation to send application-specific methods into the grid and operate directly on the objects in shards. The API consists of only five public classes. These five classes provide us with several patterns to make an ObjectGrid instance do the heavy lifting for a client application. The client application did a lot of work by operating on the objects in the grid. The client requires a network hop to get an object from the grid and performs an operation on it, persisting that the object requires another network hop to the grid. In a single client environment, the probable bottlenecks in dealing with ObjectGrid are all on the client side. A single client will not stress the resources in the ObjectGrid deployment. The client application is most likely the bottleneck. With all computers in a deployment being equal, one client application on one computer will not stress the combined resources of the grid. In a naïve application that performs single object get and put operations, our application will first notice a bottleneck due to data starvation. This is where a client cannot get the data it needs fast enough, caused by network latency. Single object get and put operations (and the corresponding Entity API calls) won't saturate a gigabit ethernet connection by any means, but the latency in making the RPC is higher than what the CPU can handle. The application works, but it's slow. A smarter application would use the ObjectMap#getAll method. This would go out to the grid and get an object for every key in the list. Instead of waiting for each individual object, the client application waits for the entire list to come over the network. While the cost of network RPC is amortized over the size of the list, the client still incurs that cost. In addition to these network latency concerns, we may not want a near-cache that eats up client-side memory. Turning off the near-cache means that every get operation is an RPC. Turning it on means that some of our JVM heap space is used to store objects, which we may not need after the first use. The fundamental problem is that our objects and client application are architecturally separated. For our application to do anything, it needs to operate on objects that exist in the grid. In the client-server model, we copy data from the server to the client. At this point, our data and code are co-located, and the application can perform some business logic with that data. This model breaks down when there are huge data sets copied between boxes. Databases co-locate data and code with stored procedures. The processing power of the stored procedure is a product of the CPU and memory resources of the computer running the database. The stored procedure is code compiled into a module and executed by the database. Within that process, the stored procedure accesses data available in the same process. ObjectGrid gives us the ability to run code in the same process that gives an object access via the DataGrid API. Unlike the database example, where the throughput and latency of getting the store procedure result is limited to the power of the server it's on, ObjectGrid's power is limited by the number of CPUs in the deployment, and it can scale out at any time. ObjectGrid co-locates our code and objects by sending serialized classes with our application code methods to primary partitions in the grid. There are two ways to do this. The first way sends the code to every primary partition in the grid. The code executes and returns a result to the client. In the second way, we supply a collection of keys to the DataGrid API. With a list of keys, ObjectGrid only sends the application code to the partitions that contain at least one object with a key in the list. This reduces the amount of container processes doing the work for our client application, and is preferred instead of making the entire grid service on one client request. Let's look at finding an object by key in the client-server distributed model. The client has a key for an object. Calling the ObjectMap#get(key) method creates some work for the client. It first needs to determine to which partition the key belongs. The partition is important because the ClientClusterContext, already obtained by the client, knows how to get to the container that holds the primary shard in one hop. We find out the partition ID (pID) for a key with the PartitionManager class: BackingMap bMap = grid.getMap("Payment");PartitionManager pm = bMap.getPartitionManager();int pId = pm.getPartition(key); After obtaining the partition ID and the host running the container process, the client performs a network hop to request the object. The object is serialized and sent back to the client, where the client performs some operation with the object. Persisting an updated object requires one more network hop to put it back in the primary shard. We can now repeat that process for every object in our multi-million object collection. On second thought, that may not be such a great idea. Instead, we'll create an agent that we send to the grid. The agent encapsulates the logic we want to perform. An AgentManager serializes the agent and sends it to each primary shard in the deployment. Once on a primary shard, the agent executes and produces a result which is sent back to the client.   Borrowing from functional programming The DataGrid API borrows the "map" and "reduce" concepts from the world of functional programming. Just so we're all on the same page, let's go over the concepts behind these two functions. Functional programming focuses more on what a program does, instead of how it does it. This is in contrast to the most imperative programming we do in the C family of languages. That's not to say we can't follow a functional programming model, it's just that we don't. Other languages, like Lisp and its descendants, make functional programming the natural thing to do. Map and reduce are commonly found in functional programming. They are known as higher-order functions because they take functions as arguments. This is similar to how we would use a function pointer in C, or an anonymous inner class in Java, to implement callbacks. Though the focus is on what to do, at some point, we need to tell our program how to do it. We do this with the function passed as an argument to map or reduce. Let's look at a simple example in Ruby, which has both functional and imperative programming influences: >> numbers = [0,1,2,3,4,5,6,7,8,9]>> numbers.map { |number| number * 2 }=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] We assign an array of numbers 0-9 to the variable numbers. The array has a method called map that we call in the second line. Map is a higher-order function and accepts a function as its argument. The Array#map method calls the passed-in function for each element in the array. It passes the element in the variable numbers. In this way, we return a new array that contains the results of each call to our function which performs number * 2. Let's look at the reduce method. In Ruby, reduce is called inject but the concept is the same: >> numbers = [0,1,2,3,4,5,6,7,8,9]>> numbers.inject(0) { |sum, number| sum = sum + number }=> 45 The inject (read as reduce) method takes a function that performs a running total on the numbers in the array. Instead of an array as our return type, we only get one number. The reduce operation returns a single result for an entire data set. The map operation returns a new set based on running the original set through a given function. These concepts are relevant in the data grid environment because we work with large data sets where we frequently need to work with large segments of data. Pulling raw data across the network, and operating over the data set on one client, are both too slow. Map and reduce helps us by using the remote CPU resources of the grid to cut down on the data sent across the network and the CPU power required on the client. This help comes from writing methods that work like map and reduce and sending them to our objects in the grid. java.util.M  ap, BackingMaps, ObjectMaps, HashMaps, like we need one more use for the word "map". We just saw the functional origin of the map concept. Let's take a look at a Java implementation. Map implements an algorithm that performs an operation on each element in a collection and returns a new collection of results: public Collection doubleOddInts(Collection c) {Collection results = new HashSet();Iterator iter = c.iterator();while (iter.hasNext()) {int i = (Integer)iter.next();if (i % 2 == 0) {[ 172 ]results.add(i);} else {results.add(i*2);}}return results;} Our needs go beyond performing a map function over an array. In order to be useful in a DataGrid environment, the map function must operate on a distributed collection of objects in an ObjectGrid instance. The DataGrid API supports this by giving us the MapGridAgent interface. A business logic class implements the two methods in MapGridAgent to encapsulate the code we intend to run in the grid. Classes that implement MapGridAgent must implement two methods, namely, MapGridAgent#process(Session session, ObjectMap map, Object key) and MapGridAgent#processAllEntries(Session session, ObjectMap map). Let's implement the doubleOddInts algorithm with MapGridAgent. We first create a class that implements the MapGridAgent interface. We give this class a meaningful name that describes the map operation implemented in the process methods: public class DoubleOddIntsMapAgent implements Serializable,MapGridAgent {public Object process(Session session, ObjectMap map, Object key){int i = (Integer)map.get(key);if (i % 2 == 0) {return i;} else {return i*2;}}public Map processAllEntries(Session session, ObjectMap map) {// nothing to do here for now!}} The map function itself is called by our client code. The process (session, map, key) method performs the how in the map function. Because ObjectGrid gives us the what for free (the map function), we only need to implement the how part. Like the Ruby example, this process (session, map, key) method is performed for each element in a collection. The Session and ObjectMap arguments are supplied by the AgentManager based on the current session and ObjectMap that starts the map function. The key is the crucial object for a given value in the collection, and that collection is supplied by us when we run the DoubleOddIntsMapAgent. After implementing the MapGridAgent#process(session, map, key) method, the DoubleOddIntsMapAgent is ready to run. We want it to run on each shard in an ObjectGrid instance that has a key in the collection we pass to it. We do this with an instance of the AgentManager class. The AgentManager class has two methods to send a MapGridAgent to the grid: AgentManager#callMapAgent(MapGridAgent agent, Collection keys) and AgentManager#callMapAgent(MapGridAgent agent). The first method provides a set of keys for our agent to use when run on each partition. Using this method is preferable to the non-keyed version because the non-keyed version runs the code on every primary shard in the grid. The Agent Manager#callMapAgent(agent, keys) method only runs the code on primary partitions that contain at least one key in the key collection. Whenever we have the choice to use part of the grid instead of the entire grid, we should take the choice that uses only part of the grid. Whenever we use the entire grid for one operation, we limit scalability and throughput. The AgentManager serializes the DoubleOddIntsMapAgent agent and sends it to each partition that has a key in the keys collection. Once on the primary partition, the process (session, map, key) method is called for each key in the keys collection supplied to AgentManager#callMapAgent(agent, keys). This set of keys is a subset of all of the keys in the BackingMap, and likely a subset of keys in each partition. Let's create an instance of this agent and submit it to the grid: Collection numbers = new ArrayList();for(int i = 0; i < 10000; i++) {numbers.add(i);}MapGridAgent agent = new DoubleOddIntsAgent();AgentManager am = session.getMap("Integer").getAgentManager();am.callMapAgent(agent, numbers); This example assumes that we have a BackingMap of Integer for both the key and value objects. The numbers collection is a list of keys to use. Once we create the agent, we submit it to the grid with the 10,000 keys to operate on. Before running the agent, the AgentManager sorts the keys by partition. The agent only runs on partitions that have a list of keys that hash to that partition. The agent runs on each partition that has a list of keys that hash to it. In each primary partition, the DoubleOddIntsMapAgent#process(session, map, key) method is called only for the keys that map to that partition. GridAgent and Entity GridAgent works with Entity classes as well. We don't directly use key objects when working with Entity objects. The Entity API hides the key/value implementation from us to make working with Entity objects easier than working with the ObjectMap API. The method definition for MapGridAgent#process(session, map, key) normally expects an object to be used as a key for an ObjectMap. We can still find the value object by converting key and value objects to their Tuple representations, but the DataGrid API makes it much easier for us. Instead of passing a key to the process method, we can convince the primary shard to pass us the Entity object itself, rather than a key using the EntityAgentMixin interface. EntityAgentMixin has one method, namely, EntityAgentMixin#getClassForEntity(). The implementation of this method should return the class object of the Entity. DataGrid needs this method defined in the grid agent implementation so it can provide the Entity object itself, rather than its key to the MapGridAgent#process(session, map, key) method. Let's assume that we have an Entity MyInteger that acts as a wrapper for Integer: public class DoubleOddIntsMapAgent implements Serializable,MapGridAgent, EntityAgentMixin {public Object process(Session session, ObjectMap map, Object key){MyInteger myInt = (MyInteger)key;if (myInt.mod(2) == 0) {return myInt;} else {return myInt.multiplyBy(2);}}public Map processAllEntries(Session session, ObjectMap map) {// nothing to do here for now!}public Class getClassForEntity() {return MyInteger.class;}} Our agent now implements the EntityAgentMixin interface and the getClassForEntity() method. The key is converted to the correct class before the MapGridAgent#process(session, map, key) method is called. Instead of the Tuple key for an Entity, the process method is passed a reference to the Entity itself. Because it is passed as an object, we must cast the Entity to its defined class. There is no need to look up for the Entity in its BackingMap because it's already the Entity we want to work with. This means the collection of keys passed to AgentManager#callMapAgent(agent, keys) is a collection with all elements of the c lass returned by getClassForEntity(). GridAgent with an unknown key set We may not always know the keys for each object we want to submit to an agent. In this situation, we send an agent into the grid without a key set. The grid agent cannot call the process (session, map, key) method because we don't know which keys to use. Instead, our grid agent method relies on the Query API to narrow the number of objects in each partition we work with. The MapGridAgent interface gives us the MapGridAgent#processAllEntries(Session session, ObjectMap map) method for this situation. The MapGridAgent#processAllEntries(session, map) method lets us specify what to do when we potentially need to work with all objects in a partition. Particularly, it lets us narrow the field with a query. In the past, we used a query to find card and address objects in a local ObjectGrid instance. This was fine for local instances with only one partition. The real power of the Query API is revealed when used with the DataGrid API. Query does not work across partitions when called from an ObjectGrid client in a distributed environment. It works with just one partition. In a distributed deployment, where we use the DataGrid API, a grid agent instance runs on one partition. Each partition has an instance of the grid agent running in it and each agent can see the objects in its partition. If we have 20 partitions, then we have 20 grid agents running, one in each partition. Because we're working with a single partition in each grid agent, we use the Query API to determine which objects are of interest to the business logic. Now that we know how to run code in the grid, the Query API is suddenly much more useful. Now, we want a query to run against just one partition. Using a query in a GridAgent is a natural fit. Each agent runs on one partition, and each query runs on that partition in the primary shard container process: public class DoubleOddIntsMapAgent implements Serializable,MapGridAgent, EntityAgentMixin {public Object process(Session session, ObjectMap map, Object key){MyInteger myInt = (MyInteger)key;if (myInt.mod(2) == 0) {return myInt;} else {return myInt.multiplyBy(2);}}public Map processAllEntries(Session session, ObjectMap map) {EntityManager em = session.getEntityManager();Query q = em.createQuery("select m from MyInteger m " +"where m.integer > 0 " +"and m.integer < 10000");Iterator iter = q.getResultIterator();Map<MyInteger, Integer> results =new HashMap<MyInteger, Integer)();while (iter.hasNext()) {MyInteger mi = (MyInteger)iter.next();results.put(mi, (Integer)process(session, map, mi));}return results;}public Class getClassForEntity() {return MyInteger.class;}} The MapGridAgent#processAllEntries(session, map) method generally follows the same pattern when implemented: Narrow the scope of objects in the partition. This is important in the MapGridAgent because it returns a result for every object it processes. This can result in hundreds of megabytes of objects sent back to a client from every partition for an indiscriminate query. Create a map to hold the results of each process operation. This map is keyed with the key object, or the value object, when using ObjectMap. The client application can perform its own gets if the keys are returned. Otherwise, it works directly with the value objects. We can also return a map of key/value objects. The map is keyed with the Entity class itself when using Entity. Iterate over the query results calling MapGridAgent#process(session, map, key) for each result. Calling the process method is required here since we didn't pass a collection of keys to the AgentManager#callMapAgent(agent) method. The key set is unknown before the agent runs. The agent finds all objects in a partition that meet our criteria for processing, and then we call process to get each result. Return the results. This map contains an entry for each object that meets our processing criteria in this partition. This map is merged, client-side, with the maps from every other partition where the agent ran. The merged map is the final result, and it is the return value to the AgentManager#callMapAgent(agent) method. Following the call to AgentManager#callMapAgent(agent), we have a Map that contains the combined agent results from every partition. We also split the workload between N partitions rather than performing all of the processing on the client. The ObjectGrid deployment performed our business logic because we passed the business logic to the grid rather than pulling objects out of the grid. One of the great things about this pattern is that our task on many partitions completes in about 1/Nth the amount of time it would take for one huge partition containing the same objects running on one computer. Of course, there is the overhead of the merge operation and network connections, but this is amortized over the number of primary partitions used by the agent. This is distinctly different than scaling up a database server when it needs more CPU speed for stored procedures. Instead of incurred downtime for database server migration, we simply add more containers on additional computers. The power of our grid increases as easily as starting a few more JVMs. >> Continue Reading: The DataGrid API with IBM WebSphere eXtreme Scale 6: Part 2
Read more
  • 0
  • 0
  • 1388

article-image-customizing-document-joomla-15-part-2
Packt
18 Nov 2009
3 min read
Save for later

Customizing the Document with Joomla! 1.5: Part 2

Packt
18 Nov 2009
3 min read
Creating a PDF in a component This recipe explains how to create a PDF view in a Joomla! MVC component. Adding PDF views is a relatively quick process, and it significantly improves the functionality of a component. Getting ready Like any other view format, we must create a new JView subclass to create a PDF view. This should be located in the corresponding view's folder and the file should be named view.pdf.php. For example, for the myview view in the mycomponent component, we create the components/com_mycomponent/views/myview/view.pdf.php file, in which we place the MycomponentViewMyview class, which extends JView. How to do it... The first thing we do is override the display() method in order to change the PDF document. We modify the document using the mutator methods. The first method changes the document title, this is the title normally shown in the title bar of the PDF viewer. $document->setTitle($title); The next method changes the filename. This is especially useful if the user is likely to save the file, as this will be the default name the user is prompted to save the file as. $document->setName($filename); The next method sets the document description, sometimes referred to as the subject. This should only be a very brief description of the document. $document->setDescription($description); The next method sets the document metadata. Currently, only keywords are supported. It is possible to set other metadata, but it will not be used in the document. $document->setMetaData('keywords', $keywords); So far, all of the methods do not print anything to the body of the PDF itself. The next method adds a common header to every page. Note that the header text itself is not formatted. $document->setHeader("My PDF Document TitlenMy Subtitle"); Lastly, we can add content to the main body of the PDF document. We achieve this in the normal way by simply outputting the content. echo 'This is my PDF! '; The outputted data can be formatted using some basic HTML tags. The following tags are supported: Type Tags Format <b>, <u>, <i>, <strong>, <em>, <sup>, <sub>, <small>, <font> Heading <h1>, <h2>, <h3>, <h4>, <h5>, <h6> Indentation <blockquote> Linked <a>, <img> List <ol>, <ul>, <li> Spacing <p>, <br>, <hr> Table <table>, <tr>, <td>, <th>
Read more
  • 0
  • 0
  • 1548

article-image-processing-twitter-and-new-york-times-apis-aspnet-ajax-microsoft-cdn
Packt
18 Nov 2009
7 min read
Save for later

Processing Twitter and New York Times APIs with ASP.NET Ajax on Microsoft CDN

Packt
18 Nov 2009
7 min read
APIs (Application Programming Interface) are application-to-application programming interfaces that support harvesting information on the web using the known web standards. These APIs are provided by the entities who wish to expose parts of their resources that a third party can use. The APIs run transparent to the user and exposes just what they want to expose, with some providing access to material for public consumption with others giving access to resources based on authentication. In a sense they may be called a basic form of SAAS. Amazon.com, Google etc have exposed their APIs for some time. Twitter and New York Times have also exposed their API's which can be used to do some digging into the information contained in them, a kind of web mining. Many others such as Netflix have provided their own APIs described on their web sites. What is Twitter API? Twitter API is provided by the Social Networking and Micro-blogging service. Twitter API adheres to the web standards and one can talk to Twitter using HTTP. You can just about access anything on the Twitter web site. One example of creating a Microsoft SQL Server Report using Twitter API is available here - Tweets with Reporting Services, wherein the response from the Twitter API was in XML format. JSON (JavaScript Object Notation) is another format in which data is returned when an API call is made. In this article we will be looking at API call that returns a JSON response. Twitter exposes a large number methods through their API's such as API's for Search, Timeline, Status, User, Direct Message, Friendship and many more. As previously mentioned the responses will be in XML or JSON. Also while some APIs may take parameters others may not. The Twitter API used in this tutorial We will be looking at trends in Twitter API exposed by the url, http://search.twiiter.com/trends.format. We will be using the GET method and we will expect a JSON response. Since the volume of traffic may overwhelm, the calls that you can make to this in an hour are limited (also known as rate limiting) but not critical for the demo in this tutorial. Here is a typical call to the trends method on the Twitter API. Herein we will search for trends on the Twitter site and expect a response in JSON, if we use json instead of Format in the next URL address. Instead of:http://search.twitter.com/trends.Formattype-in, the following for URL address,http://search.twitter.com/trends.json When you plug the above in a web Brower you would get a response trends.json which you may save to your hard drive or, use it in any way you like. The next quoted text is what you get in response (note that this is what I got on Saturday 31, 2009 and what you get will be different), the content of the file trends.json you saved to your computer. Note that presently you get about top ten trends from this API call. {"as_of":"Sat, 31 Oct 2009 20:44:46 +0000","trends":[{"name":"Happy Halloween", "url":"http://search.twitter.com/search?q=%22Happy+Halloween%22+OR+%22Feliz+ Halloween%22"},{"name":"#nxzerosetechaves","url":"http://search.twitter.com/search?q=%23nxzerosetechaves"},{"name":"Danyl","url":"http://search.twitter.com/search?q=Danyl"},{"name":"#HappyHalloween","url":"http://search.twitter.com/search?q=%23HappyHalloween"},{"name":"#potterday","url":"http://search.twitter.com/search?q =%23potterday"},{"name":"X Factor","url":"http://search.twitter.com/search?q=%22X+ Factor%22"},{"name":"It's Halloween","url":"http://search.twitter.com/search?q=%22It %27s+Halloween%22+OR+%22Its+Halloween%22"},{"name":"Trick","url":"http://search.twitter.com/search?q=Trick+OR+%23trick"},{"name":"Paranormal Activity","url":"http://search.twitter.com/search?q=%22Paranormal+Activity%22"},{"name":"This Is It","url":"http://search.twitter.com/search?q=%22This+Is+It%22"}]} First of all what you see returned is a JSON object. If you are new to JSON review this article on my blog. The various elements that you see such as 'name', 'url' etc are fields in the response that are all described in the API documentation(look for Return Values). Some of the API calls can return a ton of information and you will have to know the API method so that you can correctly parse this data. Another thing you would notice is that the JSON object you get out is a nested object with many levels. You may need a JSON Parser to get a clearer picture of this nesting and I recommend using the online parser at this site. Using the above site, the JSON Object would appear as shown (only a portion is shown). New York Times API New York Times made available to the developers sometime in the middle of October 2008 APIs that can search New York Times for various kinds of information . Just like in Twitter there are a large number of APIs that you can use such as: Article Search; Best Sellers; Campaign Finance; Congress; and many others. Interested users can get on to this resource by signing up here requesting what APIs they would like to use. After signing up, New York Times would provide keys for the APIs that you want to access. It is important therefore, that the call should include the keys provided to you. For example, I received keys to access the following resources: Movie Reviews, Article Search, Best Sellers and Times Newswire. The key for the Movies Reviews API appears as shown here (the one shown here has been doctored and will not work). Movie Reviews API Key: b57378910b9fd80ecc73461547c93e8a:10:50673441 Using the New York Times API It is a valuable resource since you can get for example with the Article Search API access to more than 2.8 million articles from 1981. Using this is quite simple, just paste the URL shown below into the address box of your browser. Note that the key shown here is fake (but of correct format). http://api.nytimes.com/svc/search/v1/article?query=India&facets=publication_year&api-key=6c208890a4880093c30020be8fe17a40:0:50633441 This will display in the browser the JSON object that is returned as shown. You can use the previously mentioned site to parse it for more friendly display. {"facets" : {"publication_year" : [{"count" : 2724 , "term" : "2008"} , {"count" : 2345 , "term" : "2006"} , {"count" : 2311 , "term" : "2009"} , {"count" : 2282 , "term" : "2007"} , {"count" : 2144 , "term" : "2002"} ,{"count" : 2111 , "term" : "2001"} , {"count" : 1988 , "term" : "2005"} , {"count" : 1951 , "term" : "2004"} , {"count" : 1921 , "term" : "1985"} , {"count" : 1798 , "term" : "2003"} , {"count" : 1761 , "term" : "1999"} , {"count" : 1720 , "term" : "2000"} , {"count" : 1642 , "term" : "1998"} , {"count" : 1442 , "term" : "1984"} , {"count" : 1382 , "term" : "1986"}]} , "offset" : "0" , "results" : [{"body" : "BARSUR, India — At the edge of the Indravati River, hundreds of miles from the nearest international border, India effectively ends. Indian paramilitary officers point machine guns across the water. The dense jungles and mountains on the other side belong to Maoist rebels dedicated to overthrowing the government. "That is their liberated" , "byline" : "By JIM YARDLEY" , "date" : "20091101" , "title" : "Maoist Rebels Widen Deadly Reach Across India" , "url" : "http://www.nytimes.com/2009/11/01/world/asia /01maoist.html"} ,.........(there is more of this but abbreviated here) Response Format As you can see the responses to the API calls return JSON objects in general of the form shown belo w (this one is of the form returned by the Twiiter API). What we propose to do is to use jQuery's GetJSON() method to get the JSON Objects and use Microsoft AJAX JavaScript files to display the data on the web page. Both jQuery javascript files and Microsoft ASP.NET AJAX files are both available on the Microsoft ECN (CDN). The GetJSON() method as well as the Microsoft ASP.NET AJAX templates can be easily implemented in the Visual Studio 2008 IDE. Alternatively Microsoft AJAX can also be used to retrieve data from the web sites. In this article the GetJSON() method will be used. {"x":{"y":[{"a1":"b1", "c1":"d1"}, {"a2":"b2", "c2":"d2"}]},.... "f":"g",....}
Read more
  • 0
  • 0
  • 3838

article-image-authentication-zendauth-zend-framework-18
Packt
18 Nov 2009
6 min read
Save for later

Authentication with Zend_Auth in Zend Framework 1.8

Packt
18 Nov 2009
6 min read
Let's get started. Authentication versus Authorization Before we go any further, we need to first look at what exactly authentication and authorization is, as they are often misunderstood. Authorization is the process of allowing someone or something to actually do something. For example, if I go into a data centre, then the security guards control my authorization to the data centre and would, for instance, not allow me access to the server room if I was just a visitor but would if I worked there as a system admin. Authentication is the process of confirming someone or something's identity. For example, when I go to into the data centre the security guards will ask me for my identity, which most probably would be a card with my name and photo on. They use this to authenticate my identity. These concepts are very important so make sure you understand the difference. This is how I remember them: Authorization: Can they do this?Authentication: Are they who they say they are? Authentication with Zend_Auth To provide our authentication layer, we are going to use Zend_Auth. It provides an easy way to authenticate a request, obtain a result, and then store the identity of that authentication request. Zend_Auth Zend_Auth has three main areas—authentication adapters, authentication results, and identity persistence. Authentication adapters Authentication adapters work in a similar way to database adapters. We configure the adapter and then pass it to the Zend_Auth instance, which then uses it to authenticate the request. The following concrete adapters are provided by default: HTTP Digest authentication HTTP Basic authentication Database Table authentication LDAP authentication OpenID authentication InfoCard authentication All of these adapters implement the Zend_Auth_Adapter_Interface, meaning we can create our own adapters by implementing this interface. Authentication results All authentication adapters return a Zend_Auth_Result instance, which stores the result of the authentication request. The stored data includes whether the authentication request was successful, an identity if the request was successful, and any failure messages, if unsuccessful. Identity persistence The default persistence used is the PHP session. It uses Zend_Session_Namespace to store the identity information in the Zend_Auth namespace. There is one other type of storage available named NonPersistent, which is used for HTTP authentication. We can also create our own storage by implementing the Zend_Auth_Storage_Interface. Authentication Service We are going to create an Authentication Service that will handle authentication requests. We are using a service to keep the authentication logic away from our User Model. Let's create this class now: application/modules/storefront/services/Authentication.phpclass Storefront_Service_Authentication{ protected $_authAdapter; protected $_userModel; protected $_auth; public function __construct(Storefront_Model_User $userModel = null) { $this->_userModel = null === $userModel ? new Storefront_Model_User() : $userModel; } public function authenticate($credentials) { $adapter = $this->getAuthAdapter($credentials); $auth = $this->getAuth(); $result = $auth->authenticate($adapter); if (!$result->isValid()) { return false; } $user = $this->_userModel ->getUserByEmail($credentials['email']); $auth->getStorage()->write($user); return true;}public function getAuth(){ if (null === $this->_auth) { $this->_auth = Zend_Auth::getInstance(); } return $this->_auth;}public function getIdentity(){ $auth = $this->getAuth(); if ($auth->hasIdentity()) { return $auth->getIdentity(); } return false;}public function clear(){ $this->getAuth()->clearIdentity();}public function setAuthAdapter(Zend_Auth_Adapter_Interface $adapter){ $this->_authAdapter = $adapter;}public function getAuthAdapter($values){ if (null === $this->_authAdapter) { $authAdapter = new Zend_Auth_Adapter_DbTable( Zend_Db_Table_Abstract::getDefaultAdapter(), 'user', 'email', 'passwd' ); $this->setAuthAdapter($authAdapter); $this->_authAdapter ->setIdentity($values['email']); $this->_authAdapter ->setCredential($values['passwd']); $this->_authAdapter ->setCredentialTreatment( 'SHA1(CONCAT(?,salt))' ); } return $this->_authAdapter; }} The Authentication Service contains the following methods: __constuct: Creates or sets the User Model instance authenticate: Processes the authentication request getAuth: Returns the Zend_Auth instance getIdentity: Returns the stored identity clear: Clears the identity (log out) setAuthAdapter: Sets the authentication adapter to use getAuthAdapter: Returns the authentication adapter The Service is really separated into three areas. They are getting the Zend_Auth instance, configuring the adapter, and authenticating the request using Zend_Auth and the Adapter. To get the Zend_Auth instance, we have the getAuth() method. This method retrieves the singleton Zend_Auth instance and sets it on the $_auth property. It is important to remember that Zend_Auth is a singleton class, meaning that there can only ever be one instance of it. To configure the adapter, we have the getAuthAdapter() method. By default, we are going to use the Zend_Auth_Adapter_DbTable adapter to authenticate the request. However, we can also override this by setting another adapter using the setAuthAdapter() method. This is useful for adding authenticate strategies and testing. The configuration of the DbTable adapter is important here, so let's have a look at that code: $authAdapter = new Zend_Auth_Adapter_DbTable( Zend_Db_Table_Abstract::getDefaultAdapter(), 'user', 'email', 'passwd', 'SHA1(CONCAT(?,salt))');$this->setAuthAdapter($authAdapter);$this->_authAdapter->setIdentity($values['email']);$this->_authAdapter->setCredential($values['passwd']); The Zend_Auth_Adapter_DbTable constructor accepts five parameters. They are database adapter, database table, table name, identity column, and credential treatment. For our adapter, we supply the default database adapter for our table classes using the getDefaultAdapter() method, the user table, the email column, the passwd column, and the encryption and salting SQL for the password. Once we have our configured adapter, we set the identity and credential properties. These will then be used during authentication. To authenticate the request, we use the authenticate method. $adapter = $this->getAuthAdapter($credentials);$auth = $this->getAuth();$result = $auth->authenticate($adapter);if (!$result->isValid()) { return false;}$user = $this->_userModel ->getUserByEmail($credentials['email']);$auth->getStorage()->write($user);return true; Here we first get the configured adapter, get the Zend_Auth instance, and then fetch the result using Zend_Auth's authenticate method, while passing in the configured adapter. We then check that the authentication request was successful using the isValid() method. At this point, we can also choose to handle different kinds of failures using the getCode() method. This will return one of the following constants: Zend_Auth_Result::SUCCESSZend_Auth_Result::FAILUREZend_Auth_Result::FAILURE_IDENTITY_NOT_FOUNDZend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUSZend_Auth_Result::FAILURE_CREDENTIAL_INVALIDZend_Auth_Result::FAILURE_UNCATEGORIZED By using these, we could switch and handle each error in a different way. However, for our purposes, this is not necessary. If the authentication request was successful, we then retrieve a Storefront_Resource_User_Item instance from the User Model and then write this object to Zend_Auth's persistence layer by getting the storage instance using  getStorage() and writing to it using write(). This will then store the user in the session so that we can retrieve the user information throughout the session. Our Authentication Service is now complete, and we can start using it to create a login system for the Storefront.
Read more
  • 0
  • 0
  • 2793

article-image-extending-joomla-blogging-and-rss
Packt
18 Nov 2009
5 min read
Save for later

Extending Joomla! Blogging and RSS

Packt
18 Nov 2009
5 min read
Using Google's FeedBurner for SEO The preferred choice for burning your feed was www.feedburner.com, and they were so good at it that Google bought FeedBurner. So now if you want to Burn your Feed you have to login to Google with your Gmail account. Once logged in, look for the service FeedBurner and click on it. You will find a small screen in the middle of the page that says: Here you can paste the link that you got after clicking on Feed Entries on your Joomla! site. That is the public RSS Feed link that is shown by your syndication module. Once you click on the Next button you have a lot of options to improve your blog feed. The first thing you have to do is to make sure you have a nice feed URL.   I wanted it to be TheGardenBlog, but it was already taken so I settled for TheCrazyBeezGardenBlog, which is also good. You can also adjust your Feed Title, if you think it will be better, this title will be shown in a RSS reader to identify your feed. Click on Next and there you are:   Are you done? No way, now we get to the best part of the FeedBurner by Google service. Choosing your FeedBurner options for optimal results The Google service has a lot of options in store that will improve our RSS visibility and provide us with some blogging features that Joomla! doesn't have. One of the most important services is the PingShot that we will be looking at later. Let's take small steps and see what we can configure to get the best of the best. First we will go through the option tabs and check what you should really use:   Analyze: This is where you will see how well you are doing looking at your feed reader's stats   Optimize: Here are two services you need to activate, BrowserFriendly and SmartFeed   Publicize: Most of your work will be done here with Email Subscriptions, PingShot, FeedCount, and NoIndex   Monetize: Only if you want AdSense advertisements into your Feeds   Troubleshootize: A great place to start if your feed doesn't work the way it should From the tabs mentioned, we will be looking more closely at some of the settings in the Optimize and Publicize tabs. Let's take a look at the Optimize tab settings:   BrowserFriendly: This makes your RSS Feed that comes out of Joomla! a lot better, because it turns the not-so-nice looking feeds into human viewable HTML pages. For this, compare the following two screenshots. And all you have to do is activate the service!   SmartFeed is all for your visitors, it will give them the choice of viewing your feed into their favorite feed reader. There are a lot of feed readers out there. If you activate this service you give your visitors an easy choice to import your feed with a single click. If they click on your RSS Feed button, they get a list of services to which, they can add your feed with just a click on the button. Now, let's take a look at the Publicize tab settings:   Email Subscriptions makes it really easy to offer an email subscription to your RSS Feed.After activation of this service, copy the code from the Subscription Form Code field, and paste it on your site in a HTML module. To create such a module, go to your administrator panel. Choose Extensions from the top menu, then choose Module Manager. Then click on New and choose Custom HTML, give it a Title, Position, and publish it after you paste the code. The subscription form and fields are now ready for use. You can also configure the time when you want those emails to be sent to your visitors using the Delivery Options setting.   PingShot: PingShot does something that Joomla! cannot, but is essential for a blog. It sends a ping after you publish your post to several services such as Technorati, My Yahoo, and Bloglines.Make sure you activate the other two and add up to five extra options. For example, Ping-o-matic which will ping several other services for you, and Newsgator, which is another good service. From the drop-down list you can add a few extra services of which Google Blog Search Pinging Service is one.The other choice of services is dependent on the niche you work in, but for me the following ones work great:      icerocket     Weblogs.Com     FeedBlitz     Syndic8         FeedCount: This is a well-known counter. You can show it on your site to let people know how many subscribers are there on your feed. Don't show the feed count until you have over a minimum of 100 subscribers. There is a psychological effect behind this tip.Nobody will subscribe to your feed if it shows that there are only 3 subscribers. The thought behind this is that it is probably not that interesting because there are few subscribers.If you get over 100 subscribers, start showing the count! With over 100 subscribers there must be value in that feed! If you reach that limit and show it you will see that the number of subscribers will soon start to grow faster than before.   NoIndex: This option makes sure that your own feed is not indexed and ranking higher than your pages. This means the feed from burner.com will not be indexed, because of that it is not possible to have it outrank your pages. If you don't use that option the feed itself has the possibility to outperform your pages (this is not likely, but I have seen it happen on some sites, although that was before Google bought FeedBurner).
Read more
  • 0
  • 0
  • 1388

article-image-integrating-websphere-extreme-scale-data-grid-relational-database-part-2
Packt
18 Nov 2009
6 min read
Save for later

Integrating Websphere eXtreme Scale Data Grid with Relational Database: Part 2

Packt
18 Nov 2009
6 min read
Removal versus eviction Setting an eviction policy on a BackingMap makes more sense now that we're using a Loader. Imagine that our cache holds only a fraction of the total data stored in the database. Under heavy load, the cache is constantly asked to hold more and more data, but it operates at capacity. What happens when we ask the cache to hold on to one more payment? The BackingMap needs to remove some payments in order to make room for more. BackingMaps have three basic eviction policies: LRU (least-recently used), LFU (least-frequently used), and TTL (time-to-live). Each policy tells the BackingMap which objects should be removed in order to make room for more. In the event that an object is evicted from the cache, its status in the database is not changed. With eviction, objects enter and leave the cache due to cache misses and evictions innumerable times, and their presence in the database remains unchanged. The only thing that affects an object in the database is an explicit call to change (either persist or merge) or remove it as per our application. Removal means the object is removed from the cache, and the Loader executes the delete from SQL to delete the corresponding row(s) from the database. Your data is safe when using evictions. The cache simply provides a window into your data. A remove operation explicitly tells both ObjectGrid and the database to delete an object. Write-through and write-behind Getting back to the slow down due to the Loader configuration, by default, the Loader uses write-through behavior: Now we know the problem. Write-through behavior wraps a database transaction for every write! For every ObjectGrid transaction, we execute one database transaction. On the up side, every object assuredly reaches the database, provided it doesn't violate any relational constraints. Despite this harsh reaction to write-through behavior, it is essential for objects that absolutely must get to the database as fast as possible. The problem is that we hit the database for every write operation on every BackingMap. It would be nice not to incur the cost of a database transaction every time we write to the cache. Write-behind behavior gives us the help we need. Write-behind gives us the speed of an ObjectGrid transaction and the flexibility that comes with storing data in a database: Each ObjectGrid transaction is now separate from a database transaction. BackingMap now has two jobs. The first job is to store our objects as it always does. The second job is to send those objects to the JPAEntityLoader. The JPAEntityLoader then generates SQL statements to insert the data into a database. We configured each BackingMap with its own JPAEntityLoader. Each BackingMap requires its own Loader because each Loader is specific to a JPA entity class. The relationship between JPAEntityLoader and a JPA entity is established when the BackingMap is initialized. The jpaTxCallback we specified in the ObjectGrid configuration coordinates the transactions between ObjectGrid and a JPA EntityManager. In a write-through situation, our database transactions are only as large as our ObjectGrid transactions. Update one object in the BackingMap and one object is written to the database. With write-behind, our ObjectGrid transaction is complete, and our objects are put in a write-behind queue map. That queue map does not immediately synchronize with the database. It waits for some specified time or for some number of updates, to write out its contents to the database: We configure the database synchronization conditions with the setWriteBehind("time;conditions") method on a BackingMap instance. Programmatically the setWriteBehind method looks like this: BackingMap paymentMap = grid.getMap("Payment");paymentMap.setLoader(new JPAEntityLoader());paymentMap.setWriteBehind("T120;C5001"); The same configuration in XML looks like this: <backingMap name="Payment" writeBehind="T120;C5001"pluginCollectionRef="Payment" /> Enabling write-behind is as simple as that. The setWriteBehind method takes one string parameter, but it is actually a two-in-one. At first, the T part is the time in seconds between syncing with the database. Here, we set the payment BackingMap to wait two minutes between syncs. The C part indicates the number (count) of changes made to the BackingMap that triggers a database sync. Between these two parameters, the sync occurs on a whichever comes first basis. If two minutes elapse between syncs, and only 400 changes (persists, merges, or removals) have been put in the write-behind queue map, then those 400 changes are written out to the database. If only 30 seconds elapse, but we reach 5001 changes, then those changes will be written to the database. ObjectGrid does not guarantee that the sync will take place exactly when either of those conditions is met. The sync may happen a little bit before (116 seconds or 4998 changes) or a little bit later (123 seconds or 5005 changes). The sync will happen as close to those conditions as ObjectGrid can reasonably do it. The default value is "T300;C1000". This syncs a BackingMap to the database every five minutes, or 1000 changes to the BackingMap. This default is specified either with the string "T300;C1000" or with an empty string (" "). Omitting either part of the sync parameters is acceptable. The missing part will use the default value. Calling setWriteBehind("T60") has the BackingMap sync to the database every 60 seconds, or 1000 changes. Calling setWriteBehind("C500") syncs every five minutes, or 500 changes. Write-behind behavior is enabled if the setWriteBehind method is called with an empty string. If you do not want write-behind behavior on a BackingMap, then do not call the setWriteBehind method at all. A great feature of the write-behind behavior is that an object changed multiple times in the cache is only written in its final form to the database. If a payment object is changed in three different ObjectGrid transactions, the SQL produced by the JPAEntityLoader will reflect the object's final state before the sync. For example: entityManager.getTransaction().begin();Payment payment = createPayment(line, batch);entityManager.getTransaction().commit();some time later...entityManager.getTransaction().begin();payment.setAmount(new BigDecimal("44.95"));entityManager.getTransaction().commit();some time later...entityManager.getTransaction().begin();payment.setPaymentType(PaymentType.REAUTH);entityManager.getTransaction().commit(); With write-through behavior, this would produce the following SQL: insert into payment (id, amount, batch_id, card_id, payment_type) values (12345, 75.00, 31, 6087, 'AUTH');update payment set (id, amount, batch_id, card_id, payment_type) values (12345, 44.95, 31, 6087, 'AUTH') where id = 12345;update payment set (id, amount, batch_id, card_id, payment_type) values (12345, 44.95, 31, 6087, 'REAUTH') where id = 12345; Now that we're using write-behind, that same application behavior produces just one SQL statement: insert into payment (id, amount, batch_id, card_id, payment_type) values (12345, 44.95, 31, 6087, 'REAUTH');
Read more
  • 0
  • 0
  • 2427
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-restful-java-web-services-design
Packt
18 Nov 2009
5 min read
Save for later

RESTful Java Web Services Design

Packt
18 Nov 2009
5 min read
We'll leave the RESTful implementation for a later article. Our sample application is a micro-blogging web service (similar to Twitter), where users create accounts and then post entries. Finally, while designing our application, we'll define a set of steps that can be applied to designing any software system that needs to be deployed as a RESTful web service. Designing a RESTful web service Designing RESTful web services is not different from designing traditional web applications. We still have business requirements, we still have users who want to do things with data, and we still have hardware constraints and software architectures to deal with. The main difference, however, is that we look at the requirements to tease out resources and forget about specific actions to be taken on these resources. We can think of RESTful web service design as being similar to Object Oriented Design (OOD). In OOD, we try to identify objects from the data we want to represent together with the actions that an object can have. But the similarities end at the data structure definition, because with RESTful web services we already have specific calls that are part of the protocol itself. The underlying RESTful web service design principles can be summarized in the following four steps: Requirements gathering—this step is similar to traditional software requirement gathering practices. Resource identification—this step is similar to OOD where we identify objects, but we don't worry about messaging between objects. Resource representation definition—because we exchange representation between clients and servers, we should define what kind of representation we need to use. Typically, we use XML, but JSON has gained popularity. That's not to say that we can't use any other form of resource representation—on the contrary, we could use XHTML or any other form of binary representation, though we let the requirements guide our choices. URI definition—with resources in place, we need to define the API, which consists of URIs for clients and servers to exchange resources' representations. This design process is not static. These are iterative steps that gravitate around   resources. Let's say that during the URI definition step we discover that one of the URI's responses is not covered in one of the resources we have identified. Then we go back to define a suitable resource. In most cases, however, we find that the resources that we already have cover most of our needs, and we just have to combine existing resources into a meta-resource to take care of the new requirement. Requirements of sample web service The RESTful web service we design in this article is a social networking web application similar to Twitter. We follow an OOD process mixed with an agile philosophy for designing and coding our applications. This means that we create just enough documentation to be useful, but not so much that we spend an inordinate amount of time deciphering it during our implementation phase. As with any application, we begin by listing the main business requirements, for which we have the following use cases (these are the main functions of our application): A web user creates an account with a username and a password (creating an account means that the user is now registered). Registered users post blog entries to their accounts. We limit messages to 140 characters. Registered and non-registered users view all blog entries. Registered and non-registered users view user profiles. Registered users update their user profiles, for example, users update their password. Registered and non-registered users search for terms in all blog entries. However simple this example may be, social networking sites work on these same principles: users sign up for accounts to post personal updates or information. Our intention here, though, is not to fully replicate Twitter or to fully create a social networking application. What we are trying to outline is a set of requirements that will test our understanding of RESTful web services design and implementation. The core value of social networking sites lies in the ability to connect to multiple users who connect with us, and the value is derived from what the connections mean within the community, because of the tendency of users following people with similar interests. For example, the connections between users create targeted distribution networks.The connections between users create random graphs in the graph theory sense, where nodes are users and edges are connections between users. This is what is referred to as the social graph. Resource identification Out of the use cases listed above, we now need to define the service's resources. From reading the requirements we see that we need users and messages. Users appear in two ways: a single user and a list of users. Additionally, users have the ability to post blog entries in the form of messages of no more than 140 characters. This means that we need resources for a single message and a list of messages. In sum, we identify the following resources: User List of users Message List of messages
Read more
  • 0
  • 0
  • 11057

article-image-ubuntu-910-how-upgrade
Packt
18 Nov 2009
5 min read
Save for later

Ubuntu 9.10: How To Upgrade

Packt
18 Nov 2009
5 min read
So the new Ubuntu is here and you’re just dying to upgrade and have a look at all the new features! With just a few simple steps you'll be up and running the new system in no time! Before you dive right in, there are a few things you should know, and a few ways to (hopefully) make your upgrade process more pleasant. This article is broken up into sections outlining the preparation, requirements and upgrade steps needed for each platform. It is important to follow the steps in order to ensure a full and painless upgrade. Also, please follow only one of the upgrade paths. In other words, there are different methods for a Desktop as compared to a Server. You only need to follow those steps applicable to you. A Note Regarding Upgrades vs Fresh Installations You may be wondering whether it is better to upgrade your current installation or do a fresh install from CD. There are benefits to doing a fresh installation to be sure, but there are also benefits to upgrading your system in place. I know people that swear by one method, and others that swear by another. In the end, both methods are supported and will give you the same Ubuntu experience. Fresh installations will require a complete wipe of your hard disk. This means that you'll need to backup any important documents, pictures or other files that you'll want to keep. Have you ever done a fresh installation before and realized only too late that you forgot to back something up? I have. It's easy to miss something. Using the in-place upgrade methods found in this article you won't need to worry about backups. With an in-place upgrade you can generally keep working on your machine while applications are upgraded in the background. This means you can continue to browse the web or send and receive email while the system is upgraded. Bottom line is that upgrades are thoroughly tested and just as well supported as fresh installations. Preparation When upgrading your system from one release to the next, there are certain requirements that you must meet in order to be successful. First of all, and most importantly in this instance, this upgrade path is only possible from Ubuntu 9.04 "Jaunty Jackalope" to Ubuntu 9.10 "Karmic Koala". If you are using a release previous to 9.04 (8.10 or earlier), stop now. This upgrade process will not work, is not supported and will likely cause problems. If you are unsure which version you have installed, you can run this command in your terminal to find out. (Applications > Accessories > Terminal) lsb_release -a If you find that you are on a release previous to Ubuntu 9.04, you will need to decide whether it is best to do a fresh installation or do an incremental upgrade leading up to 9.10. Incremental upgrades, as well as fresh installations are beyond the scope of this article, but there is detailed documentation on the matter found here: https://help.ubuntu.com/community/UpgradeNotes Updates Once you have verified that you are using Ubuntu 9.04 "Jaunty Jackalope" you will be able to begin the upgrade proccess. In order for the latest version to become available to you, you'll need to apply any pending updates to your current version. There are two ways to apply available updates pending a system upgrade. The first method applies to the graphical Desktop or Laptop platform. The second method applies to a server, or non-graphical installation. Remember, please only follow the steps applicable to you. Graphical Updates (Pre-Upgrade) If you are using the graphical environment you can check for and apply updates by way of the Update Manager tool. This can be found by navigating to: (System > Administration > Update Manager). This tool will automatically scan for and list any pending updates. Be sure to apply all available updates before moving to the next step. You can ensure that there are no more pending updates by clicking Check and verifying that it displays the message "Your system is up to date". Command Line Updates (Pre-Upgrade) For those more comfortable with the command line interface, or those running a non-graphical Server installation, you can run the following command to check for and apply any available system updates. sudo aptitude update && sudo aptitude safe-upgrade && sudo aptitude full-upgrade Apply any updates that are pending from the command above before you move to the next step. You can repeat this command until no more updates are offered to ensure you are ready. Now that you have applied the remainder of the updates for your current system, you can move to the next step. In the next step, Selecting a Mirror, you will learn how to use an alternate, often faster, package repository for your updates. This means that instead of using the default and often overwhelmed main Ubuntu servers for updates you can configure your system to use one closer to you. This often results in faster downloads and upgrades.
Read more
  • 0
  • 0
  • 13470

article-image-ground-sql-azure-migration-using-ms-sql-server-integration-services
Packt
18 Nov 2009
5 min read
Save for later

Ground to SQL Azure migration using MS SQL Server Integration Services

Packt
18 Nov 2009
5 min read
Enterprise data can be of very different kinds ranging from flat files to data stored in relational databases with the recent trend of storing data in XML data sources. The extraordinary number of database related products, and their historic evolution, makes this task exacting. The entry of cloud computing has turned this into one of the hottest areas as SSIS has been one of the methods indicated for bringing ground based data to cloud storage in SQL Azure, the next milestone in Microsoft Data Management. The reader may review my book on this site, "Beginners Guide to Microsoft SQL Server Integration Services" to get a jump start on learning this important product from Microsoft. SQL Azure SQL Azure is one of the three pillars of Microsoft's Azure cloud computing platform. It is a relational database built on SQL Server Technologies maintained on Microsoft's physical site where subscribers like you and me can rent out storage of data. Since the access is over the internet it is deemed to be in the cloud and Microsoft would provide all data maintenance. Some of the key benefits of this 'Database usage as a service' are: Manageability High Availability Scalability Which in other words means taking away a lot headache from you like worrying about hardware and software (SQL Azure Provisioning takes care of this), replication, DBAs with attitudes etc. Preparation for this tutorial You need some preparation to work with this tutorial. You must have a SQL Server 2008 installed to start with. You also need to register yourself with Microsoft to get an invitation to use SQL Azure by registering for the SQL Azure CTP. Getting permission is not immediate and may take days. After you register agreeing to the license terms, you get the permission (You become a subscriber to the service) to use the Azure Platform components (SQL Azure is one of them). After subscribing you can create a database on the SQL Azure instance. You will be the administrator of your instance (Your login will be known as the server level principal equivalent to the landbased sa login), and you can web access the server with a specific connection string provided to you and a strong password which you create. When you access the Azure URL, you provide the authentication to get connected to your instance of the server by signing in. Therein, you can create a database or delete an existing database. You have couple of tools available to work with this product. Read the blog post mentioned in the summary. Overview of this tutorial In this tutorial you will be using MS SQL Server Integration Services to create a package that can transfer a table from SQL Server 2008 to SQL Azure for which you have established your credentials. In my case the credentials are: Server: tcp:XXXXXX.ctp.database.windows.net User ID: YYYYY Password: ZZZZZ Database: PPPPPP Trusted_Connection=False; Here XXXXXX, YYYY,ZZZZZ, and PPPPPP are all the author's personal authentication values and you would get yours when you register as previously mentioned. Table to be migrated on SQL Server 2008 The table to be migrated on the SQL Server 2008 (Enterprise server, evaluation edition is shown in the next figure). PrincetonTemp is a simple table in the TestNorthwind database on the default instance of the local server on a Windows XP machine, with a few columns and no primary key. Create a SQL Server Integration Services Package Open BIDS (a Visual Studio add-in extending support to build database applications with SQL Server) and create a new SQL Server Integration Services project[Use File |New |Project...in the IDE]. Herein the Visual Studio 2008 with SP1 is used. You need to provide a name which for this project is GroundToCloud. The program creates the project for you which you can see in the Solution Explorer. By default it creates a package for you, Package.dtsx. You may rename the package (herein ToAzure.dtsx)and the project folders and file appear as shown. Add an ADO.NET Source component Drag and drop a Data Flow Task to the tabbed page Control Flow in the package designer. Into the Data flow tabbed page drag and drop an ADO.NET Source component from the Toolbox. Double click the component you just added, from the pop-up menu choose Edit... The ADO.NET Source editor gets displayed. If there are previously configured connections one of them may show up in this window. We will be creating a new connection and therefore click the New... button to display an empty Configure ADO.NET Connection Manager as shown (again, if there are existing connections they all will show up in this window). A connection is needed in connecting to a source outside the IDE. Double click the New... button to display the Connection Manager window which is all but empty. Fill in the details for your instance of ground based server as shown (the ones shown are for this article at the author's site). You may test the connection by hitting the Test Connection button. Clicking the OK buttons on the Connection Manager and the Configure ADO.NET Connection Manager will bring you back to the ADO.NET Source Editor displaying the connection you have just made as shown. A connection string also gets added to the bottom pane of the package designer as well as to the Configure ADO.NET Connection Manager. Click on the drop-down and pick the table (PrincetonTemp) that needs to be migrated to the cloud based server, SQL Azure. Click OK. The Columns navigation on the left would reveal all the columns in the table if it were to be clicked. The Preview button would return the data returned by a SELECT query on the columns as shown.
Read more
  • 0
  • 0
  • 4000

article-image-archiva-team-part-2
Packt
18 Nov 2009
7 min read
Save for later

Archiva in a Team: Part 2

Packt
18 Nov 2009
7 min read
Deleting artifacts in your repository Sometimes the need for deleting artifacts from the repository arises. For example, if an artifact was deployed by accident to the repository or the artifact has already been released but an old snapshot version is still available. In Archiva, there are different ways of deleting artifacts from the repository—through WebDAV, via the web application, through the scheduled repository purging, or by directly deleting it in the file system. It is not recommended that artifacts be deleted directly from the file system. Not only does it require access to the server itself, it is also prone to error. Artifacts that should not be deleted could be deleted by mistake. In case you still want to directly delete an artifact from the file system, all files related to the artifact such as metadata files and checksums must also be deleted. The repository must be scanned as well in order to update the metadata files. This can be done by clicking the Scan Repository Now button of the repository configuration in the Repositories page. The database scanning also needs to be explicitly executed to immediately remove the deleted artifact from the database. One of the advantages of using the Delete Artifact form in the web application is that you do not need to have direct access to the server. All you need is the required Archiva permissions, which come with the Repository Manager role (without the permissions Delete Artifact will not be visible in the navigation menu). Another advantage is that the repository scanning no longer needs to be explicitly executed as Archiva already executes the repository and database scanning consumers to update the index and the database for you. Now, let's try deleting an old artifact from one of the repositories. If you go to http://localhost:8081/archiva/repository/snapshots/com/effectivemaven/centrepoint/centrepoint, the old 1.0-SNAPSHOT version of the project still exists. We will remove this artifact from the repository using the delete artifact web form. First, click Delete Artifact from the navigation menu and then fill in the form as follows: Click the Submit button. After the artifact has been deleted, you should see the confirmation message Artifact 'com.effectivemaven.centrepoint:centrepoint:1.0-SNAPSHOT' was successfully deleted from repository 'snapshots'. If you browse the repository at http://localhost:8081/archiva/repository/snapshots, the related artifacts such as the POM, maven-metadata.xml, and the checksums were also deleted. To delete artifacts through WebDAV, just open the repository using a WebDAV client and delete the artifact like in a regular file system. As for the scheduled repository purging, we will discuss this in the following sections. We have tackled the subjects of repository groups, RSS feeds, and deleting artifacts in the repository. This article would never be complete without covering repository maintenance. The succeeding sections will be all about that. The Archiva reports Archiva generates two types of reports. These are the repository statistics, providing information such as statistical data of a repository's content and the repository health report, which makes us aware of any problems in the repository such as artifacts that have invalid POM files. Both accept different criteria for customizing the generated output as seen in the following screenshot: Now, let's discuss the configuration for each report. Repository statistics This report provides statistical repository information such as the total number of artifacts in the repository, its total size, the number of plugins in the repository, and the likes based on a given repository scan execution time. This report can be used for analyzing the current content of your repositories, and tracking its growth, usage, and evolution over time. The report can be constrained by the given Start Date and End Date. If no Start Date and End Date are provided, all statistics right from the start up to the current date will be included in the report (to a maximum of the number of rows given in the Row Count). For the Repository Statistics, we can also configure the Repositories To Be Compared. If only one repository is selected in Repositories To Be Compared, the generated report will contain details of a single repository. The following is a sample report where only one repository is selected: Let's run through the contents of the sample Repository Statistics report given previously for repository internal. The Total File Count pertains to the total number of files in the repository during each execution of the repository scan. The Total Size, on the other hand, is the size (in bytes) of the repository at that time. The number of unique groups and artifact names are broken down in the report as well as the number of plugins, archetypes, JAR, and WAR files. The last two columns—number of deployments and artifact requests—are not yet implemented but will be fixed in the future releases. On the other hand, if more than one repository is selected in the Repositories To Be Compared, the generated report would contain a comparison of the latest statistics of the repositories based on the specified End Date. This is useful for tracking which repositories are the most utilized. For example, if different development groups host their own repositories, the comparison can show which groups are using the most space. Look at the following screenshot for a sample comparison report to see the difference from the previous one: To allow you to view this report outside of the web application, the report can be exported as a CSV file by clicking on the Export to CSV link. You should be able to open the exported file as an Excel spreadsheet. Repository health One of the secrets behind a successful and reproducible build is a clean and healthy repository. Corrupt metadata or an invalid or missing POM file are the usual causes for a build to break. To prevent this from happening, we must ensure that the repositories we are getting our artifacts from are in good health. Archiva provides a way of doing this through the Repository Health report and its built-in utilities for updating metadata and fixing checksums. The Repository Health report provides a detailed list of artifacts in the repository that are found to be defective. It gives a starting point for correcting any problems and can be used when diagnosing build errors with a particular artifact. For example, a common reason for an artifact being defective is when the version of the artifact specified in the POM is different from the actual version in its filename. This could easily happen when using deploy:deploy-file (or even using the Archiva web upload form) as the actual filename used for the uploaded artifact is determined based on the supplied parameters. It is a possibility that the included POM in the upload has different coordinates from the provided parameters. These defects are discovered during Archiva's database scan, when the actual POM file is read and added to the database. We can narrow down the report by providing a specific Group ID and/or a Repository ID which will be used for querying defective artifacts that match these criteria. If you try querying for the report using the default configuration, you should be able to see a generated report similar to the following one, which shows a defective POM in repository internal. To repair such an error, you can manually fix the POM in the Archiva repository by updating it in the file system. If the defect is caused by a transfer error when the artifact was proxied, you can delete the artifact (including the metadata and checksums) then force Archiva to retrieve it again by requesting it. A word of caution though—making these changes could affect the reproducibility of a dependent project's build. For example, it is possible that the actual artifact in the central repository is the defective one. If you fixed the artifact in your internal Archiva repository, project builds that go through the local proxy may get a successful build. However, the project is built directly off central and the build fails because the dependency artifact is defective. That summarizes monitoring the health of our repositories. The next section discusses the built-in Archiva utilities which in one way or another clean up and repair broken artifacts and metadata in the repositories.    
Read more
  • 0
  • 0
  • 1572
article-image-build-advanced-contact-manager-using-jboss-richfaces-33-part-3
Packt
18 Nov 2009
11 min read
Save for later

Build an Advanced Contact Manager using JBoss RichFaces 3.3: Part 3

Packt
18 Nov 2009
11 min read
  The ajaxSingle and the process attributes The ajaxSingle property is very useful to control the form submission when ajaxSingle is set to true—the form is not submitted and, just the Ajax component data is sent. This attribute is available in every Ajax action component and we can use it to call an action from a button, skipping the form validation (like the JSF immediate property does), or to send the value of just an input into a form without validation and submitting the other ones. The second use case can be used, for example, when we need an input menu that dynamically changes the value of other inputs without submitting the entire form: <h:form> <!-- other input controls --> <h:selectOneMenu id="country" value="#{myBean.selectedCountry}"> <f:selectItems value="#{myBean.myCountries}"> <a:support event="onchange" ajaxSingle="true" reRender="city" /> </h:selectOneMenu> <h:selectOneMenu id="city" value="#{myBean.selectedCity}"> <f:selectItems value="#{myBean.myCities}"> </h:selectOneMenu> <!-- other input controls --></h:form> In this example, every time the user selects a new country, the value is submitted to the bean that recalculates the myCities property for the new country, after that the city menu will be re-rendered to show the new cities. All that without submitting the form or blocking the changes because of some validation problem. What if you would like to send more than one value, but still not the entire form? We can use Ajax regions (we will see in the next sections), or we can use the process attribute. It contains a list of components to process while submitting the Ajax action: <h:form> <!-- other input controls --> <h:inputText id="input1" ... /> <h:selectOneMenu id="country" value="#{myBean.selectedCountry}"> <f:selectItems value="#{myBean.myCountries}"> <a:support event="onchange" ajaxSingle="true" process="input2, input3" reRender="city" /> </h:selectOneMenu> <h:inputText id="input2" ... /> <h:inputText id="input3" ... /> <h:selectOneMenu id="city" value="#{myBean.selectedCity}"> <f:selectItems value="#{myBean.myCities}"> </h:selectOneMenu> <!-- other input controls --></h:form> In this example, we also wanted to submit the input2 and the input3 values together with the new country, because they are useful for retrieving the new cities list—just by setting the process attribute with the id list and during the submission, they will be processed. Thus input1 will not be sent. Also, for action components such as buttons, you can decide what to send using the ajaxSingle and process attributes. Form submission and processingWe speak about form "submission" to simplify the concept and make things more understandable. In reality, for every request, all of the form is submitted, but only the selected components (using ajaxSingle and/or process attributes) will be "processed". By "processed" we mean "pass through" the JSF phases (decoding, conversion, validation, and model updating). More Ajax! For every contact, we would like to add more customizable fields, so let's use the ContactField entity connected to every Contact instance. First of all, let's create a support bean called HomeSelectedContactOtherFieldsHelper inside the book.richfaces.advcm.modules.main package. It might look like this: @Name("homeSelectedContactOtherFieldsHelper")@Scope(ScopeType.CONVERSATION)public class HomeSelectedContactOtherFieldsHelper { @In(create = true) EntityManager entityManager; @In(required = true) Contact loggedUser; @In FacesMessages facesMessages; @In(required = true) HomeSelectedContactHelper homeSelectedContactHelper; // my code} A notable thing is highlighted—we injected the homeSelectedContactHelper component, because to get the list of the customized fields from the database, we need the contact owner. We also set the required attribute to true, because this bean can't live without the existence of homeSelectedContactHelper in the context. Now, let's add the property containing the list of personalized fields for the selected contact: private List<ContactField> contactFieldsList;public List<ContactField> getContactFieldsList() { if (contactFieldsList == null) { // Getting the list of all the contact fields String query = "from ContactField cf where cf.contact.id=:idContactOwner order by cf.id"; contactFieldsList = (List<ContactField>) entityManager.createQuery(query) .setParameter("idContactOwner", homeSelectedContactHelper.getSelectedContact() .getId()).getResultList(); } return contactFieldsList;}public void setContactFieldsList(List<ContactField> contactFieldsList) { this.contactFieldsList = contactFieldsList;} As you can see, it is a normal property lazy initialized using the getter. This queries the database to retrieve the list of customized fields for the selected contact. We have to put into the bean some other method useful to manage the customized field (adding and deleting field to and from the database), let's add those methods: public void createNewContactFieldInstance() { // Adding the new instance as last field (for inserting a new field) getContactFieldsList().add(new ContactField());}public void persistNewContactField(ContactField field) { // Attaching the owner of the contact field.setContact(homeSelectedContactHelper.getSelectedContact()); entityManager.persist(field);}public void deleteContactField(ContactField field) { // If it is in the database, delete it if (isContactFieldManaged(field)) { entityManager.remove(field); } // Removing the field from the list getContactFieldsList().remove(field);}public boolean isContactFieldManaged(ContactField field) { return field != null && entityManager.contains(field);} The createNewContactFieldInstance() method will just add a new (not yet persisted), empty instance of the ContactField class into the list. After the user has filled the values in, he/she will press a button that calls the persistNewContactField() method to save the new data into the database. In order to delete it, we are going to use the deleteContactField() method, and to determine if an instance is persisted into the database or not, we are going to use the isContactFieldManaged() method. Now, let's open the /view/main/contactView.xhtml file and add the code to show the personalized fields after h:panelGrid— i shows the standard ones: <a:repeat value="#{homeSelectedContactOtherFieldsHelper.contactFieldsList}" var="field"> <h:panelGrid columns="2" rowClasses="prop" columnClasses="name,value"> <h:outputText value="#{field.type} (#{field.label}):"/> <h:outputText value="#{field.value}"/> </h:panelGrid></a:repeat> We are using a new RichFaces data iteration component that permits us to iterate over a collection and put the data we want (the rich:dataTable component would instead create a table for the elements list). In our case, the h:panelGrid block will be repeated for every element of the collection (so for every customized field). Now, let's open the /view/main/contactEdit.xhtml file and add the code for editing the customized fields into the list: <a:region> <a:outputPanel id="otherFieldsList"> <a:repeat value="#{homeSelectedContactOtherFieldsHelper. contactFieldsList}" var="field"> <h:panelGrid columns="3" rowClasses="prop" columnClasses="name,value,validatormsg"> <h:panelGroup> <h:inputText id="scOtherFieldType" value="#{field.type}" required="true" size="5"> <a:support event="onblur" ajaxSingle="true"/> </h:inputText> <h:outputText value=" ("/> <h:inputText id="scOtherFieldLabel" value="#{field.label}" size="5"> <a:support event="onblur" ajaxSingle="true"/> </h:inputText> <h:outputText value=")"/><br/> <rich:message for="scOtherFieldType" styleClass="messagesingle" errorClass="errormsg" infoClass="infomsg" warnClass="warnmsg"/> </h:panelGroup> <h:panelGroup> <h:inputText id="scOtherFieldValue" value="#{field.value}" required="true"> <a:support event="onblur" ajaxSingle="true"/> </h:inputText><br/> <rich:message for="scOtherFieldValue" styleClass="messagesingle" errorClass="errormsg" infoClass="infomsg" warnClass="warnmsg"/> </h:panelGroup> <h:panelGroup> <a:commandButton image="/img/add.png" reRender="otherFieldsList" action="#{homeSelectedContactOtherFieldsHelper. persistNewContactField(field)}" rendered="#{!homeSelectedContactOtherFieldsHelper. isContactFieldManaged(field)}"> </a:commandButton> <a:commandButton image="/img/remove.png" reRender="otherFieldsList" ajaxSingle="true" action="#{homeSelectedContactOtherFieldsHelper. deleteContactField(field)}"> </a:commandButton> </h:panelGroup> </h:panelGrid> </a:repeat> <a:commandLink reRender="otherFieldsList" ajaxSingle="true" action="#{homeSelectedContactOtherFieldsHelper. createNewContactFieldInstance}" rendered="#{homeSelectedContactHelper. selectedContactManaged}" styleClass="image-command-link"> <h:graphicImage value="/img/add.png"/> <h:outputText value="#{messages['addNewField']}"/> </a:commandLink> </a:outputPanel></a:region> The code looks very similar to the one in the view box, except for the action buttons (to add a new instance, persist, save, or delete) and, for the presence of the surrounding tag a:region (highlighted). This is very important in order to make sure the form works correctly; we will see why in the next section. Also, notice that every input component has the a:support tag as a child that will update the bean with the edited value at the onblur event (which means that every time you switch the focus to another component, the value of the last one is submitted). So, if you delete or add a field, you will now loose the edited values for other fields. It is also used for Ajax validation, as the user is informed that the value is not valid when it moves the cursor to another input. Here is a screenshot with the new feature in the edit box: Using a:support only for Ajax validation If you want to use the a:support tag only for validation purpose, remember to set its bypassUpdates attribute to true, so the process would be faster as the JSF Update Model and Invoke Application phases will not be invoked. Ajax containers While developing a web application with RichFaces, it's very useful to know how to use Ajax containers (such as the a:region component) in order to optimize Ajax requests. In this section, we'll discuss about the a:region component. It is a very important component of the framework—it can define Ajax areas to limit the part of the component tree to be processed during an Ajax request. Regions can be nested during an Ajax request and the closest one will be used. By setting to true the a:region attribute called regionRenderOnly, you can use this component to limit the elements' update—In this way, in fact, only the components inside the region can be updated. Another important attribute is selfRendered; setting this to true tells the framework to render the response basing on component tree without referring to the page code—it is faster, but all of the transient elements that are not saved in the tree (such as f:verbatim or HTML code written directly without using JSF components) will be lost at the first refresh, so you can't use them in this case. To summarize, it is very useful to control the rendering process and optimize it, in order to limit the elements of a form to send during an Ajax request without validation problems, to show different indicators for Ajax status. Example of using a:region: <h:form> <a:region> <h:inputText id="it1" value="#{aBean.text1}"> <a:support event="onkeyup" reRender="text1" /> </h:inputText> <h:inputText id="it2" value="#{aBean.text2}" /> </a:region> <h:inputText id="it3" value="#{aBean.text3}" /> <a:commandButton action="#{aBean.saveTexts}" reRender="text1,text2" /></h:form><h:outputText id="text1" value="#{aBean.text1}" /><h:outputText id="text2" value="#{aBean.text2}" /> In this example, while the user is typing in the text1 value of inputText, a:support sends an Ajax request containing only the it1 and it2 values of inputText. In this case, in fact, a:region limits the components sent by every Ajax request originated from inside the region. So, the Ajax request will only update aBean.text1 and aBean.text2. Wrapping only a component inside an Ajax region is the equivalent of using the ajaxSingle property set to true. If the user clicks on the a:commandButton aBean.text1, the aBean.text2 and aBean.text3 values will be updated by the Ajax request. Coming back to our application, as all the customized fields are inside the same form component, we surround each one with the a:region tag. In this way, the single field is submitted regardless of the other ones. For example, without using a:region, if the user empties the name input value and then tries to insert a new customized field, the process will fail because the name input is not validated. If we use the a:region component, the name field will not be processed and a new field will be inserted. Now that we know how to use the a:region tag, we can combine it with ajaxSingle and process in order to decide what to send at every request, and to better optimize Ajax interactions into the application.
Read more
  • 0
  • 0
  • 1402

article-image-quality-assurance-asterisk-16
Packt
18 Nov 2009
10 min read
Save for later

Quality Assurance in Asterisk 1.6

Packt
18 Nov 2009
10 min read
The world has changed quite a bit in the last 150 years. Over this time, the telephone system has been invented, improved, and automated. Telephone switches no longer refer to people sitting in a large room connecting wires between the appropriate jacks. Flexible and powerful telephone service has moved from a dream to an expectation in large businesses, and for most of us it is a necessity. Today, telephone systems are the lifeblood of business. They are how we take orders, acquire supplies, and even call for emergency assistance. With the increase in prominence of telephones, the expectations of telephone users have increased proportionally. Not only have the technological expectations for telephone systems increased dramatically, but consumers are expecting more and more out of the businesses they call. Customers expect to be helped quickly and professionally. They want to know everything in a matter of minutes. Roads do not hold the only rage our society is facing today. As a business we have a variety of questions relating to our telephone system such as: How are our personnel handling angry callers? Are our employees answering the calls in a reasonable amount of time? Do we have any workers using the phone system for personal calls when they should be doing their job? We will never be able to make sure everybody does what they are supposed to do all of the time. What we will be able to do at the end of this article is perform spot-checks on how we are doing on customer service, and make sure our phone service isn't being used for unauthorized purposes. Ultimately, it comes down to a matter of trust; however, some people do not know better because they haven't been fully trained. Most will always act honorably; however, some just cannot and should not be trusted. We will try to find out who is who. Call Detail Records When we talk about security, what images come to mind? May be a big, burly guard? Perhaps a bunch of guys in green, carrying machine guns? Do we imagine a person with a metal-detecting wand? Or do we think of thick glass window panes? All of these are security features. It is just that some are a little more intrusive than others. Each time we increase security, we become a little bit less friendly. We all have to decide how far we are willing (and able) to go. In the continuum of security, Call Detail Records are the least intrusive. No special usernames or passwords have to be remembered. No fear of big brother breathing down your customers' and users' necks need be felt. We are simply doing the same thing telephone companies do—tracking what calls were made, when they were made, how long they lasted, where they came from, and a few other bits of information. This information is then available for us to review at our leisure. Asterisk gives us a few options on how we track this information. The two major choices are flat-file logging and database logging. Flat-file CDR logging By default, Asterisk includes a module called cdr_csv. Right out of the box, Asterisk logs all calls coming in and going out. The information for these calls is placed in a Comma Separated Value (CSV) file. This CSV file is located in var/log/asterisk/cdr-csv. All information is available in Master.csv, and some channels can be configured to send some information to other files as well. The benefit of using a CSV file is the simplicity. Right after compiling and installing Asterisk, this method will work. No additional configuration is required. Also, no additional network traffic is generated, and no additional services have to be installed on our server. When using the CSV form of CDR, we will see lists and lists of values. They are not very easy to parse, so here is the format, in the order in which they appear: account code: As determined by the channel (for DAHDI) or the user (for IAX and SIP) source: The source of the call destination: The destination of the call destination context caller ID channel: The channel of the source destination channel: If applicable last application: The last application run on the channel last application argument: The last argument to the last application on the channel start time: The time the call commenced answer time: The time the call was answered end time: The time the call ended duration: The difference between start time and end time billable seconds: The difference between answer time and end time, which must be less than the duration disposition: Either ANSWERED, NO ANSWER, or BUSY amaflags: As set for the channel or user, like account code uniqueid: A unique call identifier userfield: A user field set by the SetCDRUserField command We see that there are many items of information logged for each and every call. We can compare the billable seconds with our phone bill at the end of the month to make sure they're close. We can look at the destination and figure out if the calls were authorized. This gives us enough information to answer most questions we may have about a phone call. While we have enough information to answer questions, finding that answer is not very easy. We would have to scan through the entire file to try to find anything. If we are going to use an accounting package or reporting software, CSV may be exactly what we need. However, if we wish to use it in a more ad hoc sort of way, it is not very readable. Database CDR logging If we wish to read our CDR logs, it is most easily accomplished when the records are sortable. The easiest way to do this is to store our CDR records in a database. Even in this, Asterisk gives us choices. Included with Asterisk is support for PostgreSQL databases. In order to be able to install this, we must first have the postgresql-devel package installed on our system. If you have to install this package, you'll need to reinstall Asterisk. The automake system will automatically detect that we have the capability to use PostgreSQL and compile that module for us. Aside from the development packages we have installed, we will also need a PostgreSQL server somewhere in our network. It can be the same machine as the Asterisk server, but it doesn't necessarily need to be. In fact, it probably makes sense to have only one such database server on our network, and we don't want to tie up too much of our PBX's resources with database maintenance and storage. There is a script in /usr/src/asterisk/contrib/scripts/ called postgres_cdr.sql, which creates the correct table structure for us. This script should be run from the database server. If we get an error message while rebuilding that says something like "cannot find-lz", then we need to install zlib-devel. Now that we have set up our database and installed the CDR module, we must configure Asterisk to use the correct database. In order to do this, we need to edit /etc/asterisk/cdr_pgsql.conf. All of the configuration variables are in the global section. Our file should look like the following: [global]hostname=dbserver.mydomain.tldport=5432dbname=asteriskpassword=supersecretuser=asteriskuser Once we have these variables set, the next time we restart Asterisk, all CDR records will be logged in the database. If PostgreSQL is not our database of choice, we can use MySQL. This is not a part of the normal distribution of Asterisk. But as we have already installed asterisk-addons, we should already have the ability to use MySQL for CDR logging. Before we compile, we need to make sure that we have mysql-devel installed. First, we need to decide which version we're going to use. Because of some license quibbles, MySQL version 4.0 and later is not in the automatic package distribution chain. Instead, if we do need to download it, we will have to get it directly from www.mysql.com. However, the older version (3.x) will work with Asterisk and hence, you may wish to take a look at the differences between what version 3 offered and what later versions give us. Other than the development package mentioned, we will also need a MySQL server somewhere in our network. Just as with PostgreSQL, we can choose to have it on the same server as Asterisk, but for the same reasons, we probably shouldn't. Next, on the database server, we need to create the database with a user and a table for the CDR data. We do this by running the following code: # mysqladmin create database asteriskcdrdb # mysqlmysql> GRANT ALL PRIVILEGES   -> ON asteriskcdrdb.*   -> TO asteriskcdruser   -> IDENTIFIED BY 'changethis2yourpassword';mysql> USE asteriskcdrdb;mysql> CREATE TABLE cdr (   -> uniqueid varchar(32) NOT NULL default '',   -> userfield varchar(255) NOT NULL default '',   -> accountcode varchar(20) NOT NULL default '',   -> src varchar(80) NOT NULL default '',   -> dst varchar(80) NOT NULL default '',   -> dcontext varchar(80) NOT NULL default '',   -> clid varchar(80) NOT NULL default '',   -> channel varchar(80) NOT NULL default '',   -> dstchannel varchar(80) NOT NULL default '',   -> lastapp varchar(80) NOT NULL default '',   -> lastdata varchar(80) NOT NULL default '',   -> calldate datetime NOT NULL default '0000-00-00 00:00:00',   -> duration int(11) NOT NULL default '0',   -> billsec int(11) NOT NULL default '0',   -> disposition varchar(45) NOT NULL default '',   -> amaflags int(11) NOT NULL default '0'-> ); That's all there is to it! We only have to do this once, so it's really not so bad. Next, we have to modify the /etc/asterisk/cdr_mysql.conf file to correctly reflect our choices. [global]hostname=ourdbserver.ourdomain.tlddbname=asteriskcdrdbpassword=changethis2yourpassworduser=asteriskcdruserport=3306userfield=1 The next time we restart Asterisk, our CDR information will be stored in the MySQL database. What does that give us? We now have the ability to use a number of very powerful tools to search our CDR records to find trends and patterns.
Read more
  • 0
  • 0
  • 1680

article-image-user-interaction-and-email-automation-symfony-13-part1
Packt
18 Nov 2009
14 min read
Save for later

User Interaction and Email Automation in Symfony 1.3: Part1

Packt
18 Nov 2009
14 min read
The signup module We want to provide the users with the functionality to enter their name, email address, and how they found our web site. We want all this stored in a database and to have an email automatically sent out to the users thanking them for signing up. To start things off, we must first add some new tables to our existing database schema. The structure of our newsletter table will be straightforward. We will need one table to capture the users' information and a related table that will hold the names of all the places where we advertised our site. I have constructed the following entity relationship diagram to show you a visual relationship of the tables: All the code used in this article can be accessed here. Let's translate this diagram into XML and place it in the config/schema.xml file: <table name="newsletter_adverts" idMethod="native" phpName="NewsletterAds"> <column name="newsletter_adverts_id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" /> <column name="advertised" type="VARCHAR" size="30" required="true" /> </table> <table name="newsletter_signups" idMethod="native" phpName="NewsletterSignup"> <column name="id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" /> <column name="first_name" type="VARCHAR" size="20" required="true" /> <column name="surname" type="VARCHAR" size="20" required="true" /> <column name="email" type="VARCHAR" size="100" required="true" /> <column name="activation_key" type="VARCHAR" size="100" required="true" /> <column name="activated" type="BOOLEAN" default="0" required="true" /> <column name="newsletter_adverts_id" type="INTEGER" required="true"/> <foreign-key foreignTable="newsletter_adverts" onDelete="CASCADE"> <reference local="newsletter_adverts_id" foreign="newsletter_adverts_id" /> </foreign-key> <column name="created_at" type="TIMESTAMP" required="true" /> <column name="updated_at" type="TIMESTAMP" required="true" /> </table> We will need to populate the newsletter_adverts table with some test data as well. Therefore, I have also appended the following data to the fixtures.yml file located in the data/fixtures/ directory: NewsletterAds: nsa1: advertised: Internet Search nsa2: advertised: High Street nsa3: advertised: Poster With the database schema and the test data ready to be inserted into the database, we can once again use the Symfony tasks. As we have added two new tables to the schema, we will have to rebuild everything to generate the models using the following command: $/home/timmy/workspace/milkshake>symfony propel:build-all-load --no-confirmation Now we have populated the tables in the database, and the models and forms have been generated for use too. Binding a form to a database table Symfony contains a whole framework just for the development of forms. The forms framework makes building forms easier by applying object-oriented methods to their development. Each form class is based on its related table in the database. This includes the fields, the validators, and the way in which the forms and fields are rendered. A look at the generated base class Rather than starting off with a simple form, we are going to look at the base form class that has already been generated for us as a part of the build task we executed earlier. Because the code is generated, it will be easier for you to see the initial flow of a form. So let's open the base class for the NewsletterSignupForm form. The file is located at lib/form/base/BaseNewsletterSignupForm.class.php: class BaseNewsletterSignupForm extends BaseFormPropel { public function setup() { $this->setWidgets(array( 'id' => new sfWidgetFormInputHidden(), 'first_name' => new sfWidgetFormInput(), 'surname' => new sfWidgetFormInput(), 'email' => new sfWidgetFormInput(), 'activation_key' => new sfWidgetFormInput(), 'activated' => new sfWidgetFormInputCheckbox(), 'newsletter_adverts_id' => new sfWidgetFormPropelChoice (array('model' => 'NewsletterAds', 'add_empty' => false)), 'created_at' => new sfWidgetFormDateTime(), 'updated_at' => new sfWidgetFormDateTime(), )); $this->setValidators(array( 'id' => new sfValidatorPropelChoice(array ('model' => 'NewsletterSignup', 'column' => 'id', 'required' => false)), 'first_name' => new sfValidatorString(array('max_length' => 20)), 'surname' => new sfValidatorString(array('max_length' => 20)), 'email' => new sfValidatorString(array('max_length' => 100)), 'activation_key' => new sfValidatorString(array('max_length' => 100)), 'activated' => new sfValidatorBoolean(), 'newsletter_adverts_id'=> new sfValidatorPropelChoice(array ('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id')), 'created_at' => new sfValidatorDateTime(), 'updated_at' => new sfValidatorDateTime(), )); $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); $this->errorSchema = new sfValidatorErrorSchema ($this->validatorSchema); parent::setup(); } There are five areas in this base class that are worth noting: This base class extends the BaseFormPropel class, which is an empty class. All base classes extend this class, which allows us to add global settings to all our forms. All of the columns in our table are treated as fields in the form, and are referred to as widgets. All of these widgets are then attached to the form by adding them to the setWidgets() method. Looking over the widgets in the array, you will see that they are pretty standard, such as sfWidgetFormInputHidden(), sfWidgetFormInput(). However, there is one widget added that follows the relationship between the newsletter_sigups table and the newsletter_adverts table. It is the sfWidgetFormPropelChoice widget. Because there is a 1:M relation between the tables, the default behavior is to use this widget, which creates an HTML drop-down box and is populated with the values from the newsletter_adverts table. As a part of the attribute set, you will see that it has set the model needed to retrieve the values to NewsletterAds and the newsletter_adverts_id column for the actual values of the drop-down box. All the widgets on the form must be validated by default. To do this, we have to call the setValidators() method and add the validation requirements to each widget. At the moment, the generated validators reflect the attributes of our database as set in the schema. For example, the first_name field in the statement 'first_name' => new sfValidatorString(array('max_length' => 20)) demonstrates that the validator checks if the maximum length is 20. If you remember, in our schema too, the first_name column is set to 20 characters. The final part calls the parent's setup() function. The base class BaseNewsletterSignupForm contains all the components needed to generate the form for us. So let's get the form on a page and take a look at the method to customize it. There are many widgets that Symfony provides for us. You can find the classes for them inside the widget/ directory of your Symfony installation. The Symfony propel task always generates a form class and its corresponding base class. Of course, not all of our tables will need to have a form bound to them. Therefore, delete all the form classes that are not needed. Rendering the form Rendering this basic form requires us to instantiate the form object in the action. Assigning the form object to the global $this variable means that we can pass the form object to the template just like any other variable. So let's start by implementing the newsletter signup module. In your terminal window, execute the generate:module task like this: $/home/timmy/workspace/milkshake>symfony generate:module frontend signup Now we can start with the application logic. Open the action class from apps/frontend/modules/signup/actions/actions.class.php for the signup module and add the following logic inside the index action: public function executeIndex(sfWebRequest $request) { $this->form = new NewsletterSignupForm(); return sfView::SUCCESS; } As I had mentioned earlier, the form class deals with the form validation and rendering. For the time being, we are going to stick to the default layout by allowing the form object to render itself. Using this method initially will allow us to create rapid prototypes. Let's open the apps/frontend/signup/templates/indexSuccess.php template and add the following view logic: <form action="<?php echo url_for('signup/submit') ?>" method="POST"> <table><?php echo $form ?></table> <input type="submit" /> </form> The form class is responsible for rendering of the form elements only. Therefore, we have to include the <form> and submit HTML tags that wrap around the form. Also, the default format of the form is set to 'table'. Again, we must also add the start and end tags of the <table>. At this stage, we would normally be able to view the form in the browser. But doing so will raise a Symfony exception error. The cause of this is that the results retrieved from the newsletter_adverts table are in the form of an array of objects. These results need to populate the select box widget. But in the current format, this is not possible. Therefore, we have to convert each object into its string equivalent. To do this, we need to create a PHP magic function of __toString() in the DAO class NewsletterAds. The DAO class for NewlsetterAds is located at lib/model/NewsletterAds.php just as all of the other models. Here we need to represent each object as its name, which is the value in the advertised column. Remember that we need to add this method to the DAO class as this represents a row within the results, unlike the peer class that represents the entire result set. Let's add the function to the NewsletterAds class as I have done here: class NewsletterAds extends BaseNewsletterAds { public function __toString() { return $this->getAdvertised(); } } We are now ready to view the completed form. In your web browser, enter the URL http://milkshake/frontend_dev.php/signup and you will see the result shown in the following screenshot: As you can see, although the form has been rendered according to our table structure, the fields which we do not want the user to fill in are also included. Of course, we can change this quiet easily. But before we take a look at the layout of the form, let's customize the widgets and widget validators. Now we can begin working on the application logic for submitting the form. Customizing form widgets and validators All of the generated form classes are located in the lib/form and the lib/form/base directories. The latter is where the default generated classes are located, and the former is where the customizable classes are located. This follows the same structure as the models. Each custom form class inherits from its parent. Therefore, we have to override some of the functions to customize the form. Let's customize the widgets and validators for the NewsletterSignupForm. Open the lib/forms/NewsletterSignupForm.class.php file and paste the following code inside the configure() method: //Removed unneeded widgets unset( $this['created_at'], $this['updated_at'], $this['activation_key'], $this['activated'], $this['id'] ); //Set widgets //Modify widgets $this->widgetSchema['first_name'] = new sfWidgetFormInput(); $this->widgetSchema['newsletter_adverts_id'] = new sfWidgetFormPropelChoice(array('model' => 'NewsletterAds', 'add_empty' => true, 'label'=>'Where did you find us?')); $this->widgetSchema['email'] = new sfWidgetFormInput (array('label' => 'Email Address')); //Add validation $this->setValidators(array ('first_name'=> new sfValidatorString(array ('required' => true), array('required' => 'Enter your firstname')), 'surname'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your surname')), 'email'=> new sfValidatorString(array('required' => true), array('invalid' => 'Provide a valid email', 'required' => 'Enter your email')), 'newsletter_adverts_id' => new sfValidatorPropelChoice(array('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id'), array('required' => 'Select where you found us')), )); //Set post validators $this->validatorSchema->setPostValidator( new sfValidatorPropelUnique(array('model' => 'NewsletterSignup', 'column' => array('email')), array('invalid' => 'Email address is already registered')) ); //Set form name $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); //Set the form format $this->widgetSchema->setFormFormatterName('list'); Let's take a closer look at the code. Removing unneeded fields To remove the fields that we do not want to be rendered, we must call the PHP unset() method and pass in the fields to unset. As mentioned earlier, all of the fields that are rendered need a corresponding validator, unless we unset them. Here we do not want the created_at and activation_key fields to be entered by the user. To do so, the unset() method should contain the following code: unset( $this['created_at'], $this['updated_at'], $this['activation_key'], $this['activated'], $this['id'] ); Modifying the form widgets Although it'll be fine to use the remaining widgets as they are, let's have a look at how we can modify them: //Modify widgets $this->widgetSchema['first_name'] = new sfWidgetFormInput(); $this->widgetSchema['newsletter_adverts_id'] = new sfWidgetFormPropelChoice(array('model' => 'AlSignupNewsletterAds', 'add_empty' => true, 'label'=>'Where did you find us?')); $this->widgetSchema['email'] = new sfWidgetFormInput(array('label' => 'Email Address')); There are several types of widgets available, but our form requires only two of them. Here we have used the sfWidgetFormInput() and sfWidgetFormPropelChoice() widgets. Each of these can be initialized with several values. We have initialized the email and newsletter_adverts_id widgets with a label. This basically renders the label field associated to the widget on the form. We do not have to include a label because Symfony adds the label according to the column name. Adding form validators Let's add the validators in a similar way as we have added the widgets: //Add validation $this->setValidators(array( 'first_name'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your firstname')), 'surname'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your surname')), 'email'=> new sfValidatorEmail(array('required' => true), array('invalid' => 'Provide a valid email', 'required' => 'Enter your email')), 'newsletter_adverts_id' => new sfValidatorPropelChoice(array ('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id'), array('required' => 'Select where you found us')), )); //Set post validators $this->validatorSchema->setPostValidator(new sfValidatorPropelUnique(array('model' => 'NewsletterSignup', 'column' => array('email')), array('invalid' => 'Email address is already registered')) ); Our form will need four different types of validators: sfValidatorString: This checks the validity of a string against a criteria. It takes four arguments—required, trim, min_length, and max_length. SfValidatorEmail: This validates the input against the pattern of an email address. SfValidatorPropelChoice: It validates the value with the values in the newsletter_adverts table. It needs the model and column that are to be used.   SfValidatorPropelUnique: Again, this validator checks the value against the values in a given table column for uniqueness. In our case, we want to use the NewsletterSignup model to test if the email column is unique. As mentioned earlier, all the fields must have a validator. Although it's not recommended, you can allow extra parameters to be passed in. To achieve this, there are two steps: You must disable the default option of having all fields validated by $this->validatorSchema->setOption('allow_extra_fields', true). Although the above step allows the values to bypass validation, they will be filtered out of the results. To prevent this, you will have to set $this->validatorSchema->setOption('filter_extra_fields', false). Form naming convention and setting its style The final part we added is the naming convention for the HTML attributes and the style in which we want the form rendered. The HTML output will use our naming convention. For example, in the following code, we have set the convention to newsletter_signup[fieldname] for each input field's name. //Set form name $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); //Set the form format $this->widgetSchema->setFormFormatterName('list'); Two formats are shipped with Symfony that we can use to render our form. We can either render it in an HTML table or an unordered list. As we have seen, the default is an HTML table. But by setting this as list, the form is now rendered as an unordered HTML list, just like the following screenshot. (Of course, I had to replace the <table> tags with the <ul> tags.)
Read more
  • 0
  • 0
  • 2207
article-image-restful-web-service-implementation-resteasy
Packt
18 Nov 2009
2 min read
Save for later

RESTful Web Service Implementation with RESTEasy

Packt
18 Nov 2009
2 min read
Getting the tools If you have already downloaded and installed Java's JDK and the Tomcat web server, you only need to download the JBoss's RESTEasy framework. Nevertheless, the complete list of the software needed for this article is as follows: Software Web Location Java JDK http://java.sun.com/ Apache Tomcat http://tomcat.apache.org/download-60.cgi Db4o http://developer.db4o.com/files/default.aspx RESTEasy Framework http://www.jboss.org/resteasy/ Install the latest Java JDK along with the latest version of Tomcat, if you haven't done so. Download and install Db4o and RESTEasy. Remember the location of the installs, as we'll need the libraries to deploy with the web application. RESTEasy — a JAX-RS implementation   RESTEasy is a full open source implementation of the JAX-RS specification. This framework works within any Java Servlet container, but because it's developed by JBoss, it offers extra features that are not part of the JAX-RS requirements. For example, RESTEasy offers out-of-the-box Atom support and also offers seamless integration with the EJB container portion of JBoss (none of these features are explored here). Web service architecture By now, you should be familiar with the coding pattern. Because we want to reuse a large portion of code already written, we have separate layers of abstraction. In this article, therefore, we only talk about the web layer and study in detail how to implement a full RESTful web service using RESTEasy. The full architecture of our web service looks as follows: In this diagram, we depict clients making HTTP requests to our web service. Each request comes to the web container, which then delegates the request to our RESTful layer that is composed of RESTEasy resource classes. The actual serialization of user and message records is delegated to our business layer, which in turns talks directly to our database layer (a Db4o database). Again, RESTEasy is a platform independent framework and works within any Servlet container. For this article we deploy our web service in Tomcat, as we've been working with it so far and are now familiar with deploying web applications to it, though we could as easily use the JBoss web container.
Read more
  • 0
  • 0
  • 3825

article-image-most-wanted-apache-myfaces-trinidad-12-tags-and-tag-attributes
Packt
18 Nov 2009
6 min read
Save for later

Most Wanted Apache MyFaces Trinidad 1.2 Tags and Tag Attributes

Packt
18 Nov 2009
6 min read
Component library structure Trinidad's approach to web technology is comprehensive: Aimed at full control of all the bits and pieces that make up a web application, little should be left that needs to be added. So based on such a closed world, Trinidad presents itself with a wealth of components and tags that even include very basic XHTML tags as replacements for the real XHTML originals. This is no radical replacement approach, rather it enables Trinidad to remain in full control of mechanisms such as partial-page rendering (PPR, also generally known as Ajax) that otherwise would need to deal with potentially incompatible libraries externally. The following image provides an outline of Trinidad's structural package design: Trinidad is divided into the following two namespaces: tr: It is the usual tag library id that references Trinidad's core library tags. It's a large library of over 100 components ranging from layout components and navigational components, to special viewer components that all implicitly support skinning, partial-page rendering, popup dialogs, error or info messaging, and so on. trh: It is the usual tag library id that references Trinidad's XHTML support library tags, a small companion that offers alternatives for those XHTML tags that are usually applied to build XHTML structures, for example, XHTML tables. Let us take a closer look at both namespaces. The upcoming image shows the core API's hierarchical structure. The tags are backed by two types of Trinidad classes—UIX* classes that deal with the JSF component requirements to implement specific JSF lifecycle processing methods, and Core* classes that deal with the specific properties (getters or setters). Trinidad’s XHTML tag library namespace (trh) Two groups can be distinguished from the trh namespace. The first one deals with the definition of an XHTML page and provides the developer with the following tags: <trh:html>: It is used to define the whole XHTML page, analogous to <html> <trh:head>: It is used to define the header, analogous to <head> <trh:body>: It is used to define the main contents, analogous to <body> <trh:script>: It is used to define a JavaScript to be executed, analogous to <script> <trh:tableLayout>: It is used to define an XHTML table. <trh:rowLayout>: It is used to define an XHTML table line, analogous to <tr>; note that it can also be used to display an arbitrary line, particularly when elements need to be kept in one and the same line. Alternatively, it is particularly interesting to look at the tr namespace as it provides some less heavy structures free from table constructions, for instance panelGroupLayout with a layout set to vertical or a panelBorderLayout, both generating div structures instead. <trh:cellFormat>: It is used to define an XHTML table cell as part of an XHTML table. The attributes of each tag are defined in a most consistent, and thus recognizable way. By the way, there are also tags for the construction of framesets such as trh:frame in case anyone still wants to make use of framesets. However, before we deal with the attributes let us conclude this structural overview by a look at the organization of the functionality of the core tag library. Trinidad’s core tag library namespace (tr) The following groups can be functionally distinguished which is also reflected in the packages structure of Trinidad's API (all beginning with org.apache.myfaces.trinidad.component; which has been left out here to avoid repetition). Note that, for completeness, we will also include information on the pure Java side as well as information on the few components that stem from the trh namespace: Basic document composition tags from the core API: document, stylesheet, form, subform. poll also appears here although it is not a composition tag. Form input and display tags, components from the core.input API: inputText, inputDate, inputListOfValues, and so on. Command or navigation tags from core.nav that includes two tag types: One that is focused on command tags that assumes a given form, presupposing the use of form and display tags from the foregoing group—commandButton, commandLink, goButton, goLink, and so on. The other deals exclusively with navigation: navigationTree, navigationPane, breadCrumbs, and so on. Large input and output component tags from core.data, for example, table, tree, and treeTable components. Layout component tags from core.layout, for example, all the swing-like panel tags, such as panelBorderLayout, panelHorizontalLayout, panelAccordion, showDetail, showDetailItem, and so on. Basic output components from core.output that are almost always used in a web application, for example, messages, outputText, outputLabel, spacer, statusIndicator, and so on. Model objects from core.model devised for various tags ; they provide the corresponding view models for their tag viewer counterparts, for example, SortableModel, CollectionModel and RowKeySet for tr:table, ChildPropertyTreeModel for tr:tree and ChartModel for tr:chart. A couple of converter components from trinidad.convert equip JSF and Trinidad input components with powerful JSF conversion, that is, convertNumber and convertDateTime. Validator components from trinidad.validator equip JSF and Trinidad input components with powerful JSF validation such as range validation (validateDateTimeRange) and validation by regular expression match (validateRegExp). Events and event listeners from trinidad.event add new event types and listeners specific for Trinidad components such as those that support Trinidad's dialog framework, for example, commandButton to launch a popup dialogue using LaunchEvent, ReturnEvent, and ReturnListener. It provides only a few tags, but these can be very utile, for example, fileDownloadActionListener, resetActionListener, returnActionListener, and setActionListener. There is a lot more to be found on the pure Java API side that either surfaces indirectly on the tag library as attributes, or is used implicitly by the tags themselves. Furthermore, there are utility classes and context support classes—RequestContext being probably the most prominent one because it offers a lot of functionality, for example, PPR from the server side. The following figure illustrates the Java side of things (it shows what the structure of some of the classes behind core.input look like): The preceding figure is an outline of the core.input API hierarchy. Again, we can see the typical UIX* and Core* structure. Finally, let us take a closer look at the tag attributes.
Read more
  • 0
  • 0
  • 1913
Modal Close icon
Modal Close icon