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
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

The Latest Java 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,486 Views · 7 Likes
article thumbnail
Java Concurrency: Read / Write Locks
Jakob has done a great series on Java Concurrency - check out the first 14 articles at his blog. Going forward, we're delighted to announce that you'll also be able to follow the series here on JavaLobby. A read / write lock is more sophisticated lock than the Lock implementations shown in the text Locks in Java. Imagine you have an application that reads and writes some resource, but writing it is not done as much as reading it is. Two threads reading the same resource does not cause problems for each other, so multiple threads that want to read the resource are granted access at the same time, overlapping. But, if a single thread wants to write to the resource, no other reads nor writes must be in progress at the same time. To solve this problem of allowing multiple readers but only one writer, you will need a read / write lock. Java 5 comes with read / write lock implementations in the java.util.concurrent package. Even so, it may still be useful to know the theory behind their implementation. Here is a list of the topics covered in this text: Read / Write Lock Java Implementation First let's summarize the conditions for getting read and write access to the resource: Read Access If no threads are writing, and no threads have requested write access. Write Access If no threads are reading or writing. If a thread wants to read the resource, it is okay as long as no threads are writing to it, and no threads have requested write access to the resource. By up-prioritizing write-access requests we assume that write requests are more important than read-requests. Besides, if reads are what happens most often, and we did not up-prioritize writes, starvation could occur. Threads requesting write access would be blocked until all readers had unlocked the ReadWriteLock. If new threads were constantly granted read access the thread waiting for write access would remain blocked indefinately, resulting in starvation. Therefore a thread can only be granted read access if no thread has currently locked the ReadWriteLock for writing, or requested it locked for writing. A thread that wants write access to the resource can be granted so when no threads are reading nor writing to the resource. It doesn't matter how many threads have requested write access or in what sequence, unless you want to guarantee fairness between threads requesting write access. With these simple rules in mind we can implement a ReadWriteLock as shown below: public class ReadWriteLock{ private int readers = 0; private int writers = 0; private int writeRequests = 0; public synchronized void lockRead() throws InterruptedException{ while(writers > 0 || writeRequests > 0){ wait(); } readers++; } public synchronized void unlockRead(){ readers--; notifyAll(); } public synchronized void lockWrite() throws InterruptedException{ writeRequests++; while(readers > 0 || writers > 0){ wait(); } writeRequests--; writers++; } public synchronized void unlockWrite() throws InterruptedException{ writers--; notifyAll(); } } The ReadWriteLock has two lock methods and two unlock methods. One lock and unlock method for read access and one lock and unlock for write access. The rules for read access are implemented in the lockRead() method. All threads get read access unless there is a thread with write access, or one or more threads have requested write access. The rules for write access are implemented in the lockWrite() method. A thread that wants write access starts out by requesting write access (writeRequests++). Then it will check if it can actually get write access. A thread can get write access if there are no threads with read access to the resource, and no threads with write access to the resource. How many threads have requested write access doesn't matter. It is worth noting that both unlockRead() and unlockWrite() calls notifyAll() rather than notify(). To explain why that is, imagine the following situation: Inside the ReadWriteLock there are threads waiting for read access, and threads waiting for write access. If a thread awakened by notify() was a read access thread, it would be put back to waiting because there are threads waiting for write access. However, none of the threads awaiting write access are awakened, so nothing more happens. No threads gain neither read nor write access. By calling noftifyAll() all waiting threads are awakened and check if they can get the desired access. Calling notifyAll() also has another advantage. If multiple threads are waiting for read access and none for write access, and unlockWrite() is called, all threads waiting for read access are granted read access at once - not one by one. Read / Write Lock Reentrance The ReadWriteLock class shown earlier is not reentrant. If a thread that has write access requests it again, it will block because there is already one writer - itself. Furthermore, consider this case: Thread 1 gets read access. Thread 2 requests write access but is blocked because there is one reader. Thread 1 re-requests read access (re-enters the lock), but is blocked because there is a write request In this situation the previous ReadWriteLock would lock up - a situation similar to deadlock. No threads requesting neither read nor write access would be granted so. To make the ReadWriteLock reentrant it is necessary to make a few changes. Reentrance for readers and writers will be dealt with separately. Read Reentrance To make the ReadWriteLock reentrant for readers we will first establish the rules for read reentrance: A thread is granted read reentrance if it can get read access (no writers or write requests), or if it already has read access (regardless of write requests). To determine if a thread has read access already a reference to each thread granted read access is kept in a Map along with how many times it has acquired read lock. When determing if read access can be granted this Map will be checked for a reference to the calling thread. Here is how the lockRead() and unlockRead() methods looks after that change: public class ReadWriteLock{ private Map readingThreads = new HashMap(); private int writers = 0; private int writeRequests = 0; public synchronized void lockRead() throws InterruptedException{ Thread callingThread = Thread.currentThread(); while(! canGrantReadAccess(callingThread)){ wait(); } readingThreads.put(callingThread, (getAccessCount(callingThread) + 1)); } public synchronized void unlockRead(){ Thread callingThread = Thread.currentThread(); int accessCount = getAccessCount(callingThread); if(accessCount == 1){ readingThreads.remove(callingThread); } else { readingThreads.put(callingThread, (accessCount -1)); } notifyAll(); } private boolean canGrantReadAccess(Thread callingThread){ if(writers > 0) return false; if(isReader(callingThread) return true; if(writeRequests > 0) return false; return true; } private int getReadAccessCount(Thread callingThread){ Integer accessCount = readingThreads.get(callingThread); if(accessCount == null) return 0; return accessCount.intValue(); } private boolean isReader(Thread callingThread){ return readers.get(callingThread) != null; } } As you can see read reentrance is only granted if no threads are currently writing to the resource. As you can see, if the calling thread already has read access this takes precedence over any writeRequests. Write Reentrance Write reentrance is granted only if the thread has already write access. Here is how the lockWrite() and unlockWrite() methods look after that little change: public class ReadWriteLock{ private Map<Thread, Integer> readingThreads = new HashMap<Thread, Integer>(); private int writeAccesses = 0; private int writeRequests = 0; private Thread writingThread = null; public synchronized void lockWrite() throws InterruptedException{ writeRequests++; Thread callingThread = Thread.currentThread(); while(! canGrantWriteAccess(callingThread)){ wait(); } writeRequests--; writeAccesses++; writingThread = callingThread; } public synchronized void unlockWrite() throws InterruptedException{ writeAccesses--; if(writeAccesses == 0){ writingThread = null; } notifyAll(); } private boolean canGrantWriteAccess(Thread callingThread){ if(hasReaders()) return false; if(writingThread == null) return true; if(writingThread != callingThread) return false; return true; } private boolean hasReaders(){ return readingThreads.size() > 0; } } Notice how the thread currently holding the write lock is now taken into account when determining if the calling thread can get write access. Read to Write Reentrance Sometimes it is necessary for a thread that have read access to also obtain write access. For this to be allowed the thread must be the only reader. To achieve this the writeLock() method should be changed a bit. Here is what it would look like: public class ReadWriteLock{ private Map readingThreads = new HashMap(); private int writeAccesses = 0; private int writeRequests = 0; private Thread writingThread = null; public synchronized void lockWrite() throws InterruptedException{ writeRequests++; Thread callingThread = Thread.currentThread(); while(! canGrantWriteAccess(callingThread)){ wait(); } writeRequests--; writeAccesses++; writingThread = callingThread; } public synchronized void unlockWrite() throws InterruptedException{ writeAccesses--; if(writeAccesses == 0){ writingThread = null; } notifyAll(); } private boolean canGrantWriteAccess(Thread callingThread){ if(isOnlyReader(callingThread)) return true; if(hasReaders()) return false; if(writingThread == null) return true; if(writingThread != callingThread) return false; return true; } private boolean hasReaders(){ return readingThreads.size() > 0; } private boolean isOnlyReader(Thread thread){ return readers == 1 && readingThreads.get(callingThread) != null; } } Now the ReadWriteLock class is read-to-write access reentrant. Write to Read Reentrance Sometimes a thread that has write access needs read access too. A writer should always be granted read access if requested. If a thread has read access no other threads can have read nor write access, so it is not dangerous. Here is how the canGrantReadAccess() method will look with that change: public class ReadWriteLock{ private boolean canGrantReadAccess(Thread callingThread){ if(isWriter(callingThread)) return true; if(writingThread != null) return false; if(isReader(callingThread) return true; if(writeRequests > 0) return false; return true; } } Fully Reentrant ReadWriteLock Below is the fully reentran ReadWriteLock implementation. I have made a few refactorings to the access conditions to make them easier to read, and thereby easier to convince yourself that they are correct. public class ReadWriteLock{ private Map readingThreads = new HashMap(); private int writeAccesses = 0; private int writeRequests = 0; private Thread writingThread = null; public synchronized void lockRead() throws InterruptedException{ Thread callingThread = Thread.currentThread(); while(! canGrantReadAccess(callingThread)){ wait(); } readingThreads.put(callingThread, (getReadAccessCount(callingThread) + 1)); } private boolean canGrantReadAccess(Thread callingThread){ if( isWriter(callingThread) ) return true; if( hasWriter() ) return false; if( isReader(callingThread) ) return true; if( hasWriteRequests() ) return false; return true; } public synchronized void unlockRead(){ Thread callingThread = Thread.currentThread(); if(!isReader(callingThread)){ throw new IllegalMonitorStateException("Calling Thread does not" + " hold a read lock on this ReadWriteLock"); } int accessCount = getReadAccessCount(callingThread); if(accessCount == 1){ readingThreads.remove(callingThread); } else { readingThreads.put(callingThread, (accessCount -1)); } notifyAll(); } public synchronized void lockWrite() throws InterruptedException{ writeRequests++; Thread callingThread = Thread.currentThread(); while(! canGrantWriteAccess(callingThread)){ wait(); } writeRequests--; writeAccesses++; writingThread = callingThread; } public synchronized void unlockWrite() throws InterruptedException{ if(!isWriter(Thread.currentThread()){ throw new IllegalMonitorStateException("Calling Thread does not" + " hold the write lock on this ReadWriteLock"); } writeAccesses--; if(writeAccesses == 0){ writingThread = null; } notifyAll(); } private boolean canGrantWriteAccess(Thread callingThread){ if(isOnlyReader(callingThread)) return true; if(hasReaders()) return false; if(writingThread == null) return true; if(!isWriter(callingThread)) return false; return true; } private int getReadAccessCount(Thread callingThread){ Integer accessCount = readingThreads.get(callingThread); if(accessCount == null) return 0; return accessCount.intValue(); } private boolean hasReaders(){ return readingThreads.size() > 0; } private boolean isReader(Thread callingThread){ return readingThreads.get(callingThread) != null; } private boolean isOnlyReader(Thread callingThread){ return readingThreads.size() == 1 && readingThreads.get(callingThread) != null; } private boolean hasWriter(){ return writingThread != null; } private boolean isWriter(Thread callingThread){ return writingThread == callingThread; } private boolean hasWriteRequests(){ return this.writeRequests > 0; } } Calling unlock() From a finally-clause When guarding a critical section with a ReadWriteLock, and the critical section may throw exceptions, it is important to call the readUnlock() and writeUnlock() methods from inside a finally-clause. Doing so makes sure that the ReadWriteLock is unlocked so other threads can lock it. Here is an example: lock.lockWrite(); try{ //do critical section code, which may throw exception } finally { lock.unlockWrite(); } This little construct makes sure that the ReadWriteLock is unlocked in case an exception is thrown from the code in the critical section. If unlockWrite() was not called from inside a finally-clause, and an exception was thrown from the critical section, the ReadWriteLock would remain write locked forever, causing all threads calling lockRead() or lockWrite() on that ReadWriteLock instance to halt indefinately. The only thing that could unlock the ReadWriteLockagain would be if the ReadWriteLock is reentrant, and the thread that had it locked when the exception was thrown, later succeeds in locking it, executing the critical section and calling unlockWrite() again afterwards. That would unlock the ReadWriteLock again. But why wait for that to happen, if it happens? Calling unlockWrite() from a finally-clause is a much more robust solution.
June 9, 2008
by Jakob Jenkov
· 79,397 Views
article thumbnail
Spring Batch - Hello World
This is an introductory tutorial to Spring Batch. It does not aim to provide a complete guide to the framework but rather to facilitate the first contact. Spring Batch is quite rich in functionalities, and this is basically how I started learning it. Keep in mind that we will only be scratching the surface. Before we start All the examples will have the lofty task of printing "Hello World!" though in different ways. They were developed with Spring Batch 1.0. I'll provide a Maven 2 project and I'll run the examples with Maven but of course it is not a requirement to work with Spring Batch. Spring Batch in 2 Words Fortunately, Spring Batch model objects have self-explanatory names. Let's try to enumerate the most important and to link them together: A batch Job is composed of one or more Steps. A JobInstance represents a given Job, parametrized with a set of typed properties called JobParameters. Each run of of a JobInstance is a JobExecution. Imagine a job reading entries from a data base and generating an xml representation of it and then doing some clean-up. We have a Job composed of 2 steps: reading/writing and clean-up. If we parametrize this job by the date of the generated data then our Friday the 13th job is a JobInstance. Each time we run this instance (if a failure occurs for instance) is a JobExecution. This model gives a great flexibility regarding how jobs are launched and run. This naturally brings us to launching jobs with their job parameters, which is the responsibility of JobLauncher. Finally, various objects in the framework require a JobRepository to store runtime information related to the batch execution. In fact, Spring Batch domain model is much more elaborate but this will suffice for our purpose. Well, it took more than 2 words and I feel compelled to make a joke about it, but I won't. So let's move to the next section. Common Objects For each job, we will use a separate xml context definition file. However there is a number of common objects that we will need recurrently. I will group them in an applicationContext.xml which will be imported from within job definitions. Let's go through these common objects: JobLauncher JobLaunchers are responsible for starting a Job with a given job parameters. The provided implementation, SimpleJobLauncher, relies on a TaskExecutor to launch the jobs. If no specific TaskExecutor is set then a SyncTaskExecutor is used. JobRepository We will use the SimpleJobRepository implementation which requires a set of execution Daos to store its information. JobInstanceDao, JobExecutionDao, StepExecutionDao These data access objects are used by SimpleJobRepository to store execution related information. Two sets of implementations are provided by Spring Batch: Map based (in-memory) and Jdbc based. In a real application the Jdbc variants are more suitable but we will use the simpler in-memory alternative in this example. Here's our applicationContext.xml: Hello World with Tasklets A tasklet is an object containing any custom logic to be executed as a part of a job. Tasklets are built by implementing the Tasklet interface. Let's implement a simple tasklet that simply prints a message: public class PrintTasklet implements Tasklet{ private String message; public void setMessage(String message) { this.message = message; } public ExitStatus execute() throws Exception { System.out.print(message); return ExitStatus.FINISHED; } } Notice that the execute method returns an ExitStatus to indicate the status of the execution of the tasklet. We will define our first job now in a simpleJob.xml application context. We will use the SimpleJob implementation which executes all of its steps sequentailly. In order to plug a tasklet into a job, we need a TaskletStep. I also added an abstract bean definition for tasklet steps in order to simplify the configuration: ; Running the Job Now we need something to kick-start the execution of our jobs. Spring Batch provides a convenient class to achieve that from the command line: CommandLineJobRunner. In its simplest form this class takes 2 arguments: the xml application context containing the job to launch and the bean id of that job. It naturally requires a JobLauncher to be configured in the application context. Here's how to launch the job with Maven. Of course, it can be run with the java command directly (you need to specify the class path then): mvn exec:java -Dexec.mainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dexec.args="simpleJob.xml simpleJob" Hopefully, your efforts will be rewarded with a "Hello World!" printed on the console. The code source can be downloaded here. What's Next? This is the first part of 3. In the next part we will improve on this example while the third part will be dedicated to item oriented steps and flat files readers and writers. Hope you find it useful.
May 23, 2008
by Tareq Abedrabbo
· 299,613 Views
article thumbnail
Converting a Java Project to a Dynamic Web Project in Eclipse
To convert a Java Project to a Web Project switch to or open the Resource Perspective of the project, in the root of the project. Open the .project file and make sure the builders and natures are present that are needed for a web project. See the example below, the name should be the name of your project, the most important nodes are the nature children in the natures node: testProjectorg.eclipse.jdt.core.javabuilderorg.eclipse.wst.common.project.facet.core.builderorg.eclipse.wst.validation.validationbuilderorg.eclipse.wst.common.project.facet.core.natureorg.eclipse.jdt.core.javanatureorg.eclipse.wst.common.modulecore.ModuleCoreNatureorg.eclipse.jem.workbench.JavaEMFNature Once you’ve updated the .project file you can close the file and right click and choose properties on the project. When the properties window opens click on Project Facets. The Facets grid is probably empty, click the Modify Project button. Check the Dynamic Web Module and Java Facets, choose the Java and Servlet version that applies to your project. Click Next and specify the existing or new location of your src and web content directories. Click Finish. As a final step I would recommend modifying the build path to compile your source directly into your /WEB-INF/classes directory by selecting Java Build Path and modifying the Default output directory. Now you should be able to create a local tomcat server, or if you’ve already created one you should be able to add the project to the server by right clicking the server and choosing Add and Remove Projects. Original article at http://greatwebguy.com/programming/eclipse/converting-a-java-project-to-a-dynamic-web-project-in-eclipse/.
May 6, 2008
by Jason Crow
· 114,943 Views
article thumbnail
Obfuscating a NetBeans Java Application Project
Some time ago I found a couple of posts talking about how to obfuscate a NetBeans RCP module (here and here). Getting some parts of the ant targets presented in the previous post, this one presents a simple target that allows to obfuscate a normal Java library. For this, you need to have installed the obfuscator ProGuard. Take into account I am talking about obfuscating a Java library. This implies the obfuscation is lighter than if you obfuscate a closed application, that is, all public methods and interfaces must maintain its name (if not you can call your library methods anymore). Open your build.xml Java application file and paste this target: Special attention to these couple of lines:
April 30, 2008
by Antonio Santiago
· 46,104 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,824 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,465 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
· 118,026 Views
article thumbnail
1st Binary Release of Java Music Composer
Today marks the availability of the first binary release of the open source JFugue Music NotePad. The core basic functionality of the tool is available and ready to be tried out. After downloading and unzipping the archive, you need to specify the location of the JDK (JDK 5 or above, so Mac users are welcome too), in etc/mnotepad.conf, which is in the unzipped archive's main directory. The binary can be downloaded here: mnotepad_9April2008.zip Once you have done so, you can launch it, which should result in the following main window at start up: Here it is on the Mac: Then choose File | New, to define a new composition: Click Finish and you can begin composing music. To do so, choose notes from the toolbar and point/click to add them to the composition. Currently you can't add notes between existing notes. However, select one/more notes with your mouse, the notes turn red to show they are selected, and then use up/down to move notes up the scale and left/right to decrease/increase their length. While doing this part of the work, compositions typically look something like this: You can change instruments by right-clicking one in the Instruments window and choosing "Select". Finally, click Play to play the music or Save to save it. Compositions are saved to disk in midi format, the status bar shows where they are saved to, in the installation directory. Architecture. Instead of dealing directly with the Midi API, the JFugue API is used instead. The JFugue API provides an extremely transparent, simple, yet surprisngly powerful layer of functionality on top of the complexities of typical Midi programming. The user interface is pure Swing on top of the NetBeans Platform, therefore the application has a very mature window system (max/minimize, dock/undock) and is pluggable out of the box, among other features. User Comments. One of the user comments thus far: "It's definitely very cool! And as promised, you already got a whole bunch of subtle features for free from the NetBeans Platform (windowing, favorites, etc). :-D It already looks more stable and trustworthy than any other app with the same amount of work invested, just because of the solid and consistent windowing system." Known Issues. Amongst others, the following: several usability issues, such as that it isn't easy to know/see from the ui that notes can be changed by selecting them and moving up/down/left/right. Should be able to specify where saved midi file should be saved to. Some users report not being able to change instruments. Keyboard window currently unused. Playing of tune blocks the ui. Feedback Welcome. The JFugue Music NotePad is an open source project at https://nbjfuguesupport.dev.java.net/. You are welcome to join, especially if you are able to offer time/insight to add missing features. Especially programmers who are also musicians are welcome. For example, this application could do with a metronome, which could be provided by a separate plugin...
April 9, 2008
by Geertjan Wielenga
· 12,917 Views
article thumbnail
Spring: How to Create Decoupled Swing Components
The Spring Framework's applicability in the context of Swing seems to be underhighlighted, at least when one looks around on the web.
April 5, 2008
by Geertjan Wielenga
· 61,949 Views
article thumbnail
John Wilson: Groovy and XML
John Wilson is mainly known to the Groovy community because of his work on XmlSlurper, one of the easiest ways to work with XML in the JVM. Continue reading to learn what inspired John to get into Groovy. Enjoy! [img_assist|nid=2119|title=|desc=|link=none|align=right|width=220|height=188]Q. John, you are the creator of XmlSlurper, what motivated you to make it ? A. I had a problem with processing very large XML documents. XmlParser uses a simple and robust way of implementing GPath expressions which involves building an array to hold the result of each term on the expression. Unfortunately this means that you can consume large amounts of memory if the original document is large. I was getting out of memory errors quite a bit so I wrote the original version of XmlSlurper to use Iterarors rather than arrays which cut down the memory footprint quite a bit. It had the happy side effect of being faster too. I have rewritten XmlSlurper a couple of times since then and it now plays very well with StreamingMarkupBuilder, handles namespaces nicely and has an interesting way of doing edits on the fly as the slurped document is written out. Q. XmlSluper and XmlParser are so similar, is there a reason to have both ? A. On the one hand it's a disadvantage because users are unsure which one to use (The answer is - for most things it doesn't matter). However they have differences which are significant and, in my view, valuable. The most significant difference is their approach to editing the document. XmlParser is very straightforward, you just change the in memory tree structure which represents the document. XmlSlurper does not let you have direct access to the in memory data structure. It forces you to put you editing code in closures and specify where in the document the closures should be applied. It then does the editing on the fly as the document is written out. The first mechanism is simple but limited the second is more complicated but very powerful. Most of us only want to do relatively simple operations on small XML documents and XmlParser is excellent for that. For those people who want to do rather complex operations on XMl documents which can be quite large then XmlSlurper is a better choice. Fortunately they support an almost identical GPath syntax so switching from one to the other is no big deal. This was not always so - the community owes a big debt of gratitude to Paul King for doing a large amount of work on documenting these implementations and aligning them. My long term aim is to be able to do away altogether with the need to hold the whole document in memory but to stream the document through memory whist executing the GPath expressions. Q. You are also the creator of the xmlrpc module, what can you tell us about it ? A. It's a module I'm very fond of. It's an excellent example of how Groovy can make something that is quite complex in Java completely trivial. I can build an XML-RPC server in 4 lines of Groovy and a client in 1 line. Performance is excellent and it interoperates well. It is based on code I wrote a few years ago to implement XML-RPC on the Dallas Semiconductor TINI. The TINI is an amazing device the size of a memory SIMM which runs Java on a 8051 (the processor which controlled the keyboard in the original IBM PC) with 1Mb of battery backed up RAM and an Ethernet port. One of my favourite TINI apps was a weather forecasting toaster! [it's true! check it out for yourselves here] Q. Have you participated in other Open Source projects ? A. Yes, mostly in the embedded Java arena. I wrote tiny XML parsers (MinML and MinML2) and a tiny XML-RPC server (MinMl-RPC) i have also contributed to VNC and the Snort intrusion detections system. In the background I'm working on Ng which is an attempt to build a runtime system for dynamic languages on the JVM which is both simple and fast. Q. Ng, can you share more about it? A. Ng is a solo (at the moment) project which tries to answer the question: How can we implement a fully dynamic language on the current JVM which runs no more than ten times slower than Java? This is looking for an improvement of one to two orders of magnitude over current implementations (Groovy, JRuby, Jython, etc.). The idea is to design a programming language "backwards". I start with a highly optimised runtime system and then derive a language which can be optimally compiled for that runtime system. I'm hoping that some of the insights I get whist doing this can be fed back into the Groovy 2.0 MOP redesign. I'm making good but slow progress. I have arithmetic operations executing at less than twice as slow as Java in some benchmarks and method calls are coming below ten times as slow. I have started to document some of the techniques I have developed http://docs.google.com/View?docid=ah76zbd6xsx2_9ck33c8dp Q. How did you get involved with Groovy ? A. I was looking for an Open Source project to get involved in. I have a long term interest in programming languages (my first paid job was as a compiler writer in 1971). I looked at Ruby and JRuby but it was too Perlish for my tastes and the JRuby project looked moribund. Google found me Groovy and I liked the feel of the language and the community was very lively so I stuck around. Q. Do you use Groovy at work ? A. Yes. If I have to mung XML I will always do it in Groovy. I also spend quite a bit of time building DSLs in Groovy. I think the return on investment in DSLs is huge if they are done properly. Q. Do you have a preferred technique for building DSLs (builders, metaprogramming, ... ) ? A. I like builders a lot. I think that the Builder concept is one of James Strachan's best ideas. I built a little DSL to allow people to specify arbitrary graphs - it took about an hour to develop and it's saved days in allowing us to specify complex graphs simply, clearly and reliably. I tend to override invokeMethod, etc. or use Categories rather than ExpandoMetaClass to do MetaPrograming magic. That's probably because to got into the habit before Graeme wrote ExpandoMetaClass. However I do like the fact that Categories allow me to limit the extent of the change to a single thread - they need to have less impact on performance, though. Q. Is there a specific feature you would like to see in a future version of Groovy ? A. I think Inner Classes need to be added. Other than that I don't see much urgent need for language extensions. Quite a lot of work has been done on making the run time system cleaner and that work needs to continue. The speed of the implementation has been improved in the last few months but there is more work needed there (especially with Categories). the big thing I'd like to see is the ability to not compile to class files but to execute the AST (Abstract Syntax Tree) directly. JRuby does this and it can be very useful in cases where you are generating code dynamically and executing it once or twice before discarding it (which is quite a common use). It would also help with the Groovy console. Thanks John! John's bio John Wilson has been a programmer, project manager, teacher, CTO and CEO. He's now CTO of an English engineering company and is enjoying working with a great crowd in the Groovy/Grails community.
April 1, 2008
by Andres Almiray
· 17,402 Views · 1 Like
article thumbnail
Convert Java Date To GMT
This function converts a local date to GMT. This version corrects the bug common to this type of conversion where the date is incorrectly converted when the time is close to the DST crossover. WARNING: This code is for printing/string-representation only, the millis value of the returned date is NOT in GMT. private static Date cvtToGmt( Date date ) { TimeZone tz = TimeZone.getDefault(); Date ret = new Date( date.getTime() - tz.getRawOffset() ); // if we are now in DST, back off by the delta. Note that we are checking the GMT date, this is the KEY. if ( tz.inDaylightTime( ret )) { Date dstDate = new Date( ret.getTime() - tz.getDSTSavings() ); // check to make sure we have not crossed back into standard time // this happens when we are on the cusp of DST (7pm the day before the change for PDT) if ( tz.inDaylightTime( dstDate )) { ret = dstDate; } } return ret; }
March 28, 2008
by Douglas Wyatt
· 9,847 Views
article thumbnail
Ant Build File Changes for Java Web Projects in NetBeans IDE 6.1
While working with a Java Web Application in NetBeans, I noticed some slight changes in the Ant build file for my project between NetBeans 6.0 and 6.1. This article explores some of the problems these changes caused to help out anyone with similar issues. I started with a Java Web Application that was created in NetBeans 6.0.1. After adding some JSP files and several Java source files, I committed everything in the project to my CVS repository. For some of my projects, I utilize the Hudson continuous integration build server. Using a standard deployment of Hudson, I configured the project to poll the SCM every 60 minutes, check out the code from CVS (if changes had been committed), and trigger the NetBeans project’s Ant build file (calling several specific targets like compile, dist, and so on. My builds have been functioning correctly for several weeks using this standard setup. I recently opened one of those projects in NetBeans 6.1 Beta and have been thoroughly enjoying the new features (faster startup, better JSP parsing in the Source Editor). After adding some JAR files as libraries and making several configuration changes, I committed the entire project (particularly the build-related files in the nbproject directory). Suddenly, my build for that project started failing. The console output reported by Hudson was : -init-check: BUILD FAILED D:/projects/hudson-server/data/jobs/MyWebProjectl/workspace/nbproject/build-impl.xml:149: The Java EE server classpath is not correctly set up. Your active server type is Tomcat55. Either open the project in the IDE and assign the server or setup the server classpath manually. For example like this: ant -Duser.properties.file= (where you put the property “j2ee.platform.classpath” in a .properties file) or ant -Dj2ee.platform.classpath= (where no properties file is used) Total time: 2 seconds finished: FAILURE I undid the configuration changes one by one, but the build failed regardless of what I reset. Apparently the property j2ee.platform.classpath is now required. I did a DIFF on the nbproject/build-impl.xml file and discovered several changes. The -init-check target includes property checks including this new one : The Java EE server classpath is not correctly set up. Your active server type is ${j2ee.server.type}.Either open the project in the IDE and assign the server or setup the server classpath manually.For example like this: ant -Duser.properties.file= (where you put the property “j2ee.platform.classpath” in a .properties file) or ant -Dj2ee.platform.classpath= (where no properties file is used) I hadn’t really taken notice of this property in the build file before, but it is referenced in a number of other targets such as: -init-macrodef-javac, -init-macrodef-junit, -init-macrodef-java, -init-macrodef-nbjpda, -init-macrodef-debug, compile-jsps, -do-compile-single-jsp, connect-debugger, javadoc-build, -do-compile-test, -do-compile-test-single Not being able to find a definition of the property anywhere in the build file, I looked through the project’s project.properties file among the list of defined properties. The property j2ee.platform.classpath was not defined. Thus, I’m assuming this is passed into the build file dynamically by NetBeans? In general I wouldn’t care, but when running the build file via Ant inside Hudson, the property j2ee.platform.classpath is never passed in. Hudson DOES allow you to pass properties and values to the build file, so I suppose I can specify the value manually, but I would like to keep the number of per project customizations to a minimum to maintain a low level of maintenance. Unless this causes some problem with the project properties in the build system, I would suggest the following fix for anyone who is experiencing a similar issue. Open your project’s project.properties file. Navigate to the section that contains these properties: j2ee.platform=1.4 j2ee.server.type=Tomcat55 Add a new line that specifies a blank j2ee.platform.classpath property such as this: j2ee.platform=1.4 j2ee.platform.classpath= j2ee.server.type=Tomcat55 Now, if the project.properties file is committed to CVS, a Hudson build can be triggered, and the FAIL check in the build-impl.xml file will pass. I ran some quick tests with the project, and everything with the project inside NetBeans still seems to work fine. I would propose to the NetBeans team to have the j2ee.platform.classpath property automatically added to the project.properties file.
March 26, 2008
by Adam Myatt
· 15,774 Views
article thumbnail
Java Bean Code Generation In Eclipse
Where would we be without JavaBeans? We use them in all our basic Java applications. We have Struts Form Beans, Hibernate and Spring POJO's and the list goes on. We are all used to writing setters and getters in for our Java Bean's manually. With thanks to Eclipse and other plugins, this effort is now very very easy. This tip is for the beginners (seniors, this is my first quick tip post) to generate setters and getters in a Java Bean class. First declare all the variables you need in the class. Next, right click any where on the source file. Select Source and then Generate Getters and Setters. This can be done alternatively by pressing Alt+Shift+S. Now select the variables for which you want to generate the getters and setters and you're done. As you can see there are multiple options in the window. Finally, the source code is.. Happy Coding! Until Next Time... RD
March 20, 2008
by Ratna Dinakar Tumuluri
· 57,090 Views
article thumbnail
Applying Python To Modify Java Code
Python script intended to open each Java's project file, add the license term on the start of the file as a Java comment and writing it to the disk. 1) You'll need a Python interpreter. Check this out in http://python.org. 2) You'll need to enter the root file directory of the Java project, like python AddLicense.py ${path} where ${path} defines user project root directory. 3) See http://fcmanager.wiki.sourceforge.net 1:# Python script to add the LGPL notices to each java file of the FileContentManager project. 2:import os, glob, sys 3:License = """\ 4:/** 5:*FileContentManager is a Java based file manager desktop application, 6:*it can show, edit and manipulate the content of the files archived inside a zip. 7:* 8:*Copyright (C) 2008 9:* 10:*Created by Camila Sanchez [http://mimix.wordpress.com/], Rafael Naufal [http://rnaufal.livejournal.com] 11:and Rodrigo [[email protected]] 12:* 13:*FileContentManager is free software; you can redistribute it and/or 14:*modify it under the terms of the GNU Lesser General Public 15:*License as published by the Free Software Foundation; either 16:*version 2.1 of the License, or (at your option) any later version. 17:* 18:*This library is distributed in the hope that it will be useful, 19:*but WITHOUT ANY WARRANTY; without even the implied warranty of 20:*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21:*Lesser General Public License for more details. 22:* 23:*You should have received a copy of the GNU Lesser General Public 24:*License along with FileContentManager; if not, write to the Free Software 25:*Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ 26: 27:size = len(sys.argv) 28:if size == 1 or size > 2: 29: print "Usage: AddLicense.py $1" 30: sys.exit(1) 31:inputPath = sys.argv[1] 32:if not os.path.exists(inputPath): 33: print inputPath, "does not exist on disk" 34: sys.exit(1) 35:if not os.path.isdir(inputPath): 36: print inputPath, "isn't a dir" 37: sys.exit(1) 38:for path, dirs, files in os.walk(inputPath): 39: fileWithLicense = '' 40: for filepath in [ os.path.join(path, f) 41: for f in files if f.endswith(".java")]: 42: content = file(filepath).read() 43: f = file(filepath, "w") 44: print >>f, License + "\n" + content 45: f.close() 46: 47:
March 17, 2008
by Rafael Naufal
· 2,871 Views
article thumbnail
How to Create a Pluggable Photo Album in Java
Here you see a simple photo album I created in Java Swing. It is, by no definition of the term, a great photo album. However, the point is that none of the photos you see are provided by the application itself. Nor do they come from the web. So... where do they come from and how did they end up in my photo album? Read on to find out... Here's a clue. All that you would need to provide in order to add photos to my photo album is a Java application that is structured as follows: The images "demo1.png" and "demo2.png" are two of the four photos you see in the first screenshot, i.e., the screenshot of my photo album. The other two photos you see there come from another Java application, which is structured in exactly the same way as the above. "Ah," you might now think. "This article is all about the Java SE 6 java.util.ServiceLoader class. I guess I'll need to be using Java SE 6 and then I'll be able to construct small applications structured like the above and then I'll be able to plug into your photo album." Wrong. You don't need Java SE 6 at all. Although, you're close. Here's the definition of the VacPhotos class that you see in the illustration above: package com.example.vacphotos; import javax.swing.Icon; import javax.swing.ImageIcon; import photoalbumapp.Photo; public class VacPhotos implements Photo { ImageIcon icon1 = new ImageIcon(getClass().getResource("/com/example/vacphotos/demo1.png")); ImageIcon icon2 = new ImageIcon(getClass().getResource("/com/example/vacphotos/demo2.png")); public VacPhotos() { } @Override public Icon[] getPhoto() { Icon[] icons = new Icon[]{icon1, icon2}; return icons; } @Override public String[] getDescription() { String[] descs = new String[]{"pic 1", "pic2"}; return descs; } } In other words, you simply need to extend the photoalbumapp.Photo class, which the photo album itself makes available, meaning you need its JAR on your plugin's classpath. The photo album is an empty application shell that exposes the Photo class, with its two methods getPhoto and getDescription. You therefore need to create a Java class that implements those two methods, returning arrays of Icons and Strings for the photos you'd like to integrate into the photo album. Finally, you need to create a file in your META-INF.services folder, with the name of the class that you are implementing, which in this case is photoalbumapp.Photo. Within that file you need nothing more than one line, which is the FQN of your implementation class: com.example.vacphotos.VacPhotos And that's all. Now, when the JAR of your app is on the classpath of the photo album, your photos and descriptions will automatically be integrated with all the photos provided by all the other implementations of the same class. The cool thing is that the photo album is an implementation of JSR-296, the Swing Application Framework, so that lifecycle management and persistance, as well as other typical application services, are dealt with by the framework itself. For example, I don't need to provide any code for the user's resizings and repositionings to be saved across restarts. The most important part, in this context, of my photo album application (i.e., this is a totally separate application to the VacationPhotos application above), is the code that defines my photo service: package photoalbumapp; import java.util.Collection; import org.openide.util.Lookup; import org.openide.util.Lookup.Result; import org.openide.util.Lookup.Template; public class PhotoService { private static PhotoService service; private Lookup photoLookup; private Collection photos; private Template photoTemplate; private Result photoResults; private PhotoService() { photoLookup = Lookup.getDefault(); photoTemplate = new Template(Photo.class); photoResults = photoLookup.lookup(photoTemplate); photos = photoResults.allInstances(); } public static synchronized PhotoService getInstance() { if (service == null) { service = new PhotoService(); } return service; } public Collection getDefinitions() { return photos; } } The template lookup method returns a Result instance that contains multiple providers, if they exist. You can retrieve the entire collection of providers by calling the Result instance's allInstances method, which is exactly what is done above. Here you see that we are not dealing with the Java SE 6 ServiceLoader class (nor its earlier incarnations, which have been in the JDK since JDK 1.3). Instead, we are dealing with the NetBeans Platform's org.openide.util.Lookup class. Above, I have provided the bridge between your VacationPhotos application and my own separate application that provides the photo album. The bridge is accessed by the photo album to retrieve photos and descriptions. The bridge, in its role as a "service", will, in turn, access all the classes that implement the Photo implementers, as defined in the small Java applications that exist for no other reason than to provide photos, such as the VacPhotos application shown earlier. And how is the service used? The main JFrame constructor in the photo album is as follows, really simplistic as you can see: ... ... ... private PhotoService photo; public PhotoAlbum() { photo = PhotoService.getInstance(); initComponents(); JPanel content = new JPanel(); content.setLayout(new FlowLayout()); Collection coll = photo.getDefinitions(); Iterator it = coll.iterator(); while (it.hasNext()) { Photo photo = it.next(); Icon[] icons = photo.getPhoto(); String[] strings = photo.getDescription(); for (int i = 0; i < icons.length; i++) { JLabel imageLabel = new JLabel(); imageLabel.setBorder(BorderFactory.createLineBorder(Color.red)); Icon icon = icons[i]; String desc = strings[i]; imageLabel.setIcon(icon); imageLabel.setText(desc); content.add(imageLabel); } } setContentPane(content); } ... ... ... And that's really all. Now, what are the benefits of using the NetBeans Platform's org.openide.util.Lookup class instead of the JDK's ServiceLoader class? Lookup is available in versions for older JDKs and thus you can use it as a replacement of ServiceLoader when running on JDKs older than 1.6. Lookup is ready to work inside of the NetBeans runtime container, so makes even more sense when you're working with NetBeans modules. It knows how to discover all the modules in the system, how to effectively read its defined services, and similar activities. Lookup supports listeners. Client code can attach a listener and observe changes in lookup content. This is a necessary improvement to adapt to the dynamic environment created by the NetBeans runtime container, where modules can be enabled or disabled at runtime, which in turn can affect the set of registered service providers. Lookup is extensible and replaceable. While the ServiceLoader class in JDK 1.6 is a final class with hard-coded behavior, the NetBeans Lookup class is an extensible class that allows various implementations. This can be useful while writing unit tests. Or you can write an enhanced version of lookup that not only reads META-INF/services but, for example, finds the requested service providers around the Internet. Lookup is a general purpose abstraction. While the JDK's ServiceLoader can de-facto have just one instance per classloader, there can be thousands of independent Lookup instances, each representing a single place to query services and interfaces. In fact this is exactly the way Lookup is used in NetBeans IDE—it represents the context of each dialog, window element, node in a tree, etc. And now you know all that's needed for treating Java applications as plugins. (For further information, see John O'Conner's Creating Extensible Applications With the Java Platform.) In summary, by means of registration and discovery of classes and interfaces, loosely coupled photo albums, and similar applications, are clearly very easy to achieve.
March 16, 2008
by Geertjan Wielenga
· 47,288 Views
article thumbnail
Log4J the Groovy Way
While developing around, now or then one wants something printed out at the console of his/her IDE.
March 4, 2008
by Gerhard Balthasar
· 62,064 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,416 Views · 2 Likes
article thumbnail
Let's Create a Tetris Game in Compiled JavaFX Script
i thought it would be fun to spend a few posts creating a tetris game in compiled javafx script together. today i made a rough start on it, and if you promise not to laugh, i'll show you the humble beginnings. here's a screenshot of its current status: in tetris, there are several types of tetrominoes , each having a letter that it resembles. the four buttons on the left represent four of these shapes. when you press one of these buttons, the corresponding tetromino appears at the top and begins moving down the screen. when you click the rotate button, the tetromino rotates to the right, and the left / right buttons move the tetromino left and right, respectively. the code is contained in four fx program files, and needs some refactoring already. :-) before showing you the code in its current state, i'd like to point out a couple of helpful things: as explained in the spinning wheel post, the key frame animation syntax that you see here will become much less verbose as the javafx script compiler team continues to address animation. javafx script programs should always be designed with the ui binding to a model. in this program, the model is represented in one class named tetrismodel , located in the fx file of the same name. you may find it helpful to take a look the creating a compiled javafx script program with multiple fx source files post to see a hello world style program that has more than one fx file. please notice the package statments in this tetris program, as that influences where you need to put the source files and how you build them. you can obtain the javafx compiler by following the instructions in the obtaining the openjfx script compiler post. the source code (so far) here's the main program, named tetrismain.fx , that declaratively expresses the ui, and starts things up: /* * tetrismain.fx - the main program for a compiled javafx script tetris game * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. */ package tetris_ui; import javafx.ui.*; import javafx.ui.canvas.*; import java.lang.system; import tetris_model.*; frame { var model = tetrismodel { } var canvas:canvas width: 480 height: 500 title: "tetrisjfx" background: color.white content: borderpanel { center: canvas = canvas {} bottom: flowpanel { content: [ button { text: "i" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.i } into canvas.content; model.t.start(); } }, button { text: "t" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.t } into canvas.content; model.t.start(); } }, button { text: "l" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.l } into canvas.content; model.t.start(); } }, button { text: "s" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.s } into canvas.content; model.t.start(); } }, button { text: "rotate" action: function() { model.rotate90(); } }, button { text: "left" action: function() { model.moveleft(); } }, button { text: "right" action: function() { model.moveright(); } } ] } } visible: true onclose: function():void { system.exit(0); } } i made the tetrisshape class a custom graphical component. therefore, it is a subclass of the compositenode class, and overrides the composenode function. note: there is a typo in line 62 of the tetrisshape.fx listing. "returnreturn" should read "return". it will be corrected asap. /* * tetrisshape.fx - a tetris piece, configurable to the * different shape types. they are: * i, j, l, o, s, t, and z * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. * */ package tetris_ui; import javafx.ui.*; import javafx.ui.canvas.*; import java.awt.point; import java.lang.system; import tetris_model.*; class tetrisshape extends compositenode { private static attribute squareoutlinecolor = color.black; private static attribute squareoutlinewidth = 2; private attribute squarecolor; public attribute model:tetrismodel; public attribute shapetype:tetrisshapetype on replace { if (shapetype == tetrisshapetype.i) { squarelocs = []; insert new point(0, model.square_size * 1) into squarelocs; insert new point(0, 0) into squarelocs; insert new point(0, model.square_size * 2) into squarelocs; insert new point(0, model.square_size * 3) into squarelocs; squarecolor = color.red; } else if (shapetype == tetrisshapetype.t) { squarelocs = []; insert new point(model.square_size * 1, 0) into squarelocs; insert new point(0, 0) into squarelocs; insert new point(model.square_size * 2, 0) into squarelocs; insert new point(model.square_size * 1, model.square_size * 1) into squarelocs; squarecolor = color.green; } else if (shapetype == tetrisshapetype.l) { squarelocs = []; insert new point(0, model.square_size * 1) into squarelocs; insert new point(0, 0) into squarelocs; insert new point(0, model.square_size * 2) into squarelocs; insert new point(model.square_size * 1, model.square_size * 2) into squarelocs; squarecolor = color.magenta; } else if (shapetype == tetrisshapetype.s) { squarelocs = []; insert new point(model.square_size * 1, 0) into squarelocs; insert new point(model.square_size * 2, 0) into squarelocs; insert new point(0, model.square_size * 1) into squarelocs; insert new point(model.square_size * 1, model.square_size * 1) into squarelocs; squarecolor = color.cyan; } } private attribute squarelocs:point[]; public function composenode():node { return group { transform: bind [ translate.translate(model.square_size * model.tetrominohorzpos, (model.a / model.square_size).intvalue() * model.square_size), rotate.rotate(model.tetrominoangle, squarelocs[0].x + model.square_size / 2, squarelocs[0].y + model.square_size / 2) ] content: [ for (squareloc in squarelocs) { rect { x: bind squareloc.x y: bind squareloc.y width: bind model.square_size height: bind model.square_size fill: bind squarecolor stroke: squareoutlinecolor strokewidth: squareoutlinewidth } } ] }; } } the tetrisshapetype class defines the tetromino types: /* * tetrisshapetype.fx - a tetris shape type, which are * i, j, l, o, s, t, and z * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. * */ package tetris_ui; import javafx.ui.*; class tetrisshapetype { public attribute id: integer; public attribute name: string; public static attribute o = tetrisshapetype {id: 0, name: "o"}; public static attribute i = tetrisshapetype {id: 1, name: "i"}; public static attribute t = tetrisshapetype {id: 2, name: "t"}; public static attribute l = tetrisshapetype {id: 3, name: "l"}; public static attribute s = tetrisshapetype {id: 4, name: "s"}; } and finally, here's a model class, named tetrismodel: /* * tetrismodel.fx - the model behind the tetris ui * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. * */ package tetris_model; import javafx.ui.animation.*; import java.lang.system; import com.sun.javafx.runtime.pointerfactory; public class tetrismodel { public static attribute square_size = 20; public attribute a:integer; private attribute pf = pointerfactory {}; private attribute bpa = bind pf.make(a); private attribute pa = bpa.unwrap(); private attribute interpolate = numbervalue.linear; public attribute t = timeline { keyframes: [ keyframe { keytime: 0s; keyvalues: numbervalue { target: pa; value: 0; interpolate: bind interpolate } }, keyframe { keytime: 20s; keyvalues: numbervalue { target: pa; value: 370 interpolate: bind interpolate } } ] }; public attribute tetrominoangle:number; public attribute tetrominohorzpos:number = 10; public function rotate90():void { (tetrominoangle += 90) % 360; } public function moveleft():void { if (tetrominohorzpos > 0) { tetrominohorzpos--; } } public function moveright():void { if (tetrominohorzpos < 20) { //todo:replace 10 with a calculated number tetrominohorzpos++; } } } compile and execute this example, and examine the code. i'll get busy making it behave a little more like a tetris game, and show you some progress in the next post. please feel free to get ahead of me, and make your own version! regards, jim weaver javafx script: dynamic java scripting for rich internet/client-side applications immediate ebook (pdf) download available at the book's apress site
February 22, 2008
by James Weaver
· 17,868 Views
article thumbnail
VisualVM: Free and Open Source Java Troubleshooter
Trying to troubleshoot Java? VisualVM is a great, free, open source tool.
February 21, 2008
by Geertjan Wielenga
· 69,801 Views
  • Previous
  • ...
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 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
×