tag:blogger.com,1999:blog-2623981273311170992024-03-24T18:10:10.317+11:00sysadmindunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.comBlogger17125tag:blogger.com,1999:blog-262398127331117099.post-66061597513370584032013-03-23T02:21:00.002+11:002013-03-23T02:21:38.522+11:00Git clone not working in Jenkins<h3>
Poor Jenkins&Git integration... </h3>
When I was trying to setup some git jobs in Jenkins today, it turned out to be more tricky than I initially expected.<br />
Upgrading to latest Jenkins 1.506 and Git Plugin 1.3.0, we were off to a bad start:<br />
<pre>Command "/usr/bin/git clone --progress -o origin ssh://gitserver.company.com:22/project/one
/path/to/jenkins/jobs/test/workspace" returned status code 128: Cloning into an existing
directory is not allowed.</pre>
<br />
After quite a lot of digging, I finally found the root-cause, solution and a work-around...<br />
<h3>
Solution</h3>
Upgrade Git to at least version 1.6.0.2.<br />
<br />
<h3>
Work-around</h3>
Downgrade Jenkin's "Git client Plugin" to version 1.0.2 (Download from here: <a href="https://updates.jenkins-ci.org/download/plugins/git-client/1.0.2/git-client.hpi" target="">https://updates.jenkins-ci.org/download/plugins/git-client/1.0.2/git-client.hpi</a>) <br />
<br />
<h3>
Root-cause</h3>
"Cloning into an existing directory is only allowed if the directory is empty." only became reality in later versions of Git.<br />
While Jenkin's "Git client Plugin" made a change (<a href="https://issues.jenkins-ci.org/browse/JENKINS-16873" target="_blank">JENKINS-16873</a>) in version 1.0.3 which relies on this feature, any installation with an older Git will fail to work.<br />
<br />dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-27130741473263204282013-03-09T13:57:00.002+11:002013-03-09T13:57:19.205+11:00How to use Node.js with WebLogic OSBI recently came across a situation where there was a requirement to use WebLogic OSB configuration in a Node.js application. The information in question was the Endpoint URIs for all Business Services found in the OSB.<br />
Since WebLogic removed "ServiceConfigurationMBean" from WLST in 11g, writing a Python script as you normally do was out of the question. Using Java was the only possibility at this stage, but it felt a bit much writing a new app just for a simple task like this.<br />
<br />
This is when a college pointed me to <a href="https://github.com/nearinfinity/node-java" target="_blank">https://github.com/nearinfinity/node-java</a> which is a module to write native Java which is then executed inside your javascript using Node.js.<br />
<br />
It turned out to be very straight forward and I added a sample JavaScript here: <a href="https://gist.github.com/dunse/5122258">https://gist.github.com/dunse/5122258</a><br />
<br />
In order to run the script, you will need Node.js, Java and some WebLogic libraries.<br />
The WebLogic libraries been specified in the getbs.js file as:
<br />
<pre class="prettyprint">java.classpath.push('configfwk-1.1.0.0.jar');
java.classpath.push('sb-kernel-impl-1.1.3.0.jar');
java.classpath.push('sb-kernel-api-3.0.jar');
java.classpath.push('sb-kernel-common-1.1.3.0.jar');
java.classpath.push('wlfullclient-10.3.0.jar');
java.classpath.push('standalone_client-11.1.1.jar');</pre>
Copy those files to the current working directory and update any version numbers as necessary.<br />
<br />
<br />
Next, define your connection options:<br />
<pre class="prettyprint">var config = {
admin_server: {
"hostname": "z2esbadmin01.zst.optusnet.com.au",
"port": 9001,
"username": "weblogic",
"password": "w3blogic"
}
};</pre>
<div>
<br /></div>
<br />
And then run it:<br />
<div style="background-color: #dddddd; padding: 3px;">
node getbs.js</div>
<br />
If all goes well, it will output something similar to this:<br />
<div style="background-color: #dddddd; padding: 3px;">
qwerty/business_services/qwerty_bs => http://my.backend.server.com:8080/EndPoint/<br />
...</div>
<br />
<br />dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com1tag:blogger.com,1999:blog-262398127331117099.post-18160681334344160272012-11-19T21:19:00.000+11:002012-11-19T21:19:08.893+11:0021st Century Integration Testing<h2>
</h2>
<h3 style="text-align: center;">
Everything starts here</h3>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGK-UOXhTj3T5D0R15oxoJpex4XtW_csd8wVZch6gom3uAmgyiyRAbrjzPpWnC64s-LsEFlATd7VVhDJ2ZfeXcYrXpEI-gsnnnlRN7tUXLPmor_u8gkBkS-SzrdEz4HYKAYD1gdQ-WmYEa/s1600/MyApp1-Screenshot.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGK-UOXhTj3T5D0R15oxoJpex4XtW_csd8wVZch6gom3uAmgyiyRAbrjzPpWnC64s-LsEFlATd7VVhDJ2ZfeXcYrXpEI-gsnnnlRN7tUXLPmor_u8gkBkS-SzrdEz4HYKAYD1gdQ-WmYEa/s400/MyApp1-Screenshot.jpeg" width="346" /></a></div>
<br />
<br />
<h2>
So what is it?</h2>
A combination of OpenSource projects, bundled with a user-friendly interface, to bring your integration tests in to the 21st Century.<br />
<br />
<b>As shown on the screenshot above, the framework will give you access to the following:</b><br />
<ul>
<li>Run Chart - historic availability of MyApp1</li>
<li>Blame Chart - test(s) that have failed</li>
<li>Frontends,
Service and Backends - gives us a simple view of where the fault lies,
and more importantly, which functionality has been impacted </li>
</ul>
<br />
<h2>
What are the key benefits?</h2>
<ul>
<li>Increased availability</li>
<li>Improved productivity</li>
<li>Reduced maintenance costs</li>
</ul>
<br />
<h2>
Who will benefit?</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxV9soUiKF-Z3GqYUOF4HOXqh-m11AoEP-hNOgb2gjF8tLm_U3Qt1Z__i-GrXmqwgdBF-pHk6vUnSmWE1we4tTM-mnL8oF0_dne4OK2vPvNFFzjNEo95O5oojMncM0xdxHTT9qj9RfW5dH/s1600/Who+will+benefit.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxV9soUiKF-Z3GqYUOF4HOXqh-m11AoEP-hNOgb2gjF8tLm_U3Qt1Z__i-GrXmqwgdBF-pHk6vUnSmWE1we4tTM-mnL8oF0_dne4OK2vPvNFFzjNEo95O5oojMncM0xdxHTT9qj9RfW5dH/s400/Who+will+benefit.png" width="400" /></a></div>
<br />
<br />
<h2>
How does it work?</h2>
It's quite simple, we take our existing (or new) integration tests, add them to this framework and navigate to the monitor page. <b>Sound simple? That's because it is!</b><br />
<br />
<h2>
Time for a test drive?</h2>
Would you buy a car without test driving it? Even though nothing here is for sale, time still costs money, and for anyone to invest time and effort, it has to be worth it.<br />
In order to show the power of "21st Century Integration Testing", a bundle with both the server and test cases is provided below. It enables you to test run it before deciding on setting up your own solution.<br />
<br />
<b>Please make sure you have the following products installed:</b><br />
<br />
Java - <a href="http://www.java.com/getjava/" target="_blank">http://www.java.com/getjava/</a><br />
<br />
Firefox - <a href="http://www.mozilla.org/en-US/firefox/new/" target="_blank">http://www.mozilla.org/en-US/firefox/new/</a><br />
<br />
The provided package is a fully functional three layered Bottom Up Testing, including SoapUI and Selenium tests. <u>All code used to make this package, can be found on <a href="https://github.com/dunse" target="_blank">GitHub</a>.</u><br />
<ol>
<li>Download <a href="https://github.com/downloads/dunse/try-it-out/21st%20Century%20Integration%20Testing.zip" target="_blank">https://github.com/downloads/dunse/try-it-out/21st%20Century%20Integration%20Testing.zip</a></li>
<li>Unpack and run 1_startServer.bat (or 1_startServer.sh if you run *NIX)<br /><i>This should automatically start the browser, if not you can access the monitor page at: <a href="http://localhost:18080/ShowMyTests/" target="_blank">http://localhost:18080/ShowMyTests/</a></i></li>
<li>Run a test: 2_runTestSuite.bat (2_runTestSuite.sh)<br /><i>Some tests are random, so run again to get different results...<br />Cuanto (the backend) can be accessed by <a href="http://localhost:18080/cuanto/" target="_blank">http://localhost:18080/cuanto/</a></i> </li>
</ol>
<br />
<h2>
How do I get started?</h2>
Step-by-step instructions on how to setup your own test server and add your own tests can be found here: <a href="http://dsysadm.blogspot.com.au/2012/11/getting-started-with-21st-century.html">http://dsysadm.blogspot.com.au/2012/11/getting-started-with-21st-century.html</a><br />
<br />
<strong>You can now have your own system up and running in no time!</strong><br />
<br />
<br />
<h2>
Not convinced?</h2>
Please leave a comment about your thoughts. Constructive citisism is always appreciated!dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-64355266756680187862012-11-19T21:08:00.000+11:002012-11-19T21:08:03.949+11:00Getting started with 21st Century Integration Testing<br />
<h2>
Setup the server</h2>
Download and install the software listed below.<br />
Plenty of useful tutorials exist on how to install Java, Tomcat and MySQL on every possible operating system, so please follow one of those. For Cuanto, please follow the instructions in the next two sections.<br />
<ul>
<li>Java - <a href="http://www.java.com/getjava/" target="_blank">http://www.java.com/getjava/</a></li>
<li>Tomcat6+ - <a href="http://tomcat.apache.org/download-70.cgi" target="_blank">http://tomcat.apache.org/download-70.cgi</a></li>
<li>MySQL - <a href="http://dev.mysql.com/downloads/mysql/" target="_blank">http://dev.mysql.com/downloads/mysql/</a></li>
<li>Cuanto (with support for charts) - <a href="https://github.com/downloads/dunse/cuanto/cuanto.war" target="_blank">https://github.com/downloads/dunse/cuanto/cuanto.war</a></li>
<li>ShowMyTests - <a href="https://github.com/downloads/dunse/ShowMyTests/ShowMyTests.war" target="_blank">https://github.com/downloads/dunse/ShowMyTests/ShowMyTests.war</a></li>
</ul>
<br />
<h3>
Installing Cuanto and MySQL JDBC Driver</h3>
(Source: <a href="https://raw.github.com/ttop/cuanto/master/INSTALL.html" target="_blank">INSTALL.html</a>):<br />
<br />
<h4>
Creating the database</h4>
Create a database on your SQL server named "cuanto". Typically this is done with a command like
"<span class="mono">create database cuanto;</span>" when logged onto your SQL server, although you may want
to specify additional details depending on any additional database restrictions or options you want to enable.<br />
<br />
<h4>
Customizing cuanto-db.groovy</h4>
Edit <span class="mono"><a href="https://raw.github.com/ttop/cuanto/master/grails/cuanto-db.groovy" target="_blank">cuanto-db.groovy</a></span> with a text editor. You'll see a section like this:
<br />
<pre class="prettyprint">production {
dataSource {
username = "my_sql_user"
password = "my_sql_password"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://my_sql_server:3306/cuanto?autoreconnect=true"
}
}</pre>
Edit the username, password, driverClassName and url to correspond to the correct values for your database
credentials, JDBC driver and the JDBC URL for your SQL server. Make sure you edit the "production" section.<br />
<br />
<h4>
Deploying the application</h4>
Unzip the WAR into your application server's <span class="mono">webapps</span> directory into a
subdirectory named "cuanto". Copy the <span class="mono">cuanto-db.groovy</span> you customized into the
<span class="mono">cuanto/WEB-INF/classes</span> directory.<br />
<br />
<br />
<h3>
Installing ShowMyTests</h3>
Simply copy the downloaded .war file in to Tomcat's webapps directory, start Tomcat and navigate to http://<Tomcat_Server_IP>:<Tomcat_Server_Port>/ShowMyTests/.<br />
For example, <a href="http://localhost:8080/ShowMyTests/">http://localhost:8080/ShowMyTests/</a><br />
This should bring up a black screen with the friendly words "ERROR: No TestRuns found":<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHWcQhgCR0Nw61_CvobXWOU5CIU9zK4nt-0O2Bk04Tg7FktpB47g2sA_HURE7PgXo7lhlR4gUcG5diDCDo8zhO2LO_itS6tDVTG_F7UVhpuVFr_QCkI3MGzmwQdN2hkCP4j88XHTbNM_r6/s1600/ShowMyTests+-+no+runs.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHWcQhgCR0Nw61_CvobXWOU5CIU9zK4nt-0O2Bk04Tg7FktpB47g2sA_HURE7PgXo7lhlR4gUcG5diDCDo8zhO2LO_itS6tDVTG_F7UVhpuVFr_QCkI3MGzmwQdN2hkCP4j88XHTbNM_r6/s1600/ShowMyTests+-+no+runs.jpg" /></a></div>
<br />
<br />
Server installation is now ready for use! Move on to checkout the framework and start running some tests....<br />
<br />
<br />
<h2>
Get the framework</h2>
Make sure your IDE have Maven Integration and supports Git.<br />
Then clone the following Git repositories (Remember to tick the "Import all existing projects after clone finishes"):<br />
<a href="git://github.com/dunse/ShowMyTests.git"><strong>git://github.com/dunse/ShowMyTests.git</strong></a><br />
<a href="git://github.com/dunse/my-integration-tests.git"><strong>git://github.com/dunse/my-integration-tests.git</strong></a><br />
<br />
While you wait, read through the next section if you are not familiar with Integration Testing fundamentals.<br />
<br />
<br />
<h2>
Integration Testing </h2>
As the name indicates, it is used to test integration between components. It does not matter if it is one service to another, or end-user to web-applications, they are all integrated and should be tested.<br />
<br />
For the concept of 21st Century Integration Testing, we adopted the Bottom-Up Testing as it is the most suitable.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFcazEakTfpxeVYq6KgZYNydQEJQL0l2lAskbrh6MzPXg1Vc_WF2_KNIsk_WL6oqBEV-421QTTFMyHXIxLp0BhcLEVxJ7tSbwvwz4thz7i6PfFgEWpEevVsd387T_rh1973xjzfzbJkHME/s1600/Bottom-Up.jpeg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="103" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFcazEakTfpxeVYq6KgZYNydQEJQL0l2lAskbrh6MzPXg1Vc_WF2_KNIsk_WL6oqBEV-421QTTFMyHXIxLp0BhcLEVxJ7tSbwvwz4thz7i6PfFgEWpEevVsd387T_rh1973xjzfzbJkHME/s200/Bottom-Up.jpeg" width="200" /></a></div>
<span style="font-size: x-small;"><i><b>Bottom Up Testing</b> is an approach to integrated testing where the lowest level components are tested first, then used to facilitate the testing of higher level components. The process is repeated until the component at the top of the hierarchy is tested.<br />All the bottom or low-level modules, procedures or functions are integrated and then tested. After the integration testing of lower level integrated modules, the next level of modules will be formed and can be used for integration testing. This approach is helpful only when all or most of the modules of the same development level are ready. This method also helps to determine the levels of software developed and makes it easier to report testing progress in the form of a percentage. (Source: Wikipedia)</i></span><br />
<br />
<strong>It is important when creating tests for this purpose to approach them via a Top Down fashion, which means:</strong><br />
<ol>
<li>Identify a User Action on the Frontend Layer, write a test for it and verify it.</li>
<li>Move to the next Layer (Service Layer) and write tests for all calls produced by the User Action.</li>
<li>Finally, move to the Backend Layer and write tests for all calls produced by the Service Layer for the given User Action.</li>
</ol>
<strong>Making the tests generic</strong><br />
<u>Never, ever, ever</u> have any environment specific details in the test itself. It is easy enough to create a property file and load it during runtime, so there is no reason not to.<br />
<br />
<br />
<h2>
Run the provided tests</h2>
In your IDE, create a new Run Configuration as:<br />
<ul>
<li>Main class: org.testng.TestNG</li>
<li>Program arguments: ./src/main/resources/testng-MyApp1.xml</li>
<li>VM arguments:<br />-Dcuanto.url=http://localhost:8080/cuanto<br />-Dcuanto.projectkey=MyApp1<br />-Dcuanto.testrun.create=true<br />-Dcuanto.includeConfigDuration=false<br />-DcrmId=399</li>
</ul>
If your Tomcat installation is not running on localhost:8080, update the cuanto.url accordingly. <br />
<br />
<strong>Example screenshots using Eclipse:</strong><br />
<strong></strong><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaKWAglALebrICaVufaSVx9k5RgT_YPj8VRymDK6u-O31nh2TUB5GMCZH_t38ILIwwTmV3PDj_b8_3C8EIMVsfrV02k6PP08aP7PjCqFlu5F8vvpjut-m3G5mc1-boFFwhfONiyMIx3-vv/s1600/my-integration-tests+-+Run+Configuration+1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="251" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaKWAglALebrICaVufaSVx9k5RgT_YPj8VRymDK6u-O31nh2TUB5GMCZH_t38ILIwwTmV3PDj_b8_3C8EIMVsfrV02k6PP08aP7PjCqFlu5F8vvpjut-m3G5mc1-boFFwhfONiyMIx3-vv/s320/my-integration-tests+-+Run+Configuration+1.jpg" width="320" /></a> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4R95ZgGLS6bivrdmqOsDhygrAwgoC_my35rn0lGHlS-9wiGfADSfSCxypKfZ4NslN9PIPptw0IVX7zuRijopsQIKGbSHtfILi0ot_SFMiojUboToPAzlqKMHdUCyLamlpUrjhfekQuQom/s1600/my-integration-tests+-+Run+Configuration+2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4R95ZgGLS6bivrdmqOsDhygrAwgoC_my35rn0lGHlS-9wiGfADSfSCxypKfZ4NslN9PIPptw0IVX7zuRijopsQIKGbSHtfILi0ot_SFMiojUboToPAzlqKMHdUCyLamlpUrjhfekQuQom/s320/my-integration-tests+-+Run+Configuration+2.jpg" width="320" /></a></div>
<br />
<br />
When done, run the new configuration and switch back to ShowMyTests.<br />
It should now display something simliar to this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqeY4NdvWOMsG0I-xQzTvsFi64yMFUSbxY6FwzYVeCKqOKaL6LsHZxuvICWhVmUt-4J3SSMP7ajgcNXBxQdm6wQV8ZEdYyqdYaFpUu_LtXaiq5EiYXpsLmDjQi03wj35ARAWkE_t3JSKhO/s1600/my-integration-tests+-+First+run.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqeY4NdvWOMsG0I-xQzTvsFi64yMFUSbxY6FwzYVeCKqOKaL6LsHZxuvICWhVmUt-4J3SSMP7ajgcNXBxQdm6wQV8ZEdYyqdYaFpUu_LtXaiq5EiYXpsLmDjQi03wj35ARAWkE_t3JSKhO/s320/my-integration-tests+-+First+run.jpg" width="280" /></a></div>
<br />
<br />
<h2>
Add your own tests</h2>
The easiest way to get started is to reuse the existing classes:<br />
<ul>
<li>OnlineFrontend - Frontend tests, could be login or a purchase using a browser</li>
<li>CRMService - Service (SOA) tests, normally WebServices</li>
<li>CRMBackend - Backend tests, such as databases or depending WebServices</li>
</ul>
Let's say we want to test a Login User Action.<br />
First, we identify the flow through the application:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX7t4qb1ZQ3R6Pxvz3XCUCkpQVRkziQEbTCegfL8ejwDqxbe7iRvt3XrJcSGRmnt_xtCcROtmam6x4r4KPePJ3hnYD9OWwe1L9d0Xgjc_n63GwHxlX3t5XMeD7oHVqP36CMTrasgRFg4BY/s1600/Login+User+Action.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX7t4qb1ZQ3R6Pxvz3XCUCkpQVRkziQEbTCegfL8ejwDqxbe7iRvt3XrJcSGRmnt_xtCcROtmam6x4r4KPePJ3hnYD9OWwe1L9d0Xgjc_n63GwHxlX3t5XMeD7oHVqP36CMTrasgRFg4BY/s640/Login+User+Action.png" width="640" /></a></div>
<br />
As shown in the above, we have four tests to write, starting with the bottom one moving up.<br />
<br />
<h3>
User DB</h3>
We create a new method called "userDB" in CRMBackend, which will check if the database if functional or not. Preferrably executing a query against the database.<br />
<pre class="prettyprint linenums">@Test(groups = {"MyApp1", "userDB"})
public void userDB() {
assertTrue("Here we can test if the DB works", true); // Replace this line with your own test
}</pre>
<br />
Important to note here, is the specified "groups". These will be used for two purposes:<br />
<ol>
<li>"MyApp1" - groups all tests which belongs to MyApp1. This is useful for reusing the same test code for multiple applications</li>
<li>"userDB" - it is good practice to include the method in a group with the same name. You will see why when we move on to the Service layer</li>
</ol>
<br />
<h3>
Security WS & User WS</h3>
In CRMService, add the following code:<br />
<pre class="prettyprint linenums">@Test(groups = {"MyApp1", "authenticateUser"}, dependsOnGroups = {"userDB"})
public void authenticateUser() { // Part of Security WS
assertTrue("Here we can test user authentication works", true); // Replace this line with your own test
}
@Test(groups = {"MyApp1", "retrieveUserDetails"}, dependsOnGroups = {"userDB"})
public void retrieveUserDetails() { // Part of User WS
assertTrue("Here we can test if user details can be retrieved", true); // Replace this line with your own test
}
</pre>
<br />
We use the same principle as for the User DB with one exception, "dependsOnGroups".<br />
"dependsOnGroups" tells TestNG that both methods depend on the success of "userDB" and will not be executed if "userDB" fails.<br />
<br />
This is a key feature which makes your tests raise above the rest.<br />
<br />
<h3>
Login</h3>
Final method :<br />
<pre class="prettyprint linenums">@Test(groups = {"MyApp1", "login"}, dependsOnGroups = {"authenticateUser", "retrieveUserDetails"})
public void login() { // Part of User WS
assertTrue("Use Selenium to test the login sequence and verify retrieved data", true); // Replace this line with your own test
}</pre>
<br />
<h3>
Summary</h3>
As a result of the "dependsOnGroups", TestNG will manage the dependencies and execution order. If multiple methods don't specify "dependsOnGroups", they will be executed in no particular order.<br />
If we map out the execution, we would end up with something like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD6fHKz1IoBNL7azd9Op9KtVUaK1Bk2DJcSyXv4OKVxwJa45wpD58zqu7sBjKN7OJ6PSWtMEPCEwN5ftzKA45PS6hl9Q3wy9jvP5ltLAYtySeTzMj-2GAiabdkkHM43IjUkXXnSK2U8-2t/s1600/Login+User+Action+-+wf.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD6fHKz1IoBNL7azd9Op9KtVUaK1Bk2DJcSyXv4OKVxwJa45wpD58zqu7sBjKN7OJ6PSWtMEPCEwN5ftzKA45PS6hl9Q3wy9jvP5ltLAYtySeTzMj-2GAiabdkkHM43IjUkXXnSK2U8-2t/s640/Login+User+Action+-+wf.jpeg" width="640" /></a></div>
As you can see, it follows the Bottom Up Testing technique to the letter.<br />
<br />
If you now run "my-integration-tests" again, the new tests will show up on "ShowMyTests" screen (hidden since they would all pass by default). To see how the userDB affects the upstream functions, you can simply change the assertTrue() call by changing true to false and re-run the tests.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEIcg-DouJ6R8iNysbxjrKHQiTWryfVOLPaYuTDbCTnatNeUuwfXKHqHioTJpPRD8zyFGcpSCzu1BakiL5WhQint_g0Mcm-sKd12-CcaJbxTze6Wk9L2F7p_ObIxcaQ6Zy4IbhZgnBh9F9/s1600/Add+your+own+tests+-+SMT1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEIcg-DouJ6R8iNysbxjrKHQiTWryfVOLPaYuTDbCTnatNeUuwfXKHqHioTJpPRD8zyFGcpSCzu1BakiL5WhQint_g0Mcm-sKd12-CcaJbxTze6Wk9L2F7p_ObIxcaQ6Zy4IbhZgnBh9F9/s200/Add+your+own+tests+-+SMT1.jpg" width="178" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5k61MRdJFNYYwnIyaCpUp3pn4qiigsalrTalPVKjx7I3B6pvy0UrEX_5ONZwYyfnkVXXy60cy4v5Kc06Uw8OycpOOyappfzZpdFevL-WrL94aB6hecCutp7IMBzKrrcqH5B1evLY7vFTF/s1600/Add+your+own+tests+-+SMT2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5k61MRdJFNYYwnIyaCpUp3pn4qiigsalrTalPVKjx7I3B6pvy0UrEX_5ONZwYyfnkVXXy60cy4v5Kc06Uw8OycpOOyappfzZpdFevL-WrL94aB6hecCutp7IMBzKrrcqH5B1evLY7vFTF/s200/Add+your+own+tests+-+SMT2.jpg" width="77" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUw0kC4Md3V1gHVQEIKtapDUf0-U7cw822uccOx8p0ftdVQ_7gWvvRW4csrR52fv2Q3LgfPIvEVvR5_p6qgNfcDyvq8xHZGeIss8Se56AmGJx07OXMCu2E_Gpwkf4DPVH90r0V-raB_oBk/s1600/Add+your+own+tests+-+SMT3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUw0kC4Md3V1gHVQEIKtapDUf0-U7cw822uccOx8p0ftdVQ_7gWvvRW4csrR52fv2Q3LgfPIvEVvR5_p6qgNfcDyvq8xHZGeIss8Se56AmGJx07OXMCu2E_Gpwkf4DPVH90r0V-raB_oBk/s200/Add+your+own+tests+-+SMT3.jpg" width="87" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5DmVESA_URhER7rdt8RWjYfWZL4gIDOyYmtPWSjVLqHM-QHHxOeq0DY50jZjSvn1_ZG1eHIrj_oZ68TRyhBBoLJH_CzXKx5yboIWQCSmGFRp0hFlhQZjG8VPX4mBr4f_wu4ckCj8Php58/s1600/Add+your+own+tests+-+SMT4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5DmVESA_URhER7rdt8RWjYfWZL4gIDOyYmtPWSjVLqHM-QHHxOeq0DY50jZjSvn1_ZG1eHIrj_oZ68TRyhBBoLJH_CzXKx5yboIWQCSmGFRp0hFlhQZjG8VPX4mBr4f_wu4ckCj8Php58/s200/Add+your+own+tests+-+SMT4.jpg" width="95" /></a></div>
<br />
<br />
<strong>Congratulations! You have now created your very first tests as part of 21st Century Integration Testing!</strong><br />
<br />
But this is only the beginning. If you haven't already, replace the tests with your own tests and see it all come to life.<br />
<br />
<br />
<h2>
Some help along the way</h2>
<h3>
Refactoring classes</h3>
The names of the classes probably won't make much sense for your application, so let's take a closer look at how we can change them.<br />
<br />
ShowMyTests will put any tests run from a class' name ending with Frontend into the Frontends layer, ending with Service into Service layer and Backend into the Backends Layer. This means <strong>no change should be required for the monitor page</strong>, as long as you follow the given naming convention.<br />
<br />
TestNG however, needs to know which classes to look for tests in. Inside /src/main/resources/testng-MyApp1.xml make sure your new classes are mentioned inside <classes></classes> and they will be included in the test run.<br />
<br />
<h3>
Making SoapUI tests smarter</h3>
SoapUI provides an interfact to override any custom properties defined inside its projects. For example, we pass in "-DcrmId=399" while running the tests above.<br />
This crmId is then picked up in CRMService and passed to the SoapUI project as:<br />
<pre class="prettyprint linenums:25">String[] props = {
"crmId=" + Init.getProperty("crmId")
};
System.out.println("crmId=" + Init.getProperty("crmId"));
runner.setProjectProperties(props);</pre>
<br />
Inside SoapUI we have crmId declared as a Project Customer Property:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZJ7Aicn7b2ntJc7KvLbRvcOcsW08BMhzjwoc_naeXeqGSwd95jSfFGEQQuk_WvZJUc9MKZNhkqz9VdsuodxxdKlKagsChBawrAfwFn7voGesU45cPfVgzNdvE6jrTuXcNzTKphM0xTnAT/s1600/SoapUI+-+crmId1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZJ7Aicn7b2ntJc7KvLbRvcOcsW08BMhzjwoc_naeXeqGSwd95jSfFGEQQuk_WvZJUc9MKZNhkqz9VdsuodxxdKlKagsChBawrAfwFn7voGesU45cPfVgzNdvE6jrTuXcNzTKphM0xTnAT/s320/SoapUI+-+crmId1.jpg" width="320" /></a></div>
<br />
<br />
Which we use inside one of the Test Steps:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYnDe3RvB2owJ7siXvRLOu_rMBcVB2CWemgODHQx68jxnbdAdOqDKIb23XSv1EU2KkDoS-i4cE_lGWJMEP4frsRPNZ5XdbdFrPtoojYIl-q-Rj3kBty332xMpWVcex993QKeqUWATpeMxr/s1600/SoapUI+-+crmId2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYnDe3RvB2owJ7siXvRLOu_rMBcVB2CWemgODHQx68jxnbdAdOqDKIb23XSv1EU2KkDoS-i4cE_lGWJMEP4frsRPNZ5XdbdFrPtoojYIl-q-Rj3kBty332xMpWVcex993QKeqUWATpeMxr/s320/SoapUI+-+crmId2.jpg" width="320" /></a></div>
<br />
<br />
This makes it very easy to adjust any (for example) environment specific values while running the integration tests, without the need to duplicate the tests.<br />
<br />
One custom property that normaly comes in handy is the "myEndpoint". This makes it possible to pick and choose which server you want to run the tests against.<br />
<br />
<strong></strong><br />
<strong>Here is how it can be implemented:</strong><br />
<br />
1. In SoapUI, add the custom property (which also serves as default value when running the tests inside SoapUI):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkOPhxmBPsFdhej3FJPYdxY8dbDphdD8d50hS3oFaTZqU2KPlnHFE0vzl3zERgm8SPP7KR5a5lkdcayR2tNOCVzums2JtC1oprw1oz_ApE7ANKzRmfl2FOhAaYi13ogY4LK215zJR5btdV/s1600/SoapUI+-+myEndpoint1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkOPhxmBPsFdhej3FJPYdxY8dbDphdD8d50hS3oFaTZqU2KPlnHFE0vzl3zERgm8SPP7KR5a5lkdcayR2tNOCVzums2JtC1oprw1oz_ApE7ANKzRmfl2FOhAaYi13ogY4LK215zJR5btdV/s320/SoapUI+-+myEndpoint1.jpg" width="320" /></a></div>
<br />
<br />
2. In SoapUI, change the endpoint(s) to use this new custom property:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY2wA7iQtYB7larzTEO_b5G8xn-Z2la4pvsVHJikVjDTpB4OkctNnz6smJ_5CZcty03YzFqZGSsPRbVB3pe_pdOrv9NMcz9V9Mckvz3nTP0_YpYId-nIRe4wik2L4OAKkDoKYRmbAm3MSX/s1600/SoapUI+-+myEndpoint2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY2wA7iQtYB7larzTEO_b5G8xn-Z2la4pvsVHJikVjDTpB4OkctNnz6smJ_5CZcty03YzFqZGSsPRbVB3pe_pdOrv9NMcz9V9Mckvz3nTP0_YpYId-nIRe4wik2L4OAKkDoKYRmbAm3MSX/s320/SoapUI+-+myEndpoint2.jpg" width="320" /></a></div>
<br />
<br />
3. Inside CRMService, add line 27:<br />
<pre class="prettyprint linenums:25">String[] props = {
"crmId=" + Init.getProperty("crmId")
"myEndpoint=" + Init.getProperty("myEndpoint")
};
System.out.println("crmId=" + Init.getProperty("crmId"));
runner.setProjectProperties(props);</pre>
<br />
Now the next time you run my-integration-tests, it is possible to run the Service tests against any server, using the "-DmyEndoint=<new_value>" argument.dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com1tag:blogger.com,1999:blog-262398127331117099.post-18312887520860722192012-09-14T01:47:00.001+10:002012-09-30T12:37:28.566+10:00Human readable cron expressions using JavaScriptSo I was faced with the task of presenting cron expression (like '3 * * * *') on a web page in human readable format. I figured there would be plenty of scripts available to do just that, ready for use.<br />
With the limitations to using JavaScript, I found nothing! Either it is hidden away somewhere or no one have published a script to convert cron expression in to human readable format.<br />
<br />
After a little soul searching I decided to write my own implementation.<br />
The final result looks like this:<br />
<div style="background-color: #dddddd; padding: 3px;">
<b>Cron expression: </b>15 3,8,10,12,14,16,18 16 * *<br />
<b>Human readable: </b>Every 15th minute past the 3, 8, 10, 12, 14, 16 and 18th hour on the 16th every month<br />
<b>Next run: </b>Sunday at 3:15 AM<br />
<br />
<b>Cron expression: </b>30 * * * *<br />
<b>Human readable: </b>Every 30th minute past every hour<br />
<b>Next run: </b>Today at 1:30 AM<br />
<br />
<b>Cron expression: </b>0 * * * *<br />
<b>Human readable: </b>Every hour, on the hour<br />
<b>Next run: </b>Today at 2:00 AM<br />
<br />
<b>Cron expression: </b>* 8,10,12,14,16,18,20 * * * <br />
<b>Human readable: </b>Every minute of 8, 10, 12, 14, 16, 18 and 20th hour<br />
<b>Next run: </b>Today at 8:00 AM<br />
<br />
<b>Cron expression: </b>2 8,10,12,14,16,18 * 4 0,3 <br />
<b>Human readable: </b>Every 2nd minute past the 8, 10, 12, 14, 16 and 18th hour in Apr on Sun and Thu<br />
<b>Next run: </b>04/07/2013</div>
<br />
<h2>
Dependencies</h2>
First download the dependencies:<br />
<a href="https://raw.github.com/timrwood/moment/1.7.0/min/moment.min.js" target="_blank">moment.min.js</a> - Credit Moment.js<br />
<a href="https://github.com/bunkat/later/raw/master/later.min.js" target="_blank">later.min.js</a> - Credit bunkat<br />
<a href="https://raw.github.com/gist/3714957/7e1b353d2edc6dd8d0f5ee8063cda0323af44386/prettycron.js" target="_blank">prettycron.js</a> - Credit myself<br />
<br />
<h2>
How to use</h2>
Copy the dependencies above to the web server.<br />
In the same directory, create a new basic HTML file called prettycron.html.<br />
Include the dependencies in prettycron.html:<br />
<pre class="prettyprint"> <script src="later.min.js" type="text/javascript"></script>
<script src="moment.min.js" type="text/javascript"></script>
<script src="prettycron.js" type="text/javascript"></script>
</pre>
<br />
Add this in the body of prettycron.html:<br />
<pre class="prettyprint">document.write(getPrettyCron(cronParser().parse('0 * * * *', false)));
</pre>
<br />
Open the page in a web browser and you should see the following output:<br />
<div style="background-color: #dddddd; padding: 3px;">
Every minute</div>
<br />
That is it, just replace '0 * * * *' with any basic cron expression to see it in human readable format.<br />
<br />
<br />
<h2>
Full example</h2>
Please find the full example below to produce the example from introduction:<br />
<pre class="prettyprint"><html>
<head>
<title>Human readable cron schedule</title>
<script src="later.min.js" type="text/javascript"></script>
<script src="moment.min.js" type="text/javascript"></script>
<script src="prettycron.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript">
function printCron(cron) {
var s1 = cronParser().parse(cron, false);
document.write('<b>Cron expression: </b>' + cron + '<br/>');
document.write('<b>Human readable: </b>' + getPrettyCron(s1['schedules'][0]) + '<br/>');
document.write('<b>Next run: </b>' + moment(later(60,true).getNext(s1)).calendar() + '<br/><br/>');
}
printCron('15 3,8,10,12,14,16,18 16 * *');
printCron('30 * * * *');
printCron('0 * * * *');
printCron('* 8,10,12,14,16,18,20 * * * ');
printCron('2 8,10,12,14,16,18 * 4 0,3 ');
</script>
</body>
</pre>
<br />
<br />
<h2>
Internet Explorer pre-9</h2>
Only in Internet Explorer 9 support came for Array.indexOf. The scripts depends heavily on this method so to make it work in Internet Explorer 6-8, perform the following steps:<br />
1. Create ielt9.js containing the code from "Compatibility" section of:<br />
<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf">https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf</a><br />
<br />
2. Inside <head></head> add:<br />
<!--[if lt IE 9]><script src="js/ltie9.js" type="text/javascript" charset="UTF-8"></script><![endif]-->dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com35tag:blogger.com,1999:blog-262398127331117099.post-14751957560651269932012-09-09T20:11:00.000+10:002012-09-23T20:34:52.945+10:00My first Android appI just finished writing my first Android app <a href="https://play.google.com/store/apps/details?id=dunse.sspf" target="_blank">Screensaver Photo Frame</a>:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_RLwAfne_FrLNAfLw_9Hs-rcEverBlxwsn3Ho2lCaMQUaig2lN84wxvvJ9WM-E0-EqhufGLO-5IvxhGZ8odmGj5AKG0EpDnKJNAdqFCHrFohorN1tp1Xc8M1Lbqrp7J1ei3loTzlefusK/s1600/slideshow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_RLwAfne_FrLNAfLw_9Hs-rcEverBlxwsn3Ho2lCaMQUaig2lN84wxvvJ9WM-E0-EqhufGLO-5IvxhGZ8odmGj5AKG0EpDnKJNAdqFCHrFohorN1tp1Xc8M1Lbqrp7J1ei3loTzlefusK/s320/slideshow.png" width="192" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifzcxN-ugyqgp5omDcEDuPXD-rszlLccfPqWJSglbX53wXvlTjGzPei8yBjSBMjsP95vd1u8ucEu6Ti9qZGk5XKWfAk4HAusjuZL4DoJhjm5Pn7oqd45fpA4__KYx7ikLnB160ufqeFLfe/s1600/main_activity.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifzcxN-ugyqgp5omDcEDuPXD-rszlLccfPqWJSglbX53wXvlTjGzPei8yBjSBMjsP95vd1u8ucEu6Ti9qZGk5XKWfAk4HAusjuZL4DoJhjm5Pn7oqd45fpA4__KYx7ikLnB160ufqeFLfe/s320/main_activity.png" width="192" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnW1YQuJc2Jz5vpB_d5R2CK3ecHSlj-DFJsVHp_Gd2mD4KjPHIqtRhFa98X54OsZjvtoAWTm9nEPNsgZt0qxE3dokIXg0fOnKw7HWlQtWtTxLX3uspnA8FK7VyYH_7YtKFg_I6fDWl6DdS/s1600/smb_location_browser.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnW1YQuJc2Jz5vpB_d5R2CK3ecHSlj-DFJsVHp_Gd2mD4KjPHIqtRhFa98X54OsZjvtoAWTm9nEPNsgZt0qxE3dokIXg0fOnKw7HWlQtWtTxLX3uspnA8FK7VyYH_7YtKFg_I6fDWl6DdS/s320/smb_location_browser.png" width="192" /></a>
</td>
</tr>
</tbody>
</table>
During this project I hit a few gotchas which I wanted to share. Below I listed the most critical things to remember while starting developing for Android.<br />
<br />
<h2>
1. Exceptions = Force close</h2>
Whenever the application throws an exception, it will cause Android to force close the application. So to avoid frustration for the users, catch any potential exception and handle them properly.<br />
<br />
<h2>
2. Database locking issues</h2>
As you might already noticed, multi-threading is critical for any Android application. The issue with multi-threading and built in database is however that you are destined to run into SQLiteDatabaseLockedException by following most getting started guides.<br />
The trick is to use SQLiteOpenHelper as a singleton and let SQLite manage the synchronization.<br />
In the cut-down example below, two threads can call addSomething() simultaneously and SQLite will handle the synchronization within itself preventing lock exception.<br />
DBAdapter:<br />
<pre class="prettyprint">public class DBAdapter {
private static SQLiteDatabase db = null;
public static synchronized DBAdapter getInstance(Context ctx)
{
if (db == null ) {
DatabaseHelper DBHelper = new DatabaseHelper(ctx);
db = DBHelper.getWritableDatabase();
}
return new DBAdapter();
}
public void addSomething(String something) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_SOMETHING, something);
db.insertWithOnConflict(DATABASE_SOMETHING_TABLE, null, initialValues,
SQLiteDatabase.CONFLICT_REPLACE);
}
private static class DatabaseHelper extends SQLiteOpenHelper
{
...
}
}
</pre>
Activity:<br />
<pre class="prettyprint">public class MainActivity extends Activity {
private DBAdapter database;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = DBAdapter.getInstance(getApplicationContext());
database.addSomething("something");
}
}
</pre>
<br />
<br />
<h2>
3. Multi-row SQL execution</h2>
Since Android 4-ish it is not possible to run queries separated by semi-colon anymore.<br />
So instead of executing SQL as:<br />
<pre class="prettyprint">db.execSQL("CREATE TABLE A ...; CREATE TABLE B ...;");
</pre>
Split it up into multiple executions:<br />
<pre class="prettyprint">db.execSQL("CREATE TABLE A ...;");
db.execSQL("CREATE TABLE B ...;");
</pre>
<br />
<br />
<br />dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com2tag:blogger.com,1999:blog-262398127331117099.post-53147082399052063582012-03-27T21:18:00.002+11:002012-03-27T21:18:54.065+11:00Dansguardian Log AnalyserI recently started to learn Dojo and thought that writing an application would be a fun way of doing so.<br />
<br />
When writing a new web application I wanted to create something useful, so I reflected back to when I installed Dansguardian on the My Book Live.<br />
<br />
After installation I found out there wasn't any good log analyser available (at least not outside webmin). So when deciding to write an application, the choice was easy. The application turned out to be "Dansguardian Log Analyser" which is based on Dojo and PHP.<br />
<br />
As with my other small projects I published the final product on <a href="https://github.com/dunse/dla" target="_blank">github</a> for everyone's use.<br />
<br />
Read on for details and installation instructions...<br />
<br />
<br />
<h2>
<span style="font-size: large;">Details</span></h2>
There's three sections available:<br />
<ul>
<li><b>Summary</b><br />Shows a graph of today's usage.</li>
<li><b>Denied requests</b><br />A list of today's denied requests. By clicking on a row it will open the denied page in the iframe below the grid.</li>
<li><b>Realtime log</b><br />This simply tail the Dansguardian logfile to the browser. It displays all requests logged in the access.log from the time the tab is selected.</li>
</ul>
<br />
<br />
<h2>
<b><span style="font-size: large;">Screenshots</span></b></h2>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh88OJ-HC2O1S0NJCImKCRCQ7SIA1KHHzWy4es8_wix48dUNlXNkuhdJIbj9tZfBBKHyCva8OpYVWccqc9xGxUP37Qak9GVMR-cliwZ0c_0iZv7CVAdvxC1Ldh2IBkeTNcuef8L4ueMhOeg/s1600/dla-summary.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh88OJ-HC2O1S0NJCImKCRCQ7SIA1KHHzWy4es8_wix48dUNlXNkuhdJIbj9tZfBBKHyCva8OpYVWccqc9xGxUP37Qak9GVMR-cliwZ0c_0iZv7CVAdvxC1Ldh2IBkeTNcuef8L4ueMhOeg/s640/dla-summary.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMQfl96C25UKshgnSH-WO2uUYdLMlfaa6WgGXnTR-6IpRUgIOheFqO_HURJwdVTLeOLv9bW9dk89VgTEjyJThyphenhyphenyRC6DaVlwB4WFC_LnCO4aSw-fymzjENzqdtsaeVHaBem088TNS-rVNO9/s1600/dla-denied.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMQfl96C25UKshgnSH-WO2uUYdLMlfaa6WgGXnTR-6IpRUgIOheFqO_HURJwdVTLeOLv9bW9dk89VgTEjyJThyphenhyphenyRC6DaVlwB4WFC_LnCO4aSw-fymzjENzqdtsaeVHaBem088TNS-rVNO9/s640/dla-denied.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH9QHq_PTrDh9Xa0t5p6V7q1jKgsnaQCfQ98QP7GFr_q1KJUVdHucK50xErS9Q3PJOH-l1JwZnYtfqymYW6pCeJIAmmpL-qqy3l8zC-e06HodE8649VBGhnMfDl567WzO3D39KJGDq7KAj/s1600/dla-realtime.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH9QHq_PTrDh9Xa0t5p6V7q1jKgsnaQCfQ98QP7GFr_q1KJUVdHucK50xErS9Q3PJOH-l1JwZnYtfqymYW6pCeJIAmmpL-qqy3l8zC-e06HodE8649VBGhnMfDl567WzO3D39KJGDq7KAj/s640/dla-realtime.jpg" width="640" /></a></div>
<br />
<h2>
<span style="font-size: large;">Installation</span></h2>
Download the package from <a href="https://github.com/dunse/dla/zipball/master">here</a>.<br />
Copy dla/ directory to your web/proxy server running Dansguardian. (E.g. to /var/www/dla/)<br />
Access it through: <a href="http://your.web.server/dla/">http://your.web.server/dla/</a><br />
<br />
<h2>
<span style="font-size: large;">Notes</span></h2>
Make sure "logfileformat = 1" is set in dansguardian.conf<br />
If "loglocation" is change from default, update getDansguardianLog.php with the correct path.<br />
<br />
<br />dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com4tag:blogger.com,1999:blog-262398127331117099.post-12454102099524486202012-03-01T14:33:00.000+11:002012-03-01T14:33:44.771+11:00My Book Live with Squid and fakeauthNext for the new My Book Live was to install and configure Squid. Since MBL runs Debian, it is fairly straight forward to get Squid up and running. But here we also want to setup fakeauth which complicate things a little bit.<br />
<br />
The reason for using fakeauth is to authenticate users on a home network without the need for any central authentication database. It is far from secure, but for the sole purpose of identifying the user it is good enough.<br />
<br />
<br />
<b><span style="font-size: large;">Install Squid3</span></b><br />
First we need to update the package repositories from where we wish to install.<br />
Simply run the following in the shell:<br />
<blockquote class="tr_bq">
echo 'deb http://ftp.us.debian.org/debian/ squeeze main<br />deb-src http://ftp.us.debian.org/debian/ squeeze main<br />#deb http://ftp.us.debian.org/debian/ sid main<br />#deb http://ftp.us.debian.org/debian/ experimental main' > /etc/apt/sources.list</blockquote>
Then execute the next line to update the local cache and install Squid 3:<br />
<blockquote class="tr_bq">
apt-get update && apt-get -y install squid3</blockquote>
This will download and install Squid3 followed by starting it up listening on port 3128.<br />
<br />
<br />
<b><span style="font-size: large;">Install fakeauth</span></b><br />
Fakeauth is part of Squid3 but it is not bundled with the installable package. This forces us to build it from source.<br />
<br />
Let's start by installing dependencies:<br />
<blockquote class="tr_bq">
apt-get -y install gcc g++ make patch</blockquote>
Next we download and unpack the source for Squid 3.1.6: <br />
<blockquote class="tr_bq">
cd ~/ && apt-get source squid3<br />tar xzf squid3_3.*.orig.tar.gz && cd squid-*/</blockquote>
Below we build fakeauth with a patch required for the MBL: <br />
<blockquote class="tr_bq">
./configure<br />cd compat/ && make<br />cd ../lib && make<br />cd ../helpers/ntlm_auth/fakeauth/<br />echo '279a280,281<br />> // Fix for platform<br />> auth->flags = le32toh(auth->flags);<br />282c284<br />< debug("ntlmDecodeAuth: usr o(%d) l(%d)\n", auth->user.offset, auth->user.len);<br />---<br />> debug("ntlmDecodeAuth: usr o(%d) l(%d)\n", le32toh(auth->user.offset), le16toh(auth->user.len));<br />' | patch fakeauth_auth.c<br />make</blockquote>
This would produce an executable file called <i>fakeauth_auth</i> in the current directory compatible with MBL.<br />
Copy this file to Squid's library path to keep it all in one place:<br />
<blockquote class="tr_bq">
cp fakeauth_auth /usr/lib/squid3/<br />chmod 755 /usr/lib/squid3/fakeauth_auth</blockquote>
<br />
<b><span style="font-size: large;">Configure fakeauth</span></b><br />
Fakeauth is a NTLM authentication helper which we configure in /etc/squid3/squid.conf by adding the following:<br />
<blockquote class="tr_bq">
auth_param ntlm program /usr/lib/squid3/fakeauth_auth -S<br />auth_param ntlm children 5<br />auth_param ntlm keep_alive on</blockquote>
This will enable the module but won't restrict any users accessing the proxy.<br />
<br />
At approx. line 760 add the following two lines to enable fakeauth module:<br /><span style="font-size: x-small;"><i>(Note that http_access deny needs to be above any other </i><i>http_access allow to work properly)</i></span><br />
<blockquote class="tr_bq">
acl dummyAuth proxy_auth REQUIRED<br />http_access deny !dummyAuth all</blockquote>
This basically tells Squid only to allow clients that support NTLM. This will only work for Windows users so additional rules needs to be added to allow other clients to bypass the authentication.<br />
<br />
After restarting Squid (/etc/init.d/squid3 restart) the username should now appear in the logfile when using Squid as a proxy:<br />
<blockquote class="tr_bq">
# tail /var/log/squid3/access.log<br />1221111156.178 170 10.0.0.9 TCP_MISS/204 351 GET http://www.google.com/csi? <b style="color: red;">charlie</b> DIRECT/74.125.237.112 image/gif</blockquote>
<br />
<br />
<br />
<br />
<br />dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com9tag:blogger.com,1999:blog-262398127331117099.post-87408384214861560452012-03-01T10:38:00.003+11:002012-03-01T10:38:48.958+11:00My Book Live not working with iTunes 10.5I recently bought a My Book Live and wanted to use it as a streaming server for Apple appliances.<br />
During initial setup there was a prompt for a new firmware which I installed straight away. Then after moving some music over to the shared drive, I tried to connect to it in iTunes which obviously failed.<br />
<br />
After searching around for a while I found a lot of people experiencing similar issue with not only the MBL, but with a lot of other NAS' as well.<br />
Amongst the suggested resolutions were:<br />
<ol>
<li>Make sure "Media Serving" option on the share was set to "All"</li>
<li>Under Settings > Media > iTunes, execute the "rescan"</li>
</ol>
<br />
None of them worked, iTunes just showed the spinning wheel as it tried to connect.<br />
<br />
Then spending a bit more time checking debug logs from forked-daapd (the iTunes Server) and the network traffic, everything looked good, it just wouldn't work.<br />
<br />
Since the firmware already been updated, I didn't have much hope when visiting WD's support page to look for a new firmware to support the iTunes 10.5. Surprisingly there was a much newer version available. After installing it and the library was rescanned, <b>it worked perfectly</b>. Well, for music at least.<br />
As it turns out the iTunes 10.5 and My Book Live currently only support streaming of music. For videos, iTunes 10.2 must be used.<br />
<br />
<br />
<u><b><span style="font-size: large;">Resolution</span></b></u><br />
Install latest firmware (MyBookLive 02.10.09-124 : Core F/W). <br />
<img alt="" src="data:image/png;base64,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" /><br />
For instructions on how to update the firmware, please follow the instructions provided by WD: http://wdc.custhelp.com/app/answers/detail/a_id/5735<br />
<br />
<br />
<span style="font-size: large;">Note</span><br />
Clearly Apple is causing all these issues for the sole purpose of saying, "You should be using our products!". Time will tell if it is right or wrong.<br />
<br />dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-27862682660568077702012-02-27T16:30:00.000+11:002012-02-27T16:30:11.985+11:00Flotr2JFExamplesWeb - How to use Flotr2JF with Flotr2Flotr2JFExamplesWeb is a web project built to illustrate how to use
Flotr2JF together with Flotr2. Flotr2 is a branch of Flotr, which is a replacement for Plotr.<br />
Behind the scenes it is very similar interface to Flot, which is why FlotJF was used as a base when creating Flotr2JF and Flotr2JFExamplesWeb.<br />
<br />
This is a follow-up article from <i><a href="http://dsysadm.blogspot.com.au/2012/02/flotjfexamplesweb-how-to-use-flotjf.html" target="_blank">FlotJFExamplesWeb - How to use FlotJF with Flot</a></i>.<br />
In the previous article we go through every example one by one and explain the source code. This article assumes you already read this previous article and will only discuss differences between the two.<br />
<br />
First, check out the running version here: <a href="http://flotr2jfexamplesweb.appspot.com/" target="_blank">http://flotr2jfexamplesweb.appspot.com/</a><br />
<br /><br />
<br />
<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">Screenshots</span></b></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirWTyQHJ2Fne5pej5vddelOUX8G9RQTtyjMq77L0NJEl_Pi0V0M4i2qkksg2kcoaIMdJEbWV9o8CCptFNRv3amWOAO1gWYo_mI04jCUY6yftoJb4UHy-x_Ys9JanKZI9lxsSlvQA6N3lRR/s1600/Flotr2JFExamplesWeb-ExampleGraph1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="107" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirWTyQHJ2Fne5pej5vddelOUX8G9RQTtyjMq77L0NJEl_Pi0V0M4i2qkksg2kcoaIMdJEbWV9o8CCptFNRv3amWOAO1gWYo_mI04jCUY6yftoJb4UHy-x_Ys9JanKZI9lxsSlvQA6N3lRR/s200/Flotr2JFExamplesWeb-ExampleGraph1.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEqBfb3GhWSZ39oElrEwN5KPGadQxs3vBa-iLaxvtQSurTvGtkuH6dK21Zy3N8JBMcnkQjhwQ4SVGa8t8YiTszz_MnvJmLTKYG5sYuvTCeUx99vJp9HwjpBsd-vF4P49qbqJLF9SkmiCm/s1600/Flotr2JFExamplesWeb-ExampleGraph2.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="101" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEqBfb3GhWSZ39oElrEwN5KPGadQxs3vBa-iLaxvtQSurTvGtkuH6dK21Zy3N8JBMcnkQjhwQ4SVGa8t8YiTszz_MnvJmLTKYG5sYuvTCeUx99vJp9HwjpBsd-vF4P49qbqJLF9SkmiCm/s200/Flotr2JFExamplesWeb-ExampleGraph2.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw28rrJAc7-KtazTksoKXFIn-WDK9mMHKkIdszajsW5Y_zzTz5uGl82lsyJHqE5oAXpFNCCmku5C-ci55e99ihtX5g71fQxwUHY1yWKs-rDEfNczvAY8lcU2SdK0OuofkzeFgNd8i2yXOL/s1600/Flotr2JFExamplesWeb-ExampleGraph3.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="105" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw28rrJAc7-KtazTksoKXFIn-WDK9mMHKkIdszajsW5Y_zzTz5uGl82lsyJHqE5oAXpFNCCmku5C-ci55e99ihtX5g71fQxwUHY1yWKs-rDEfNczvAY8lcU2SdK0OuofkzeFgNd8i2yXOL/s200/Flotr2JFExamplesWeb-ExampleGraph3.jpg" width="200" /></a>
</td></tr>
<tr><td class="tr-caption" style="text-align: center;">ExampleGraph1.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph2.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph3.java</td></tr>
<tr>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgcnwhAaULXCVkZTCy4GRyjC5vU5uRY5rmUJJYOe1g0rSzGtHM2-QfxTwlNjbKwEVeBsLEPvGQtGmSCws20NfOvw8BhWAXm5mVlJnt1Napi6arRF04cetiAB7m0qie5vlngpXkWf2yLAT/s1600/Flotr2JFExamplesWeb-ExampleGraph4.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgcnwhAaULXCVkZTCy4GRyjC5vU5uRY5rmUJJYOe1g0rSzGtHM2-QfxTwlNjbKwEVeBsLEPvGQtGmSCws20NfOvw8BhWAXm5mVlJnt1Napi6arRF04cetiAB7m0qie5vlngpXkWf2yLAT/s200/Flotr2JFExamplesWeb-ExampleGraph4.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEY8Gbd6NeEMwz9J_rFqkd_mE9W5QlmIN5riHAkfzsw0Nvds06VrbimbA76O5aglSWV-Gc7s058gttHrcFk37lWhIQKu9LZyqTnq80SvIQsgC4mmFJXMJLPhnMcXyjgtSI3kTT38f-BN7b/s1600/Flotr2JFExamplesWeb-ExampleGraph5.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEY8Gbd6NeEMwz9J_rFqkd_mE9W5QlmIN5riHAkfzsw0Nvds06VrbimbA76O5aglSWV-Gc7s058gttHrcFk37lWhIQKu9LZyqTnq80SvIQsgC4mmFJXMJLPhnMcXyjgtSI3kTT38f-BN7b/s200/Flotr2JFExamplesWeb-ExampleGraph5.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWOUuQhc8dvu5jb4DzQ2qJDxdVOXinD1WdixOPeOgj1rnjo2wfqd97zl6LGr_zhUYzi6t45Xv5msoL8Uh-cntIhByECJllfCX9jX27WkQ-p3ICllIvQ5IT6Mpu0unMVpQey7oyetbdbK_m/s1600/Flotr2JFExamplesWeb-ExampleGraph6.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWOUuQhc8dvu5jb4DzQ2qJDxdVOXinD1WdixOPeOgj1rnjo2wfqd97zl6LGr_zhUYzi6t45Xv5msoL8Uh-cntIhByECJllfCX9jX27WkQ-p3ICllIvQ5IT6Mpu0unMVpQey7oyetbdbK_m/s200/Flotr2JFExamplesWeb-ExampleGraph6.jpg" width="200" /></a>
</td>
</tr>
<tr><td class="tr-caption" style="text-align: center;">ExampleGraph4.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph5.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph6.java</td></tr>
</tbody></table>
<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">Getting FlotJFExamplesWeb</span></b></div>
<br />
The best way is to build it from source and import it to IDE of choice. See instructions under <b>Building</b>: <a href="https://github.com/dunse/Flotr2JFExamplesWeb/blob/master/README.md">https://github.com/dunse/Flotr2JFExamplesWeb/blob/master/README.md</a><br />
<br />
To just checkout the running examples, download <a href="https://github.com/downloads/dunse/Flotr2JFExamplesWeb/Flotr2JFExampleWeb-0.2-SNAPSHOT.war" target="_blank">https://github.com/downloads/dunse/Flotr2JFExamplesWeb/Flotr2JFExampleWeb-0.2-SNAPSHOT.war</a> and deploy it to application server of choice.<br />
After deployment open <a href="http://hostname/context-root/"><i>http://hostname/context-root/</i></a> (For Tomcat the default would be <a href="http://localhost:8080/Flotr2JFExampleWeb-0.2-SNAPSHOT/" target="_blank">http://localhost:8080/Flotr2J<span id="goog_2045249256"></span><span id="goog_2045249257"></span>FExampleWeb-0.2-SNAPSHOT/</a>)<br />
<br />
<br />
<br />
<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">The Application</span></b></div>
<br />
The application is quite simple. There is a static landing page (index.html) which has links to all the examples. These examples are displayed using example.jsp which we will take a look at first.<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">example.jsp</span></b></div>
<br />
First change is the included plotting library, which in this case is Flotr2 on Line 16:<br />
<pre class="prettyprint linenums:16"><script src="javascripts/flotr2/flotr2.js" type="text/javascript" charset="UTF-8"></script>
</pre>
<br /><br />
The code to draw the graph is only one function <i>Flotr.draw()</i>.<br />
<pre class="prettyprint linenums:40"> } )
function update() {
Flotr.draw(document.getElementById('placeholder'), getGraph(), options);
</pre>
<br /><br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph1.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirWTyQHJ2Fne5pej5vddelOUX8G9RQTtyjMq77L0NJEl_Pi0V0M4i2qkksg2kcoaIMdJEbWV9o8CCptFNRv3amWOAO1gWYo_mI04jCUY6yftoJb4UHy-x_Ys9JanKZI9lxsSlvQA6N3lRR/s1600/Flotr2JFExamplesWeb-ExampleGraph1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirWTyQHJ2Fne5pej5vddelOUX8G9RQTtyjMq77L0NJEl_Pi0V0M4i2qkksg2kcoaIMdJEbWV9o8CCptFNRv3amWOAO1gWYo_mI04jCUY6yftoJb4UHy-x_Ys9JanKZI9lxsSlvQA6N3lRR/s1600/Flotr2JFExamplesWeb-ExampleGraph1.jpg" /></a></div>
<br />
ExampleGraph1 will return a simple graph with one data series based on <i>sin()</i>.<br />
<br />
This code is identical to FlotJFExamplesWeb so please read <i><a href="http://dsysadm.blogspot.com.au/2012/02/flotjfexamplesweb-how-to-use-flotjf.html" target="_blank">FlotJFExamplesWeb - How to use FlotJF with Flot</a></i> for more information.
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph2.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEqBfb3GhWSZ39oElrEwN5KPGadQxs3vBa-iLaxvtQSurTvGtkuH6dK21Zy3N8JBMcnkQjhwQ4SVGa8t8YiTszz_MnvJmLTKYG5sYuvTCeUx99vJp9HwjpBsd-vF4P49qbqJLF9SkmiCm/s1600/Flotr2JFExamplesWeb-ExampleGraph2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEqBfb3GhWSZ39oElrEwN5KPGadQxs3vBa-iLaxvtQSurTvGtkuH6dK21Zy3N8JBMcnkQjhwQ4SVGa8t8YiTszz_MnvJmLTKYG5sYuvTCeUx99vJp9HwjpBsd-vF4P49qbqJLF9SkmiCm/s1600/Flotr2JFExamplesWeb-ExampleGraph2.jpg" /></a></div>
<br />
ExampleGraph2 will return a simple graph with two data series based on <i>sin()</i> and <i>cos()+5</i> using two y axis.<br />
<br />
Flotr2 handles y axes slightly different, so we had to change our <i>GetOptions()</i> method to use the method <i>addY2Axis()</i> on line 18:<br />
<pre class="prettyprint linenums:18"> chart.addY2Axis(yAxisRight);
</pre>
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph3.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw28rrJAc7-KtazTksoKXFIn-WDK9mMHKkIdszajsW5Y_zzTz5uGl82lsyJHqE5oAXpFNCCmku5C-ci55e99ihtX5g71fQxwUHY1yWKs-rDEfNczvAY8lcU2SdK0OuofkzeFgNd8i2yXOL/s1600/Flotr2JFExamplesWeb-ExampleGraph3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw28rrJAc7-KtazTksoKXFIn-WDK9mMHKkIdszajsW5Y_zzTz5uGl82lsyJHqE5oAXpFNCCmku5C-ci55e99ihtX5g71fQxwUHY1yWKs-rDEfNczvAY8lcU2SdK0OuofkzeFgNd8i2yXOL/s1600/Flotr2JFExamplesWeb-ExampleGraph3.jpg" /></a></div>
<br />
ExampleGraph3 will return a multi-type graph with three data series representing line, dot and bar series.<br />
<br />
This code is identical to FlotJFExamplesWeb so please refer to <i><a href="http://dsysadm.blogspot.com.au/2012/02/flotjfexamplesweb-how-to-use-flotjf.html" target="_blank">FlotJFExamplesWeb - How to use FlotJF with Flot</a></i> for more information.
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph4.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgcnwhAaULXCVkZTCy4GRyjC5vU5uRY5rmUJJYOe1g0rSzGtHM2-QfxTwlNjbKwEVeBsLEPvGQtGmSCws20NfOvw8BhWAXm5mVlJnt1Napi6arRF04cetiAB7m0qie5vlngpXkWf2yLAT/s1600/Flotr2JFExamplesWeb-ExampleGraph4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgcnwhAaULXCVkZTCy4GRyjC5vU5uRY5rmUJJYOe1g0rSzGtHM2-QfxTwlNjbKwEVeBsLEPvGQtGmSCws20NfOvw8BhWAXm5mVlJnt1Napi6arRF04cetiAB7m0qie5vlngpXkWf2yLAT/s1600/Flotr2JFExamplesWeb-ExampleGraph4.jpg" /></a></div>
<br />
ExampleGraph4 is a bit more advanced where we use the <i>poll</i> parameter to update the graph every 3 seconds. <br />
<br />
Since Flotr2 doesn't autoscale the x axis when using time mode we had to compensate for this by adding line 21:<br />
<pre class="prettyprint linenums:21"> xAxis.setTimeFormat("%H:%M:%S");
</pre>
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph5.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEY8Gbd6NeEMwz9J_rFqkd_mE9W5QlmIN5riHAkfzsw0Nvds06VrbimbA76O5aglSWV-Gc7s058gttHrcFk37lWhIQKu9LZyqTnq80SvIQsgC4mmFJXMJLPhnMcXyjgtSI3kTT38f-BN7b/s1600/Flotr2JFExamplesWeb-ExampleGraph5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEY8Gbd6NeEMwz9J_rFqkd_mE9W5QlmIN5riHAkfzsw0Nvds06VrbimbA76O5aglSWV-Gc7s058gttHrcFk37lWhIQKu9LZyqTnq80SvIQsgC4mmFJXMJLPhnMcXyjgtSI3kTT38f-BN7b/s1600/Flotr2JFExamplesWeb-ExampleGraph5.jpg" /></a></div>
<br />
ExampleGraph5 shows the use of colour and opacity gradient. <br />
<br />
In this example we put in some additional options available in Flotr2. First we specify the <i>Title</i> which is printed above the plotting area.<br />
Then we specify the y axis title and rotate this 90 degrees. Important is to disable <i>HtmlText</i> as done on line 13 or the text rotation will not be possible. <br />
<pre class="prettyprint linenums:12"> chart.setTitle("This is the Graph Title");
chart.setHtmlText(false);
Axis yAxis = new Axis();
yAxis.setTitle("Y Title");
yAxis.setTitleAngle(90);
chart.addYAxis(yAxis);
Axis xAxis = new Axis();
xAxis.setTitle("X Title");
chart.addXAxis(xAxis);
</pre>
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph6.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWOUuQhc8dvu5jb4DzQ2qJDxdVOXinD1WdixOPeOgj1rnjo2wfqd97zl6LGr_zhUYzi6t45Xv5msoL8Uh-cntIhByECJllfCX9jX27WkQ-p3ICllIvQ5IT6Mpu0unMVpQey7oyetbdbK_m/s1600/Flotr2JFExamplesWeb-ExampleGraph6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWOUuQhc8dvu5jb4DzQ2qJDxdVOXinD1WdixOPeOgj1rnjo2wfqd97zl6LGr_zhUYzi6t45Xv5msoL8Uh-cntIhByECJllfCX9jX27WkQ-p3ICllIvQ5IT6Mpu0unMVpQey7oyetbdbK_m/s1600/Flotr2JFExamplesWeb-ExampleGraph6.jpg" /></a></div>
<br />
ExampleGraph6 shows the use of tooltips (Based on Example 3).<br />
<br />
To enable a simple tooltip in Flotr2 is much simpler than in Flot. In the Java code we just call <i>useMouse()</i> to enable tooltip on the graph. This is a helper method in Flotr2JF which enables the following <i>mouse</i> options: <i>track, trackAll</i> and <i>relative</i>.<br />
<pre class="prettyprint linenums:10">
// Enable interaction with grid. (In this case for tooltip)
chart.useMouse()
</pre>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-792678906376646842012-02-21T13:31:00.000+11:002012-02-27T12:25:38.749+11:00FlotJFExamplesWeb - How to use FlotJF with FlotFlotJFExamplesWeb is a web project built to illustrate how to use FlotJF together with Flot. Flot is a pure JavaScript plotting library for jQuery while FlotJF is a Java Framework to generate the JSON data for use with Flot.<br />
FlotJF was built to simplify the process of generating graphs with J2EE applications using Flot.<br />
<br />
Check out the running version here: <a href="http://flotjfexamplesweb.appspot.com/" target="_blank">http://flotjfexamplesweb.appspot.com/</a><br />
<br />
<br />
<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">Screenshots</span></b></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxWlIQeCIv0RQk6DFxop8F_hQryovCUgKl_I3m_FBVC1d_Emm0uvy7J7nbIj-2CT8kZnvU6MJwiTV2nAZSHuFmdG-TsHN4vQj8DpguoLnqUnJBfoJhJd-w9_fKf-6PQzjB6N1h1p_xzb1/s1600/FlotJFExamplesWeb-ExampleGraph1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="107" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxWlIQeCIv0RQk6DFxop8F_hQryovCUgKl_I3m_FBVC1d_Emm0uvy7J7nbIj-2CT8kZnvU6MJwiTV2nAZSHuFmdG-TsHN4vQj8DpguoLnqUnJBfoJhJd-w9_fKf-6PQzjB6N1h1p_xzb1/s200/FlotJFExamplesWeb-ExampleGraph1.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-rHWfRHW60mamvR38nKGVC_uCfXjotkVB9rzaJupioKIEDgl-gc0v26BtbDpBHx9tGpspYqjODoR_r6MsvTMJQ-tKX06OVPg6GoAGl3RiU-r35Buva3-a72_X8FnpPnPP7aM5zMF69utS/s1600/FlotJFExamplesWeb-ExampleGraph2.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="101" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-rHWfRHW60mamvR38nKGVC_uCfXjotkVB9rzaJupioKIEDgl-gc0v26BtbDpBHx9tGpspYqjODoR_r6MsvTMJQ-tKX06OVPg6GoAGl3RiU-r35Buva3-a72_X8FnpPnPP7aM5zMF69utS/s200/FlotJFExamplesWeb-ExampleGraph2.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidzBl3dRtbd-2tmJzrewWSOeHj_DXbwuoQ5hVad5Zbj5Ykylqge4nSztXTdlsA5_z98jpEtBm9mTHEJdGMlyImsuQq6DBQTzS5-xloZAWS1HP_Bm5ZgX7so3_fB_A3N6mGNf7uPlyRR9yY/s1600/FlotJFExamplesWeb-ExampleGraph3.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="105" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidzBl3dRtbd-2tmJzrewWSOeHj_DXbwuoQ5hVad5Zbj5Ykylqge4nSztXTdlsA5_z98jpEtBm9mTHEJdGMlyImsuQq6DBQTzS5-xloZAWS1HP_Bm5ZgX7so3_fB_A3N6mGNf7uPlyRR9yY/s200/FlotJFExamplesWeb-ExampleGraph3.jpg" width="200" /></a>
</td></tr>
<tr><td class="tr-caption" style="text-align: center;">ExampleGraph1.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph2.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph3.java</td></tr>
<tr>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSm6Jjoc6IZsOAbGt_pc3FKXcxxCaA6Dgc50zRIBkswkXFVoTWGNBwwtDmkK0pdmEO0UCSyzd5zb8KXsrU5FQPpnbL_sF8ASfENuETBk5P4ulXtRp10WAeznW4JHjsUrMD2KlJj6ouDbsx/s1600/FlotJFExamplesWeb-ExampleGraph4.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSm6Jjoc6IZsOAbGt_pc3FKXcxxCaA6Dgc50zRIBkswkXFVoTWGNBwwtDmkK0pdmEO0UCSyzd5zb8KXsrU5FQPpnbL_sF8ASfENuETBk5P4ulXtRp10WAeznW4JHjsUrMD2KlJj6ouDbsx/s200/FlotJFExamplesWeb-ExampleGraph4.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv-fMiQTjN9M-Imt9ilISlAPPl39voFdsT4dEo15WnyY8oXLbTjuEkKajVp66OGeLRzp5IlUfLrMi_pFN9E0q3yCELVlu2RsZn8hFm6_ONjADuZppv57_M1RhrQo6hbl2zIM13y45FBuOj/s1600/FlotJFExamplesWeb-ExampleGraph5.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv-fMiQTjN9M-Imt9ilISlAPPl39voFdsT4dEo15WnyY8oXLbTjuEkKajVp66OGeLRzp5IlUfLrMi_pFN9E0q3yCELVlu2RsZn8hFm6_ONjADuZppv57_M1RhrQo6hbl2zIM13y45FBuOj/s200/FlotJFExamplesWeb-ExampleGraph5.jpg" width="200" /></a>
</td>
<td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEBJkuyTgpKAGKz276X4QIXtgPaVNxbEsYfusPc0kmDLKFilmWdLJgebiTIcIck9w7qQPTRxlbj2ECp0af1j1GQwOFKs6qGYwGoqmaO-2kuP8lyGlmJEwg1IV96MDK8QtdP3qDMazKGQvI/s1600/FlotJFExamplesWeb-ExampleGraph6.jpg" imageanchor="1" style="margin-left: 0.5em; margin-right: 0.5em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEBJkuyTgpKAGKz276X4QIXtgPaVNxbEsYfusPc0kmDLKFilmWdLJgebiTIcIck9w7qQPTRxlbj2ECp0af1j1GQwOFKs6qGYwGoqmaO-2kuP8lyGlmJEwg1IV96MDK8QtdP3qDMazKGQvI/s200/FlotJFExamplesWeb-ExampleGraph6.jpg" width="200" /></a>
</td>
</tr>
<tr><td class="tr-caption" style="text-align: center;">ExampleGraph4.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph5.java</td>
<td class="tr-caption" style="text-align: center;">ExampleGraph6.java</td></tr>
</tbody></table>
<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">Getting FlotJFExamplesWeb</span></b></div>
<br />
The best way is to build it from source and import it to IDE of choice. See instructions under <b>Building</b>: <a href="https://github.com/dunse/FlotJFExamplesWeb/blob/master/README.md">https://github.com/dunse/FlotJFExamplesWeb/blob/master/README.md</a><br />
<br />
To just checkout the running examples, download <a href="https://github.com/downloads/dunse/FlotJFExamplesWeb/FlotJFExampleWeb-0.2-SNAPSHOT.war">https://github.com/downloads/dunse/FlotJFExamplesWeb/FlotJFExampleWeb-0.2-SNAPSHOT.war</a> and deploy it to application server of choice.<br />
After deployment open <a href="http://hostname/context-root/"><i>http://hostname/context-root/</i></a> (For Tomcat the default would be <a href="http://localhost:8080/FlotJFExampleWeb-0.2-SNAPSHOT/">http://localhost:8080/FlotJFExampleWeb-0.2-SNAPSHOT/</a>)<br />
<br />
<br />
<br />
<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">The Application</span></b></div>
<br />
The application is quite simple. There is a static landing page (index.html) which has links to all the examples. These examples are displayed using example.jsp which we will take a look at first.<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">example.jsp</span></b></div>
<br />
At the top of example.jsp we have some Java code to create session scope variables which we will use when calling the backend system for the correct data.<br />
<i>exampleId</i> is passed from index.html using number 1 to 4. (If not set it default to 1, or we would get a <i>NullPointerException</i>).<br />
<i>poll</i> is either set or not set. If not set it is <i>null</i> which we programmatically check later in the code.<br />
<pre class="prettyprint linenums"><%
String exampleId = request.getParameter("id");
String poll = request.getParameter("poll");
String tooltip = request.getParameter("tooltip");
if (exampleId == null) {
exampleId = "1";
}
%>
</pre>
<br />
In the HEAD section the jQuery and Flot scripts needs to be included. Line 15 and 17 are the main scripts that are required while 16 is optional to support Internet Explorer 6 and 7.
<br />
<pre class="prettyprint linenums:11"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>FlotJF Example <%=exampleId %></title>
<link rel="stylesheet" media="screen" href="stylesheets/main.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<!--[if lte IE 8]><script src="javascripts/flot/excanvas.min.js" type="text/javascript" charset="UTF-8"></script><![endif]-->
<script src="javascripts/flot/jquery.flot.js" type="text/javascript" charset="UTF-8"></script>
<script src="javascripts/flotjf.tooltip.js" type="text/javascript" charset="UTF-8"></script>
</head>
</pre>
<br />
Line 22 is the <div> container which will be used to draw the graph on screen. We named it <i>placeholder</i> which will be used to reference this object from the JavaScript code.<br />
The custom size set bye the <i>style</i> argument cannot be bigger than the <iframe> that has been specified in index.html.
<br />
<pre class="prettyprint linenums:20"><body id="example">
<h2>Example <%=exampleId %></h2>
<div id="placeholder" style="width:95%;height:400px;" ></div>
</pre>
<br />
This determine if tooltips should be used. If <i>tooltip</i> is set <i>useTooltip()</i> inside flotjf.tooltip.js is called to bind the necessary events to handle this.<br />
<pre class="prettyprint linenums:25"> <% if (tooltip != null) { %>
useTooltip();
<% } %>
</pre>
<br />
In the first section of the JavaScript we define <i>data</i> which will hold the JSON data object fetched from the backend server. This <i>data</i> variable is populated using the <i>getGraph()</i> function. This in turn uses jQuery to perform a HTTP POST request to the /GetGraph URI to retrieve the JSON data.<br />
In the code below the 'GetGraph' gets translated to /GetGraph by jQuery.<br />
<i>{example: <%=exampleId %>}</i> will add one request parameter to the HTTP call named <i>example</i> using the <i>exampleId</i> from line 2.<br />
The response from the HTTP POST call is passed back into <i>data</i> and can be used by other parts of the JavaScript and Flot.<br />
<pre class="prettyprint linenums:28"> var data = [];
function getGraph() {
$.post('GetGraph',{example: <%=exampleId %>},
function(response) {
data = response
} )
return data;
}
</pre>
<br />
Next we setup the plot. Below we first get the GraphOptions using HTTP POST in the same way as the GetGraph, which we then pass to <i>$.plot</i> to create a new graph.<br />
Important to note is the <i>async:false</i> which disable asynchronous calls while initiating the graph. This is to ensure <i>options</i> and <i>data</i> have values before first use.
<br />
<pre class="prettyprint linenums:37"> // setup plot
var options = [];
var updateInterval = 3000;
$.ajaxSetup({async:false});
$.post('GetGraphOptions',{example: <%=exampleId %>},
function(response) {
options = response
} )
var plot = $.plot($("#placeholder"), getGraph(), options);
</pre>
<br />
The <i>update()</i> function is used to draw the graph. This is done in three steps.<br />
<ol>
<li><i>setData()</i> which retrieves the <i>data</i> using <i>getGraph()</i> function described above. </li>
<li><i>setupGrid()</i> to adjust the grid to the new <i>data</i>.</li>
<li><i>draw()</i> the graph(plot) on the screen.</li>
</ol>
If <i>poll</i> was passed as a request parameter, <i>setTimeout()</i> will create a timer which will call <i>update()</i> every 3seconds (<i>updateInterval</i> = 3000ms on line 39) to update the graph with new data from the backend server.<br />
<pre class="prettyprint linenums:48"> function update() {
plot.setData(getGraph());
plot.setupGrid();
plot.draw();
<% if (poll != null) { %>
setTimeout(update, updateInterval);
<% } %>
}
</pre>
<br />
Finally we call <i>update()</i> to initiate the drawing of the graph for the first time and set jQuery to use asynchronous calls again. <br />
<pre class="prettyprint linenums:58"> update();
$.ajaxSetup({async:true});
</pre>
<br />
The above will of couse not do anything without the backend servlets which we will look closer at next.<br />
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">GetGraph.java</span></b></div>
<br />
This is one of the servlet which is used to retrieve <i>data</i> by the frontend JavaScript. Servlet mapping is defined in web.xml as /GetGraph.<br />
<pre class="prettyprint linenums:13">/**
* Servlet implementation class GetGraph
*/
public class GetGraph extends HttpServlet {
</pre>
<br />
In the below code we process the HTTP POST request starting by extracting the request scope parameter <i>example</i>. Then we set the response content type to <i>application/json</i> so the client will interpret the response correctly.<br />
<pre class="prettyprint linenums:22"> protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String example = request.getParameter("example");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
</pre>
<br />
The rest of the code is to print the <i>data</i> in the response body based on the <i>example</i> parameter.<br />
<pre class="prettyprint linenums:30"> switch (Integer.valueOf(example)) {
case 1:
out.println(ExampleGraph1.getGraph());
</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">GetGraphOptions.java</span></b></div>
<br />
The same as for GetGraph.java, the servlet mapping declaration of /GetGraphOptions is located in web.xml.<br />
<pre class="prettyprint linenums:13">/**
* Servlet implementation class GetGraphOptions
*/
public class GetGraphOptions extends HttpServlet {
</pre>
<br />
Again, the same as for GetGraph.java with the minor difference that the response body consist of the <i>options</i> from <i>getGraphOptions().</i>
<br />
<pre class="prettyprint linenums:32"> out.println(ExampleGraph1.getGraphOptions());
</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph1.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxWlIQeCIv0RQk6DFxop8F_hQryovCUgKl_I3m_FBVC1d_Emm0uvy7J7nbIj-2CT8kZnvU6MJwiTV2nAZSHuFmdG-TsHN4vQj8DpguoLnqUnJBfoJhJd-w9_fKf-6PQzjB6N1h1p_xzb1/s1600/FlotJFExamplesWeb-ExampleGraph1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxWlIQeCIv0RQk6DFxop8F_hQryovCUgKl_I3m_FBVC1d_Emm0uvy7J7nbIj-2CT8kZnvU6MJwiTV2nAZSHuFmdG-TsHN4vQj8DpguoLnqUnJBfoJhJd-w9_fKf-6PQzjB6N1h1p_xzb1/s1600/FlotJFExamplesWeb-ExampleGraph1.jpg" /></a></div>
<br />
ExampleGraph1 will return a simple graph with one data series based on <i>sin()</i>.<br />
<br />
We start by creating a new <i>Chart</i> object which is the container for <i>options</i> and <i>data</i>.<br />
Next we create both X and Y Axis from the <i>Axis</i> class. Flot use the same options for both axis which is why we use the common class <i>Axis</i>.<br />
In this example we use the default options so we just add the axis to the <i>chart</i> object which will make them visible on the graph.<br />
<pre class="prettyprint linenums:8"> public static String getGraphOptions() {
Chart chart = new Chart();
Axis yAxis = new Axis();
chart.addYAxis(yAxis);
Axis xAxis = new Axis();
chart.addXAxis(xAxis);
return chart.printChartOptions();
}
</pre>
<br />
When creating a data series we use the <i>PlotData</i> object. The <i>PlotData</i> constructor takes two optional arguments, Label and Colour. In this example we want to use the default colour and therefore we pass <i>null</i>. Using <i>null</i> to set properties will set them to the default value.<br />
Next we create 100 data points using <i>addPoint(x,y)</i>. The <i>addPoint()</i> method takes two arguments of type <i>Object</i> to make it as flexible as Flot itself. In this example however we use simple data types <i>int</i> and <i>double</i>.<br />
Last we create the <i>Chart</i> object and add the data serie which will populate <i>data</i> and then print it in JSON format using <i>printChart()</i> method.<br />
<pre class="prettyprint linenums:20"> public static String getGraph() {
PlotData sinPlot = new PlotData("sin(x)", null);
int i = 0;
while( i++ < 100 ) {
sinPlot.addPoint(i, Math.sin(i));
}
Chart chart = new Chart();
chart.addElements(sinPlot);
return chart.printChart();
}
</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph2.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-rHWfRHW60mamvR38nKGVC_uCfXjotkVB9rzaJupioKIEDgl-gc0v26BtbDpBHx9tGpspYqjODoR_r6MsvTMJQ-tKX06OVPg6GoAGl3RiU-r35Buva3-a72_X8FnpPnPP7aM5zMF69utS/s1600/FlotJFExamplesWeb-ExampleGraph2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-rHWfRHW60mamvR38nKGVC_uCfXjotkVB9rzaJupioKIEDgl-gc0v26BtbDpBHx9tGpspYqjODoR_r6MsvTMJQ-tKX06OVPg6GoAGl3RiU-r35Buva3-a72_X8FnpPnPP7aM5zMF69utS/s1600/FlotJFExamplesWeb-ExampleGraph2.jpg" /></a></div>
<br />
ExampleGraph2 will return a simple graph with two data series based on <i>sin()</i> and <i>cos()</i> using two y axis.<br />
<br />
Here we add a second y axis and use <i>setPosition()</i> to define location of the axis (valid locations are "bottom", "top", "left" or "right").<br />
<pre class="prettyprint linenums:14"> Axis yAxisRight = new Axis();
yAxisRight.setPosition("right");
chart.addYAxis(yAxisRight);
</pre>
<br />
To use the right y axis, simply call the <i>setRightYAxis()</i> on the dataseries.<br />
<pre class="prettyprint linenums:26"> PlotData cosPlot = new PlotData("cos(x)+5", null);
cosPlot.setRightYAxis();</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph3.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidzBl3dRtbd-2tmJzrewWSOeHj_DXbwuoQ5hVad5Zbj5Ykylqge4nSztXTdlsA5_z98jpEtBm9mTHEJdGMlyImsuQq6DBQTzS5-xloZAWS1HP_Bm5ZgX7so3_fB_A3N6mGNf7uPlyRR9yY/s1600/FlotJFExamplesWeb-ExampleGraph3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidzBl3dRtbd-2tmJzrewWSOeHj_DXbwuoQ5hVad5Zbj5Ykylqge4nSztXTdlsA5_z98jpEtBm9mTHEJdGMlyImsuQq6DBQTzS5-xloZAWS1HP_Bm5ZgX7so3_fB_A3N6mGNf7uPlyRR9yY/s1600/FlotJFExamplesWeb-ExampleGraph3.jpg" /></a></div>
<br />
ExampleGraph3 will return a multi-type graph with three data series representing line, dot and bar series.<br />
<br />
To change the type from default line, just call <i>setBarOptions()</i> or <i>setPointOptions()</i>.<br />
<pre class="prettyprint linenums:25"> // Flot use Line as default type. To change the type, just call the helper method use<Type>.
sqrtPlot.setBarOptions();
cosPlot.setPointOptions();
</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph4.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSm6Jjoc6IZsOAbGt_pc3FKXcxxCaA6Dgc50zRIBkswkXFVoTWGNBwwtDmkK0pdmEO0UCSyzd5zb8KXsrU5FQPpnbL_sF8ASfENuETBk5P4ulXtRp10WAeznW4JHjsUrMD2KlJj6ouDbsx/s1600/FlotJFExamplesWeb-ExampleGraph4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSm6Jjoc6IZsOAbGt_pc3FKXcxxCaA6Dgc50zRIBkswkXFVoTWGNBwwtDmkK0pdmEO0UCSyzd5zb8KXsrU5FQPpnbL_sF8ASfENuETBk5P4ulXtRp10WAeznW4JHjsUrMD2KlJj6ouDbsx/s1600/FlotJFExamplesWeb-ExampleGraph4.jpg" /></a></div>
<br />
ExampleGraph4 is a bit more advanced where we use the <i>poll</i> parameter to update the graph every 3 seconds. <br />
<br />
Here we set <i>max</i> property on the y axis which will lock the maximum to 550 instead of the default dynamic setting.<br />
The x axis is set to <i>time</i> mode which by default automatically scales the axis depending on the range of the data series. In this case we use 5 minutes range which defaults to 30 second ticks.<br />
<pre class="prettyprint linenums:15"> Axis yAxis = new Axis();
yAxis.setMax(550L);
chart.addYAxis(yAxis);
Axis xAxis = new Axis();
xAxis.setMode("time");
chart.addXAxis(xAxis);
</pre>
<br />
This <i>getGraph()</i> method is a bit more advanced to illustrate the use of realtime updates of the graph.<br />
First we create two Calendar objects, one using local time and one UTC. Flot only use UTC which means the time must be adjusted before Flot process it.<br />
Variable <i>timeSpan</i> will be used to limit the returned data series x range to 5 minutes.
<br />
<pre class="prettyprint linenums:26"> public static String getGraph() {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0000"));
Calendar localCal = Calendar.getInstance();
// Timespan in seconds
Integer timeSpan = 300;
</pre>
<br />
Since we are simulating a realtime graph the highest x value must be current time while startTime 5 minutes (timeSpan) earlier. We use time in milliseconds since it makes data manipulation easier and Flot expect this format.<br />
On line 34 we adjust the endTime to compensate for the limitation of Flot only supporting UTC. This is by adjusting the time in milliseconds by the offset between UTC and local timezone. In Flot this will make sure the time is presented as it would be using the local timezone.<br />
<pre class="prettyprint linenums:32"> Long endTime = cal.getTimeInMillis();
// Adjust endTime
endTime += localCal.getTimeZone().getOffset(cal.getTimeInMillis());
Long startTime = endTime-(timeSpan*1000);
PlotData sinPlot = new PlotData("rand(x)", null);
</pre>
<br />
The next few lines is using a Java's Random class to create a identical sequences of number which we navigate through using a subset of the time in milliseconds. The sole purpose is to simulate retrieval data from a persistent layer such as a database where we end up with a realtime feel on the client side.<br />
<pre class="prettyprint linenums:39"> // Create new random generator using seed 1
Random rand = new Random(1);
// Create sequence based on time to be used to retrieve numbers from random seed
int startIntSeed = (int)Math.floor((startTime % 10000000) / 1000);
// Forward to the current next random number
for (int seq = 0; seq < startIntSeed; seq++) {
rand.nextInt(500);
}
</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph5.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv-fMiQTjN9M-Imt9ilISlAPPl39voFdsT4dEo15WnyY8oXLbTjuEkKajVp66OGeLRzp5IlUfLrMi_pFN9E0q3yCELVlu2RsZn8hFm6_ONjADuZppv57_M1RhrQo6hbl2zIM13y45FBuOj/s1600/FlotJFExamplesWeb-ExampleGraph5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv-fMiQTjN9M-Imt9ilISlAPPl39voFdsT4dEo15WnyY8oXLbTjuEkKajVp66OGeLRzp5IlUfLrMi_pFN9E0q3yCELVlu2RsZn8hFm6_ONjADuZppv57_M1RhrQo6hbl2zIM13y45FBuOj/s1600/FlotJFExamplesWeb-ExampleGraph5.jpg" /></a></div>
<br />
ExampleGraph5 shows the use of colour and opacity graident. <br />
<br />
We start by defining we want a Bar series. Then specify the options such as width (0.8 = 80%) and the line width (0 = no border lines).<br />
Next we define a three colors to use as a gradient, blue, red then blue. This is all that is requied to create a gradient based on colour. We also use <i>setColors()</i> to set another level of gradient using opacity 0.8 to 0.1 in order to show how it can be used..
<br />
<pre class="prettyprint linenums:26"> sqrtPlot.setBarOptions();
sqrtPlot.getBars().setBarWidth(0.8);
sqrtPlot.getBars().setLineWidth(0);
Colors colors = new Colors("#afefef", "#ff5522","#afefef");
colors.setColors(new Gradient(0.8,null), new Gradient(0.1, null));
sqrtPlot.getBars().setFillColor(colors);
</pre>
<br />
<br />
<hr />
<div class="separator" style="clear: both; text-align: center;">
<b><span style="font-size: large;">ExampleGraph6.java</span></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEBJkuyTgpKAGKz276X4QIXtgPaVNxbEsYfusPc0kmDLKFilmWdLJgebiTIcIck9w7qQPTRxlbj2ECp0af1j1GQwOFKs6qGYwGoqmaO-2kuP8lyGlmJEwg1IV96MDK8QtdP3qDMazKGQvI/s1600/FlotJFExamplesWeb-ExampleGraph6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEBJkuyTgpKAGKz276X4QIXtgPaVNxbEsYfusPc0kmDLKFilmWdLJgebiTIcIck9w7qQPTRxlbj2ECp0af1j1GQwOFKs6qGYwGoqmaO-2kuP8lyGlmJEwg1IV96MDK8QtdP3qDMazKGQvI/s1600/FlotJFExamplesWeb-ExampleGraph6.jpg" /></a></div>
<br />
ExampleGraph6 shows the use of tooltips (Based on Example 3).<br />
<br />
In the Java code the only requirement is to specify that the grid is <i>hoverable</i>.<br />
<pre class="prettyprint linenums:12"> // Enable interaction with grid. (In this case for tooltip)
Grid grid = new Grid();
grid.setHoverable(true);
chart.addGrid(grid);
</pre>
<br />
This alone will not make tooltip appear so we borrow some code from one of the Flot examples, <a href="http://people.iola.dk/olau/flot/examples/interacting.html" target="_blank">http://people.iola.dk/olau/flot/examples/interacting.html</a><br />
The only change to notice is on line 15 where we put the <i>bind</i> into a function so we can control whether to use tooltips or not from <i>example.jsp</i> (line 26).<br />
<pre class="prettyprint linenums">function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css({
position : 'absolute',
display : 'none',
top : y + 5,
left : x + 5,
border : '1px solid #fdd',
padding : '2px',
'background-color' : '#fee',
opacity : 0.80
}).appendTo("body").fadeIn(200);
}
var previousPoint = null;
function useTooltip() {
$("#placeholder").bind(
"plothover",
function(event, pos, item) {
$("#x").text(pos.x.toFixed(2));
$("#y").text(pos.y.toFixed(2));
if (item) {
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
$("#tooltip").remove();
var x = item.datapoint[0].toFixed(2), y = item.datapoint[1]
.toFixed(2);
showTooltip(item.pageX, item.pageY, item.series.label
+ " of " + x + " = " + y);
}
} else {
$("#tooltip").remove();
previousPoint = null;
}
});
}
</pre>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-57000946078027726962011-11-21T19:20:00.001+11:002012-01-21T12:51:40.451+11:00Cygwin - Get that Linux feeling - on Windows!<span style="color: #0b5394;"><strong>Ever get frustrated about the limitations of CMD?</strong></span><br />
<br />
Many companies enforce a SOE installation of Windows to keep all client systems alike. For the company it isusually a good strategy, but for IT professionals it can sometimes be somewhat challanging.<br />
<br />
From the day I started working in companies where it wasn't suitable running a *NIX flavour on the PC, I have used Cygwin.<br />
<br />As it states on their homepage (<a href="http://cygwin.com/">http://cygwin.com/</a>) "Get that Linux feeling - on Windows!"... That's exactly what it does.<br />
<br />Here's a quick guide to get Cygwin installed with X server and xterm.<br />
<br />
<span style="font-size: large;"><b>Installation</b></span><br />
<ol>
<li>Download and run setup.exe from http://cygwin.com/<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAJd1P5dXoeGwVRJ8VJSkTy_dyyq4mnvEyyHYt5j_vph_9o1GlBj9Ty3Skginqg87E6ymv2pjQOh3JMNp3G1OBrYd-uMjQ6cvhbA73zc_jNvLmPyuChJWOjfIHOqIokKzVFyKn5tNE8yL1/s1600/cygwin-01-download.jpg"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAJd1P5dXoeGwVRJ8VJSkTy_dyyq4mnvEyyHYt5j_vph_9o1GlBj9Ty3Skginqg87E6ymv2pjQOh3JMNp3G1OBrYd-uMjQ6cvhbA73zc_jNvLmPyuChJWOjfIHOqIokKzVFyKn5tNE8yL1/s1600/cygwin-01-download.jpg" /></a></li>
<li>Follow the on-screen instructions up until the dialog Select Packages.<br /><i>To find packages, enter the name in the Search box.</i></li>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgup-OTrx2ylpus6uAfdkkxSHZSsUTDTvG7H_eE37PYvpqzBVMiRVtkiaqHr2E0gqVX5uIDYxkuflS4ccf4Ku7vS2vHt-d8KQeozevaFKaIaw7C4jotZAWTcekc7PT3oadfqzrPBnbGez3P/s1600/cygwin-02_5-packages-all.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<ol>
<li>First select the package X-start-menu-icons<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ7YelRw36f6BXtsA0CDFD4MfGDgZLlRDJ9aP8-VErp6tm5mBLU-ZtUuJhBiKNhpkpvdc_N8UU-95BLow-7UxeFsrl3Mjxq2Xnmm9zX5UngfwIJCwbCwzFL5heUWHlTwyrhbr1FvfXCi9o/s1600/cygwin-02-packages-xstartmenuicons.jpg"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ7YelRw36f6BXtsA0CDFD4MfGDgZLlRDJ9aP8-VErp6tm5mBLU-ZtUuJhBiKNhpkpvdc_N8UU-95BLow-7UxeFsrl3Mjxq2Xnmm9zX5UngfwIJCwbCwzFL5heUWHlTwyrhbr1FvfXCi9o/s1600/cygwin-02-packages-xstartmenuicons.jpg" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ7YelRw36f6BXtsA0CDFD4MfGDgZLlRDJ9aP8-VErp6tm5mBLU-ZtUuJhBiKNhpkpvdc_N8UU-95BLow-7UxeFsrl3Mjxq2Xnmm9zX5UngfwIJCwbCwzFL5heUWHlTwyrhbr1FvfXCi9o/s1600/cygwin-02-packages-xstartmenuicons.jpg" style="margin-left: 1em; margin-right: 1em;"><br /></a></li>
<li>Then select patchutils<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpi9svXbqsgBXJL5MEE8REf-lUqL8WHVUSG-WhOYbbgcbMUCQQg3H0KR9uFHzIIfGmVLl0vuRhx1RlR6gywfTWMJ8R20Ppi-ySYB97-Cfweofs3q2HHI7Am-BKtM1A1coZiwdx_9G0McCZ/s1600/cygwin-03-packages-patchutils.jpg"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpi9svXbqsgBXJL5MEE8REf-lUqL8WHVUSG-WhOYbbgcbMUCQQg3H0KR9uFHzIIfGmVLl0vuRhx1RlR6gywfTWMJ8R20Ppi-ySYB97-Cfweofs3q2HHI7Am-BKtM1A1coZiwdx_9G0McCZ/s1600/cygwin-03-packages-patchutils.jpg" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpi9svXbqsgBXJL5MEE8REf-lUqL8WHVUSG-WhOYbbgcbMUCQQg3H0KR9uFHzIIfGmVLl0vuRhx1RlR6gywfTWMJ8R20Ppi-ySYB97-Cfweofs3q2HHI7Am-BKtM1A1coZiwdx_9G0McCZ/s1600/cygwin-03-packages-patchutils.jpg" style="margin-left: 1em; margin-right: 1em;"><br /></a></li>
<li>Click Next to install the packages</li>
<li>(If you have 7GB to spare, you could of course just select everything to install)<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgup-OTrx2ylpus6uAfdkkxSHZSsUTDTvG7H_eE37PYvpqzBVMiRVtkiaqHr2E0gqVX5uIDYxkuflS4ccf4Ku7vS2vHt-d8KQeozevaFKaIaw7C4jotZAWTcekc7PT3oadfqzrPBnbGez3P/s1600/cygwin-02_5-packages-all.jpg"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgup-OTrx2ylpus6uAfdkkxSHZSsUTDTvG7H_eE37PYvpqzBVMiRVtkiaqHr2E0gqVX5uIDYxkuflS4ccf4Ku7vS2vHt-d8KQeozevaFKaIaw7C4jotZAWTcekc7PT3oadfqzrPBnbGez3P/s1600/cygwin-02_5-packages-all.jpg" /></a></li>
</ol>
<li>The next screen will automatically select all dependencies. Just click Next to start the installation phase.<br /> </li>
</ol>
<span style="font-size: large;"><b>Running Cygwin - XWin Server</b></span><br />
During installation, start menu icons is created by default. To use the XWin Server (and xterm), click on the Start icon:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNQOT8sfx0zLjSYBQkOZr8awf7MHNst1mm5CvOujlYJZcU0Di_y1cb7LbUORHi9nRg9SbNuFD4OSOz-Tk1aMV9d46VmHPwhUNO6WZY3gE6XGqdOq1_6cHmVfWyxYFFhfBmFrZO8dwSTI-X/s1600/cygwin-04-launch-xwin.jpg" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNQOT8sfx0zLjSYBQkOZr8awf7MHNst1mm5CvOujlYJZcU0Di_y1cb7LbUORHi9nRg9SbNuFD4OSOz-Tk1aMV9d46VmHPwhUNO6WZY3gE6XGqdOq1_6cHmVfWyxYFFhfBmFrZO8dwSTI-X/s1600/cygwin-04-launch-xwin.jpg" /></a></div>
At startup an xterm session is started as well:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW5txwc5_B7bnWsi7ai09WP2ODAyLB2dq7HQNBs7K6e5PYz9aSQHAkV-_DcH7Fl64ksmrAhwlRBMU0EgzjXDywvUG848eiXQxdSiq2PYdkFThHVan4YLWX-m7mGkpy7mJ6cLtaoXH1X13X/s1600/cygwin-05-xterm.jpg" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW5txwc5_B7bnWsi7ai09WP2ODAyLB2dq7HQNBs7K6e5PYz9aSQHAkV-_DcH7Fl64ksmrAhwlRBMU0EgzjXDywvUG848eiXQxdSiq2PYdkFThHVan4YLWX-m7mGkpy7mJ6cLtaoXH1X13X/s1600/cygwin-05-xterm.jpg" /></a></div>
The default look is quite plain and straining on the eye, so I tend to make it a bit more retro as described in the next section...<br />
<br />
To start additional xterm sessions, find the X icon next to the clock and click Applications > xterm:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1c_8KnWK2MFSXIsje2FpRY9k39SFLmQzzpRMIX81KGMmsn_t6JipDtclYZ5EJ2-bOIDGUTvWdbyugJSKaR-zOgkrJkFF3Ui2H0yqTNEiDTraQ-F0SHhrd21jzUZntYwhBh1Aui7CuJXbb/s1600/cygwin-04_5-launch-xterm.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1c_8KnWK2MFSXIsje2FpRY9k39SFLmQzzpRMIX81KGMmsn_t6JipDtclYZ5EJ2-bOIDGUTvWdbyugJSKaR-zOgkrJkFF3Ui2H0yqTNEiDTraQ-F0SHhrd21jzUZntYwhBh1Aui7CuJXbb/s1600/cygwin-04_5-launch-xterm.jpg" /></a></div>
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNQOT8sfx0zLjSYBQkOZr8awf7MHNst1mm5CvOujlYJZcU0Di_y1cb7LbUORHi9nRg9SbNuFD4OSOz-Tk1aMV9d46VmHPwhUNO6WZY3gE6XGqdOq1_6cHmVfWyxYFFhfBmFrZO8dwSTI-X/s1600/cygwin-04-launch-xwin.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;">
</a><br />
<span style="font-size: large;"><b>Customisation</b></span> <br />
At startup, a new xterm window will appear, use this window to patch the system.XWinrc:<br />
<ol>
<li>Execute the following, which will create a new config file for XTerm:<br />
<div style="background: rgb(170, 170, 170); font-size: 90%;">
<pre>echo 'XTerm*Background: black
XTerm*Foreground: white
XTerm*toolBar: false
XTerm*geometry: 160x40' > ~/XTerm
</pre>
</div>
</li>
</ol>
As a result, you will end up with a much nicer look and feel:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWI5lSKocNSrdUtMtFbw8I7WoTygyp1-RqQHp5X4dcrYJ42n026D2ZX56shWs1cHU0nniuH04Pt6axJ-bl8XW4xYCugyO8oGjaNeo6mbEiRa_jY6KB-2ubh8U7gAh5ZsLrQpGzJ9WVrgqp/s1600/cygwin-06-xterm-new.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWI5lSKocNSrdUtMtFbw8I7WoTygyp1-RqQHp5X4dcrYJ42n026D2ZX56shWs1cHU0nniuH04Pt6axJ-bl8XW4xYCugyO8oGjaNeo6mbEiRa_jY6KB-2ubh8U7gAh5ZsLrQpGzJ9WVrgqp/s1600/cygwin-06-xterm-new.jpg" /></a></div>
<div align="left" class="separator" style="clear: both; text-align: center;">
<br /></div>
<div align="left" class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
</div>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-37129037190979151822011-11-13T16:05:00.001+11:002012-02-20T13:46:50.009+11:00Runtime log4j configurationRecently I got struck with the question what log level the rootLogger in log4j was set to.<br />
Normally a quick check of the log4j's property file would give you the answer, but in this case there was a third party application that had multiple ways of defining the log level both at startup and then runtime. <br />
<br />
After a quick search I found this blog post: <a href="http://nelz.net/2008/04/08/log4j-runtime-configuration/" target="_blank">http://nelz.net/2008/04/08/log4j-runtime-configuration/</a><br />
This will not only show you the runtime value of the loggers but also enable you to change this without require a restart of the application.<br />
<br />
<span style="font-size: x-small;">In order to rename log4jAdmin.jsp, grab it from the blog and perform the following steps:</span><br />
<span style="font-size: x-small;">Find: "/log4jAdmin.jsp</span><br />
<span style="font-size: x-small;">Replace with: request.getContextPath() + request.getServletPath() + "</span><br />
<span style="font-size: x-small;">Find: log4jAdmin.jsp</span><br />
<span style="font-size: x-small;">Replace with: <%=request.getContextPath() + request.getServletPath() %></span><br />
<br />
Screenshot:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguHBLjY6HgCf6p6I_UrFGfLEo1pDuqR1HmZxRVKcNPkC0nGSYA1OyDFIiFkIxyyzEptRSauwnuQbWCFMmltve2cZd2_p6Mw7_bxo62QVErI0zSubW_8xPBj_vX8vKMCMyJQSQKDsNQnDGf/s1600/log4jadmin.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguHBLjY6HgCf6p6I_UrFGfLEo1pDuqR1HmZxRVKcNPkC0nGSYA1OyDFIiFkIxyyzEptRSauwnuQbWCFMmltve2cZd2_p6Mw7_bxo62QVErI0zSubW_8xPBj_vX8vKMCMyJQSQKDsNQnDGf/s1600/log4jadmin.jpg" /></a></div>
<br />
The JSP with the customisations:<br />
<pre class="prettyprint linenums">
<%@ page language="java" contentType="text/html;charset=UTF-8" %>
<%@ page import="org.apache.log4j.Level" %>
<%@ page import="org.apache.log4j.LogManager" %>
<%@ page import="org.apache.log4j.Logger" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.Arrays" %>
<% long beginPageLoadTime = System.currentTimeMillis();%>
<html>
<head>
<title>Log4J Administration</title>
<style type="text/css">
<!--
#content {
margin: 0px;
padding: 0px;
text-align: center;
background-color: #ccc;
border: 1px solid #000;
width: 100%;
}
body {
position: relative;
margin: 10px;
padding: 0px;
color: #333;
}
h1 {
margin-top: 20px;
font: 1.5em Verdana, Arial, Helvetica sans-serif;
}
h2 {
margin-top: 10px;
font: 0.75em Verdana, Arial, Helvetica sans-serif;
text-align: left;
}
a, a:link, a:visited, a:active {
color: red;
text-decoration: none;
text-transform: uppercase;
}
table {
width: 100%;
background-color: #000;
padding: 3px;
border: 0px;
}
th {
font-size: 0.75em;
background-color: #ccc;
color: #000;
padding-left: 5px;
text-align: center;
border: 1px solid #ccc;
white-space: nowrap;
}
td {
font-size: 0.75em;
background-color: #fff;
white-space: nowrap;
}
td.center {
font-size: 0.75em;
background-color: #fff;
text-align: center;
white-space: nowrap;
}
.filterForm {
font-size: 0.9em;
background-color: #000;
color: #fff;
padding-left: 5px;
text-align: left;
border: 1px solid #000;
white-space: nowrap;
}
.filterText {
font-size: 0.75em;
background-color: #fff;
color: #000;
text-align: left;
border: 1px solid #ccc;
white-space: nowrap;
}
.filterButton {
font-size: 0.75em;
background-color: #000;
color: #fff;
padding-left: 5px;
padding-right: 5px;
text-align: center;
border: 1px solid #ccc;
width: 100px;
white-space: nowrap;
}
-->
</style>
</head>
<body onLoad="javascript:document.logFilterForm.logNameFilter.focus();">
<%
String containsFilter = "Contains";
String beginsWithFilter = "Begins With";
String[] logLevels = {"debug", "info", "warn", "error", "fatal", "off"};
String targetOperation = (String) request.getParameter("operation");
String targetLogger = (String) request.getParameter("logger");
String targetLogLevel = (String) request.getParameter("newLogLevel");
String logNameFilter = (String) request.getParameter("logNameFilter");
String logNameFilterType = (String) request.getParameter("logNameFilterType");
%>
<div id="content">
<h1>Log4J Administration</h1>
<div class="filterForm">
<form action="<%=request.getContextPath() + request.getServletPath() %>" name="logFilterForm">
Filter Loggers:&nbsp;&nbsp;
<input name="logNameFilter" type="text" size="50" value="<%=(logNameFilter == null ? "":logNameFilter)%>"
class="filterText"/>
<input name="logNameFilterType" type="submit" value="<%=beginsWithFilter%>" class="filterButton"/>&nbsp;
<input name="logNameFilterType" type="submit" value="<%=containsFilter%>" class="filterButton"/>&nbsp;
<input name="logNameClear" type="button" value="Clear" class="filterButton"
onmousedown='javascript:document.logFilterForm.logNameFilter.value="";'/>
<input name="logNameReset" type="reset" value="Reset" class="filterButton"/>
<param name="operation" value="changeLogLevel"/>
</form>
</div>
<table cellspacing="1">
<tr>
<th width="25%">Logger</th>
<th width="25%">Parent Logger</th>
<th width="15%">Effective Level</th>
<th width="35%">Change Log Level To</th>
</tr>
<%
Enumeration loggers = LogManager.getCurrentLoggers();
HashMap loggersMap = new HashMap(128);
Logger rootLogger = LogManager.getRootLogger();
if (!loggersMap.containsKey(rootLogger.getName())) {
loggersMap.put(rootLogger.getName(), rootLogger);
}
while (loggers.hasMoreElements()) {
Logger logger = (Logger) loggers.nextElement();
if (logNameFilter == null || logNameFilter.trim().length() == 0) {
loggersMap.put(logger.getName(), logger);
} else if (containsFilter.equals(logNameFilterType)) {
if (logger.getName().toUpperCase().indexOf(logNameFilter.toUpperCase()) >= 0) {
loggersMap.put(logger.getName(), logger);
}
} else {
// Either was no filter in IF, contains filter in ELSE IF, or begins with in ELSE
if (logger.getName().startsWith(logNameFilter)) {
loggersMap.put(logger.getName(), logger);
}
}
}
Set loggerKeys = loggersMap.keySet();
String[] keys = new String[loggerKeys.size()];
keys = (String[]) loggerKeys.toArray(keys);
Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < keys.length; i++) {
Logger logger = (Logger) loggersMap.get(keys[i]);
// MUST CHANGE THE LOG LEVEL ON LOGGER BEFORE GENERATING THE LINKS AND THE
// CURRENT LOG LEVEL OR DISABLED LINK WON'T MATCH THE NEWLY CHANGED VALUES
if ("changeLogLevel".equals(targetOperation) && targetLogger.equals(logger.getName())) {
Logger selectedLogger = (Logger) loggersMap.get(targetLogger);
selectedLogger.setLevel(Level.toLevel(targetLogLevel));
}
String loggerName = null;
String loggerEffectiveLevel = null;
String loggerParent = null;
if (logger != null) {
loggerName = logger.getName();
loggerEffectiveLevel = String.valueOf(logger.getEffectiveLevel());
loggerParent = (logger.getParent() == null ? null : logger.getParent().getName());
}
%>
<tr>
<td><%=loggerName%></td>
<td><%=loggerParent%></td>
<td><%=loggerEffectiveLevel%></td>
<td class="center">
<%
for (int cnt = 0; cnt < logLevels.length; cnt++) {
String url = request.getContextPath() + request.getServletPath() +
"?operation=changeLogLevel&logger=" + loggerName + "&newLogLevel=" + logLevels[cnt] +
"&logNameFilter=" + (logNameFilter != null ? logNameFilter : "") +
"&logNameFilterType=" + (logNameFilterType != null ? logNameFilterType : "");
if (logger.getLevel() == Level.toLevel(logLevels[cnt]) ||
logger.getEffectiveLevel() == Level.toLevel(logLevels[cnt])) {
%>
[<%=logLevels[cnt].toUpperCase()%>]
<%
} else {
%>
<a href='<%=url%>'>[<%=logLevels[cnt]%>]</a>&nbsp;
<%
}
}
%>
</td>
</tr>
<%
}
%>
</table>
<h2>
Revision: 1.0<br/>
Page Load Time (Millis): <%=(System.currentTimeMillis() - beginPageLoadTime)%>
</h2>
</div>
</body>
</html></pre>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com7tag:blogger.com,1999:blog-262398127331117099.post-88359202276556372322011-11-12T21:51:00.001+11:002012-02-20T13:48:24.095+11:00Automated DB deploymentAutomation is one of the key stones in the build and deploy process.<br />
Taking a closer look at dbdeploy (<a href="http://dbdeploy.com/" target="_blank">http://dbdeploy.com/</a>) shows us how to automate the database changes.<br />
<br />
To illustrate a structured approach using dbdeploy, I have taken the example/build.xml and rewritten it to show how it can be used in an enterprise environment.<br />
<br />
<br />
<u><b>Step 1 - Download dbdeploy</b></u><br />
Download package from <a href="http://code.google.com/p/dbdeploy/downloads/list" target="_blank">http://code.google.com/p/dbdeploy/downloads/list</a><br />
Unzip and put build-sysadmin.xml from the bottom of this post in the example directory.<br />
<br />
<br />
<u><b>Step 2 - Prepare the database</b></u><br />
In the downloaded package, apply the createSchemaVersionTable.<DB>.sql script to the database schemas for which dbdeploy will be used.<br />
This will create the changelog table which dbdeploy uses to keep track of which changes have been applied.<br />
<br />
Using the build-sysadmin.xml, execute:<br />
<div style="background: #AAAAAA; font-size: 90%;">
ant -f build-sysadmin.xml init</div>
<br />
<br />
<u><b>Step 3 - Setup source repository for deltas</b></u><br />
In the source repository, create a new folder where the deltas will be stored.<br />
For each database change, create a new file in sequence, e.g.:<br />
001_create_test_table.sql<br />
002_insert_addresses_into_test_table.sql<br />
003_....<br />
<br />
Prepare for using build-sysadmin.xml, create example/TESTDB directory and copy example/*.sql into this directory.<br />
<br />
<br />
<u><b>Step 4 - Build script</b></u><b></b><br />
A common practise is to create artifacts during the build process which are then promoted to the different environments. This script could be as simple as a Ant zip task packaging all files in the delta folder.
<br />
<br />
Using the build-sysadmin.xml, execute:<br />
<div style="background: #AAAAAA; font-size: 90%;">
ant -f build-sysadmin.xml build</div>
This will create the artifact dist/dbdeploy-artifact.zip<br />
<br />
<br />
<u><b>Step 5 - Deploy script</b></u><br />
A good approach is to use "update-database-and-apply-as-separate-step" in example/build.xml as a template. This twostep approach gives more control over the changes that have been made in the database.<br />
The build-sysadmin.xml makes use of this, print change and the rollback script before executing them.<br />
<br />
Using the build-sysadmin.xml, execute:<br />
<div style="background: #AAAAAA; font-size: 90%;">
ant -f build-sysadmin.xml deploy</div>
<br />
<br />
<br />
<u><b>More information</b></u><br />
See <a href="http://code.google.com/p/dbdeploy/w/list" target="_blank">http://code.google.com/p/dbdeploy/w/list</a> for more details on how to use dbdeploy. <br />
<br />
build-sysadmin.xml:<br />
<pre class="prettyprint linenums">
<?xml version="1.0" encoding="UTF-8"?>
<project name="dbdeploy_modified_example" default="build">
<property name="db.driver" value="org.hsqldb.jdbcDriver" />
<property name="db.url" value="jdbc:hsqldb:file:db/testdb;shutdown=true" />
<property name="db.user" value="sa" />
<property name="db.password" value="" />
<property name="dir.delta" value="TESTDB" />
<property name="dist" value="dist" />
<property name="work" value="work" />
<path id="hsql.classpath">
<fileset dir=".">
<include name="hsqldb*.jar"/>
</fileset>
</path>
<path id="dbdeploy.classpath">
<!-- include the dbdeploy-ant jar -->
<fileset dir="..">
<include name="dbdeploy-ant-*.jar"/>
</fileset>
<!-- the dbdeploy task also needs the database driver jar on the classpath -->
<path refid="hsql.classpath" />
</path>
<taskdef name="dbdeploy" classname="com.dbdeploy.AntTarget" classpathref="dbdeploy.classpath"/>
<target name="build" depends="-clean-build-artifact, -build-artifact"/>
<target name="deploy" depends="-clean-deploy-artifact, -deploy-artifact"/>
<target name="init" depends="-clean-build-artifact, -clean-deploy-artifact, drop-and-create-database, create-changelog-table"/>
<target name="create-changelog-table">
<sql driver="${db.driver}" url="${db.url}"
userid="sa" password="" classpathref="hsql.classpath" >
<fileset file="../scripts/createSchemaVersionTable.hsql.sql"/>
</sql>
</target>
<target name="-clean-build-artifact" description="Cleanup old artifact">
<delete dir="${dist}"/>
</target>
<target name="-build-artifact" description="Build an artifact with database changes">
<mkdir dir="${dist}"/>
<!-- Create artifact with all deltas -->
<zip destfile="${dist}/dbdeploy-artifact.zip"
basedir="${dir.delta}"
includes="*.sql"
/>
</target>
<target name="-clean-deploy-artifact" description="">
<delete dir="${work}"/>
</target>
<target name="-deploy-artifact" description="generate a sql upgrade script">
<mkdir dir="${work}/deltas"/>
<!-- Unzip deltas to be used for deployment -->
<unzip src="${dist}/dbdeploy-artifact.zip" dest="${work}/deltas"/>
<!-- use dbdeploy to generate the change script -->
<dbdeploy driver="${db.driver}" url="${db.url}"
userid="${db.user}"
password="${db.password}"
dir="${work}/deltas"
outputfile="${work}/output.sql"
undoOutputfile="${work}/undo.sql"
dbms="hsql"
/>
<!-- Print actions to be taken for auditing purposes -->
<echo message="Executing dbdeploy agains ${db.url} as ${db.user}:"/>
<loadfile property="outputsql" srcFile="${work}/output.sql"/>
<echo>${outputsql}</echo>
<!-- Print undo actions for rollback purposes -->
<echo message="Undo:"/>
<loadfile property="undosql" srcFile="${work}/undo.sql"/>
<echo>${undosql}</echo>
<!-- now apply the changescript to the database -->
<sql driver="${db.driver}" url="${db.url}"
userid="sa" password="" classpathref="hsql.classpath">
<fileset file="${work}/output.sql"/>
</sql>
</target>
<target name="drop-and-create-database">
<delete dir="db"/>
<mkdir dir="db"/>
</target>
</project></pre>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com1tag:blogger.com,1999:blog-262398127331117099.post-72076035248559544942011-11-12T21:02:00.001+11:002011-11-15T20:35:17.672+11:00How to change file type in CVSWhen working with CVS there might be times where files get added in the wrong format (binary or ascii). To convert between these two there are a couple of commands you can use.<br />
<br />
<ul>
<li>Convert from binary to ascii</li>
<ul>
<li>Using CVSNT<br />Remove all files and then execute in a shell:<br />
<div style="background: #AAAAAA; font-size: 90%;">
cvs update -ktkv <FILES><br />
cvs commit -fm "Changed format to ascii" <FILES>
</div>
</li>
<li>Using CVS<br />
<div style="background: #AAAAAA; font-size: 90%;">
cvs admin -kkv <FILES><br />
cvs update -A <FILES><br />
cvs commit -fm "Changed format to ascii" <FILES></div>
</li>
</ul>
<li>Convert from ascii to binary</li>
<ul>
<li>Using CVSNT<br />Remove all files and then execute in a shell:<br />
<div style="background: #AAAAAA; font-size: 90%;">
cvs update -kb <FILES><br />
cvs commit -fm "Changed format to binary" <FILES>
</div>
</li>
<li>Using CVS<br />
<div style="background: #AAAAAA; font-size: 90%;">
cvs admin -kb <FILES><br />
cvs update -A <FILES><br />
cvs commit -fm "Changed format to binary" <FILES></div>
</li>
</ul>
</ul>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com2tag:blogger.com,1999:blog-262398127331117099.post-19431570068963226782011-11-12T19:58:00.001+11:002012-02-20T13:49:19.388+11:00updateCVSRoot.batEver been in a situation where you needed users to update the CVSROOT of their locally checked out source?<br />
It's not always as straight forward as checking out the code again so here's a batch script to recursively update the CVS\Root file with the new CVSROOT.<br />
Synopsis: updateCVSRoot.bat <WORKSPACEDIR> <CVSROOT><br />
<br />
<pre class="prettyprint linenums">
@echo off
REM This script is used to update CVS Root in checked out directory
REM SYNOPSIS: updateCVSRoot.bat <WORKSPACEDIR> <CVSROOT>
REM Check arguments
if "%2" == "" goto MISSINGARGUMENT
REM Check if workspace directory exist
IF NOT EXIST %1 GOTO NOWORKSPACEDIR
REM Setup variables
SET W_DRIVE=%~d1
SET W_PATH=%~dp1
REM Enter drive and directory for the root of CVS checked out directory
%W_DRIVE%
cd %W_PATH%
REM Find all Root files and replace with CVSROOT
for /f "tokens=*" %%a in ('dir Root /b /s') do (
echo %2> "%%a"
)
echo Done!
exit /b 0
:MISSINGARGUMENT
printf "Usage: %~n0 <WORKSPACEDIR> <CVSROOT>\n"
printf " WORKSPACEDIR Root directory to search for filenames called Root\n"
printf " CVSROOT Fully qualified CVSROOT\n"
printf "\n"
printf "Example: %~n0 c:\\\\cvsdir :sserver:username@cvs.localdomain:/my/cvsroot\n"
printf "\n"
exit /b 1
:NOWORKSPACEDIR
echo ERROR: %1 doesn't exist
echo.
exit /b 1</pre>dunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0tag:blogger.com,1999:blog-262398127331117099.post-32293927477451343302011-11-12T19:57:00.000+11:002011-11-13T11:49:40.270+11:00Welcome to a sysadmin's blog<br />
After many years in the IT industry it is time to start sharing my knowledge and findings, both to myself and others.<br />
Without over-committing, I will try to keep the blog going with frequent updates.<br />
<br />
Enjoydunsehttp://www.blogger.com/profile/17979237127695999060noreply@blogger.com0