The next firefox. Instead of slapping RSS, Blogging, Flickr,Del.icio.us onto the existing browser, these fine folks have created this new browser called FLOCK. Now I am blogging with FLOCK. Hope I can blog at a faster pace henceforth with blogging toolbar built right onto the browser window.
0 to Struts in 60 minutes – Part II
Our goal :
First Iteration :
* set up a login page, login formbean, login process Actionservlet stub
Second Iteration :
* set up log4j for logging purposes
* use a pattern – Data Access Object pattern
* talk to mysql database
* logic for authentication in login actionservlet
First Iteration :
Setting up login page, login process servlet stub, welcome page
Lets build this project iteratively. First the skeleton and then slowly we will start adding sinews adding more complexity. A simple login page that submits the values to the login servlet. ( Well it has to go through the controller servlet – we will see that in a minute ) – and the login process servlet right now doesnt do anything big – just accepts the values and displays the username being entered. It still does not have the ability to talk to database – it has to wait !!
We need to create 4 files ( 2 JSP,2 Java) and tamper with struts_config.xml
Login.jsp
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<body>
<html:form action="/actions/actionlogin">
Username : <html:text property="username"/><BR>
Password : <html:password property="password"/>
<html:submit>Submit</html:submit>
</html:form>
</body>
</html>
We are taking advantage of struts tags. The html code we have to write gets minimised a lot when we use struts tags – things like populating a select drop down menu and selecting default value can all be written in a single line – makes the html code look neat and maintenance is easy. Perhaps in the next blog entry I will write a page takes advantage of all the html tags, and other bells and whistles in struts. Save this file as login.jsp and let it sit in under webapps/antiPC/pages
The login ActionServlet stub
package net.kvrlogs.antiPC.actions;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.*;
public class Login extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return mapping.findForward("success");
}
}
Save this under WEB-INF/src/net/kvrlogs/antiPC/actions as Login.java
If you notice this servlet returns a value called “success”. This is picked up by the controller servlet and based on the value it calls up the appropriate jsp page or another servlet.
We are slowly starting to get into struts.
How is this servlet (its called ActionServlet) different from a simple servlet.
1. We extend Action – instead of HTTPServlet.
2. We do not override doGet or doPost anymore. Instead we override a method called execute.
3. execute is given 4 important objects
mapping
form
request – same as a servlet
response – same as a servlet
What is mapping?
Mapping is the hotline to the controller servlet. This login servlet cannot ( and should not ) direct control to another JSP page. It it does like that then it blows away the main concept of MVC Architecture.
This mapping is done in struts-config.xml file from where controller servlet reads and gets its intelligence.
What is form?
Form is the ActionForm. Whenever a html form is submitted all the field values are put inside a JavaBean called FormBean. For now just type the following code and save it under /WEB-INF/src/net/kvrlogs/antiPC/forms/LoginFormBean.java
If you are using eclipse there is a short cut to generate this file :
Just type the following
private String username;
private String password;
Then eclipse can automatically generate getters and setters for the variables like this
Point to note is the variable names username and password should be same as the html field names given in login.jsp page. When the login.jsp page is submitted, the values in username and password are put in this formbean and sent to the login servlet. This is done by Java reflection where it automatically discovers the right variable names.
Also note that the getters and setters follow javabean naming rules ( if you let eclipse do its job you will not be mistyping the getter setter names and would not later haunt you when the form variables are not seen in the login servlet ).
*the getter, setter should be named as get/set and First letter of variable capitalized.
if variable name is username
getter will be getUsername
* the variable should be private, whereas the getter setter should be public.
package net.kvrlogs.antiPC.forms;
import org.apache.struts.action.ActionForm;
public class LoginFormBean extends ActionForm {
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
In struts-config.xml
Go to the node called
<action
path="/Welcome"
forward="/pages/Welcome.jsp"/>
Change the above to this
<action
path="/Welcome"
forward="/pages/login.jsp"/>
Now if you open the index.jsp page there is a redirect command
<logic:redirect forward="welcome"/>
When index.jsp page is typed in the browser, when the page loads it sees the redirect to “welcome” action. The controller servlet picks this up – finds the action mapping in struts-config.xml as directing to forward to /pages/login.jsp – and thats what the browser will see – login.jsp
Next we have to define the action for /actions/actionlogin
<action
path="/actions/actionlogin"
type="net.kvrlogs.antiPC.actions.Login"
parameter="/pages/index.jsp">
<forward
name="success"
path="/pages/homepage.jsp"/>
</action>
path : Its the path we type in the form action
type : which actionservlet is called to execute this action
parameter : from where this request is coming from
forward : After the actionservlet does it job it sends a message – “success” in our case. Heres where we decide where the “success” should take us to – to /pages/homepage.jsp
Lets create homepage.jsp
<html>
<body>
You are in
</body>
</html>
Save this in pages/homepage.jsp
Now this is what we have done so far in our first iteration.
*Created login.jsp, Login.java, homepage.jsp
* Mapped the logic flow in struts-config.xml under the node
Time to build, and try it out in your browser.
Save everything.
Click on the directiory WEB-INF/src ( or where the build.xml file is )
Select the little run button from eclipse and choose Run as ant build.
Pray 🙂
I got a build error saying this :
[javac] /usr/local/tomcat/webapps/antiPC/WEB-INF/src/net/kvrlogs/antiPC/actions/Login.java:3: package javax.servlet.http does not exist
I added this to the pathelement in build.xml
<pathelement path ="/Library/Tomcat/common/lib/servlet-api.jar"/>
Once it builds, restart antiPC in tomcat manager. And type http://localhost:8080/antiPC in webbrowser.
You will see this
Hit submit and you will see this
Second Iteration :
* set up log4j for logging purposes
* use a pattern – Data Access Object pattern
* talk to mysql database
* logic for authentication in login actionservlet
Setting up log4j
log4j – is a replacement for system.out.printlns developers write for debugging. Production level code should not spit out these. So these printlns have to be hunted and commented out,source code compiled – and also has to be uncommented later in maintenance mode when something breaks – its a pain.
Log4j allows us to set logging on or off globally – so when code goes to production, all we have to do is set the logging level we need by editing a configuration file. Also there are lots of other advantages
* granularity can be controlled – debug,warn,info,error,fatal,log
* output can be anything – sysout, file etc.
* since its only job is to log it does an efficient job of it
Get log4j from good old apache.
http://logging.apache.org/log4j/docs/download.html
Under dist directory there is the jar file : log4j-1.2.9.jar
Copy it to TOMCAT_HOME/common/lib directory and also in antiPC/WEB-INF/lib.
Inside TOMCAT_HOME/common/classes, create a file called log4j.properties and type these into it.
log4j.rootLogger=info, R
log4j.appender.R=org.apache.log4j.ConsoleAppender
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%-5p %-30.30c{1} %x – %m%n
( I have no clue what all these appenders and layouts are – I just got it from log4j website. But it works 🙂 )
There are lot of steps involved before we can talk to the database. Heres what we are trying to accomplish.
When the server loads the application it will load a listener defined in WEB.XML file under the node . When the container initializes this class is called.
Now the listener reads the entries in WEB.XML for database driver name,database name,database username,password and created a DBHandler object – which is kind of a helper class that accepts sql statements and executes them for you. The listener puts this DBHandler object as an attribute in context. Think of context variables as a global variable for the entire application. So whenever any servlet when it wants to talk to the database it can get the DBHandler object from the context and use it.
Advantages of this long winded approach?
* Database connection code sits at one place
* Database details are stored in web.xml – so application need not be recompiled when any of the values change. Just a reload is sufficient.
So to start the long winded road- lets first enter the database related stuff in web.xml
<context-param>
<param-name>dbDriver</param-name>
<param-value>org.gjt.mm.mysql.Driver</param-value>
</context-param>
<context-param>
<param-name>dbDatabase</param-name>
<param-value>jdbc:mysql://localhost/antiPC</param-value>
</context-param>
<context-param>
<param-name>dbUsername</param-name>
<param-value>yourusername</param-value>
</context-param>
<context-param>
<param-name>dbPassword</param-name>
<param-value>yourpassword</param-value>
</context-param>
<listener>
<listener-class>
net.kvrlogs.antiPC.init.MyServletContextListener
</listener-class>
</listener>
Next save DBHandler.java under /WEB-iNF/src/net/kvrlogs/antipc/utils/DBHandler.java
package net.kvrlogs.antiPC.utils;
import java.sql.*;
import org.apache.log4j.*;
import java.util.*;
public class DBHandler {
static Logger _logger = Logger.getLogger(DBHandler.class.getName());
String _driverName = null;
String _databaseUrl = null;
Connection _dbConn = null;
PreparedStatement _pstmt = null;
String _queryString = null;
public DBHandler(String driverName, String databaseUrl, String _username,
String _password) {
String methodsig = "DBHandler.DBHandler()";
_logger.info(driverName + "," + databaseUrl + ","
+ _username + "," + _password);
//Verify that the driver class exists
try {
Class.forName(driverName);
} catch (Exception e) {
e.printStackTrace();
_logger.error("Driver could not be found: " + e);
return;
}
//Get a connection
try {
_dbConn = DriverManager.getConnection(databaseUrl, _username,
_password);
_driverName = driverName;
_databaseUrl = databaseUrl;
} catch (SQLException sqle) {
_logger.error( "Couldn't connect to database. "
+ sqle);
return;
}
//Log it
_logger.info("Connected to " + _databaseUrl);
}
public boolean setQueryString(String queryString) {
String methodsig = "DBHandler.setQueryString()";
if (_dbConn == null) {
_logger.error("Error!!! - Couldn't set the query string because the Connection was null. The DBHandler was not constructed correctly or the database does not exist.");
return false;
}
try {
_pstmt = _dbConn.prepareStatement(queryString);
_queryString = queryString;
} catch (SQLException sqle) {
_logger.error("Couldn't create PreparedStatement. " + sqle);
return false;
}
return true;
}
public void close() {
try {
_dbConn.close();
} catch (SQLException sqle) {
}
}
/**
*
* Do a select query on a prepared statement with no arguments
*
*/
public ResultSet lookup() {
String methodsig = "DBHandler.lookup(String)";
_logger.debug("Looking up query: '" + _queryString
+ "'");
try {
if (!_pstmt.execute()) {
_logger.debug("The resultset was empty on the lookup..");
}
return _pstmt.getResultSet();
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database, or create PreparedStatement. "
+ sqle);
return null;
}
}
/**
*
* Do a select query on a prepared statement with single argument,
*
* where the argument is a String
*
*/
public ResultSet lookup(String arg) {
String methodsig = "DBHandler.lookup(String)";
_logger.debug("Looking up: '" + arg + "' with query '"
+ _queryString + "'");
try {
_pstmt.setString(1, arg);
if (!_pstmt.execute()) {
_logger.debug("The resultset was empty on the lookup..");
}
return _pstmt.getResultSet();
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database, or create PreparedStatement. "
+ sqle);
return null;
}
}
/**
*
* Do a select query on a prepared statement with single argument,
*
* where the argument is an integer
*
*/
public ResultSet lookup(int arg) {
final String methodsig = "DBHandler.lookup(int)";
ResultSet returnval = lookup(Integer.toString(arg));
return returnval;
}
/**
*
* Do a select query on a prepared statement with a vector of arguments,
*
*/
public ResultSet lookup(Vector v) {
final String methodsig = "DBHandler.lookup(Vector)";
try {
//i is the a counter that places each variable in the vector into
// the
//proper place in the prepared statement
int i = 1;
for (Enumeration enum = v.elements(); enum.hasMoreElements();) {
_pstmt.setString(i++, (String) enum.nextElement());
}
if (!_pstmt.execute()) {
_logger.debug("The resultset was empty on the lookup..");
}
return _pstmt.getResultSet();
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database, or create PreparedStatement. "
+ sqle);
return null;
}
}
/**
*
* Insert a row of values (Vector of args) into a table
*
*/
public int insert(Vector args) {
String methodsig = "DBHandler.insert()";
//Create a string out of the args so we can pass it into the logfile
// message
String argString = new String("(");
for (Enumeration enum = args.elements(); enum.hasMoreElements();) {
Object o = enum.nextElement();
if (o instanceof Integer) {
argString = argString.concat((Integer) o + ",");
}
else {
argString = argString.concat((String) o + ",");
}
}
argString = argString.substring(0, argString.length() - 1);
argString = argString.concat(")");
_logger.debug("Inserting: '" + argString
+ "' with query '" + _queryString + "'");
//Create the prepared statement by passing in each argument of the
// vector
try {
for (int i = 0; i < args.size(); i++) {
Object o = args.elementAt(i);
// if (o == null) {
// _pstmt.setNull(i+1, 1);
// }
if (o instanceof Integer) {
_pstmt.setInt(i + 1, ((Integer) o).intValue());
}
else {
_pstmt.setString(i + 1, (String) o);
}
}
int updateResult = _pstmt.executeUpdate();
_logger.debug("The insert updated " + updateResult
+ " rows");
return updateResult;
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database, or create PreparedStatement. "
+ sqle);
//sqle.printStackTrace();
return 0;
}
}
/**
*
* Execute a query with no args
*
*/
public boolean execute() {
String methodsig = "DBHandler.execute()";
_logger.debug("Executing query: '" + _queryString
+ "'");
try {
boolean returnval = _pstmt.execute();
return returnval;
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database. "
+ sqle);
return false;
}
}
/**
*
* Execute an update query with a String as an arg
*
*/
public int executeUpdate(String arg) {
String methodsig = "DBHandler.executeUpdate(String)";
_logger.debug("Updating '" + arg
+ "' with query string '" + _queryString + "'");
try {
_pstmt.setString(1, arg);
int updateResult = _pstmt.executeUpdate();
_logger.debug("The updated affected "
+ updateResult + " rows");
return updateResult;
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database. "
+ sqle);
return 0;
}
}
/**
*
* Execute an update query with a Vector of Strings as args
*
*/
public int executeUpdate(Vector args) {
String methodsig = "DBHandler.executeUpdate(Vector)";
//Create a string out of the args so we can pass it into the logfile
// message
String argString = new String("(");
for (Enumeration enum = args.elements(); enum.hasMoreElements();) {
Object o = enum.nextElement();
if (o instanceof Integer) {
argString = argString.concat((Integer) o + ",");
}
else {
argString = argString.concat((String) o + ",");
}
}
argString = argString.substring(0, argString.length() - 1);
argString = argString.concat(")");
_logger.debug("Executing: '" + argString
+ "' with query '" + _queryString + "'");
//Create the prepared statement by passing in each argument of the
// vector
try {
for (int i = 0; i < args.size(); i++) {
Object o = args.elementAt(i);
if (o instanceof Integer) {
_pstmt.setInt(i + 1, ((Integer) o).intValue());
}
else {
_pstmt.setString(i + 1, (String) o);
}
}
int executeResult = _pstmt.executeUpdate();
_logger.debug("The execute updated returned '"
+ executeResult + "'");
return executeResult;
} catch (SQLException sqle) {
_logger.debug("Couldn't connect to database, or create PreparedStatement. "
+ sqle);
//sqle.printStackTrace();
return 0;
}
}
public String getDatabaseUrl() {
return _databaseUrl;
}
public String getDriverName() {
return _driverName;
}
public String getQueryString() {
return _queryString;
}
}
Next we define the listener.
Save the following code into WEB-INF/src/net.kvrlogs.antiPC.init as MyServletContextListener
package net.kvrlogs.antiPC.init;
import javax.servlet.*;
import net.kvrlogs.antiPC.utils.DBHandler;
public class MyServletContextListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent event){
ServletContext cx = event.getServletContext();
String _databaseUrl = cx.getInitParameter("dbDatabase");
String _driverName = cx.getInitParameter("dbDriver");
String _user = cx.getInitParameter("dbUsername");
String _password = cx.getInitParameter("dbPassword");
DBHandler dbHandler = new DBHandler(_driverName,_databaseUrl,_user,_password);
cx.setAttribute("dbhandle",dbHandler);
}
public void contextDestroyed (ServletContextEvent event){
}
}
For now I am skipping writing DAO pattern – this post is getting long. Will just go ahead and put the dbhandler calls inside login action servlet itself.
Now add the following lines into Login.java
ServletContext context = getServlet().getServletContext();
DBHandler db = (DBHandler)context.getAttribute("dbhandle");
LoginFormBean loginBean = (LoginFormBean) form;
String username = loginBean.getUsername();
String password = loginBean.getPassword();
String sql = "SELECT * FROM antiPC_users where user_name = '" + username + "'" +
" and user_password = '" + password +"'" ;
db.setQueryString(sql);
ResultSet rs = db.lookup();
if(rs != null && rs.next())
return mapping.findForward("success");
else
return mapping.findForward("failure");
Now I have added a new mapping called failure. Lets define it in struts-config.xml and we are all set.
Add this below or above success mapping.
<forward
name="failure"
path="/pages/login.jsp"/>
Save all. Build. Stop antiPC. Start antiPC. Go to browser. Enter tester,tester ( or whatever value you have given in user table ) – see if it goes to You are in page. Enter wrong values – see if it takes you back to login page again.
You are now free to move about struts.ting.
ps: If you need the war file email me to kvrlogs@gmail.com
0 to struts in 60 minutes
Steps to build a struts application
Struts has become a standard for web applications. It lets you fit your web application in a MVC framework – which earlier I had wrongly thought – MVC is only for desktop GUI clients.
What is MVC?
M-Model
V-View
C-Controller
Model deals with the data – JavaBeans containing the data.
View deals with how you want to display the model
Controller – How the model and view are to be controlled
Lets take the example of a login process.
Here is the scenario : User enters login name, password hits submit. The form data are sent to a servlet that talks to the database to see if the information is valid. If it is valid, it puts the name, role information in a javabean and sends the browser to a welcome page. If not it sends the browser an error page.
Now with MVC scenario. When the user hits submit, the control does not directly go to the servlet but to a controller servlet. The form values are put in a javabean ( model ). The controller’s main job is to see what the request is and finds the correct servlet to work on it – in this case the login servlet. The login servlet then authenticates and sends either of these two words : success or failure. Also if the authentication succeeds it puts the name,role information into a javabean ( the model ). The controller if it sees the return value as “success” sends the browser the welcome page ( view ) . The welcome page takes the value from the javabean ( model ) and displays it. You might be thinking what do we call the login servlet – the model or the controller.You might also think it lies somewhere between the model and the controller – but actually its the model. It just tells the controller where the logic flow should be going next – but it cannot change the logic flow by itself. Only the almighty controller has the power to change the logic flow.
What are the advantages of MVC?
* The control logic is in one place. Otherwise the servlets, jsps have fun throwing the logic between one another – and when it comes to maintenance mode its a headache when one has to follow the trial. Now if we have all the logic flows in one place its enough to go to that one place and see how things flow.
In Struts this logic flow, controller, is held in an xml file – struts-config.xml. Now you can play with the logic flow – lets say for some reason you do not want to show the welcome page but want to directly take the users to another page. You need not touch the servlets ( means compiling, testing etc. ) – vs – you touch the struts-config.xml file – make the change you want to do – and restart the server. ( no recompiling, testing is necessary yes – but need not worry about the servlets throwing any exception etc.. as we never touched the servlets ).
* Lets say you want to toss out the current JSP view and have a new design. All you have to make sure is you display the javabeans in the right places. The Model and controller need not be touched at all. Isnt it beautiful !!
* Lets say you change the model – for instance your application goes the EJB way. Authentication is not done with a simple servlet but by a complex session bean. As long as the session bean creates the same javabean with name,role – the View(JSP page) need not be touched at all.
* Lets say tomorrow a new version of Struts comes – or another alternative to Struts comes along – it has a new controller – instead of struts-config.xml file they change a couple of things. No problem your model, view never get affected.
What you need?
Tomcat
MySQL
Eclipse
A web browser
Coffee/Tea
Where to start?
Go to the wonderful Apache site.
http://struts.apache.org/download.cgi
Download the binaries. Unzip it.
There will be a directory called webapps. There is a nice little template file called struts-blank.war. Rename it as antiPC.war ( anti ProCrastinator – thats the name of our struts app – you can name it whatever you want )
Start tomcat.
Go to tomcat manager ( http://localhost:8080 ) and deploy this war file. Tomcat will neatly unzip the war file and deploy the application too. Click on the link for antiPC
http://localhost:8080/antiPC
You should be able to see the welcome message. If not try to fix the problem. Look at catalina.out to see if there is any error messages, missing class library anything.
And then?
Time to start working on our struts application.
Fire up eclipse.
If you havent installed sysdeo eclipse plugin its a good idea to install it. Google for sysdeo plugin and load it in. Its useful.
Start a new Java Tomcat project (antiPC) and make it point to webapps/antiPC directory.
PS: If its not windows OS – eclipse might start throwing tantrums saying file not found. In your terminal editor navigate to the directory where webapps is. See the ownership of antiPC directory – it might be root. So do a
sudo chown -R youruserid antiPC
build.xml – the oxygen for your struts app !! It cannot live without it.
In eclipse navigate to antiPC/WEB-INF/src/build.xml
The build.xml cleans,compiles,generates javadoc and creates war file for deploying. We can remove the last 2 steps for now.
So make these changes
from :
<!-- Build entire project -->
<target name="project" depends="clean,prepare,compile,javadoc"/>
to:
<!-- Build entire project -->
<target name="project" depends="clean,prepare,compile"/>
and line 1 :
from :
<project name="blank" basedir="../" default="all">
to:
<project name="blank" basedir="../" default="project">
Click on the WEB-INF/src directory and click on Run as ant build. It should say something like this :
Buildfile: /usr/local/tomcat/webapps/antiPC/WEB-INF/src/build.xml
clean:
[delete] Deleting directory /usr/local/tomcat/webapps/antiPC/WEB-INF/classes
[mkdir] Created dir: /usr/local/tomcat/webapps/antiPC/WEB-INF/classes
prepare:
resources:
[copy] Copying 1 file to /usr/local/tomcat/webapps/antiPC/WEB-INF/classes
compile:
project:
BUILD SUCCESSFUL
Total time: 4 seconds
And thennnnn?
You are all set to play with your struts application. Next I will explain how to set up a login page, connect to database, etc.
Exposé
I am a multi tasker. I have a minimum of 5 applications running simultaneously. Exposé offers a beautiful solution to manage this mess. It has spoilt me !! Now I open even more apps and do not bother to close them. Ah the power of OS X in managing memory. These are the applications I was running when I took this screen shot.
Eclipse
Mail
Safari
ITunes
2 Text documents
Terminal
CocoaMySQL
2 Finder windows
Now when I have to switch between apps all I have to do is click my middle mouse button or press F9 or move my mouse to the left most top corner ( my choice ) – and everything zooms out neatly to this ( see screenshot below) – and I just click on the window I need. Fast simple easy.
OS X started small. Jaguar did not have much fancy stuff other than the eye candy gui. With a solid foundation built with Jaguar, OS X engineers started innovating on top of it. Exposé was introduced in Panther. Things are just getting started. On April 29th Tiger is being released. It will be a while before I lay my hands on it. Things are going to get pretty interesting in macworld. Longhorn is no slouch and I am eagerly awaiting for it to hit the market soon. I recently read that the new KDE is having some new GUI effects – linux/GNU developers have caught up with the mainstream OSes( Windows,Solaris,OS X) and now have started innovating. It seems it has a wobbly effect whenever windows are minimized,maximized. Gotto try it out sometime.
There seems to be a shareware app called Winexpose but its not GPU driven – where the graphical processing unit (GPU) takes care of doing such calculations and zooming out the windows. So the movement is jerky and buggy – if CPU load gets high it does not perform as expected. When I am building my java project machine crawls ( its a G3 800Mhz laptop – and ofcourse – its a mac 😉 ) but when I hit expose button it zooms out as if the machine is just sitting bored doing nothing.
Bottomline when it comes to exposé and because of it the productivity levels I reach :
Mac – 1, Rest – 0.
Heres a screenshot.
Macs can do more
Starting today, I am going to write about how on a mac you can do more things. Of course you can do the same or even more in Windows – but you have to bend your back. I will examine each of the applications, tricks I enjoy in my mac – also to be fair will try to find equivalent application in windows and linux and review them.
Mac sets you free…!!
UML
UML is like the Grand Unified Theory (GUT) for physicists – only difference is UML has been developed and is being used everywhere. I started this book a few days ago and I am hooked. I will be telling you some of the interesting things I discover in this journey.
As a briefer : UML defines nine types of diagrams
1. Class diagrams
2. Sequence diagrams
3. Collaboration diagrams
4. Object diagrams
5. Statechart diagrams
6. Activity diagrams
7. Use case diagrams
8. Component diagrams
9. Deployment diagrams
Incredibles
I remember 1992 when I saw Terminator 2 – I thought this is it – we have reached the end of graphics and imagination – nothing else can be made which can top this. 12 years later I want to laugh at that kvr who watched Terminator in awe.
Sangeeta and I went to the 12.00am show on Nov 5th to see Incredibles. There was a sparse crowd so we could choose our seats. The movie rolled and it looked like some ordinary cartoon movie. The movements were smooth, animation was amazing but still nothing ground breaking. You have to wait till the jungle scenes to realize Pixar’s prowess. The leaves and greenery were real. I dont know if they used real shots and mixed it with the virtual scenery – or they recreated it. I gotto wait for the making of Incredibles DVD to know how they did it. The greenery was real. The rocket, the buildings – I had to keep telling myself this is all CGI and its not real. Thats for the graphics and animation.
The story, humour, conversation, the contradictions in character’s minds, the thrill and suspense – this movie can compete with the last second thrill of Indiana Jones, or any James Bond Movie. An intelligent well made movie. I loved it and lapped it up.
So whats bad about this movie? A little bit long – not that I noticed – but I had a kind of itch when the story would drag on the pitiable boring lifestyle of our ex-superhero. My mind was waiting on and on for the action scenes to begin – and when it did I forgave my mini-grudge with all my heart. There were 2 scenes which stand out when I think of the movie. The chase of the kid – who can run sooo fast – and the instant when our kid discovers he can run on water – wow. It was like that instant when I realized am riding the bicycle without any support. The second when our elastigirl gets into a complicated situation. Difficult to describe this sticky situation – am just wondering how the screenplayer, story writer and director sat and discussed this scene.
Incredibles will be in the top 100 movies of my list.