Monday, August 05, 2013

Why I finally began to like TDD

I actually wrote this Controller Method in Ruby, but I will translate it to C# style. This is why I finally began to like TDD. Notice that there is a single call to Model (Movie) from Controller Method.

Without TDD

String ByDirector(int id)
{
 Movie movie = Movie.Find(id);
 if (movie.director == null || movie.director.size == 0)
 {
  String text = movie.title + " has no director info";
  this.Flash(FlashType.Warning, text);
  return RedirectToAction("HomePage");
 }
 this.movies = Movie.where("director = " + movie.director);
}

With TDD

String ByDirector(int id)
{
 try
 {
  this.movies = Movie.FindByDirector(id);
 }
 catch (NoDirectorException e)
 {
  this.Flash(FlashType.Warning, e.description);
  return RedirectToAction("HomePage");
 }
}

Wednesday, June 05, 2013

MSF Agile 6.x Boards and how to do Bug Management

I am using Microsoft Solutions Framework (MSF) for Agile Software Development latest version, i.e. 6.2, although this should be valid for any 6.x version.

In TFS 2012, latest version of both Agile and Scrum templates come with boards for tasks and work items. I am not able to find anything for Agile boards, although there is info available for boards in Scrum Template which are very similar to boards in Agile Template.

Agile boards are for user stories and for tasks within user stories  So one cannot see bugs and their child tasks. Same is the case with issues and their child tasks. One solution to this problem is to make a fake user story per bug. Others have explained how to modify the template so that one can see bugs in the board. MSF folks also seem indifferent to whether TFS users plan bugs with user stories or separately hence they have also written on how to add bugs to the task board or backlog.

I discourage above solution as giving story point to bugs is not a good idea (see last para) and so is cluttering real stories with fake stories especially when bugs count is usually more than stories count. Now here is my take on how to manage bug in MSF Agile 6.x.

  • If a bug is in a user story that one is currently working on in the given iteration, then add both that bug and associated tasks such as "fixing the bug with approach XYZ" as direct child of the user story. I believe time tracking is actually needed to know how much time one needs to spend, or for that matter has spent, on the user story. And this way you have included time spent on fixing bugs injected while working on that story. If one really needs to audit time spent on such bugs, then link appropriate tasks with the bug as "related". 
  • Now if a bug is in a legacy story then that should be fixed outside iteration capacity and thus should not be managed on board. So a team that has lot of legacy bugs can commit spending 30% capacity on fixing legacy bugs i.e. they will plan for stories with 70% capacity. Now as long as these bugs are stack ranked, whenever a developer has time he can pick the top one and fix it. 

Why outside iteration and why no need for story points? Well actually these legacy bugs are slowing ones velocity and to me are more like impediment to the user stories that otherwise team would be working on.  So if there are lots of high priority legacy bugs then a developer or two can commit 100% of their capacity to bug fixing for a few iterations. Bottom line is board are for real stories.

Monday, February 18, 2013

Daily Standup

This is basically an email that I sent to my subordinates in March 2012.

Daily Stand-up (also known as Daily Scrum) is:

  • where you share your progress (what I did since last meeting), commitment (what I have planned to do till next meeting) and identify obstacles.
  • a meeting so prepare for it. Don’t stand there wondering what you did and what you want to do. You should know this precisely when you enter the room.
  • for team; not for Product Owner or Scrum Master. Team needs to know what you are doing to align their efforts.
    • Team member can ask clarifying question. But even ask these questions conservatively i.e. only when really needed.
  • not for problem solving. Do that after the meeting.
  • not for storytelling. Tell the headlines, not the whole story.

If there is low energy in the status meeting then the chances are that the team is not following the above. If a team member has an obstacle, then he should not wait till Stand-up  Inform your Scrum Master ASAP. Stand-up is just a sure way to know daily about any new obstacles. Since, sprint goals are team goals therefore any obstacle is a team obstacle: try identifying it proactively.

If you want to read more about Daily Stand-up then here are some good articles:



Sunday, February 17, 2013

Setting up Astrid to get things done


I have come up with following Tags after some search on internet and tweaking the findings based on my experience & me needs. It might be useful to others who use To-Do apps with Tagging support e.g. Astrid, RTM, etc.

Group

These are mutually exclusive: #GIKIAA #LMKR #Personal

GIKIAA is volunteer work and LMKR is my employer. So one can replace these two with #Volunteer and #Work respectively.

Context(s) 

Some of these are not mutually exclusive e.g. something you can do both at office or home BUT if something can be done anywhere then you may have @anywhere

@Car @Computer @Home @Office @Pharmacy @Market @Dubai

Type(s)

First four are generally Mutually exclusive: .Call .Email .Read .Meet .BrainDead .Block

BrainDead are the tasks that I do when I have zero energy to do something more meaningful e.g. Take backup. Block are the tasks that are impeding something e.g. Heater is broken, Server has crashed)

Person(s)

Mostly mutually exclusive but there can be tasks that need feedback from multiple people: !Father !Wife !CEO !VP 

I have many more especially for key people and even group !subordinate but since I am a manager so I also have tags for the teams that i am managing: -Team1 -Team2

Project

Actually this is for any thing that is multistep. So it can be projects or releases or even epics (grand user stories): --ProjectNextGen --Release2013.1.0 -Epic2314

Example

For task "Hire Waqqas Jabbar's replacement" I would put: #Work @Computer @Home @Work .Email !VP -ARCH

As I am using Astrid so I don't need tags for “When” and Priority. Otherwise I would have had something like +Today *HIGH

Hope this helps.

Evaluating a few To-Do apps for iPhone

Conclusion is at the end

Wunderlist

Pros: Wunderlist has some good things. first of all it is very simple. Second it has two somewhat related features: remind via email or share via email (or SMS), which are cool. It doesn't have priority but it has ranking. You can prioritize a bit with star. so no Low-Medium-Hi things but starred/unstarred should work for some. One nice thing is that have app for all platforms even a standalone one for PC.

Cons: Problems with Wunderlist are a few: you can't have recurring task, due date is there but not time, similarly reminder date is there but not time. a task can only be in one list. and last but not least accidental deletion of task means it’s gone as you cannot undo that.

Astrid

It has changed a bit in last couple of months. It is much more sophisticated app than other apps in the TO DO category due to its heavy focus on sharing and collaborating.

Pros: Best thing about is that you can assign tasks to others and they can reply with an email if they accept or not. Besides assigning, you can share the task with other people. So task essentially becomes like a thread with comments and you can paste pictures in the comments as well. Another plus point is that a task can be in multiple lists (pseudo tagging) this is great as you can have context list @pharmacy @Market. Astrid can post items on your calendar. I am not big voice fan but you can use speech-to-text while adding tasks. No star option, but you get Hi-Med-Lo prioritization.

Cons: There is no all task view. Again no undelete option (but you can have a peek on deleted task in log). Biggest let down is that there is no reminder. Yes Astrid is a TO DO app with no real reminder option (though indirectly you can post the task to calendar and set alert there or Astrid reminds you when the task is due or overdue).

Errand To-do List

It is a nice app and it is fit-for-purpose if all you need is a free app for a single device

Pros: Recurring task options are good but the best part is multiple alerts – I really liked “nag every minute” alert. Another great option is that the notes can be made like check list – so sort of subtasks or shopping list. I also liked that Star is there in addition to priorities – this should help in focusing if the list of tasks becomes huge. Ranking is there but resets if you toggle between manual and automatic option.

Cons: Ranking is done separately for each view. This could be useful to some but to me it makes more sense to have one ranking system for views. Again no undelete option. Finally, major problem: the free edition doesn’t have a cloud backup or web-syncing.

"To Do"

"To Do" is a simple application… Not bad as first To Do app.

Pros: You can prioritize tasks and set due date/time. Only other option is to email the task to any email address you want (you can set default email though). It has just one view with three sorting option (date, priority, alphabetically). That is it.

Cons: no cloud backup, no alarms, no recurring task etc…

“Task This”

It is the most sophisticated application that I have evaluated. It has three types of to-do items: (1) Event with start & due date /time, (2) Task – an event without a Start Time and (3) Milestone – a task without a Start Date. For simplicity, let’s call all of these as tasks for remainder of this review

Pros: I find it useful that for each task multiple alerts/notification can be created. Then there are two (optional) orthogonal organizations: One is by Category (e.g. work, home etc), other is by project (e.g. Setup home office, Recruitment Drive 2012). Finally, you can also tag tasks – there is no browse-by-tags option yet but you can search via tag. There is no comment option (you can use description field but that is not neat) but you can attach photos and voice memos. Prioritization is also there and so does web sync.

oh by the way, although not very intuitively designed but you can manage shopping tasks separately from rest.

Cons: Application is not stable i.e. 4-5 crashed in a day and views are not consistent between web application and mobile application. Also, application seems like in beta for some functionality even some options in task are also half-done e.g. Action type, Lock.

List n Do Lite

It seems like a good application with a neat home window (minus advertisement) but the lite version has some serious limits.

Pros: Two differentiating things: (1) Sub Task is first class citizen and has all the attributes as parent task except category (Lite version has only one level of sub task). (2) An interesting yet useful feature is Task Action i.e. visit location, send email, go to website, call contact, send SMS etc. Like all good to do application it has: a few options for Repeat and alert, 3 level priorities and ability to categorize tasks in folders (single level).

Cons: Sync with various services including Google task only available in full version. Google Calendar option was not functional (this may be due to the fact that I was testing lite version but still there was no notification). GIS screen froze otherwise I was keen in checking out near-me tasks which seemed useful. Dropbox connect screen also got stuck. Had a couple of crashes: once while deleting and another while ranking. Finally, the mother of all cons: you can only have 10 main tasks in Lite version.

There is a connect to Dropbox option that I haven’t explored.

Any.Do

Unless you are a power user or feature freak this application is highly recommended.

Pros: Simple, neat and user friendly. Alarm has 5 pre-defined settings e.g. 10 min, 30 min, 1 hour etc. Considering that the focus is on keeping things simple, this makes sense to people who do not want fancy options in everything. You can mark task important i.e. red. You can add multiple steps/notes to task. You can share tasks with friends (assuming they are in your address book and they use any.do). Default view is date wise but you can switch it to folder. In either view, you can drag items up/down to sort. Rotate phone to view calendar in date view. Clicking tasks starting with word “call” starts the call. Other action words are email and text (for SMS).

AnyDo Moment, accessed via settings, is the most interesting feature. Basically it lets you, go through all tasks for selected days and, choose today, done, later etc. Very quick and neat for day planning in the morning. Finally, AnyDo automatically backs up all the tasks on cloud.

Cons: Not for power users. No recurring tasks. No tagging. Sub tasks do not have any attribute except title.

Orchestra

For personal-only use Orchestra is definitely inferior to better apps that I reviewed but then it shines for collaboration when compared to those other free apps. So despite its short coming I can think about using it for (volunteer) work I do where someone in the group needs to do something and everyone forgets. and why not for office work? Outlook “Flag for Recipient” do the trick there for me.

Pros: You can assign task to others who may accept the request or deny. Or you can put task in the list which is shared with other and people can pick the tasks themselves. Once a task is entered people can add comments. History is also visible as comments. You can add comments to that thread via email and web interface as well (nice thing if you are not registered). Recurring task options are great and you can mark a task important with Star. Views configuration options are good – you can always go to default by shaking the phone.

Cons: as soon as I signed up and clicked on the first tip, it crashed. Though no more crashes after that, but the first crash was disappointing. No integration with Facebook or Google which is sad since the app is collaborative in nature. Ranking is missing. Due date is there but not time. Reminder time is there but not date (not a very big problem though). Task cannot be deleted manually. And completed tasks are automatically deleted after 7 days (read this somewhere)

Remember the Milk

It is really one of the best when it comes to organizing but iPhone app has some limitations. Some of these are mitigated by the web app e.g. sharing task, reminder settings etc.

Pros: Adding a task from any view is really great as you can quickly set important attribute right there. Task postpone is kind of nice feature. Task estimation could be nice feature for some. You can add notes to Task and as many as you want. Cloud backup is available. Searching is good for power users. Recurring tasks & Ranking is supported but could have been better implemented.
Best feature is tagging. Really nicely done and is powerful enough for people who like to organize tasks using multiple dimensions i.e. @Home @computer @pharmacy @HR .Email .Read .Call .Braindead -Activity1 -Project2 (# is not supported for tagging so I used minus '-' )

Cons: Crashed as soon as I signed up. Only one crash but one too many to spoil the party. Push was not working. Reminder option per task is not there i.e. you can set general reminder setting via web app for all the tasks. Task completion icon seems more like close dialogue then complete task.

Conclusion

And the winner for me is: Astrid.

Thursday, January 21, 2010

OCI, NetBeans and Cygwin


Prerequisites

Here are the software and the versions used
  • OCI Instance Client
    • instantclient-sdk-win32-11.1.0.7.0.zip
    • instantclient-basiclite-win32-11.1.0.7.0.zip
  • NetBeans 6.8
  • Cygwin 1.7.1-1
    • gdb 6.8
    • make 3.81
    • gcc 3.4.4
    • g++ 3.4.4
I am assuming you have unzipped OCI files into d:/oracle

Setup

  1. In NetBeans, create new project. Choose Category: C/C++ and Projects: C/C++ Application.
  2. Give project any name e.g. OCIDemo. Give main file any name e.g. main, but ensure its extension is C rather than C++.
  3. Copy paste code from cdemo81.c to main.c
    1. cdemo81.c is in d:/oracle/instantclient_11_1\sdk\demo
  4. Open OCIDemo project properties using right click on the project name in Project Windows (CTRL + 1).
    1. Go to Category Build -> C Compiler and add D:/oracle/instantclient_11_1/sdk/include to Include Directories
    2. Go to Category Build -> Linker and add D:/oracle/instantclient_11_1/oci.dll to Libraries (not same as Additional Library Directories)

Connection String

cdemo81.c uses scott account for the demo. AS no connection string is given, so edit the line
(void) OCIServerAttach(srvhp, errhp, (text *) "", strlen(""), 0);
to
(void) OCIServerAttach(srvhp, errhp, (text *) "//localhost:1521/orcl", strlen("//localhost:1521/orcl"), (ub4) OCI_DEFAULT);
Note that on my machine SID is ORCL and its available on port 1521. Change this as per your instance setting.

Run

Press F6.

Sunday, August 23, 2009

File upload and download in Toplink, ADF Faces, Oracle 11g and JDeveloper 10g (10.1.3.3)


Application is simple; you upload files to Oracle database. Uploaded files are shown in a grid. To download a file, you simply click on link in the code column.




I have tried hard to write this blog-entry in detail, but still if there is anything you wanna ask then just email me at asim.ghaffar@gmail.com or simply add a comment.



Create Database

Schema

CREATE USER file_example
IDENTIFIED BY "12"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
PROFILE DEFAULT
ACCOUNT UNLOCK;


GRANT CONNECT TO file_example;
GRANT RESOURCE TO file_example;

Table

CREATE TABLE file_example.x_files
(
file_code NUMBER(38),
file_name VARCHAR2(256 BYTE),
file_content BLOB,
file_type VARCHAR2(50 BYTE),
file_size NUMBER(38),
CONSTRAINT x_files_pk PRIMARY KEY (file_code)
);


In this example, file_code is the primary key It is basically a hash-value of uploaded file's contents calculated using Java Arrays.hashCode(Byte[]). This ensures that a same file cannot be added more than once.


Create JDeveloper Application

Basic Application

  1. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select General (if not already selected).
    2. In the Items pan on the right, select Application (if not already selected).
    3. Press Ok button.
  2. Create Application dialogue should appear. Fill it as following.
    1. Application name: Cabinet
    2. Application Package Prefix: cabinet
    3. Application Template: Web Application [JSF, EJB, Toplink]
  3. You should be seeing newly created project under Applications node in the Application Navigator window. If the window is not open press (Ctrl+Shift+A).
  4. In Application Navigator select Model project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select Business Tier -> TopLink.
    2. In the Items pan on the right, select Java Objects from Tables.
    3. Press Ok button.
  5. Multistep dialogue will appear Create Java Objects from Tables
    1. Step 1 of 4: Select DB Connection.
      1. Click on the new button in row of Connection or press (Alt + w)
        1. Connection Name: file_conn, Connection Type: Oracle (JDBC)
        2. Username: file_example, Password: 12, Deploy password: [CHECKED]
        3. Driver: thin, Host Name: localhost, JDBC port: 1521, SID: ORCL
          1. This should reflect your DBMS specific values. Above should hold if you installed DB yourself on your machine with default SID.
        4. Test to verify. Else see what you missed in 2 or 3.
        5. Press Finish
      2. Click on the new button in row of Toplink Map or press (Alt + .)
        1. ToplLink Map Name: tlMap1, Connection: file_conn, Database platform: Oracle 10g
        2. Press Ok
      3. Press Next
    2. Step 2 of 4: Select Tables.
      1. Press Query or [CHECK] Auto-Query.
      2. Move table X_FILES from Available to Selected.
      3. Press Next
    3. Step 3 of 4: General Options.
      1. Press Next
    4. Step 4 of 4: Specifiy Object Details.
      1. Press Fnish.
  6. In Application Navigator select Model project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select Business Tier -> EJB.
    2. In the Items pan on the right, select Session Bean (EJB 1.1/2.x/3.0).
      1. If you are using other version of JDeveloper than 10.1.3.3 and you don't have this item available then choose the one which says Session Bean and Version 3.0.
    3. Press Ok button.
  7. Multistep dialogue will appear Create Java Objects from Tables
    1. Step 1 of 4: EJB Name and Options
      1. EJB Name: SessionEJB
      2. Session Type: Stateless
      3. Transaction Type: Container
      4. Generate Session Façade Methods: [CHECKED]
      5. Entity Implementation: TopLink POJOs
      6. Press Next
    2. Step 2 or 4: Session Façade – Select Service Methods
      1. [Check All]
      2. Press Next
    3. Step 3 or 4: Class Definitions
      1. Press Next
    4. Step 4 or 4: Class Definitions
      1. Implement a Remote Interface: [UNCHECKED]
      2. Implement a Local Interface: [CHECKED]
      3. Include Web Service Endpoint Interface: [Unchecked]
      4. Press Finish
  8. In Application Navigator select ViewController project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select Web Tier -> JSF.
    2. In the Items pan on the right, select JSF JSP.
    3. Press Ok button.
  9. Multistep dialogue will appear Create JSF JSP
    1. Step 1 of 4: JSP File
      1. File Name: index.jspx
      2. Type: [JSP Document (*.jspx)]]
      3. Add Mobile Support: [UNCHECKED]
      4. Press Next
    2. Step 2 or 4: Component Binding
      1. Select [Do Not Automatically Expose UI Components in a Managed Bean]
      2. Press Next
    3. Step 3 or 4: Tag Libraries
      1. Filter by Project Technologies.
      2. Move these from available libraries to selected Libraries.
        1. ADF Faces Component 10_1_3_3_0
        2. ADF Faces HTML 10_1_3_3_0
        3. JSF Core 1.0
        4. JSF HTML 1.0
      3. Press Next
    4. Step 4 or 4: HTML Options
      1. HTML Version: 4.0.1 Transitional
      2. Title: Index
      3. Press Finish

Model Project enhancement to complete example application

  1. In Application Navigator collapse tree for Model project and double click Application Sources à cabinet.model.SessionEjbBean.java to open it in the editor. Add following:

public Integer saveXFile(String fileName,byte[] fileContent,int filesize,String filetype){
int code=Arrays.hashCode(fileContent);
XFiles list=findXFiles(code);
if(list!=null) {
System.err.println("File already exist");
return code;
}


list=new XFiles();
list.setFileName(fileName);
list.setFileContent(fileContent);
list.setFileSize((double)filesize);
list.setFileType(filetype);
list.setFileCode((double)code);


try{
persistEntity(list);
} catch(Exception e){
e.printStackTrace();
return null;
}


return code;
}


public XFiles findXFiles(int code){
Session sess=getSessionFactory().acquireSession();
Expression exp=new ExpressionBuilder().get("fileCode").equal(code);
XFiles list=(XFiles)sess.readObject(XFiles.class,exp);
sess.release();
return list;
}


  1. While in SessionEjbBean.java, press (Ctrl+Shift+S) to open structure window. Collapse Sources, and double click on SessionEJBLocal.java to open it in the editor. Add following:

Integer saveXFile(String fileName,byte[] fileContent,int filesize,String filetype);


XFiles findXFiles(int code);


  1. In Application Navigator collapse tree for Model project and select Application Sources à cabinet.model.SessionEjbBean.java. Right click on the file and choose option Create Data Control.

ViewController Project enhancement to complete example application

  1. In Application Navigator collapse tree for ViewController project and double click web Content à index.jspx to open it in the editor. Choose Design view.
  2. From Data Control Palette, drag and drop SessionEJBLocal à findAllXFiles() à XFiles at center of open page. Use option Tables à ADF Read-only Table…
    1. You will get a notification Client Project Libraries Added, if this is the first page on which you are dropping a data control.
  3. Click anywhere on page and press (Ctrl + Shift + S).
  4. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form à af:table and then double click on it to open Table Properties dialogue.
    1. On Column Summary tab, rename Column headers
      1. #{bindings.findAllXFiles1.labels.fileCode} à Code
      2. #{bindings.findAllXFiles1.labels.fileName} à Name
      3. #{bindings.findAllXFiles1.labels.fileSize} à File Size
      4. #{bindings.findAllXFiles1.labels.fileType} à File Type
    2. On same tab, change Component of first row (value=#{row.fileCode})
      1. af:outputText à af:commandLink
    3. Press OK
  5. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form à af:table à af:column – Code à af:commandLink - #{row.fileCode} and then press (Ctrl + Shift + I) to open Property Inspector.
    1. In Action property write #{backing_index.download}
  6. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form and then right click on it and choose Convert option. Convert Form dialogue will open.
    1. From ADF Faces Core category choose Form.
    2. Press OK
  7. With af:form still selected press (Ctrl + Shift + I) to open Property Inspector.
    1. In UsesUpload choose true. (IMPORTANT)
  8. Click anywhere on page. Press (Ctrl + Shift + P) to open Component Palette.
  9. From Component Palette, drag and drop ADF Faces Core à InputFile at center of open page. New Component will appear below existing Table.
  10. With newly added component selected press (Ctrl + Shift + I) to open Property Inspector.
    1. In Label property remove existing text i.e. Label 1
    2. In Value property enter #{backing_index.uploadedFile}
  11. Click anywhere on page. Press (Ctrl + Shift + P) to open Component Palette.
  12. From Component Palette, drag and drop ADF Faces Core à CommandButton at center of open page. New Component will appear below existing File Input Compoent.
  13. With newly added component selected press (Ctrl + Shift + I) to open Property Inspector.
    1. In Text property replace existing text with Upload
  14. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form and then (Ctrl) select af:inputFile and af:commandButton - Upload. Right click on the selection and choose Surround With. Surround With dialogue will open.
    1. From ADF Faces Core category choose PanelHorizontal.
    2. Press OK
  15. In Structure window navigate to jsp:root à f:view à afh:html à afh:body à h:form à af:table à af:column – Code à af:commandLink - #{row.fileCode} and then right click and choose Insert inside of af:commandLink - #{row.fileCode} à ADF Faces Core. Insert ADF Faces Core Item dialogue will appear.
    1. From ADF Faces Core category choose SetActionListener.
    2. Press OK.
    3. Insert SetActionaListener dialogue will appear. Add following values
      1. From: #{row.fileCode}
      2. To: #{backing_index.fileCode}

Now we need a backing bean. For this we will create a file and then add relevant information in faces-config.

  1. In Application Navigator select ViewController project of the newly created application. Press (Ctrl + n) to open New Gallery dialogue.
    1. In the Categories pan on the left, select General.
    2. In the Items pan on the right, select Java Class.
    3. Copy paste Index.java code from next section: Complete Source
    4. Press Ok.
  2. Create Java Class dialogue will appear
    1. Set following values
      1. Name: Index
      2. Package: cabinet.view
      3. Public: [CHECKED]
      4. Generate Default Constructor: [UNCHECKED]
      5. Generate Main Method: [UNCHECKED]
    2. Press OK
  3. In Application Navigator collapse tree for ViewController project and double click web Content à faces-config.xml to open it in the editor. Choose Source view.
    1. Before last tag </faces-config> add these lines

    <managed-bean>
    <managed-bean-name>backing_index</managed-bean-name>
    <managed-bean-class>cabinet.view.Index</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
    <property-name>bindings</property-name>
    <value>#{bindings}</value>
    </managed-property>
    </managed-bean>

Complete Source

index.jspx

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0" xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces" xmlns:afh="http://xmlns.oracle.com/adf/faces/html">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML" doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<afh:html>
<afh:head title="Index">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
</afh:head>
<afh:body>
<af:messages/>
<af:form usesUpload="true">
<af:table value="#{bindings.findAllXFiles1.collectionModel}" var="row" rows="#{bindings.findAllXFiles1.rangeSize}" first="#{bindings.findAllXFiles1.rangeStart}"
emptyText="#{bindings.findAllXFiles1.viewable ? 'No rows yet.' : 'Access Denied.'}">
<af:column sortProperty="fileCode" sortable="false" headerText="Code">
<af:commandLink text="#{row.fileCode}" action="#{backing_index.download}">
<af:setActionListener from="#{row.fileCode}" to="#{backing_index.fileCode}"/>
</af:commandLink>
</af:column>
<af:column sortProperty="fileName" sortable="false" headerText="Name">
<af:outputText value="#{row.fileName}"/>
</af:column>
<af:column sortProperty="fileSize" sortable="false" headerText="File Size">
<af:outputText value="#{row.fileSize}">
<f:convertNumber groupingUsed="false" pattern="#{bindings.findAllXFiles1.formats.fileSize}"/>
</af:outputText>
</af:column>
<af:column sortProperty="fileType" sortable="false" headerText="File Type">
<af:outputText value="#{row.fileType}"/>
</af:column>
</af:table>
<af:panelHorizontal>
<af:inputFile value="#{backing_index.uploadedFile}"/>
<af:commandButton text="Upload"/>
</af:panelHorizontal>
</af:form>
</afh:body>
</afh:html>
</f:view>
</jsp:root>

faces-config.xml

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config xmlns="http://java.sun.com/JSF/Configuration">
<application>
<default-render-kit-id>oracle.adf.core</default-render-kit-id>
</application>
<lifecycle>
<phase-listener>oracle.adf.controller.faces.lifecycle.ADFPhaseListener</phase-listener>
</lifecycle>
<managed-bean>
<managed-bean-name>backing_index</managed-bean-name>
<managed-bean-class>cabinet.view.Index</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>bindings</property-name>
<value>#{bindings}</value>
</managed-property>
</managed-bean>
</faces-config>

Index.java

package cabinet.view;


import cabinet.model.SessionEJBLocal;
import cabinet.model.XFiles;
import java.io.IOException;
import java.io.InputStream;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import oracle.adf.view.faces.model.UploadedFile;
import oracle.binding.BindingContainer;


public class Index{
private BindingContainer bindings;
private UploadedFile uploadedFile;
private Double fileCode;


public BindingContainer getBindings(){
return this.bindings;
}


public void setBindings(BindingContainer bindings){
this.bindings=bindings;
}


public void setFileCode(Double fileCode){
this.fileCode=fileCode;
}


public Double getFileCode(){
return fileCode;
}


public void setUploadedFile(UploadedFile uploadedFile){
this.uploadedFile=uploadedFile;
try{
Integer code=uploadFile(uploadedFile);


// To refresh table
bindings.getOperationBinding("findAllXFiles").execute();
} catch(IOException e){
e.printStackTrace();
}
}


public UploadedFile getUploadedFile(){
return uploadedFile;
}


public String download(){
if(fileCode!=null)
try{
XFiles atchmn=getEJBLocal().findXFiles(fileCode.intValue());
if(atchmn==null)
return null;
downloadFile(atchmn);
} catch(Exception ex){
ex.printStackTrace();
}
return null;
}


public static Integer uploadFile(UploadedFile file) throws IOException{
byte[] array=new byte[(int)file.getLength()];
InputStream in=file.getInputStream();
int offset=0;
int numRead=0;
do{
numRead=in.read(array,offset,array.length-offset);
offset+=2048;
} while(numRead!=-1);
Integer code=getEJBLocal().saveXFile(file.getFilename(),array,(int)file.getLength(),file.getContentType());
return code;
}


public static void downloadFile(XFiles atchmn){
String fname=atchmn.getFileName();
byte[] pdf=atchmn.getFileContent();
FacesContext faces=FacesContext.getCurrentInstance();
HttpServletResponse response=(HttpServletResponse)faces.getExternalContext().getResponse();
response.setContentType(atchmn.getFileType());
response.setContentLength(atchmn.getFileSize().intValue());
response.setHeader("Cache-Control","must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma","public");
response.setHeader("Content-disposition","attachment; filename=\""+fname+"\"");
try{
ServletOutputStream out;
out=response.getOutputStream();
out.write(pdf);
} catch(IOException ex){
ex.printStackTrace();
}
faces.responseComplete();
}


public static Object getELObj(String s){
FacesContext fc=FacesContext.getCurrentInstance();
ValueBinding vb=fc.getApplication().createValueBinding(s);
return vb.getValue(fc);
}


public static SessionEJBLocal getEJBLocal(){
try{
SessionEJBLocal ejb=(SessionEJBLocal)getELObj("#{data.SessionEJBLocal.dataProvider}");
return ejb;
} catch(Exception ex){
ex.printStackTrace();
}
return null;
}
}

Note:

If in Index.java you get error on oracle.binding.BindingContainer then this means certain required libraries are not added to your project. When you drag and drop control from Data Control Palette these libraries are automatically added.

Wednesday, July 23, 2008

Avoid ADF Table selection-state reset

This is a small code snippet on how to avoid selection-state reset of an ADF table when the underlying method is re-queried.

Assuming that you have a Master-Detail situation. Lets says, you have a methodAction e.g. id="findAllItems" and an associated methodIterator e.g. id="findAllItemsIter", then following is the code snippet that you can use in the page's managed backing bean to avoid selection-state reset.

public void refreshButRetainSelection(){
CoreTable table=this.getMasterTable();
RowKeySet rowSet=table.getSelectionState();
OperationBinding operationBinding=
bindings.getOperationBinding("findAllItems");
operationBinding.execute(); // requery
table.setSelectionState(rowSet);
Set keySet=table.getSelectionState().getKeySet();
Iterator rowSetIter=keySet.iterator();
DCIteratorBinding iterBinding=
(DCIteratorBinding)bindings.get("findAllItemsIter");
while(rowSetIter.hasNext()){
Key key=(Key)rowSetIter.next();
iterBinding.setCurrentRowWithKey(key.toStringFormat(true));
}
}

Saturday, January 26, 2008

TOPLINK SORTING: How to

-- all code tested on Jdeveloper 10.1.3
One of the first queries I had when I started working with Toplink was how to get sorted results. Here are few techniques that may help newbies (especially the tip at the end regarding child collections sorting).

All example will be given using a two table schema:
CREATE TABLE ACCOUNTS (
LOGIN VARCHAR2(10) NOT NULL PRIMARY KEY,
PASSWORD VARCHAR2(20),
CREATED DATE);

CREATE TABLE LOGINS(
ACCOUNTS_LOGIN VARCHAR2(10) NOT NULL PRIMARY KEY, -- FK pointing to ACCOUNT->LOGIN
LOGIN_TIME DATE NOT NULL PRIMARY KEY,
LOGOUT_TIME DATE,
COMPUTER VARCHAR2(30));

ALTER TABLE LOGINS ADD CONSTRAINT
LOGINS_ACCOUNTS_FK FOREIGN KEY(ACCOUNTS_LOGIN)REFERENCES ACCOUNTS(LOGIN) ENABLE;


For rest of the article, I will assume you have generated pojo using toplink over above mentioned tables.

Sorting Toplink Named Queries
Named queries can be sorted using “AfterLoad” technique.

Let say you have a readAllquery findAllLoginsOf that takes a parameter login (String). A expression is defined as 1. accountsLogin EQUAL login. This query will return all logins by a particular user - without sorting. To add sorting do this:

1. Create static function (This can be any class e.g. MyAfterLoads)
public static void afterLoadLogins(ClassDescriptor descriptor){ // Again, name of method is immaterial.
ReadAllQuery raq=(ReadAllQuery)descriptor.getDescriptorQueryManager().getQuery("findAllLogins");// Default query generated using Toplink POJO creations
raq.addAscendingOrdering("loginTime");
raq=(ReadAllQuery)descriptor.getDescriptorQueryManager().getQuery("findAllLoginsOf");// Our query
raq.addAscendingOrdering("loginTime");
}


2. In Structure window right click on the Logins descriptor. (You will need to first select Toplink map file using the application navigator first).

3. Through the context menu choose Advanced Properties -> After Load

4. Point to the class MyAfterLoads and choose from the drop-down your method. Note: In earlier versions of jdev 10.1.3 there is a bug. So if your method des not appear in the list you may need to open up the class MyAfterLoads choose “Reformat”.

That is all. Note that you can have different sorting for different named queries.

Sorting Toplink Direct Queries
In case you writing Toplink queries directly in java then there is a way for sorting there as well. Taking the same example i.e. findAllLoginsOf, a common way to query it will be:

// LIST (A)
Session session=getSessionFactory().acquireSession();
ExpressionBuilder builder=new ExpressionBuilder();
Expression exp=builder.get("accountsLogin").equal(user);
List account=(List)session.readAllObjects(Logins.class,exp);


However this will return unsorted list. To sort you will need to use the following code. Notice that instead of readAllObject now I am using executeQuery. Bold is the different part in list A & list B.

// LIST (B)
Session session=getSessionFactory().acquireSession();
ExpressionBuilder builder=new ExpressionBuilder();
Expression exp=builder.get("accountsLogin").equal(user);
ReadAllQuery query=new ReadAllQuery(builder);
query.setReferenceClass(Logins.class);
query.addAscendingOrdering("loginTime");
query.setSelectionCriteria(exp);

List logins=(List)session.executeQuery(query);


Sorting Java Entities
You can always do sorting using java Collections i.e. implement Comparable in your POJO (In our case that would be class Logins). We will have something like this after implementing

public int compareTo(Object o){
Logins that = (Logins) o;
return this.getLoginTime().compareTo(that.getLoginTime());
}

Now when you have your collection then all you need to do is to pass it to Collections.sort(List). So list A can be modified as (modification in bold):

Session session=getSessionFactory().acquireSession();
ExpressionBuilder builder=new ExpressionBuilder();
Expression exp=builder.get("accountsLogin").equal(user);
List account=(List)session.readAllObjects(Logins.class,exp);
logins = (List)Collections.sort(account);


The main strength (or weakness) of this particular java technique is that you have only one sorting criteria per table. There are other java techniques as well that give you more options in this regard.

Sorting Child Collection
Use above mentioned technique (i.e. implementing Comparable) to sort child collections.
Accounts account;
//accounts initialization code
//.....
List logins = account.getLoginsCollection();
logins = (List)Collections.sort(account);

Tuesday, January 22, 2008

Do not Automatically Expose UI components in a Managed Bean

I made a JSF page that initially had auto-exposing (of UI components to Managed Bean) disabled -call it Page A. Then I redid with aut-exposing enabled - call it Page B. These were some stats.


Next comes performance. Page (A) needed around 11 seconds to open up (cold start) in Jdeveloper design view. Page (B) took around 35 seconds. However, both pages took same amount of time while opening in a web browser.


Conclusion: 44% additional code is maintanence over-head and a lagging designer is productivity killer. So do not use it unless you have a solid reason for it.

Note (1): All pages were formatted using line length = 127 Character. This has an impact on # of lines.
Note (2): I used Jdeveloper Version 10.1.3.3 on NT 5.