Well, for someone like me who is color illiterate , this seems to be the perfect place to start. By learning it you make sure that the user that uses your web doesn't get a headache every time they uses your apps. :D
And this tool from Adobe is amazing :D
Saturday, May 1, 2010
My SUN has set, its time to SEARCH for a new one :)
I wonder if the title is appropriate :D
Tepat 38 hari semenjak gw berhenti kerja kantoran. Yes, my SUN has set. Sebuah perusahaan yang punya begitu banyak inovasi dan penuh kultur keterbukaan. Untuk pindah dari kultur seperti ini ke kultur big O yang menomorsatukan sales number diatas semua hal, sepertinya bukanlah sebuah pilihan. Dan untungnya, gw bukan sendirian yang berpikir seperti ini. Bahkan JG pun memutuskan untuk berhenti.
In SEARCHing a new one, sepertinya memang hanya perusahaan itu yang punya kultur mirip. Mendorong keterbukaan dan juga mendorong melakukan good things. Tetapi itu mungkin akan menjadi cerita di lain waktu.
Kembali ke gw, udah Mei saja :D. Tadinya mau menulis banyak hal, merayakan tepat satu bulan berhenti dengan menumpahkan pikiran dan angan-angan kedalam blog. Tapi gak jadi :) gak usah dipaksakan, pada waktunya nanti yang ingin ditulis pasti akan ditulis. Goodnewsnya waktu yg tadinya akan digunakan untuk menulis, dimanfaatkan untuk mengerjakan sesuatu yang lebih produktif.
Di awal-awal long vacation, gw berhasil olahraga rutin, lari pagi untuk mengembalikan kondisi fisik. Masih keep kontak dengan teman2 kantor dengan main bulutangkis dan futsal. Tapi sayangnya karena cedera futsal dipertandingan lain di kampus, olahraga paginya terpaksa di stop dulu. Gw gak mau cedera ini jadi parah dan kambuhan.
I'm a lot happier, sholat lebih teratur, bisa Nemenin nyokap di rumah karena lagi gak ada pembantu buat nemenin. Banyak belajar ulang mengenai konsep-konsep di tapestry dan javascript yang selama ini belajar sambil lalu. Dan akhir pekan lalu, gw merelease aplikasi pertama gw di android.
Ternyata banyak hal dapat dilakukan dalam waktu satu bulan. Perbedaannya besar sekali melakukan hal yang lu bener-bener suka, dengan melakukan mundane job at the office. Ucapan terimakasih dari tiap-tiap orang yang menggunakan aplikasi lu benar2 terasa nikmatnya.
Tapi satu hal yang benar-benar gw pahamin. Dalam keadaan seperti ini, dukungan dari teman dan komunitas benar-benar terasa manfaatnya. Disaat ragu dan bimbang tentang apa yang kita kerjakan, would it matter ? A nudge in the shoulder and a little push really2 meant a lot.
So whats next ?
Gw berencana untuk tetap fokus di hal yang gw kerjakan saat ini. Belajar lebih jauh tentang android, memperbaiki aplikasi gw, dan mudah2an me-release aplikasi ke dua gw di android di bulan ini. Wish me luck :D
Tepat 38 hari semenjak gw berhenti kerja kantoran. Yes, my SUN has set. Sebuah perusahaan yang punya begitu banyak inovasi dan penuh kultur keterbukaan. Untuk pindah dari kultur seperti ini ke kultur big O yang menomorsatukan sales number diatas semua hal, sepertinya bukanlah sebuah pilihan. Dan untungnya, gw bukan sendirian yang berpikir seperti ini. Bahkan JG pun memutuskan untuk berhenti.
In SEARCHing a new one, sepertinya memang hanya perusahaan itu yang punya kultur mirip. Mendorong keterbukaan dan juga mendorong melakukan good things. Tetapi itu mungkin akan menjadi cerita di lain waktu.
Kembali ke gw, udah Mei saja :D. Tadinya mau menulis banyak hal, merayakan tepat satu bulan berhenti dengan menumpahkan pikiran dan angan-angan kedalam blog. Tapi gak jadi :) gak usah dipaksakan, pada waktunya nanti yang ingin ditulis pasti akan ditulis. Goodnewsnya waktu yg tadinya akan digunakan untuk menulis, dimanfaatkan untuk mengerjakan sesuatu yang lebih produktif.
Di awal-awal long vacation, gw berhasil olahraga rutin, lari pagi untuk mengembalikan kondisi fisik. Masih keep kontak dengan teman2 kantor dengan main bulutangkis dan futsal. Tapi sayangnya karena cedera futsal dipertandingan lain di kampus, olahraga paginya terpaksa di stop dulu. Gw gak mau cedera ini jadi parah dan kambuhan.
I'm a lot happier, sholat lebih teratur, bisa Nemenin nyokap di rumah karena lagi gak ada pembantu buat nemenin. Banyak belajar ulang mengenai konsep-konsep di tapestry dan javascript yang selama ini belajar sambil lalu. Dan akhir pekan lalu, gw merelease aplikasi pertama gw di android.
Ternyata banyak hal dapat dilakukan dalam waktu satu bulan. Perbedaannya besar sekali melakukan hal yang lu bener-bener suka, dengan melakukan mundane job at the office. Ucapan terimakasih dari tiap-tiap orang yang menggunakan aplikasi lu benar2 terasa nikmatnya.
Tapi satu hal yang benar-benar gw pahamin. Dalam keadaan seperti ini, dukungan dari teman dan komunitas benar-benar terasa manfaatnya. Disaat ragu dan bimbang tentang apa yang kita kerjakan, would it matter ? A nudge in the shoulder and a little push really2 meant a lot.
So whats next ?
Gw berencana untuk tetap fokus di hal yang gw kerjakan saat ini. Belajar lebih jauh tentang android, memperbaiki aplikasi gw, dan mudah2an me-release aplikasi ke dua gw di android di bulan ini. Wish me luck :D
Tuesday, April 20, 2010
Tapestry 5 and Quartz
Using Quartz with tapestry is pretty easy , thanks to the ChenilleKit project. By following the guide in the chenillekit-quartz project you can use quartz to schedule jobs in your application. But in my case, i need to modify the example a little to make it work.
in the Bundle class :
private void createBundle() {
try {
trigger = new CronTrigger("myCronTrigger", "CronTriggerGroup", "0 42 7/1 ? * *");
} catch (ParseException e) {
e.printStackTrace();
}
jobDetail = new JobDetail("myJob", null, CrawlingJob.class);
jobDetail.getJobDataMap().put("crawlingJob", crawlingJob);
jobDetail.getJobDataMap().put("crawler", crawler);
}
I'm adding the job (crawlingJob) and the actual services (crawler) into the job data map. So in the Job class :
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Execute job Scheduled Crawler at : " + new Date());
Crawler crawler = (Crawler) context.getJobDetail().getJobDataMap().get("crawler");
crawler.updateData(CrawlAjax.URL);
}
we only need to extract the service class from the context and then execute the real method. As simple as that. And the AFAIK the minimum library you need to include to make it work is :
in the Bundle class :
private void createBundle() {
try {
trigger = new CronTrigger("myCronTrigger", "CronTriggerGroup", "0 42 7/1 ? * *");
} catch (ParseException e) {
e.printStackTrace();
}
jobDetail = new JobDetail("myJob", null, CrawlingJob.class);
jobDetail.getJobDataMap().put("crawlingJob", crawlingJob);
jobDetail.getJobDataMap().put("crawler", crawler);
}
I'm adding the job (crawlingJob) and the actual services (crawler) into the job data map. So in the Job class :
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Execute job Scheduled Crawler at : " + new Date());
Crawler crawler = (Crawler) context.getJobDetail().getJobDataMap().get("crawler");
crawler.updateData(CrawlAjax.URL);
}
we only need to extract the service class from the context and then execute the real method. As simple as that. And the AFAIK the minimum library you need to include to make it work is :
- chenillekit-quartz-1.0.2.jar
- commons-codec-1.4.jar
- commons-collections-3.1.jar
- quartz-all-1.6.5.jar
Monday, April 19, 2010
Using block to invoke client Javascript code in Tapestry 5
In my previous post I've been able to create a self updating tapestry zone.But its only halfway of what i want to do. Although I already give 2 button to start and stop the timer, i want it to be more automatic.Simply said, the server need to send a response that will trigger the stopTimer js function when the crawling process is finished.
To do this I'm using 2 block. The first block contain the regular content that will be returned from the server. While the second block contains the content and an embedded javascript that will invoke the stopTimer function in the client browser. This is the changes I made to my previous post.
Template Code :
<t:zone t:id="infoZone" t:update="show">
</t:zone><br/>
<t:block id="info">
Updating Message ....
<p t:type="OutputRaw" t:value="${message}">
Text Output
</p>
</t:block>
<t:block id="infoWithScript">
Update Stopped
<p t:type="OutputRaw" t:value="${message}">
Text Output
</p>
<script type="text/javascript">
stopTimer();
</script>
</t:block>
<a t:type="actionlink" t:id="crawl" href="#">Start Crawl</a>
In the template code I'm adding the two block named "info" and "infoWithScript". The block will not be rendered by default. And also I added another actionlink that will change the state in the server and start the crawling process.
Changes in java code :
@Inject
private Block _info;
@Inject
private Block _infoWithScript;
Object onActionFromRefreshZone() {
if(crawler.getCrawlStatus() == Crawler.CRAWL_STARTED) {
crawler.setCrawlStatus(Crawler.CRAWL_CRAWLING);
crawler.updateData(URL);
}
if(crawler.getCrawlStatus() == Crawler.CRAWL_END) {
crawler.setCrawlStatus(Crawler.CRAWL_IDLE);
return _infoWithScript;
}
return _info;
}
void onActionFromCrawl() {
crawler.setCrawlStatus(Crawler.CRAWL_STARTED);
}
public String getMessage() {
return ProgressNotifier.getMessage();
}
In the java code we add the two block that will be return by onActionFromRefrezhZone. This method will check if the crawl status in our services class is changed to started, then it will execute update data (the method that will invoke the real process and add messages to the ProgressNotifier).
At the end of the updateData method, the crawl status will be set to CRAWL_END. So the onActionFromRefreshZone will return the block that contain the embedded js to turn off the timer. And return the status to the original CRAWL_IDLE state.
Nothing changes in the client js script.
~FD
To do this I'm using 2 block. The first block contain the regular content that will be returned from the server. While the second block contains the content and an embedded javascript that will invoke the stopTimer function in the client browser. This is the changes I made to my previous post.
The Code
Template Code :
<t:zone t:id="infoZone" t:update="show">
</t:zone><br/>
<t:block id="info">
Updating Message ....
<p t:type="OutputRaw" t:value="${message}">
Text Output
</p>
</t:block>
<t:block id="infoWithScript">
Update Stopped
<p t:type="OutputRaw" t:value="${message}">
Text Output
</p>
<script type="text/javascript">
stopTimer();
</script>
</t:block>
<a t:type="actionlink" t:id="crawl" href="#">Start Crawl</a>
In the template code I'm adding the two block named "info" and "infoWithScript". The block will not be rendered by default. And also I added another actionlink that will change the state in the server and start the crawling process.
Changes in java code :
@Inject
private Block _info;
@Inject
private Block _infoWithScript;
Object onActionFromRefreshZone() {
if(crawler.getCrawlStatus() == Crawler.CRAWL_STARTED) {
crawler.setCrawlStatus(Crawler.CRAWL_CRAWLING);
crawler.updateData(URL);
}
if(crawler.getCrawlStatus() == Crawler.CRAWL_END) {
crawler.setCrawlStatus(Crawler.CRAWL_IDLE);
return _infoWithScript;
}
return _info;
}
void onActionFromCrawl() {
crawler.setCrawlStatus(Crawler.CRAWL_STARTED);
}
public String getMessage() {
return ProgressNotifier.getMessage();
}
In the java code we add the two block that will be return by onActionFromRefrezhZone. This method will check if the crawl status in our services class is changed to started, then it will execute update data (the method that will invoke the real process and add messages to the ProgressNotifier).
At the end of the updateData method, the crawl status will be set to CRAWL_END. So the onActionFromRefreshZone will return the block that contain the embedded js to turn off the timer. And return the status to the original CRAWL_IDLE state.
Nothing changes in the client js script.
~FD
Friday, April 16, 2010
Ajax Zone with Interval in Tapestry 5
I'm planning to create a zone that will keep requesting data from the server at certain interval. Seems simple enough to do in Tapestry 5 right ?
But because of my lack of understanding about Ajax and Javascript concept, I've venture to many place to make it. ZoneUpdate, Progressive Display is a few things that i look a bit deep. Trying to understand their inner work, just to find out in which part could I add this simple code.
After a day struggling, i found out that my original idea work just fine. Yeah when its about web, ajax and javascript, I SUCKS ! BIG TIME !
So here's the code to make it work, so no other newbie would get lost like I Do.
I'm using the sample code from Zone Component in Tapestry 5.
Here's the template page :
<body>
<h1> Crawl Ajax </h1>
<h2> Timer Zone </h2>
<div style="margin-left: 50px">
<t:zone t:id="time2zone">
time2: ${time2}
</t:zone><br/>
<a t:type="actionlink" t:id="refreshZone" href="#"
t:zone="time2zone">Refresh time2 </a>
<br/><br/>
</div>
<input type="button" onclick="startTimer()" value="Start Timer" /> <br/>
<input type="button" onclick="stopTimer()" value="Stop Timer" /> <br/>
</body>
Here's the java class code :
@InjectComponent
private Zone _time2Zone;
// The code
void onActionFromRefreshPage() {
// Nothing to do - the page will call getTime1() and getTime2() as it renders.
}
// Isn't called if the link is clicked before the DOM is fully loaded. See
// https://issues.apache.org/jira/browse/TAP5-1 .
Object onActionFromRefreshZone() {
// Here we can do whatever updates we want, then return the content we want rendered.
return _time2Zone.getBody();
}
public Date getTime2() {
return new Date();
}
Object onChangeOfTimerZone() {
return _time2Zone.getBody();
}
And finally, Here's the Java Script code :
<script type="text/javascript">
var timerId;
var linkId='refreshZone';
function updateMyZone() {
alert("Update Zone!" + linkId);
var actionLink = $(linkId);
Tapestry.findZoneManager( actionLink ).updateFromURL( actionLink.href );
}
function startTimer() {
alert('yohoo');
timerId = window.setInterval('updateMyZone()', 2000);
}
function stopTimer() {
clearInterval (timerId);
}
</script>
As you can see i only add 2 input button to start and stop the timer. The button will invoke the javascript function that uses setInterval that will invoke updateMyZone every 2 second. The updateMyZone will emulate the actionLink behaviour to update the zone.
Bleah its 10+ line of code and it took me the whole day. *still angry and ashamed with my self*
~FD
But because of my lack of understanding about Ajax and Javascript concept, I've venture to many place to make it. ZoneUpdate, Progressive Display is a few things that i look a bit deep. Trying to understand their inner work, just to find out in which part could I add this simple code.
After a day struggling, i found out that my original idea work just fine. Yeah when its about web, ajax and javascript, I SUCKS ! BIG TIME !
So here's the code to make it work, so no other newbie would get lost like I Do.
The Code
I'm using the sample code from Zone Component in Tapestry 5.
Here's the template page :
<body>
<h1> Crawl Ajax </h1>
<h2> Timer Zone </h2>
<div style="margin-left: 50px">
<t:zone t:id="time2zone">
time2: ${time2}
</t:zone><br/>
<a t:type="actionlink" t:id="refreshZone" href="#"
t:zone="time2zone">Refresh time2 </a>
<br/><br/>
</div>
<input type="button" onclick="startTimer()" value="Start Timer" /> <br/>
<input type="button" onclick="stopTimer()" value="Stop Timer" /> <br/>
</body>
Here's the java class code :
@InjectComponent
private Zone _time2Zone;
// The code
void onActionFromRefreshPage() {
// Nothing to do - the page will call getTime1() and getTime2() as it renders.
}
// Isn't called if the link is clicked before the DOM is fully loaded. See
// https://issues.apache.org/jira/browse/TAP5-1 .
Object onActionFromRefreshZone() {
// Here we can do whatever updates we want, then return the content we want rendered.
return _time2Zone.getBody();
}
public Date getTime2() {
return new Date();
}
Object onChangeOfTimerZone() {
return _time2Zone.getBody();
}
And finally, Here's the Java Script code :
<script type="text/javascript">
var timerId;
var linkId='refreshZone';
function updateMyZone() {
alert("Update Zone!" + linkId);
var actionLink = $(linkId);
Tapestry.findZoneManager( actionLink ).updateFromURL( actionLink.href );
}
function startTimer() {
alert('yohoo');
timerId = window.setInterval('updateMyZone()', 2000);
}
function stopTimer() {
clearInterval (timerId);
}
</script>
As you can see i only add 2 input button to start and stop the timer. The button will invoke the javascript function that uses setInterval that will invoke updateMyZone every 2 second. The updateMyZone will emulate the actionLink behaviour to update the zone.
Bleah its 10+ line of code and it took me the whole day. *still angry and ashamed with my self*
~FD
It happened again !
Okay, some reminder for the future. I don't know how many simple tapestry project I've made, but definitely more than a few. Yet i keep falling into the same hole. I keep missing some minor detail when creating a new project. And this time I'm facing a weird problem.
My project run fine in my development environment (eclipse WTP, tomcat, windows). But when ever i deploy it to my linux server. The page didn't show , it keep getting exception :
And then when i try to rebuild my project, clean, rebuild the class, and refreshing the project tree on eclipse explorer. You need to refresh the tree to make sure new/old/deleted file in the project will be the same with the wtp tomcat temp folder. The temp folder usually located in :
It's a great framework no doubt. Years in front of its competitor. But it has its own perks *meh* Hope this can be a gentle reminder for me in the future.
~FD
My project run fine in my development environment (eclipse WTP, tomcat, windows). But when ever i deploy it to my linux server. The page didn't show , it keep getting exception :
Page xxx did not generate any markup when rendered. This could be because its template file could not be located, or because a render phase method in the page prevented rendering.First I thought it was Case Sensitive problem. The name of the template (.tml) didn't match up to the class page. Although tapestry is case insensitive in many other things unfortunately this is something that T5 couldn't control. To bad that wasn't the problem :(
And then when i try to rebuild my project, clean, rebuild the class, and refreshing the project tree on eclipse explorer. You need to refresh the tree to make sure new/old/deleted file in the project will be the same with the wtp tomcat temp folder. The temp folder usually located in :
<your_workspace>/.metadata/.plugins/org.eclipse.wst.server.core/tmpXAfter rebuilding I found out that my local project also has the same problem, so it isn't about case sensitive. It turn out the source of my problem is this :
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">while the correct one is :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">Tapestry 5 could be very strict about this stuff, and no IDE support for Tapestry 5 certainly didn't help :( There are times when i build the html page from scratch just to test simple case, i forgot to ade the xmlns and Tapestry would just sprout some error.
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
It's a great framework no doubt. Years in front of its competitor. But it has its own perks *meh* Hope this can be a gentle reminder for me in the future.
~FD
Tuesday, March 23, 2010
One foot at the entrace
If you remember my september break down and what i think about my future, finally I've arrived to that point. Its a few days sooner than i expected but now I'm in front of the gate into a new world. I'm glad how it turn out. But first i need to finish few things first :)
Here's my check list for this week :
Here's my check list for this week :
- clean up my room and computer
- backup my work to dvd
- start jogging, and what i can get with 5 days of regular road work
- Finish my tax
- create my first apps
- commemorate today's event with another post on my blog :D
Subscribe to:
Posts (Atom)