DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

The Latest Data Engineering Topics

article thumbnail
Hibernate - Tuning Queries Using Paging, Batch Size, and Fetch Joins
This article covers queries - in particular a tuning test case and the relations between simple queries, join fetch queries, paging query results, and batch size. Paging the Query Results I will start with a short introduction about paging in EJB3: To support paging the EJB3 Query interface defines the following two methods: setMaxResults - sets the number of maximum rows to retrieve from the database setFirstResult - sets the first row to retrieve For example if our GUI displays a list of customers and we have 500,000 customers (database rows) in out database we wouldn't like to display all 500,000 records is one view (even if we put performance considerations aside - nobody can do anything with a list of 500,000 rows). The GUI design would usually include paging - we break the list of records to display into logical pages (for example 100 records per page) and the user can navigate between pages (same as Google's results navigator down the search page). When using the paging support it is important to remember that the query has to be sorted otherwise we can't be sure that when fetching the "next page" it will really be the next page (since in the absence of the 'order by' clause form a SQL query the order in which rows are fetch is unpredictable). Here is a sample use, for fetching the first tow pages of 100 rows each: Query q = entityManager.createQuery("select c from Customer c order by c.id"); q.setFirstResult(0).setMaxResults(100); .... next page ... Query q = entityManager.createQuery("select c from Customer c order by c.id"); q.setFirstResult(100).setMaxResults(100); This is a simple API and it's important (for performance) to remember using it when we need to fetch only parts of the results. Test Case Description This test cased is based on a real tuning I did for an application, I just changed the class names to Customer and Order. Let's assume that I have a Customer entity with a set of orders (lazily fetched - but it happens in eager fetch as well) and we need to: Fetch customers and their orders Do it in a "paging mode" - 100 customers per page Tuning Requirement #1 - Fetch Customers and Their Orders There are two possibilities to perform this kind of fetch: Simple select: select c from customer c order by c.id Join fetch: select distinct c from Customer c left outer join fetch c.orders order by c.id The simple select is as simple as it can be, we load a list of customers with a proxy collection in their orders field. The orders collection will be filled with data once I access it (for example c.getOrders().getSize() ). The 'join fetch' means that we want to fetch an association as an integral part of the query execution. The joined fetched entities (in the example above: c.orders) must be part of an association that is referenced by an entity returned from the query (in the example above: c). The 'join fetch' is one of the tools used for improving queries performance (see more in here). The Hibernate core documentations explains that "a 'fetch' join allows associations or collections of values to be initialized along with their parent objects, using a single select" (see here). I have in my database 18,998 customer records, each with few orders. Let's compare execution time for the two queries. My code looks the same for both queries (except of the query itself), I execute the query, then I iterate the results checking the size of of each customer orders collection and print the execution time and number of records fetch (as a sanity for the query syntax): Query q = entityManager.createQuery(queryStr); long a = System.currentTimeMillis(); List l = q.getResultList(); for (Customer c : l) { c.getOrders().size(); } long b = System.currentTimeMillis(); System.out.println("Execution time: " + (b - a)+ "; Number of records fetch: " + l.size() ); And to the numbers (avg. 3 executions): Simple select: 24,984 millis Join fetch: 1,219 millis The join fetch query execution time was 20 times faster(!) than the simple query. The reason is obvious, using the join fetch select I had only one round trip to the database. While using a simple select I had to fetch the customers (1 round trip to the database) and each time I accessed a collection I had another round trip (that's 18,998 additional round trips!). The winner is 'join fetch'. But does it? wait for the next one - the paging... Tuning Requirement #2 - Use Paging The second requirement was to do it in paging - each page will have 100 customers (so we will have 18,900/100+1 pages - the last page has 98 customers). So let's change the code above a little bit: Query q = entityManager.createQuery(queryStr); q.setFirstResult(pageNum*100).setMaxResults(100); long a = System.currentTimeMillis(); List l = q.getResultList(); for (Customer c : l) { c.getOrders().size(); } long b = System.currentTimeMillis(); System.out.println("Execution time: " + (b - a)+ "; Number of records fetch: " + l.size() ); I added the second line which limits the query result to a specific page with up to 100 records per page. And the numbers are (avg. 3 executions): Simple select: 328 millis Join fetch: 1,660 millis The wheel has turned over. Why? First a quote from the EJB3 Persistence specification: "The effect of applying setMaxResults or setFirstResult to a query involving fetch joins over collections is undefined" (section 3.6.1 - Query Interface) We could have stopped here but it is interesting to understand the issue and to see what Hibernate does. To implement the paging features Hibernate delegates the work to the database using its syntax to limit the number of records fetched by the query. Each database has its own proprietary syntax for limiting the number of fetched records, some examples: Postgres uses LIMIT and OFFSET Oracle has rownum MySQL uses its version of LIMIT and OFFSET MSSQL has the TOP keyword in the select and so on The important thing to remember here is meaning of such limit: the database returns a subset of the query result. So if we asked for the first 100 customers which their names contain 'Eyal' the outcome is logically the same as building a table in memory out of all customers that match the criteria and take from there the first 100 rows. And here is the catch: if the query with the limit includes a join clause for a collection than the first 100 row in the "logical table" will not necessarily be the first 100 customers. the outcome of the join might duplicate customers in the "logical tables" but the database doesn't aware or care about that - it performs operations on tables not on objects!. For example think of the extreme case, the customer 'Eyal' has 100 orders. The query will return 100 rows, hibernate will identify that all belong to the same customer and return only one Customer as the query result - this is not what we were asking for. This also works, of course, the other way around. If a customer had more than 100 orders and the result set size was limited to 100 rots the orders collection would not contain all of the customer's orders. To deal with that limitation Hibernate actually doesn't issue an SQL statement with a LIMIT clause. Instead it fetches all of the records and performs the paging in memory. This explains why using the 'join fetch' statement with paging took more than the one without paging - the delta is the in-memory paging done by Hibernate. If you look at Hibernate logs you will find the next warning issued by Hibernate: WARNING: firstResult/maxResults specified with collection fetch; applying in memory! Final Tuning - BatchSize Does it mean that in the case of paging we shouldn't use a join fetch? usually it does (unless your page size is very close to the actual number of records). But even if you use a simple select this is a classic case for using the @BatchSize annotation. If my session/entity manager has 100 customers attached to it than, be default, for each first access to one of the customers' order collection Hibernate will issue a SQL statement to fill that collection. At the end I will execute 100 statements to fetch 100 collections. You can see it in the log: Hibernate: /* select c from Customer c order by c.id */ select customer0_.id as id0_, customer0_.ccNumber as ccNumber0_, customer0_.name as name0_, customer0_.fixedDiscount as fixedDis5_0_, customer0_.DTYPE as DTYPE0_ from CUSTOMERS customer0_ order by customer0_.id limit ? offset ? Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id=? Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id=? Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id=? ............ Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id=? Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id=? Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id=? The @BatchSize annotation can be used to define how many identical associations to populate in a single database query. If the session has 100 customers attached to it and the mapping of the 'orders' collection is annotated with @BatchSize of size n. It means that whenever Hibernate needs to populate a lazy orders collection it checks the session and if it has more customers which their orders collections need to be populated it fetches up to n collections. Example: if we had 100 customers and the batch size was set to 16 when iterating over the customers to get their number of orders hibernate will go to the database only 7 times (6 times to fetch 16 collections and one more time to fetch the 4 remaining collections - see the sample below). If our batch size was set to 50 it would go only twice. @OneToMany(mappedBy="customer",cascade=CascadeType.ALL, fetch=FetchType.LAZY) @BatchSize(size=16) private Set orders = new HashSet(); And in the log: Hibernate: /* select c from Customer c order by c.id */ select customer0_.id as id0_, customer0_.ccNumber as ccNumber0_, customer0_.name as name0_, customer0_.fixedDiscount as fixedDis5_0_, customer0_.DTYPE as DTYPE0_ from CUSTOMERS customer0_ order by customer0_.id limit ? offset ? Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: /* load one-to-many par2.Customer.orders */ select orders0_.customer_id as customer4_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.customer_id as customer4_1_0_, orders0_.description as descript2_1_0_, orders0_.orderId as orderId1_0_ from ORDERS orders0_ where orders0_.customer_id in (?, ?, ?, ?) Back to our test case. In my example setting the batch size to 100 looks like a nice tuning opportunity. And indeed when setting it to 100 the total execution time dropped to 188 millis (that's an 132 (!!!) times faster than worse result we had). The batch size can also be set globally by setting the hibernate.default_batch_fetch_size property for the session factory. From http://www.jroller.com/eyallupu/
June 9, 2008
by Eyal Lupu
· 256,367 Views · 7 Likes
article thumbnail
Taking the New Swing Tree Table for a Spin
Announcing the new Swing Tree Table yesterday, Tim Boudreau writes: Usage is incredibly easy - you just provide a standard Swing TreeModel of whatever sort you like, and an additional RowModel that can be queried for the other columns contents, editability and so forth. I found an example from some time ago, by Tim, and have been playing with it to get used to this new development. The result is as follows: To get started, I simply download the latest NetBeans IDE development build from netbeans.org and then attached the platform8/org-netbeans-swing-outline.jar to my Java SE project. For the rest, I wasn't required to do anything with NetBeans, necessarily. I could have attached the JAR to a project in Eclipse or anywhere else. Then I created a JFrame. To work with this Swing tree table, you need to provide the new "org.netbeans.swing.outline.Outline" class with the new "org.netbeans.swing.outline.OutlineModel" which, in turn, is built from a plain old javax.swing.tree.TreeModel, together with the new "org.netbeans.swing.outline.RowModel". Optionally, to change the default rendering, you can use the new "org.netbeans.swing.outline.RenderDataProvider". Let's first create a TreeModel for accessing files on disk. We will receive the root of the file system as a starting point: private static class FileTreeModel implements TreeModel { private File root; public FileTreeModel(File root) { this.root = root; } @Override public void addTreeModelListener(javax.swing.event.TreeModelListener l) { //do nothing } @Override public Object getChild(Object parent, int index) { File f = (File) parent; return f.listFiles()[index]; } @Override public int getChildCount(Object parent) { File f = (File) parent; if (!f.isDirectory()) { return 0; } else { return f.list().length; } } @Override public int getIndexOfChild(Object parent, Object child) { File par = (File) parent; File ch = (File) child; return Arrays.asList(par.listFiles()).indexOf(ch); } @Override public Object getRoot() { return root; } @Override public boolean isLeaf(Object node) { File f = (File) node; return !f.isDirectory(); } @Override public void removeTreeModelListener(javax.swing.event.TreeModelListener l) { //do nothing } @Override public void valueForPathChanged(javax.swing.tree.TreePath path, Object newValue) { //do nothing } } The above could simply be set as a JTree's model and then you'd have a plain old standard JTree. It would work, no problems, it would be a normal JTree. However, it wouldn't be a tree table since you'd only have a tree, without a table. Therefore, let's now add two extra columns, via the new "org.netbeans.swing.outline.RowModel" class, which will enable the creation of a tree table instead of a tree: private class FileRowModel implements RowModel { @Override public Class getColumnClass(int column) { switch (column) { case 0: return Date.class; case 1: return Long.class; default: assert false; } return null; } @Override public int getColumnCount() { return 2; } @Override public String getColumnName(int column) { return column == 0 ? "Date" : "Size"; } @Override public Object getValueFor(Object node, int column) { File f = (File) node; switch (column) { case 0: return new Date(f.lastModified()); case 1: return new Long(f.length()); default: assert false; } return null; } @Override public boolean isCellEditable(Object node, int column) { return false; } @Override public void setValueFor(Object node, int column, Object value) { //do nothing for now } } Now, after dragging-and-dropping an Outline object onto your JFrame (which is possible after adding the beans from the JAR to the NetBeans IDE Palette Manager) which, in turn, automatically creates a JScrollPane as well, this is how you could code the JFrame's constructor: public NewJFrame() { //Initialize the ui generated by the Matisse GUI Builder, which, //for example, adds the JScrollPane to the JFrame ContentPane: initComponents(); //Here I am assuming we are not on Windows, //otherwise use Utilities.isWindows() ? 1 : 0 //from the NetBeans Utilities API: TreeModel treeMdl = new FileTreeModel(File.listRoots()[0]); //Create the Outline's model, consisting of the TreeModel and the RowModel, //together with two optional values: a boolean for something or other, //and the display name for the first column: OutlineModel mdl = DefaultOutlineModel.createOutlineModel( treeMdl, new FileRowModel(), true, "File System"); //Initialize the Outline object: outline1 = new Outline(); //By default, the root is shown, while here that isn't necessary: outline1.setRootVisible(false); //Assign the model to the Outline object: outline1.setModel(mdl); //Add the Outline object to the JScrollPane: jScrollPane1.setViewportView(outline1); } Alternatively, without the NetBeans Matisse GUI Builder and NetBeans Palette Manager, i.e., simply using a standard Java class, you could do something like this: private Outline outline; public NewJFrame() { setDefaultCloseOperation(EXIT_ON_CLOSE); getContentPane().setLayout(new BorderLayout()); TreeModel treeMdl = new FileTreeModel(File.listRoots()[0]); OutlineModel mdl = DefaultOutlineModel.createOutlineModel( treeMdl, new FileRowModel(), true); outline = new Outline(); outline.setRootVisible(false); outline.setModel(mdl); getContentPane().add(new JScrollPane(outline),BorderLayout.CENTER); setBounds(20, 20, 700, 400); } At this point, you can run the JFrame, with this result: So, we see a lot of superfluous info that doesn't look very nice. Let's implement "org.netbeans.swing.outline.RenderDataProvider", as follows: private class RenderData implements RenderDataProvider { @Override public java.awt.Color getBackground(Object o) { return null; } @Override public String getDisplayName(Object o) { return ((File) o).getName(); } @Override public java.awt.Color getForeground(Object o) { File f = (File) o; if (!f.isDirectory() && !f.canWrite()) { return UIManager.getColor("controlShadow"); } return null; } @Override public javax.swing.Icon getIcon(Object o) { return null; } @Override public String getTooltipText(Object o) { File f = (File) o; return f.getAbsolutePath(); } @Override public boolean isHtmlDisplayName(Object o) { return false; } } Now, back in the constructor, add the renderer to the outline: outline1.setRenderDataProvider(new RenderData()); Run the JFrame again and the result should be the same as in the first screenshot above. Look again at the rendering code and note that, for example, you have tooltips:
June 4, 2008
by Geertjan Wielenga
· 84,079 Views
article thumbnail
HashMap is not a Thread-Safe Structure
Last few months I have seen too much code where a HashMap (without any extra synchronization) is used instead of a thread-safe alternative like the ConcurrentHashMap or the less concurrent but still thread-safe HashTable. This is an example of a HashMap used in a home grown cache (used in a multi-threaded environment): interface ValueProvider{V retrieve(K key);}public class SomeCache{private Map map = new HashMap();private ValueProvider valueProvider;public SomeCache(ValueProvider valueProvider){this.valueProvider = valueProvider;}public V getValue(K key){V value = map.get(key);if(value == null){value = valueProvider.get(key);if(value!=null)map.put(key,value);}return value;} There is much wrong with this innocent looking piece of code. There is no happens before relation between the put of the value in the map, and the get of the value. This means that a thread that receives the value from the cache, doesn’t need to see all fields if the value has publication problems (most non thread-safe structures have publication problems). The same goes for the value and the internals (the buckets for example) of the HashMap. This means that updates to the internals of the HashMap while putting, don’t need to be visible to a thread that does the get. So it could be that the state of the cache in main memory is not in an allowed state (some of the changes maybe are stuck in the cpu-cache), and the cache could start behaving erroneous and if you are lucky starts throwing exceptions. And last, but certainly not least, there also is a classic race problem: if 2 threads do a interleaved map.put, the internals of the HashMap can get in an inconsistent state. In most cases an application reboot/redeploy would be the only way to fix this problem. There are other problems with the cache behavior of this code as well. The items don’t have a timeout, so once a value gets in the cache, it stays in the cache. In practice this could lead to web-page that keeps displaying some value, even though in the main repository the value has been updated. An application reboot also is the only way to solve this problem. Using a Common Of The Shelf (COTS) cache would be a much saver solution, even though a new library needs to be added. It is important to realize that a HashMap can be used perfectly in a multi-threaded environment if extra synchronization is added. But without extra synchronization, it is a time-bomb waiting to go off.
May 29, 2008
by Peter Veentjer
· 64,920 Views
article thumbnail
Understanding HBase and BigTable
The hardest part about learning Hbase (the open source implementation of Google's BigTable), is just wrapping your mind around the concept of what it actually is. I find it rather unfortunate that these two great systems contain the words table and base in their names, which tend to cause confusion among RDBMS indoctrinated individuals (like myself). This article aims to describe these distributed data storage systems from a conceptual standpoint. After reading it, you should be better able to make an educated decision regarding when you might want to use Hbase vs when you'd be better off with a "traditional" database. It's all in the terminology Fortunately, Google's BigTable Paper clearly explains what BigTable actually is. Here is the first sentence of the "Data Model" section: A Bigtable is a sparse, distributed, persistent multidimensional sorted map. Note: At this juncture I like to give readers the opportunity to collect any brain matter which may have left their skulls upon reading that last line. The BigTable paper continues, explaining that: The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted array of bytes. Along those lines, the HbaseArchitecture page of the Hadoop wiki posits that: HBase uses a data model very similar to that of Bigtable. Users store data rows in labelled tables. A data row has a sortable key and an arbitrary number of columns. The table is stored sparsely, so that rows in the same table can have crazily-varying columns, if the user likes. Although all of that may seem rather cryptic, it makes sense once you break it down a word at a time. I like to discuss them in this sequence: map, persistent, distributed, sorted, multidimensional, and sparse. Rather than trying to picture a complete system all at once, I find it easier to build up a mental framework piecemeal, to ease into it... map At its core, Hbase/BigTable is a map. Depending on your programming language background, you may be more familiar with the terms associative array (PHP), dictionary (Python), Hash (Ruby), or Object (JavaScript). From the wikipedia article, a map is "an abstract data type composed of a collection of keys and a collection of values, where each key is associated with one value." Using JavaScript Object Notation, here's an example of a simple map where all the values are just strings: { "zzzzz" : "woot", "xyz" : "hello", "aaaab" : "world", "1" : "x", "aaaaa" : "y" } persistent Persistence merely means that the data you put in this special map "persists" after the program that created or accessed it is finished. This is no different in concept than any other kind of persistent storage such as a file on a filesystem. Moving along... distributed Hbase and BigTable are built upon distributed filesystems so that the underlying file storage can be spread out among an array of independent machines. Hbase sits atop either Hadoop's Distributed File System (HDFS) or Amazon's Simple Storage Service (S3), while a BigTable makes use of the Google File System (GFS). Data is replicated across a number of participating nodes in an analogous manner to how data is striped across discs in a RAID system. For the purpose of this article, we don't really care which distributed filesystem implementation is being used. The important thing to understand is that it is distributed, which provides a layer of protection against, say, a node within the cluster failing. sorted Unlike most map implementations, in Hbase/BigTable the key/value pairs are kept in strict alphabetical order. That is to say that the row for the key "aaaaa" should be right next to the row with key "aaaab" and very far from the row with key "zzzzz". Continuing our JSON example, the sorted version looks like this: { "1" : "x", "aaaaa" : "y", "aaaab" : "world", "xyz" : "hello", "zzzzz" : "woot" } Because these systems tend to be so huge and distributed, this sorting feature is actually very important. The spacial propinquity of rows with like keys ensures that when you must scan the table, the items of greatest interest to you are near each other. This is important when choosing a row key convention. For example, consider a table whose keys are domain names. It makes the most sense to list them in reverse notation (so "com.jimbojw.www" rather than "www.jimbojw.com") so that rows about a subdomain will be near the parent domain row. Continuing the domain example, the row for the domain "mail.jimbojw.com" would be right next to the row for "www.jimbojw.com" rather than say "mail.xyz.com" which would happen if the keys were regular domain notation. It's important to note that the term "sorted" when applied to Hbase/BigTable does not mean that "values" are sorted. There is no automatic indexing of anything other than the keys, just as it would be in a plain-old map implementation. multidimensional Up to this point, we haven't mentioned any concept of "columns", treating the "table" instead as a regular-old hash/map in concept. This is entirely intentional. The word "column" is another loaded word like "table" and "base" which carries the emotional baggage of years of RDBMS experience. Instead, I find it easier to think about this like a multidimensional map - a map of maps if you will. Adding one dimension to our running JSON example gives us this: { "1" : { "A" : "x", "B" : "z" }, "aaaaa" : { "A" : "y", "B" : "w" }, "aaaab" : { "A" : "world", "B" : "ocean" }, "xyz" : { "A" : "hello", "B" : "there" }, "zzzzz" : { "A" : "woot", "B" : "1337" } } In the above example, you'll notice now that each key points to a map with exactly two keys: "A" and "B". From here forward, we'll refer to the top-level key/map pair as a "row". Also, in BigTable/Hbase nomenclature, the "A" and "B" mappings would be called "Column Families". A table's column families are specified when the table is created, and are difficult or impossible to modify later. It can also be expensive to add new column families, so it's a good idea to specify all the ones you'll need up front. Fortunately, a column family may have any number of columns, denoted by a column "qualifier" or "label". Here's a subset of our JSON example again, this time with the column qualifier dimension built in: { // ... "aaaaa" : { "A" : { "foo" : "y", "bar" : "d" }, "B" : { "" : "w" } }, "aaaab" : { "A" : { "foo" : "world", "bar" : "domination" }, "B" : { "" : "ocean" } }, // ... } Notice that in the two rows shown, the "A" column family has two columns: "foo" and "bar", and the "B" column family has just one column whose qualifier is the empty string (""). When asking Hbase/BigTable for data, you must provide the full column name in the form ":". So for example, both rows in the above example have three columns: "A:foo", "A:bar" and "B:". Note that although the column families are static, the columns themselves are not. Consider this expanded row: { // ... "zzzzz" : { "A" : { "catch_phrase" : "woot", } } } In this case, the "zzzzz" row has exactly one column, "A:catch_phrase". Because each row may have any number of different columns, there's no built-in way to query for a list of all columns in all rows. To get that information, you'd have to do a full table scan. You can however query for a list of all column families since these are immutable (more-or-less). The final dimension represented in Hbase/BigTable is time. All data is versioned either using an integer timestamp (seconds since the epoch), or another integer of your choice. The client may specify the timestamp when inserting data. Consider this updated example utilizing arbitrary integral timestamps: { // ... "aaaaa" : { "A" : { "foo" : { 15 : "y", 4 : "m" }, "bar" : { 15 : "d", } }, "B" : { "" : { 6 : "w" 3 : "o" 1 : "w" } } }, // ... } Each column family may have its own rules regarding how many versions of a given cell to keep (a cell is identified by its rowkey/column pair) In most cases, applications will simply ask for a given cell's data, without specifying a timestamp. In that common case, Hbase/BigTable will return the most recent version (the one with the highest timestamp) since it stores these in reverse chronological order. If an application asks for a given row at a given timestamp, Hbase will return cell data where the timestamp is less than or equal to the one provided. Using our imaginary Hbase table, querying for the row/column of "aaaaa"/"A:foo" will return "y" while querying for the row/column/timestamp of "aaaaa"/"A:foo"/10 will return "m". Querying for a row/column/timestamp of "aaaaa"/"A:foo"/2 will return a null result. sparse The last keyword is sparse. As already mentioned, a given row can have any number of columns in each column family, or none at all. The other type of sparseness is row-based gaps, which merely means that there may be gaps between keys. This, of course, makes perfect sense if you've been thinking about Hbase/BigTable in the map-based terms of this article rather than perceived similar concepts in RDBMS's. And that's about it Well, I hope that helps you understand conceptually what the Hbase data model feels like. As always, I look forward to your thoughts, comments and suggestions.
May 22, 2008
by Jim Wilson
· 85,091 Views · 5 Likes
article thumbnail
Getting to Know Immutable Data Structures - Immutability and Concurrency – Part I
When asking the question how does functional programming help me with concurrent programming? The standard response tends to be functional programming use immutable data structures, read-only data structures can be shared between threads without issues, end of problem. Except it isn’t. Immutable data structures have a different set of problems associated with them when working on concurrent problems. This post will examine what these problems are, and then show that this is just a special case of a more general set of problems when working with immutable data structures. Finally will start taking a look at how we solve some of these problems, but in a single thread environment first of all. First let’s frame the problem by looking at how imperative programs work with threads. In a classic imperative/OO languages programmers tend to use either instance or static member variables to send messages between threads, let’s look a fragment of C# that does something classically multi-threaded: object workQueueLock = new object(); Queue workQueue = new Queue(); // this method runs on its own thread private void Worker() { while (true) { // define a work item attempt to retrive work from the queue WorkItem item = null; lock (workQueueLock) { if (workQueue.Count > 0) { item = workQueue.Dequeue(); } } // check if we have work to do, otherwise sleep if (item != null) { // do some work } else { Thread.Sleep(QueuePollInterval); } } } // an even that resets our flag private void Some_Event(object sender, WorkEventArgs ea) { lock (workQueueLock) { workQueue.Enqueue(ea.WorkItem); } } I wouldn’t recommend you use this naive version of a work queue, but the above code is straight forward enough to understand easily and illustrate the typical way imperative programs communicate between threads. We have a member variable “workQueue” that controls stores the work to be done, the method “Worker” is designed to read from this queue and if there’s some work to do, do the work, otherwise sleep till it’s time to poll the queue again. We use “workQueue” again in “Some_event” to send a message to “Worker”, to enqueue some work for it to do. It’s easy to see that mutation of the variable “workQueue” is essential to get this to work; if we couldn’t change the content of “workQueue” then we couldn’t send the message. It’s also easy to see that we now have a huge number of implementation choices: Do we lock on the queue, or have separate lock? What’s the shortest possible time we can hold the lock for (to avoid other threads be blocked when they want to write to the queue)? How long do we sleep for before polling the queue? Too shorter time and we risk wasting too much processor time polling the queue, too longer time and risk that the queue because unreactive because the worker wastes too much time sleeping when there’s work to be done. In pure functional programming there are no variables or mutation, so the above scenario simply isn’t possible. Sure, F# isn’t a pure function language, actually most functional languages aren’t, so you can indeed use mutable data structures to implement something similar to the C# fragment we showed earlier, but that’s not the point we want to learn how to use immutable data structures. To fully understand the limitations of immutable data structures, let’s look at another C# example do something simpler. Imagine that we want compute a key of a value then store it in a member variable, a dictionary in this case, for later use: Dictionary myDict = new Dictionary(); public void ReceiveValue(string val) { myDict.Add(ComputerKey(val), val); } Now let’s think about how we can translate this into F#. Firstly, if don’t mind being dirty and mutable we can translate this fragment verbatim: type Store() = let myDict = new Dictionary() member x.ReceiveValue (value:string) = myDict.Add(x.ComputeKey value, value) However, if we don’t want to be mutable it’s not quite so straight forward. F# contains a type called “Map”, which is very similar to a Dictionary except that it is immutable. When you add a new item to a map you don’t change the map you create a new version of the map with the new key added. So here is how our store class would look to if we used an immutable “Map” data structure: type ComputeKeys(myDict:Map) = member x.ReceiveValue (value:string) = new ComputeKeys(myDict.Add(x.ComputeKey value, value)) The important thing to notice is that we now have no “let” definition where we store our dictionary; instead the dictionary is passed to the class constructor. So our constructor receives a “Map”, and when we use our “ReceiveValue” method we create a new instance of the “ComputerKeys” which contains the newly created value. I think the type signature really helps us understand what’s going on: type ComputeKeys = class end with member ReceiveValue : value:string -> ComputeKeys new : myDict:Map -> ComputeKeys end This is pretty much the revelation of immutable data structures, “let” definitions become merely short conveniences for values, not memory location that can be updated at a later data if we want to. These new values are all held on the threads stack, if were being pure and fully immutable that we have no memory locations that we can write them to. Okay let’s have a look at how we might use these two classes: /// wraps a Dictionary to provide /// some hashing and printing functions type Store() = // the dictionary that stores the values let myDict = new Dictionary() /// receive a value, hash it store it member x.ReceiveValue (value:string) = myDict.Add(x.ComputeKey value, value) /// computers the hash (a bit naff for now) member x.ComputeKey (value:string) = value.GetHashCode().ToString() /// prints the stored values override x.ToString() = let stringWriter = new StringWriter() for key in myDict.Keys do stringWriter.WriteLine("{0}: {1}", key, myDict.[key]) stringWriter.ToString() let useStore() = let store = new Store() store.ReceiveValue("One") store.ReceiveValue("Two") store.ReceiveValue("Three") printfn "%s" (store.ToString()) The mutable version needs little explanation, it is classical imperative programming, we create an instance of store then add values to our store, and finally we print them out. Now compare this with the immutable version: /// wraps a Map to provide /// some hashing and printing functions type ComputeKeys(myDict:Map) = /// receive a value, hash it, return the new value member x.ReceiveValue (value:string) = new ComputeKeys(myDict.Add(x.ComputeKey value, value)) /// computers the hash (a bit naff for now) member x.ComputeKey (value:string) = value.GetHashCode().ToString() /// prints values in the map override x.ToString() = myDict.Fold (fun key value acc -> Printf.sprintf "%s \r\n%s: %s" acc key value) "" let useComputeKeys() = let keysEmpty = new ComputeKeys(Map.empty) let keysOne = keysEmpty.ReceiveValue("One") let keysTwo = keysOne.ReceiveValue("Two") let keysThree = keysTwo.ReceiveValue("Three") printfn "%s" (keysThree.ToString()) The thing to notice here is how similar using the immutable ComputeKeys class is to using the Store class. We create an instance of the class, we add values to it, and then finally we print it. The only difference being that we need to catch the value returned from RecieveValue and use this value in the next step. Here we’ve used different names for each instance – to illustrate that each let binding is to a different instances, but we don’t need to do that we can reuse the same name to save inventing new names: let useComputeKeysAlt() = let keys = new ComputeKeys(Map.empty) let keys = keys.ReceiveValue("One") let keys = keys.ReceiveValue("Two") let keys = keys.ReceiveValue("Three") printfn "%s" (keys.ToString()) The take away from this is that programming with immutable data structures when we have one thread of execution is not that different to programming with imperative mutable structures, we just have to remember that every time we want to make a change we copy and add rather than update. Wrapping It Up In this inductor post we’ve looked at why mutation is important to classical concurrent programming, and indeed classical imperative programming. Then we looked at immutable data structures and compared they way that they work to mutable data structures. In the next post we’ll dig deeper into immutable data structures, to really get a feel for the programming possibilities they offer. Then in the post after that we’ll look at concurrent programming with immutable data structures and finally get to grips the problem we posed ourselves in the first couple of paragraphs of this post. Patience is a virtue and good things come to those who wait J.
May 20, 2008
by Robert Pickering
· 8,280 Views
article thumbnail
Python and the Star Schema
The star schema represents data as a table of facts (measurable values) that are associated with the various dimensions of the fact. Common dimensions include time, geography, organization, product and the like. I'm working with some folks whose facts are a bunch of medical test results, and the dimensions are patient, date, and a facility in which the tests were performed. I got an email with the following situation: "a client who is processing gigs of incoming fact data each day and they use a host of C/C++, Perl, mainframe and other tools for their incoming fact processing and I've seriously considered pushing Python in their organization.". Here are my thoughts on using Python for data warehousing when you've got Gb of data daily. Small Dimensions The pure Python approach only works when your dimension will comfortably fit into memory -- not a terribly big problem with most dimensions. Specifically, it doesn't work well for those dimensions which are so huge that the dimensional model becomes a snowflake instead of a simple star. When dealing with a large number of individuals (public utilities, banks, medical management, etc.) the "customer" (or "patient") dimension gets too big to fit into memory. Special bridge-table techniques must be used. I don't think Python would be perfect for this, since this involves slogging through a lot of data one record at a time. However, Python is considerably faster than PL/SQL. I don't know how it compares with Perl. Any programming language will be faster than any SQL procedure, because there's no RDBMS overhead. For all small dimensions. Load the dimension values from the RDBMS into a dict with a single query. Read all source data records (ideally from a flat file); conform the dimension, tracking changes; write a result record with the dimension FK information to a flat file. Iterate through the dimension dictionary and persist the dimension changes. The details vary with the Slowly Changing Dimension (SCD) rules you're using. The conformance algorithm is is essentially the following: row= Dimension(...) ident= ( row.field, row.field, row.field, ... ) dimension.setdefault( ident, row ) In some cases (like the Django ORM) this is called the get-or-create query. The Dimension Bus For BIG dimensions, I think you still have to implement the "dimension bus" outlined in The Data Warehouse Toolkit. To do this in Python, you should probably design things to look something like the following. For any big dimensions. Use an external sort-merge utility. Seriously. They're way fast for data sets too large to fit into memory. Use CSV format files and the resulting program is very tidy. The outline is as follows: First, sort the source data file into order by the identifying fields of the big dimension (customer number, patient number, whatever). Second, query the big dimension into a data file and sort it into the same order as the source file. (Using the SQL ORDER BY may be slower than an external sort; only measurements can tell which is faster.) Third, do a "match merge" to locate the differences between the dimension and the source. Don't use a utility like diff, it's too slow. This is a simple key matching between two files. The match-merge loop looks something like this. src= sourceFile.next() dim= dimensionFile.next() try: while True: src_key = ( src['field'], src['field'], ... ) dim_key= ( dim['field'], dim['field'], ... ) if src_key < dim_key: # missing some dimension values update_dimension( src ) src= sourceFile.next() elif dim_key < src_key: # extra dimension values dim= dimensionFile.next() else: # src and dim keys match # check non-key attributes for dimension change. src= sourceFile.next() except StopIteration, e: # if source is at end-of-file, that's good, we're done. # if dim is at end of file, all remaining src rows are dimension updates. for src in sourceFile: update_dimension( src ) At the end of this pass, you'll accumulate a file of customer dimension adds and changes, which is then persisted into the actual customer dimension in the database. This pass will also write new source records with the customer FK. You can also handle demographic or bridge tables at this time, too. Fact Loading The first step in DW loading is dimensional conformance. With a little cleverness the above processing can all be done in parallel, hogging a lot of CPU time. To do this in parallel, each conformance algorithm forms part of a large OS-level pipeline. The source file must be reformatted to leave empty columns for each dimension's FK reference. Each conformance process reads in the source file and writes out the same format file with one dimension FK filled in. If all of these conformance algorithms form a simple OS pipe, they all run in parallel. It looks something like this. src2cvs source | conform1 | conform2 | conform3 | load At the end, you use the RDBMS's bulk loader (or write your own in Python, it's easy) to pick the actual fact values and the dimension FK's out of the source records that are fully populated with all dimension FK's and load these into the fact table. I've written conformance processing in Java (which is faster than Python) and had to give up on SQL-based conformance for large dimensions. Instead, we did the above flat-file algorithm to merge large dimensions. The killer isn't the language speed, it's the RDBMS overheads. Once you're out of the database, things blaze. Indeed, products like the syncsort data sort can do portions of the dimension conformance at amazing speeds for large datasets. Hand Wringing "But," the hand-wringers say, "aren't you defeating the value of the RDBMS by working outside it?" The answer is NO. We're not doing incremental, transactional processing here. There aren't multiple update transactions in a warehouse. There are queries and there are bulk loads. Doing the prep-work for a bulk load outside the database is simply more efficient. We don't need locks, rollback segments, memory management, threading, concurrency, ACID rules or anything. We just need to match-merge the large dimension and the incoming facts.
May 20, 2008
by Steven Lott
· 11,342 Views · 1 Like
article thumbnail
5 Techniques for Creating Java Web Services From WSDL
WSDL is a version of XML used to better work with web severs. In this post, we'll learn how to better use it alongside the Java language.
April 29, 2008
by Milan Kuchtiak
· 604,644 Views
article thumbnail
Migrate4j - Database Migration Tool for Java
Migrate4j is a migration tool for java, similar to Ruby's db:migrate task. Unlike other Java based migration tools, database schema changes are defined in Java, not SQL. This means your migrations can be applied to different database engines without worrying about whether your DDL statements will still work. Schema changes are defined in Migration classes, which define "up" and "down" methods - "up" is called when a Migration is being applied, while "down" is called when it is being rolled back. A simple Migration, which simply adds a table to a database, is written as: package db.migrations; import static com.eroi.migrate.Define.*; import static com.eroi.migrate.Define.DataTypes.*; import static com.eroi.migrate.Execute.*; import com.eroi.migrate.Migration; public class Migration_1 implements Migration { public void up() { createTable( table("simple_table", column("id", INTEGER, primaryKey(), notnull()), column("desc", VARCHAR, length(50), defaultValue("NA")))); } public void down() { dropTable("simple_table"); } } This Migration can be applied at application startup, from an Ant task (included in migrate4j) or from the command line. Migrate4j will only apply the migration if it has not yet been applied. LIkewise, migrate4j will roll back the migration when instructed, only if the migration has been previously applied. The migrate4j team is happy to announce a new release which adds improved usability (simplified syntax), additional schema changes and support for more database products. While migrate4j does not yet have support for all database products, we are actively seeking developers interested in helping fix this situation. Visit http://migrate4j.sourceforge.net for more information on how migrate4j can simplify synchronizing your databases. To obtain migrate4j, go to http://sourceforge.net/projects/migrate4j and download the latest release. For questions or to help with future development of migrate4j, email us at migrate4j-users AT lists.sourceforge.net (replacing the AT with the "at symbol").
April 28, 2008
by Todd Runstein
· 3,420 Views
article thumbnail
Pathway from ACEGI to Spring Security 2.0
Formerly called ACEGI Security for Spring, the re-branded Spring Security 2.0 has delivered on its promises of making it simpler to use and improving developer productivity. Already considered as the Java platform's most widely used enterprise security framework with over 250,000 downloads from SourceForge, Spring Security 2.0 provides a host of new features. This article outlines how to convert your existing ACEGI based Spring application to use Spring Security 2.0. What is Spring Security 2.0 Spring Security 2.0 has recently been released as a replacement to ACEGI and it provides a host of new security features: Substantially simplified configuration. OpenID integration, single sign on standard. Windows NTLM support, single sign on against Windows corporate networks. Support for JSR 250 ("EJB 3") security annotations. AspectJ pointcut expression language support. Comprehensive support for RESTful web request authorization. Long-requested support for groups, hierarchical roles and a user management API. An improved, database-backed "remember me" implementation. New support for web state and flow transition authorization through the Spring Web Flow 2.0 release. Enhanced WSS (formerly WS-Security) support through the Spring Web Services 1.5 release. A whole lot more... Goal Currently I work on a Spring web application that uses ACEGI to control access to the secure resources. Users are stored in a database and as such we have configured ACEGI to use a JDBC based UserDetails Service. Likewise, all of our web resources are stored in the database and ACEGI is configure to use a custom AbstractFilterInvocationDefinitionSource to check authorization details for each request. With the release of Spring Security 2.0 I would like to see if I can replace ACEGI and keep the current ability to use the database as our source of authentication and authorization instead of the XML configuration files (as most examples demonstrate). Here are the steps that I took... Steps The first (and trickiest) step was to download the new Spring Security 2.0 Framework and make sure that the jar files are deployed to the correct location. (/WEB-INF/lib/) There are 22 jar files that come with the Spring Security 2.0 download. I did not need to use all of them (especially not the *sources packages). For this exercise I only had to include: spring-security-acl-2.0.0.jar spring-security-core-2.0.0.jar spring-security-core-tiger-2.0.0.jar spring-security-taglibs-2.0.0.jar Configure a DelegatingFilterProxy in the web.xml file. springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* Configuration of Spring Security 2.0 is far more concise than ACEGI, so instead of changing my current ACEGI based configuration file, I found it easier to start from a empty file. If you do want to change your existing configuration file, I am sure that you will be deleting more lines than adding. The first part of the configuration is to specifiy the details for the secure resource filter, this is to allow secure resources to be read from the database and not from the actual configuration file. This is an example of what you will see in most of the examples: Replace this with: The main part of this piece of configuration is the secureResourceFilter, this is a class that implements FilterInvocationDefinitionSource and is called when Spring Security needs to check the Authorities for a requested page. Here is the code for MySecureResourceFilter: package org.security.SecureFilter; import java.util.Collection; import java.util.List; import org.springframework.security.ConfigAttributeDefinition; import org.springframework.security.ConfigAttributeEditor; import org.springframework.security.intercept.web.FilterInvocation; import org.springframework.security.intercept.web.FilterInvocationDefinitionSource; public class MySecureResourceFilter implements FilterInvocationDefinitionSource { public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException { FilterInvocation filterInvocation = (FilterInvocation) filter; String url = filterInvocation.getRequestUrl(); // create a resource object that represents this Url object Resource resource = new Resource(url); if (resource == null) return null; else{ ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor(); // get the Roles that can access this Url List roles = resource.getRoles(); StringBuffer rolesList = new StringBuffer(); for (Role role : roles){ rolesList.append(role.getName()); rolesList.append(","); } // don't want to end with a "," so remove the last "," if (rolesList.length() > 0) rolesList.replace(rolesList.length()-1, rolesList.length()+1, ""); configAttrEditor.setAsText(rolesList.toString()); return (ConfigAttributeDefinition) configAttrEditor.getValue(); } } public Collection getConfigAttributeDefinitions() { return null; } public boolean supports(Class arg0) { return true; } } This getAttributes() method above essentially returns the name of Authorities (which I call Roles) that are allowed access to the current Url. OK, so now we have setup the database based resources and now the next step is to get Spring Security to read the user details from the database. The examples that come with Spring Security 2.0 shows you how to keep a list of users and authorities in the configuration file like this: You could replace these examples with this configuration so that you can read the user details straight from the database like this: While this is a very fast and easy way to configure database based security it does mean that you have to conform to a default databases schema. By default, the requires the following tables: user, authorities, groups, group_members and group_authorities. In my case this was not going to work as my security schema it not the same as what the requires, so I was forced to change the : By adding the users-by-username-query and authorities-by-username-query properties you are able to override the default SQL statements with your own. As in ACEGI security you must make sure that the columns that your SQL statement returns is the same as what Spring Security expects. There is a another property group-authorities-by-username-query which I am not using and have therefore left it out of this example, but it works in exactly the same manner as the other two SQL statements. This feature of the has only been included in the past month or so and was not available in the pre-release versions of Spring Security. Luckily it has been added as it does make life a lot easier. You can read about this here and here. The dataSource bean instructs which database to connect to, it is not included in my configuration file as it's not specific to security. Here is an example of a dataSource bean for those who are not sure: And that is all for the configuration of Spring Security. My last task was to change my current logon screen. In ACEGI you could create your own logon by making sure that you POSTED the correctly named HTML input elements to the correct URL. While you can still do this in Spring Security 2.0, some of the names have changed. You can still call your username field j_username and your password field j_password as before. However you must set the action property of your to point to j_spring_security_check and not j_acegi_security_check. Logout Conclusion This short guide on how to configure Spring Security 2.0 with access to resources stored in a database does not come close to illustrating the host of new features that are available in Spring Security 2.0, however I think that it does show some of the most commonly used abilities of the framework and I hope that you will find it useful. One of the benefits of Spring Security 2.0 over ACEGI is the ability to write more consice configuration files, this is clearly shown when I compare my old ACEGI configration (172 lines) file to my new one (42 lines). Here is my complete securityContext.xml file: As I said in step 1, downloading Spring Security was the trickiest step of all. From there on it was plain sailing...
April 22, 2008
by Chris Baker
· 117,908 Views
article thumbnail
A Portable JPA Boolean Magic Converter
The current Java Persistence API (JPA) standard does not mandate JPA provider to support data type conversions through annotations, not even a with simple boolean field. For readers who are unfamiliar with JPA, what I mean is, to persist a boolean field, JPA expects the database data type to be integer, where value of "1" means true, and value of "0" means false. We simply just can't annotate the boolean field, specifying our own boolean field value, such as "True/False", "T/F", "Yes/No", "Y/N", "-1/0" and then let the JPA provider to convert those boolean field on the fly. As an example, I am expecting JPA will allow me to annotate a boolean field @boolean(trueValue="Yes", falseValue="No") private boolean enabled; For me, this is a very annoying limitation, espeically when you have to deliver applications on an existing database with hundreds of tables. After some research, I decided to create my own Java Annotation Processing Tool (APT) Compile Time Annotation, called @BooleanMagic, with a compile time Java APT preprocessing factory, which will automatically generate additional code to work around the issue. I've also made some changes on the original code: Fixed the bug of Null pointer exception, when JPA return null on the annotated field. Introduce a new properties call ifNull, which allows user to configure what to return if JPA returns null, it expect enum of org.jbpcc.util.jpa.ReturnType, which have values of ReturnType.True, ReturnType.FALSE, and ReturnType.Null,. The default value of ifNull is ReturnType.Null So here is an example, assuming we have model class defined as below: package org.jbpcc.domain.model; import javax.persistence.Entity; import javax.persistence.Id; import org.jbpcc.util.jpa.BooleanMagic; import org.jbpcc.util.jpa.BooleanMagic.ReturnType; @Entitypublic class SomeVO { @Id private Integer id; @BooleanMagic(trueValue = "Yes", falseValue = "No", columnName = "OVERDUED", ifNull = ReturnType.FALSE) private transient Boolean overdued; public Boolean isOverdued() { return overdued; } public void setOverdued(Boolean overdued) { this.overdued = overdued; } } Using Java APT with JPABooleanMagicConverter factory, the code above will be now be converted to: @Entitypublic class SomeVO { @Id private Integer id; private transient Boolean overdued; //--- Lines below are generated by JBPCC BooleanMagicConvertor PROCESSOR //--- START : @Column(name="OVERDUED") private String magicBooleanOverdued; public Boolean isOverdued() { if (this.magicBooleanOverdued == null) return false; return this.magicBooleanOverdued.equals("Yes") ? Boolean.TRUE : Boolean.FALSE; } public Boolean getOverdued() { if (this.magicBooleanOverdued == null) return false; return this.magicBooleanOverdued.equals("Yes") ? Boolean.TRUE : Boolean.FALSE; } public void setOverdued(Boolean trueFlag) { this.magicBooleanOverdued = trueFlag ? "Yes" : "No"; } //--- END //--- GENERATED BY JBPCC BooleanMagicConvertor PROCESSOR } I have put the annotation with it compile time process factory under my open source project - Java Batch Process control center, at http://code.google.com/p/jbpcc, I also posted an article at my blog detailing the usage of the annotation. I hope some readers will find the annotation and the APT preprocessing factory useful. Do share your thoughts and suggestions.
April 14, 2008
by Khoo Chen Shiang
· 29,139 Views
article thumbnail
Quick Tip: Granting Access to Meta-Data on MySQL
If you have root access to your MySQL database then you can simply run a query on the database to resolve the problem.
March 22, 2008
by Schalk Neethling
· 45,421 Views
article thumbnail
DJ NativeSwing - reloaded: JWebBrowser, JFlashPlayer, JVLCPlayer, JHTMLEditor.
Today's release of DJ Native Swing 0.9.4 greatly improves stability and brings new components: in addition to the JWebBrowser and JFlashPlayer, there is now the JVLCPlayer and the JHTMLEditor. Here is a summary of what to expect when using this library. 1. Various native components DJ Native Swing was designed to handle all the complexity of native integration, mainly in the form of components with a simple Swing-like API. Here are some screenshots of several components in action (click to enlarge): JWebBrowser: JFlashPlayer: JVLCPlayer: JHTMLEditor: 2. Simple API First, we need to initialize the framework. This needs to happen before any feature is used. A common place for this call is the first line of the main(): public static void main(String[] args) { NativeInterfaceHandler.init(); // Here goes the rest of the initialization. } Now, let's see how to create a JWebBrowser, with its URL set to Google's homepage: JWebBrowser webBrowser = new JWebBrowser(); webBrowser.setURL("http://www.google.com"); myContentPane.add(webBrowser); Note that we set a URL, but we could as well set the HTML text. The JWebBrowser also allows to execute Javascript calls, and we can even propagate notifications from custom pages to our Swing application. Moving to a more practical example, we may want to be notified of URL change events or track window opening events, potentially preventing navigation to occur or open the page elsewhere. This is easily achieved by attaching a listener: webBrowser.addWebBrowserListener(new WebBrowserAdapter() { public void urlChanging(WebBrowserNavigationEvent e) { String newURL = e.getNewURL(); if(newURL.startsWith("http://www.microsoft.com/")) { // Prevent the navigation to happen. e.consume(); } else { // We can consume the event and decide to open this page in a tab. } } public void windowWillOpen(WebBrowserWindowWillOpenEvent e) { // We can prevent, add the URL to a tab, etc. } // There are of course more events that can be received. }); Let's have a look at the JFlashPlayer: JFlashPlayer flashPlayer = new JFlashPlayer(); flashPlayer.setURL(myFlashURL); The JFlashPlayer can open local or remote Flash files, and files from the classpath; the latter being a general capability of the library by proxying files using a minimalistic web server. Of course, the JFlashPlayer allows to retrieve and set Flash variables, and play/pause/stop the execution. The JVLCPlayer and the JHTMLEditor are no exceptions to this simplicity: playlist can be manipulated in the VLC player, HTML can be set and retrieved from the HTML editor, etc. There is also the possibility to integrate Ole controls on Windows, still with a simple Swing-like API. An example is provided in the library in the form of an embedded Windows Media Player. 3. Advanced capabilities The library takes care of most common integration issues. This covers modal dialog handling, Z-ordering, heavyweight/lightweight mix (to a certain extent), invisible native components with regards to focus handling and threading. Here are some more screenshots, showing a lightweight/heavyweight mix, and Z-ordering capability: The demo application that is part of the distribution shows all the features along with the source code, so check it out! 4. Project info and technical notes Webstart demo: http://djproject.sourceforge.net/ns/DJNativeSwingDemo.jnlp Screenshots: http://djproject.sourceforge.net/ns/screenshots Native Swing: http://djproject.sourceforge.net/ns The DJ Project: http://djproject.sourceforge.net The 0.9.4 version has a completely new architecture. It still uses SWT under the hood, but it does not use the SWT_AWT bridge anymore. The JWebBrowser and browser-based components require XULRunner to be installed, except on Windows when using Internet Explorer. The JVLCPlayer requires VLC to be installed. The JHTMLEditor uses the FCKeditor. 5. Conclusions This project finally brings all that is needed to make Java on the desktop a reality. A web browser, a flash player, a multimedia player, and even an HTML editor. So, what next? Yes, what next? For that one, I am waiting for your feedback. So, what do you think? What are your comments and suggestions? -Christopher
March 12, 2008
by Christopher Deckers
· 54,610 Views
article thumbnail
SVNKit: Tame Subversion with Java!
SVNKitis an Open Source pure Java Subversion library. SVNKit literally brings Subversion, popular open source version control system, to the Java world. With SVNKit you can do the following: All standard Subversion operations: For instance, the following snipped checks out project from repository: File dstPath = new File("c:/svnkit"); SVNURL url = SVNURL. parseURIEncoded("http://svn.svnkit.com/repos/svnkit/branches/1.1.x/"); SVNClientManager cm = SVNClientManager.newInstance(); SVNUpdateClient uc = cm.getUpdateClient(); uc.doCheckout(url, dstPath, SVNRevision.UNDEFINED, SVNRevision.HEAD, true); Updates it to the latest revision: uc.doUpdate(dstPath, SVNRevision.HEAD, true); And finally commits local changes in "www" subdirectory if there are any: SVNCommitClient cc = cm.getCommitClient(); cc.doCommit(new File[] {new File(dstPath, "www")}, false, "message", false, true); SVNKit supports all standard Subversion operations and compatible with the latest version of Subversion. Access Subversion repository directly: Some applications will benefit from working with repository directly, without keeping working copy locally. Example below displays list of files in "www" directory. SVNURL url = SVNURL.parseURIEncoded("http://svn.svnkit.com/repos/svnkit/branches/1.1.x/"); SVNRepository repos = SVNRepositoryFactory.create(url); long headRevision = repos.getLatestRevision(); Collection entriesList = repos.getDir("www", headRevision, null, (Collection) null); for (Iterator entries = entriesList.iterator(); entries.hasNext();) { SVNDirEntry entry = (SVNDirEntry) entries.next(); System.out.println("entry: " + entry.getName()); System.out.println("last modified at revision: " + entry.getDate() + " by " + entry.getAuthor()); } Direct repository access API allows to perform operations like update, commit, diff and many other. Additionaly to the performance benefits of the direct access to repository, this API makes it possible to version arbitrary objects or object models within Subevrsion repository, not only files from the file system. Replace JNI Subversion bindings with SVNKit: Native Subversion provides Java interface that works with Subversion binaries through JNI. In case you already using it or would like to use as an option, you may also use SVNKit through exactly the same interface. This way you'll let your application dynamically switch between JNI and SVNKit implementation of the same API or let your application work on the platforms where there are no native Subversion binaries. For example: // pure Java implementation of the standard Subversion Java interface SVNClientInterface jniAPI = SVNClientImpl.newInstance(); byte[] contents = jniAPI.fileContent("http://svn.svnkit.com/repos/svnkit/branches/1.1.x/changelog.txt", Revision.HEAD); SVNKit is widely used in different applications, including IntelliJ IDEA, Eclipse Subversion integrations, SmartSVN, JDeveloper, bug tracking server side applications (e.g. Atlassian JIRA) and repository management and tracking tools (e.g. Atlassian FishEye) and many others. Where to get more information: Recently we've released SVNKit version 1.1.6 which is bugfix release. At http://svnkit.com/ you will find more information on that new version and, of course, downloads, documentation, source code example and articles explaining how to use SVNKit. In case of any questions you're welcome at our mailing list, or just contact us at [email protected] SVNKit is widely used in different applications, including IntelliJ IDEA, Eclipse Subversion integrations, SmartSVN, JDeveloper, bug tracking server side applications (e.g. Atlassian JIRA) and repository management and tracking tools (e.g. Atlassian FishEye) and many others. With best regards, TMate Software, http://svnkit.com/ - Java [Sub]Versioning Library!
February 26, 2008
by Alexander Kitaev
· 11,365 Views · 2 Likes
article thumbnail
Optimizing Your Website Structure For Print Using CSS
As much as I read articles online, I still print a fair amount of them out. Sometimes I print them to pass on to others, other times to read again when I have more time. Unfortunately a great deal of websites put no effort into providing their content in a printer-friendly fashion. The result of them overlooking the print audience is a great article not being read. If only these writers knew how easy it can be to optimize their site for print and how it can greatly enhance the value of their website. The secret to creating printable pages is being able to identify and control the "content area(s)" of your website. Most websites are composed of a header, footer, sidebars/subnavigation, and one main content area. Control the content area and most of your work is done. The following are my tips to conquering the print media without changing the integrity of your website. Create A Stylesheet For Print Of course you have at least one stylesheet to control the layout of the page and formatting of the content, but do you have a stylesheet to control how your page will look like in print? Add the print style sheet, with the media attribute set to "print", at the end of the list of stylesheets in the header. This will allow you to create custom CSS classes applied only at the time of print. Make sure your structure CSS file is given a media attribute of "all." Avoid Unnecessary HTML Tables As much as I try to steer clear of using tables, there's no way to avoid the occasional experience. Forms are much easier to code when using tables. Tables are also great for...get this...data tables. Other than these two situations, a programmer should try to avoid using table, especially when considering print. Controlling the content area of your website can be extremely challenging when the page structure is trapped in a table. Know Which Portions Of The Page Don't Have Any Print Value You know that awesome banner you have at the top of your site? Ditch it. And those ads on the right and left sides of the page? Goodbye. Web visitors print your page because of the content on it, not to see the supporting images on your website. Create a class called "no-print" and add that class declaration to DIVS, images, and other elements that have no print value: .no-print { display:none; } .... Use Page Breaks Page breaks in the browser aren't as reliable as they are in Microsoft Word, especially considering the variable content lengths on dynamically created pages, but when utilized well make all the different in printing your website. The CSS specs don't provide a lot of print flexibility but the "page-break-before" / "page-break-after" properties prove to be useful. Page breaks are much more reliable when used with DIV elements instead of table cells. .page-break { page-break-before: always; } /* put this class into your main.css file with "display:none;" */ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Fusce eu felis. Curabitur sit amet magna. Nullam aliquet. Aliquam ut diam... Lorem ipsum dolor sit amet, consectetuer adipiscing elit.... Size Your Page For Print Obviously your computer monitor can provide a large amount of width to view a page, but I recommend setting the content area width to 600px (an inch equivalent may be better, but I try to deal with one unit specifically, which is pixels). This ensures that words wont bleed outside the print area. Use this width measurement with the page break DIVs you've created in your stylesheet. After you know the width of your printed content area, adjust the dimensions of content blocks inside the main content area if necessary. Test! Like any type of programming, testing is important. Note that if you have a website that serves dynamic data, you wont be able to win all the time but you may be able to figure a scheme to format content well most of the time. Be sure to test in multiple browsers (when creating customer websites, I try to check all "Grade A" browsers). Modifying your page structure for better print results is probably easier than you think -- at least improving your existing template will be. Check back soon for part two, where we analyze optimizing a website's content for print.
February 6, 2008
by David Walsh
· 14,141 Views · 1 Like
article thumbnail
Custom Date Formatting in SQL Server
SQL Server doesn't always adhere to its date/time formatting. Here's how to create your own.
February 4, 2008
by Boyan Kostadinov
· 152,973 Views
article thumbnail
Ruby On Rails: Change Class Name Into Human Readable String
This turns a class name (like LineItem) into a nice string (like "line item") line_item = LineItem.new puts line_item.class.name.underscore.humanize.lowcase #spits out "line item"
January 9, 2008
by Chris O'Sullivan
· 7,201 Views
article thumbnail
Convert Ruby Array To Ranges
# Array#to_ranges # Converts an array of values (which must respond to #succ) to an array of ranges. For example, # [3,4,5,1,6,9,8].to_ranges => [1,3..6,8..9] class Array def to_ranges array = self.compact.uniq.sort ranges = [] if !array.empty? # Initialize the left and right endpoints of the range left, right = self.first, nil array.each do |obj| # If the right endpoint is set and obj is not equal to right's successor # then we need to create a range. if right && obj != right.succ ranges << Range.new(left,right) left = obj end right = obj end ranges << Range.new(left,right) end ranges end end
October 19, 2007
by Bill Siggelkow
· 8,268 Views
article thumbnail
DOM Mouse-Over Element Selection And Isolation
DOM ISO.v.0.3.0.7.bookmarklet.js bookmarklet for selecting and isolating an element on a page. two sections: section 1: Mouseover DOM, setup and handle mouse events and show information about element in informational div. Click to select, Any key to cancel. section 2: Element Isolation with help of XPath. prompt user for XPath expression e.g., //DIV[@id='post-body']. then use XPath to select all elements not(ancestor or descendant or self), then delete those elements. also ignore self-or-descendants of head and title. tools: Ruderman's javascript development environment: https://www.squarefree.com/bookmarklets/webdevel.html#jsenv Mielczarek's js to bookmarklet generator: http://ted.mielczarek.org/code/mozilla/bookmarklet.html (function() { //GLOBALS //globals for classMausWork var gSelectedElement; //currently only one selection var gHoverElement; //whatever element the mouse is over var gHovering=false; //mouse is over something var gObjArrMW=[]; //global array of classMausWork objects. for removing event listeners when done selecting. //extended var infoDiv; //currently just container for InfoDivHover, might add more here var infoDivHover; //container for hoverText text node. var hoverText; //show information about current element that the mouse is over //const EXPERIMENTAL_NEW_CODE=true; //debugging. new features. //START SetupDOMSelection(); //(Section 1) Element Selection function SetupDOMSelection() { { //setup event listeners //var pathx="//div | //span | //table | //td | //tr | //ul | //ol | //li | //p"; var pathx="//div | //span | //table | //th | //td | //tr | //ul | //ol | //li | //p | //iframe"; var selection=$XPathSelect(pathx); for(var element, i=0;element=selection(i);i++) { if(element.tagName.match(/^(div|span|table|td|tr|ul|ol|li|p)$/i)) //redundant check. { var m = new classMausWork(element); gObjArrMW.push(m); attachMouseEventListeners(m); } } document.body.addEventListener('mousedown',MiscEvent,false); document.body.addEventListener('mouseover',MiscEvent,false); document.body.addEventListener('mouseout',MiscEvent,false); document.addEventListener('keypress',MiscEvent,false); } { //setup informational div to show which element the mouse is over. infoDiv=document.createElement('div'); var s=infoDiv.style; s.position='fixed'; s.top='0'; s.right='0'; s.display='block'; s.width='auto'; s.padding='0px'; document.body.appendChild(infoDiv); infoDivHover=document.createElement('div'); s=infoDivHover.style; s.fontWeight='bold'; s.padding='3px'; s.Opacity='0.8'; s.borderWidth='thin'; s.borderStyle='solid'; s.borderColor='white'; s.backgroundColor='black'; s.color='white'; infoDiv.appendChild(infoDivHover); hoverText=document.createTextNode('selecting'); infoDivHover.appendChild(hoverText); } } function CleanupDOMSelection() { for(var m; m=gObjArrMW.pop(); ) { detachMouseEventListeners(m); } ElementRemove(infoDiv); document.body.removeEventListener('mousedown',MiscEvent,false); document.body.removeEventListener('mouseover',MiscEvent,false); document.body.removeEventListener('mouseout',MiscEvent,false); document.removeEventListener('keypress',MiscEvent,false); } function attachMouseEventListeners(c) { //c is object of class classMausWork c.element.addEventListener("mouseover",c.mouse_over,false); c.element.addEventListener("mouseout",c.mouse_out,false); c.element.addEventListener("mousedown",c.mouse_click,false); } function detachMouseEventListeners(c) { //c is object of class classMausWork c.resetElementStyle(); c.element.removeEventListener("mouseover",c.mouse_over,false); c.element.removeEventListener("mouseout",c.mouse_out,false); c.element.removeEventListener("mousedown",c.mouse_click,false); } //mouse event handling class for element, el. function classMausWork(element) { //store information about the element this object is assigned to handle. element, original style, etc. this.element=element; var elementStyle=element.getAttribute('style'); var target; this.mouse_over=function(ev) { if(gHovering)return; var e=element; var s=e.style; s.backgroundColor='yellow'; s.borderWidth='thin'; s.borderColor='lime'; s.borderStyle='solid'; InfoMSG(ElementInfo(e),'yellow','blue','yellow'); gHoverElement=e; gHovering=true; target=ev.target; ev.stopPropagation(); }; this.mouse_out=function(ev) { if(!gHovering)return; if(gHoverElement!=element ||ev.target!=target)return; var e=element; e.setAttribute('style',elementStyle); InfoMSG('-','white','black','white'); gHoverElement=null; gHovering=false; target=null; //ev.stopPropagation(); }; this.mouse_click=function(ev) { if(!gHovering)return; if(gHoverElement!=element ||ev.target!=target)return; var e=element; e.setAttribute('style',elementStyle); ev.stopPropagation(); CleanupDOMSelection(); gHoverElement=null; gHovering=false; target=null; if(ev.button==0) { gSelectedElement=e; ElementSelected(e); //finished selecting, cleanup then move to next part (section 2), element isolation. } }; this.resetElementStyle=function() { element.setAttribute('style',elementStyle); }; } function MiscEvent(ev) //keypress, and mouseover/mouseout/mousedown event on body. cancel selecting. { if(ev.type=='mouseout' && !gHovering) { InfoMSG('-','white','black','white'); } else if(ev.type=='mouseover' && !gHovering) { InfoMSG('cancel','yellow','red','yellow'); } else //keypress on document or mousedown on body, cancel ops. { CleanupDOMSelection(); } } function InfoMSG(text,color,bgcolor,border) { var s=infoDivHover.style; if(color)s.color=color; if(bgcolor)s.backgroundColor=bgcolor; if(border)s.borderColor=border; if(text)hoverText.data=text; } //(Section 2) Element Isolation function ElementSelected(element) //finished selecting element. setup string to prompt user. { PromptUserXpath(ElementInfo(element)); } function PromptUserXpath(defaultpath) //prompt user, isolate element. { var userpath = prompt("XPath of elements to isolate : ", defaultpath); if(userpath && userpath.length>0) { var addPredicate = "[count(./ancestor-or-self::head)=0][count(./ancestor-or-self::title)=0]"; //exclude head & title elements from selection so they aren't removed var addPath = "//script | //form | //object | //embed"; //include these elements in selection for removal var pathx=TransformXPath_NoAncestorDescendentSelf(userpath, addPredicate, addPath); //the xpath selection of all elements to be removed/deleted. try { var element; var elements=$XPathSelect(pathx); for(var i=0;element=elements(i);i++) { if(!element.nodeName.match(/^(head|title)$/i)) //redundant check. { ElementRemove(element); } } } catch(err) { alert("wtf: "+err); } } } //support function $XPathSelect(p, context) { if (!context) context = document; var i, arr = [], xpr = document.evaluate(p, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); return function(x) { return xpr.snapshotItem(x); }; //closure. wooot! returns function-type array of elements (usually elements, or something else depending on the xpath expression). } function ElementRemove(e) { if(e)e.parentNode.removeChild(e); } function ElementInfo(element) { var txt=''; if(element) { txt=element.tagName.toLowerCase(); //txt=element.tagName; txt=attrib(txt,element,'id'); txt=attrib(txt,element,'class'); txt='//'+txt; } return txt; function attrib(t,e,a) { if(e.hasAttribute(a)) { t+="[@"+a+"='"+e.getAttribute(a)+"']"; } return t; } } //function to 'invert' the XPath by selecting all elements that are not ancestor and not descendent and not self. function TransformXPath_NoAncestorDescendentSelf(u, includePredicates, includePaths) { //sample input (u): //div[@class='sortbox'] //sample output //*[ not(./descendant-or-self::*=//div[@class='sortbox'])][ not(./ancestor-or-self::*=//div[@class='sortbox'])] //sample output with additional conditions: //*[ not(./descendant-or-self::*=//div[@class='sortbox'])][ not(./ancestor-or-self::*=//div[@class='sortbox'])][count(./ancestor-or-self::head)=0][count(./ancestor-or-self::title)=0] //obsolete method. much faster but can only be used for limited types of (simple) xpath expressions -- unlike the current version, which should be able to convert any xpath. //input: table[@id='topbar'] //output: //*[not(./descendant-or-self::table[@id='topbar']) and not(./ancestor-or-self::table[@id='topbar'])] //output (alternative): //*[count(./descendant-or-self::table[@id='topbar'])=0 and count(./ancestor-or-self::table[@id='topbar'])=0] var o1= './descendant-or-self::*='+gr(u); o1= 'not' + gr(o1); o1= nt(o1); var o2= './ancestor-or-self::*='+gr(u); o2= 'not' + gr(o2); o2= nt(o2); var o= '//*'+o1+o2; if(includePredicates && includePredicates.length>0) o += includePredicates; if(includePaths && includePaths.length>0) o += ' | ' + includePaths; return o; function nt(term){return wrap(term,'[]');} //node test; predicate - enclose with bracket. function gr(term){return wrap(term,'()');} //group - parenthesize. function wrap(term, enclosure){return enclosure.charAt(0)+term+enclosure.charAt(1);} } })();
September 9, 2007
by Jon C
· 2,309 Views
article thumbnail
Reverse TinyURL
PHP // Resolves a TinyURL.com encoded URL to its source. // Example: reverse_tinyurl('http://tinyurl.com/2ocfun') => "http://logankoester.com" function reverse_tinyurl($url) { $url = explode('.com/', $url); $url = 'http://preview.tinyurl.com/' . $url[1]; $preview = file_get_contents($url); preg_match('/redirecturl" href="(.*)">/', $preview, $matches); return $matches[1]; }
July 2, 2007
by Logan Koester
· 8,507 Views
article thumbnail
Compress/decompress Byte Array
using System; using System.Collections.Generic; using System.IO.Compression; using System.IO; using System.Collections; namespace Utilities { class Compression { public static byte[] Compress(byte[] data) { MemoryStream ms = new MemoryStream(); DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress); ds.Write(data, 0, data.Length); ds.Flush(); ds.Close(); return ms.ToArray(); } public static byte[] Decompress(byte[] data) { const int BUFFER_SIZE = 256; byte[] tempArray = new byte[BUFFER_SIZE]; List tempList = new List(); int count = 0, length = 0; MemoryStream ms = new MemoryStream(data); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); while ((count = ds.Read(tempArray, 0, BUFFER_SIZE)) > 0) { if (count == BUFFER_SIZE) { tempList.Add(tempArray); tempArray = new byte[BUFFER_SIZE]; } else { byte[] temp = new byte[count]; Array.Copy(tempArray, 0, temp, 0, count); tempList.Add(temp); } length += count; } byte[] retVal = new byte[length]; count = 0; foreach (byte[] temp in tempList) { Array.Copy(temp, 0, retVal, count, temp.Length); count += temp.Length; } return retVal; } } }
June 10, 2007
by Snippets Manager
· 8,686 Views
  • Previous
  • ...
  • 885
  • 886
  • 887
  • 888
  • 889
  • 890
  • Next
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook
×