tag:blogger.com,1999:blog-40105577588032966472024-02-20T18:45:34.934-08:00Ashik's Techno Blogs...Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.comBlogger13125tag:blogger.com,1999:blog-4010557758803296647.post-14590201892054165132014-04-29T06:45:00.003-07:002014-05-14T23:46:35.847-07:00Angular Tricks and Tips, Do's and Dont's.<div dir="ltr" style="text-align: left;" trbidi="on">
Using Angular In my view<br />
<br />
Here I had listed some tips, do's, dont's to use in Angular based on my experience and some references from web, these are basically drafted for my own reference and I share this as it might be useful to others as well.<br />
<br />
When you write something in Angular it's very easy to start, perhaps a controller and directive will let you start things.<br />
<br />
But when you start write serious apps, the basics plays a larger role and your app can go easily wrong If the basics are not applied properly.<br />
<br />
A break in a $scope can cause you a serious performance problems.<br />
<br />
And when the application grows larger the separation of concerns is more important as your application<br />
should be maintainable.<br />
<br />
Some Tips Here<br />
<br />
<ul style="text-align: left;">
<li>Controller is not the place to manipulate your DOM, use directives.</li>
<li>Services can be used to share data between controllers instead of creating inherit scope.</li>
<li>Link function in Directive is to add behaviors to your extended element.</li>
<li>Having Directives allowed as 'A' attributes allows you to add more than one behavior and reusable.</li>
<li>Can use scope.$apply to invoke Controller methods from Directive link function.</li>
<li>Can talk to DirectiveA via adding that as 'require' inside the DirectiveB and access the ControllerA as fourth param of link function from DirectiveB.</li>
<li>Use angular.extend to get Controller Inheritance.</li>
<li>Scope is not your model - a binding between Controller and model.</li>
<li>Create sub scope when invoking sub controller from your controller.</li>
<li>Single change in $scope can invoke multiple functions watch out for performance.</li>
<li>Have a service layer to perform reusable tasks.</li>
<li>Control logic in directive controller and DOM logic in directive link function, glue with scope sharing.</li>
<li>Write $watch on a scope value to make directive react to changes. when value changes all $watche observing that element are executed.</li>
<li>If the code is outside of angular scope you have to call scope.$apply to trigger the update.</li>
<li>You should have a $scope.$apply() anywhere it fires a callback.</li>
<li>$scope.$watch should replace the need for events.</li>
<li>Directives are able to directly communicate with each other through directive controllers.</li>
<li>Extend directives by using directive controllers, and can place methods and properties there and can override the same.</li>
<li>Don't wrap element inside of $() as all Angular elements are already jq-objects.</li>
<li>If you find yourself triggering the '$apply already in progress' error while developing with Angular.JS (can happen when u trigger a lot of DOM events), you can use a 'safeApply' method that checks the current phase before executing your function.</li>
<li>A <b>must</b> read on $scope, understanding prototypical scope and scope inheritance in Angular -<a href="https://github.com/angular/angular.js/wiki/Understanding-Scopes"> https://github.com/angular/angular.js/wiki/Understanding-Scopes</a>.</li>
<li>Parent Scope: scope: false, [Default] so no new scope at all - This is also helpful for child directives that are only used in the context of the parent directive.</li>
<li>Child Scope: scope: true - Directives with a child scope are context-aware and are intended to interact with the current scope. </li>
<li>Isolate scope: scope: {} - This is for reusable components. - The intent is that they are to be used for a specific purpose, so combining them with other directives or adding other interpolated attributes to the DOM node inherently doesn't make sense . - To be more specific, anything needed for this standalone functionality is provided through specified attributes evaluated in the context of the parent scope; they are either one-way strings ('@'), one-way expressions ('&'), or two-way variable bindings ('=').</li>
<li><span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 17.804800033569336px;">AngularJS allows only one isolated scope per element.</span></li>
<li>angular.element wraps a raw DOM element or Html String as a JQuery element.</li>
<li>In keeping with the Angular way, most DOM manipulation and 2-way binding using $watchers is usually done in the link function while the API for children and $scope manipulation is done in the controller. This is not a hard and fast rule, but doing so will make the code more modular and help in separation of concerns (controller will maintain the directive state and link function will maintain the DOM + outside bindings).</li>
<li>Modularization - Modularize your modules by creating sub modules for a root module.. something like angular.module('myApp'); [myapp.sub1, myapp.sub2]);</li>
<li><div style="background-color: white; border: 0px; color: #222222; font-family: Arial, Helvetica, sans-serif; font-size: 13px; margin: 0px; padding: 0px; vertical-align: baseline;">
NgModelController provides an API for the ng-model directive. It specifically does not contain any logic which deals with DOM rendering or listening to DOM events. A use case for using NgModelController would be when you need to build a control other than the standard input, select, textarea, etc. In that case NgModelController is extended or complemented by the new directive. This is done by requiring ngModel in the new directive and accessing its methods through ngModel as in “ngModel.$render().” New directives using this approach provide DOM manipulation and the NgModelController methods are used to implement data-binding and data validation. </div>
<div style="background-color: white; border: 0px; color: #222222; font-family: Arial, Helvetica, sans-serif; font-size: 13px; margin: 0px; padding: 0px; vertical-align: baseline;">
<br /></div>
<div style="background-color: white; border: 0px; color: #222222; font-family: Arial, Helvetica, sans-serif; font-size: 13px; margin: 0px; padding: 0px; vertical-align: baseline;">
Interacting with NgModelController involves three related values: </div>
<div style="background-color: white; border: 0px; color: #222222; font-family: Arial, Helvetica, sans-serif; font-size: 13px; margin: 0px; padding: 0px; vertical-align: baseline;">
<ol>
<li style="line-height: 17px;">The value of an element within the DOM such as element.html().</li>
<li style="line-height: 17px;">$viewValue which is the NgModelController representation of the value in the DOM (which may differ from the actual DOM value). </li>
<li style="line-height: 17px;">$modelValue which is the NgModelController representation of the model’s data (which may differ from $viewValue).</li>
</ol>
<div>
<span style="line-height: 17px;"><br /></span></div>
</div>
</li>
<li><div>
<span style="line-height: 17px;">Transclusion - the </span><span style="background-color: white; box-sizing: border-box; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14.399999618530273px; font-weight: 700; line-height: 16px;">contents</span><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14.399999618530273px; line-height: 16px;"> of a transcluded directive will have whatever scope is in the outside of the </span><span style="background-color: white;"><span style="color: #333333; font-family: Helvetica Neue, Helvetica, Arial, sans-serif;"><span style="font-size: 14.399999618530273px; line-height: 16px;">directive, and use transclusion when u want to use </span><span style="font-size: 14px; line-height: 16px;">arbitrary</span><span style="font-size: 14.399999618530273px; line-height: 16px;"> contents inside and have the transclusion as a wrapper that adds some common </span><span style="font-size: 14px; line-height: 16px;">behavior</span><span style="font-size: 14.399999618530273px; line-height: 16px;"> to the </span><span style="font-size: 14px; line-height: 16px;">arbitrary</span><span style="font-size: 14.399999618530273px; line-height: 16px;"> content.</span></span></span></div>
</li>
<li><span style="background-color: white;"><span style="color: #333333; font-family: Helvetica Neue, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 16px;">Transclusion requires the context of the scope tree, not the isolated scope of the directive. Because of that, transcluded content scope is not the child of the directive scope, it is instead a child of the directives parent scope, effectively making it like the directive scope does not exist in the inheritance chain.</span></span></li>
<li><span style="background-color: white;"><span style="color: #333333; font-family: Helvetica Neue, Helvetica, Arial, sans-serif;"><span style="font-size: 14px; line-height: 16px;">When you have to deal with some Html DOM/Node level manipulations before using a plain JS be conscious about the angular provided JQlite api functions. Have a look here. </span></span></span><span style="color: #333333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 16px;"><a href="https://gist.github.com/esfand/9638882">https://gist.github.com/esfand/9638882</a></span></li>
</ul>
<div>
<span style="color: #333333; font-family: Helvetica Neue, Helvetica, Arial, sans-serif;"><span style="font-size: 14px; line-height: 16px;"><br /></span></span></div>
</div>
Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-48051032280125172292011-11-10T01:57:00.000-08:002014-04-29T06:45:47.716-07:00ANT- 'upToDate' to rescue build time overhead<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="font-family: inherit;">
I kind of performing a refactor on ANT Build.,<br />
<br />
Goals in mind - <br />
<br />
* Do not run ANT targets unnecessarly.<br />
* Make build to get pass ant-clean test. (If there are no code changes and when you run ANT build nothing should happen.)<br />
<br />
Observed - <br />
<br />
* ANT was running unnecessarly for TARGETS that doesn't have code changes.<br />
Say the build contains <br />
3 targets - <br />
<br />
1. CodeGen - Do's, Sql's, Ejb's, Annotations<br />
2. Compile - Javac<br />
3. Package - Jar<br />
<br />
1. Target CodeGen It contains child targets GenDo's , GenSql's, GenEjb's, GenAnnotation's - ANT doesn't know when to run and not.<br />
2. Compile - Javac determines code changes and run on those files alone, thus reduces the build time.<br />
3. Package - Jar, Ant runs Jar target only If compiled set of files are modified, thus skips the unnecessary runs.<br />
<br />
A build with out refactor will try to run all the Code gen targets irrespect of Code changes, thus increases build time.<br />
<br />
So we need a way to tell ANT Skip Targets If there are no Code Changes made.<br />
<br />
ANT provides a mechanism called UP-TO-DATE<br />
<br />
It Sets a property if a target file or set of target files is more up-to-date than a source file or set of source files.<br />
A single source file is specified using the srcfile attribute.<br />
A set of source files is specified using the nested <srcfiles> elements. These are FileSets, whereas multiple target files are specified using a nested <mapper> element.<br />
<br />
By default, the value of the property is set to true if the timestamp of the target file(s) is more recent than the timestamp of the corresponding source file(s). You can set the value to something other than the default by specifying the value attribute.<br />
<br />
If a <srcfiles> element is used, without also specifying a <mapper> element, the default behavior is to use a merge mapper, with the to attribute set to the value of the targetfile attribute.<br />
<br />
Normally, this task is used to set properties that are useful to avoid target execution depending on the relative age of the specified files.<br />
<br />
For Example - <br />
<br />
If you need to do an xml build only If any changes made to DTD files, you can do this by., checking the timestamp of the final output.<br />
<br />
This can be written as:<br />
<br />
<uptodate property="xmlBuild.notRequired"><br />
<srcfiles dir= "${src}/xml" includes="**/*.dtd"/><br />
<mapper type="merge" to="${deploy}\xmlClasses.jar"/><br />
</uptodate><br />
<br />
sets the property xmlBuild.notRequired to true if the ${deploy}/xmlClasses.jar file is more up-to-date than any of the DTD files in the ${src}/xml directory.<br />
<br />
The xmlBuild.notRequired property can then be used in a <target> tag's unless attribute to conditionally run that target. For example, running the following target:<br />
<br />
<target name="xmlBuild" depends="chkXmlBuild" unless="xmlBuild.notRequired"><br />
...<br />
</target><br />
<br />
will first run the chkXmlBuild target, which contains the <uptodate> task that determines whether xmlBuild.notRequired gets set. The property named in the unless attribute is then checked for being set/not set. If it did get set (ie., the jar file is up-to-date), then the xmlBuild target won't be run. <br />
<br />
Courtesy - http://www.jajakarta.org/ant/ant-1.6.1/docs/en/manual/CoreTasks/uptodate.html<br />
<br />
Thus you can Skip unnecessary Targets, and reduces build time a lot.<br />
<br />
</div>
</div>
Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-9264208121713006172011-07-11T02:25:00.000-07:002011-07-11T02:29:17.221-07:00Html Select width problem in IE : workaround using JQuerySetting the fixed width of a <select> element in Internet Explorer will cause all of the select options that are wider than the select's set width to be cropped.<br />
<br />
If you set a static width on the <select> element, any <option> elements that are wider get cut of in IE. There is no good CSS solution for this.<br />
<br />
So here are some workarounds to rsolve the problem.<br />
<br />
Keep the static width on the select element by default, but when moused over in IE, change the width to "auto". And then put it back to static width when moused out. <br />
<br />
<blockquote style="background-color: #93c47d;">onmouseover="autoWidth(this)" <br />
onblur="resetWidth(this)" </blockquote>So when a user clicks on the select the width will automatically expand, and user moves out of the select box, the width will be reset to original.<br />
<br />
But the problem with this approach is the original size of the select box is not maintained and the width of the box expands, thus causes the page look and feel collapse.<br />
<br />
It has been tackled with JavaScript a number of ways. but nothing suits to the problem.<br />
<br />
So tried in a jQuery way of solving the Problem.<br />
<br />
<blockquote style="background-color: #93c47d;">$(function() {<br />
<br />
if(browser.isIE){ <br />
$("#select")<br />
.mouseover(function(){<br />
$(this)<br />
.data("origWidth", $(this).css("width"))<br />
.css("width", "auto");<br />
})<br />
.mouseout(function(){<br />
$(this).css("width", $(this).data("origWidth"));<br />
}); <br />
}<br />
});</blockquote><br />
1. When mouse pointer goes over select element, keep track of the original width, then set it to "auto".<br />
<br />
2. When mouse pointer leaves, set width back to original width.<br />
<br />
But again the problem of page collapse and not consistent...oops!<br />
<br />
There is a jQuery plugin proposes a work around.<br />
<br />
<a href="http://www.jainaewen.com/files/javascript/jquery/ie-select-width/">http://www.jainaewen.com/files/javascript/jquery/ie-select-width/</a><br />
<br />
Download this Plugin and add it to your class path, and call the function.. thats it.. <br />
<br />
<blockquote style="background-color: #93c47d;"><script type="text/javascript" src="/js/jquery/plugins/jquery.ie-select-width.min.js">&#xa0;</script><br />
<script type="text/javascript" language="JavaScript"><br />
//Workaround for IE html select issue(Width of the dropdown is not expanding w.r.t to the content width.)<br />
var $j = jQuery.noConflict();<br />
$j(document).ready(function() {<br />
if (browser.isIE) {<br />
$j('select[name*=xxxxx]').ieSelectWidth({});<br />
}<br />
});<br />
</script></blockquote><br />
Applying this plugin makes the select element in Internet Explorer appear to work as it would work in Firefox, Opera etc...<br />
<br />
However I noticed this works exactly as FF on IE versions 8 & 9, but in 6 & 7 the width of the dropdown also expands to the max width of the options but solves the Problem.Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com1tag:blogger.com,1999:blog-4010557758803296647.post-89152242215843046602011-07-11T00:51:00.000-07:002011-07-11T02:01:16.311-07:00Struts Custom tag to support JSTL functionsThe Struts Taglib component provides a set of JSP custom tag libraries that help developers create interactive form-based applications. There are tags to help with everything from displaying error messages to dealing with nested ActionForm beans.<br />
<br />
Struts Taglib is composed of four distinct tag libraries: Bean, HTML, Logic, and Nested.<br />
Bean : The bean tags are useful in defining new beans (in any scope) from a variety of possible sources, as well as a tag to render a particular bean (or bean property) to the output response.<br />
HTML : The HTML tags are used to create input forms, as well as other tags generally useful in the creation of HTML-based user interfaces. The output is HTML 4.01 compliant or XHTML 1.0 when in XHTML mode.<br />
Logic : The Logic tags that are useful in managing conditional generation of output text, looping over object collections for repetitive generation of output text, and application flow management<br />
Nested : The Nested tags extend the base Struts tags to allow them to relate to each other in a nested nature. The fundamental logic of the original tags doesn't change, except in that all references to beans and bean properties will be managed in a nested context. <br />
<br />
A 'custom' tag is something which a browser will not understand, because it is not a pre-defined HTML tag. you need to write/tell the browser so that it understands what it is supposed to do. <br />
A custom tag, is thus, a tag, you write in your Java Server Page. When the jsp compiler encounters that tag, it knows what to do with it. It generates the proper HTML after doing whatever it is supposed to do.<br />
<br />
Custom Tag<br />
<br />
1. Write the tag handler class.<br />
2. Create the tag library descriptor (TLD).<br />
3. Make the TLD file and handler classes accessible.<br />
4. Reference the tag library.<br />
5. Use the tag in a JSP page. <br />
<br />
* Tag Handlers implement one of three interfaces:<br />
<br />
1. Tag<br />
Implement the javax.servlet.jsp.tagext.Tag interface if you are creating a custom tag that does not need access to its interface. The API also provides a convenience class TagSupport that implements the Tag interface and provides default empty methods for the methods defined in the interface.<br />
<br />
2. BodyTag<br />
Implement the javax.servlet.jsp.tagext.BodyTag interface if your custom tag needs to use a body. The API also provides a convenience class BodyTagSupport that implements the BodyTag interface and provides default empty methods for the methods defined in the interface. Because BodyTag extends Tag it is a super set of the interface methods.<br />
<br />
3. IterationTag<br />
Implement the javax.servlet.jsp.tagext.IterationTag interface to extend Tag by defining an additional method doAfterBody() that controls the reevaluation of the body.<br />
Sample Java program implements Custom Tag.<br />
<blockquote style="background-color: #93c47d;">package com.xyz.CustomTag;<br />
<br />
import java.io.IOException;<br />
import java.text.SimpleDateFormat;<br />
import java.util.Date;<br />
<br />
import javax.servlet.jsp.JspException;<br />
import javax.servlet.jsp.JspWriter;<br />
import javax.servlet.jsp.PageContext;<br />
import javax.servlet.jsp.tagext.BodyContent;<br />
import javax.servlet.jsp.tagext.BodyTag;<br />
import javax.servlet.jsp.tagext.Tag;<br />
<br />
public class DateTag implements BodyTag {<br />
private PageContext pc = null;<br />
private Tag parent = null;<br />
private BodyContent bd = null;<br />
private boolean showInUpperCase;<br />
<br />
public boolean isShowInUpperCase() {<br />
return showInUpperCase;<br />
}<br />
<br />
public void setShowInUpperCase(boolean showInUpperCase) {<br />
this.showInUpperCase = showInUpperCase;<br />
}<br />
<br />
@Override<br />
public void doInitBody() throws JspException {<br />
<br />
}<br />
<br />
@Override<br />
public void setBodyContent(BodyContent arg0) {<br />
bd = arg0;<br />
<br />
}<br />
<br />
@Override<br />
public int doAfterBody() {<br />
try {<br />
String bodyString = bd.getString();<br />
JspWriter out = bd.getEnclosingWriter();<br />
SimpleDateFormat simDate = new SimpleDateFormat("dd-MM-yyyy");<br />
out.print((bodyString + simDate.format(new Date())).toUpperCase());<br />
<br />
bd.clear(); // empty buffer for next evaluation<br />
} catch (IOException e) {<br />
System.out.println("Error in BodyContentTag.doAfterBody()" + e.getMessage());<br />
<br />
} // end of catch<br />
<br />
return SKIP_BODY;<br />
}<br />
<br />
@Override<br />
public int doEndTag() throws JspException {<br />
return EVAL_PAGE;<br />
}<br />
<br />
@Override<br />
public int doStartTag() throws JspException {<br />
return EVAL_BODY_AGAIN;<br />
}<br />
<br />
@Override<br />
public Tag getParent() {<br />
return parent;<br />
}<br />
<br />
@Override<br />
public void release() {<br />
pc = null;<br />
parent = null;<br />
<br />
}<br />
<br />
@Override<br />
public void setPageContext(PageContext arg0) {<br />
pc = arg0;<br />
}<br />
<br />
@Override<br />
public void setParent(Tag arg0) {<br />
parent = arg0;<br />
}<br />
<br />
}</blockquote> <br />
* Write a tag library descriptor.<br />
<br />
that will contain the mappings between our custom tag and the Java class (or classes) that will handle it. This library is defined within an XML document called a tag library descriptor (TLD). We'll call the TLD for our DateTag example DateTagLib.tld. Note that ".tld" is the standard extension for such files.<br />
<blockquote style="background-color: #93c47d;"> <?xml version="1.0" encoding="ISO-8859-1" ?><br />
<taglib><br />
<tlibversion>1.0</tlibversion><br />
<info>A simple tag library</info><br />
<br />
<tag><br />
<name>displayDate</name><br />
<tagclass>myTags.DateTag</tagclass><br />
<bodycontent>empty</bodycontent><br />
<info>Display Date</info><br />
</tag> <br />
</taglib></blockquote> Although not mandatory, the TLD file is usually placed under WEB-INF/tlds/.<br />
<br />
* Reference the Library <br />
<br />
* Declare the TLD in the web.xml file. web.xml is the standard descriptor file for any web application. The TLD is declared like:<br />
<blockquote style="background-color: #93c47d;"> <webapp><br />
....<br />
<taglib><br />
<taglib-uri>DateTag</taglib-uri><br />
<taglib-location>/WEB-INF/tlds/DateTagLib.tld</taglib-location><br />
</taglib><br />
</webapp></blockquote>Using a Custom Tag in your page<br />
<br />
Now that the custom tag is built and deployed, it can be used in a JSP by:<br />
* Declaring the TLD as a directive:<br />
<blockquote><blockquote><span style="background-color: #93c47d;"> <@ taglib uri="DateTag" prefix="test" %></span></blockquote></blockquote> * Using a tag "displayDate", which is a part of the TLD, in the JSP page:<br />
<br />
<blockquote style="background-color: #93c47d;"> <test:displayDate attr1="value1" attr2="value2" /></blockquote>As soon as the runtime encounters the "test" tag, it knows a tag in that library is going to be used. In the above example, "displayDate" in the library is used. When the runtime parses "<test:displayDate", the method of "displayDate" is triggered.<br />
<br />
<b>JSTL Functions <br />
</b>JSTL functions are supported from version 1.1.<br />
<br />
If you are in version 1.0 here is the steps to upgrade.<br />
<br />
1. Reference the correct servlet specification in your deployment descriptor:<br />
<br />
<blockquote><blockquote><span style="background-color: #93c47d;"> <?xml version="1.0"?></span><br />
<span style="background-color: #93c47d;"> <web-app version="2.4"</span><br />
<span style="background-color: #93c47d;"> xmlns="http://java.sun.com/xml/ns/j2ee"</span><br />
<span style="background-color: #93c47d;"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</span><br />
<span style="background-color: #93c47d;"> xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"></span></blockquote></blockquote> 2. Reference the correct JSTL uri in your JSP:<br />
change<br />
<br />
<blockquote style="background-color: #93c47d;"> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c'%><br />
to<br />
<%@ taglib uri='http://java.sun.com/jsp/jstl/core' prefix='c'%></blockquote><br />
And ofcourse upgrade the JSTL jar in your class path.<br />
<br />
The JSTL 1.1. library provides inbuilt function support .. refer <a href="http://download.oracle.com/docs/cd/E17802_01/products/products/jsp/jstl/1.1/docs/tlddocs/fn/tld-summary.html">http://download.oracle.com/docs/cd/E17802_01/products/products/jsp/jstl/1.1/docs/tlddocs/fn/tld-summary.html</a><br />
<br />
<blockquote style="background-color: #93c47d;"><%@ taglib uri="http://java.sun.com/jsp/jstl/core"<br />
prefix="c" %><br />
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions"<br />
prefix="fn" %><br />
<html><br />
<body><br />
<c:if test="${fn:length(param.username) > 0}" ><br />
<%@include file="response.jsp" %><br />
</c:if><br />
</body><br />
</html></blockquote><br />
Use a Custom Tag with function support from Apache commons-lang.<br />
Defining a Tag function in a web app<br />
<br />
The TLD File<br />
<br />
<blockquote style="background-color: #93c47d;"><!-- WEB-INF/tld/commons-lang.tld --><br />
<?xml version="1.0" encoding="UTF-8" ?><br />
<taglib xmlns="http://java.sun.com/xml/ns/javaee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"<br />
version="2.1"><br />
<description>Tags used escape characters</description><br />
<display-name>escape tags</display-name><br />
<tlib-version>1.0</tlib-version><br />
<short-name>escape</short-name><br />
<uri>http://commons.apache.org/lang/StringEscapeUtils</uri><br />
<function><br />
<description>Escapes the characters in a String using HTML entities.</description><br />
<name>html</name><br />
<function-class>org.apache.commons.lang.StringEscapeUtils</function-class><br />
<function-signature>java.lang.String escapeHtml(java.lang.String)</function-signature><br />
</function><br />
<function><br />
<description>Escapes the characters in a String using JavaScript String rules.</description><br />
<name>javaScript</name><br />
<function-class>org.apache.commons.lang.StringEscapeUtils</function-class><br />
<function-signature>java.lang.String escapeJavaScript(java.lang.String)</function-signature><br />
</function><br />
<function><br />
<description>Escapes the characters in a String using XML entities.</description><br />
<name>xml</name><br />
<function-class>org.apache.commons.lang.StringEscapeUtils</function-class><br />
<function-signature>java.lang.String escapeXml(java.lang.String)</function-signature><br />
</function><br />
</taglib></blockquote><br />
The JSP<br />
<br />
Somewhere near the top of the file.<br />
<br />
<blockquote style="background-color: #93c47d;"><%@ taglib prefix="lang" uri="/WEB-INF/tld/commons-lang.tld"%><br />
<br />
...</blockquote><br />
// example of escaping for javascript<br />
<br />
<blockquote style="background-color: #93c47d;">img.description = '${escape:javaScript(xxxxx)}';</blockquote><br />
// example of escaping an html attribute<br />
<br />
<blockquote style="background-color: #93c47d;">title="${escape:html(pic.description)} ${escape:html(xxxxxx)}"<br />
... /></blockquote>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-74068862971874334312011-07-01T05:59:00.000-07:002011-07-01T05:59:39.128-07:00Struts html:form with focus element creates JS error on IE<div dir="ltr" style="text-align: left;" trbidi="on">The built in struts <html:form ... focus="[elementname]"> functionality automatically generates this X-browser <br />
javascript.<br />
<br />
you can see this on your browser at the end of form element.<br />
<br />
<script type="text/javascript" language="JavaScript"><br />
<!--<br />
var focusControl = document.forms["component"].elements["contents"];<br />
if (focusControl.type != "hidden") {<br />
focusControl.focus();<br />
}<br />
// --><br />
</script> <br />
<br />
With this, Internet Explorer (version 6, 7 and 8) generates the following error:<br />
<br />
"Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."<br />
<br />
The problem seems to be that for some very odd reason the javascript is being called before the element is available.<br />
<br />
So, to comeout of this, I had removed the struts generated focus field(form tag) and added a own script., below the form with a try/catch. <br />
<br />
Something like this.. <br />
<br />
</html:form><br />
-<br />
+<script type="text/javascript" language="JavaScript"><br />
+<!--<br />
+ var focusControl = document.forms["component"].elements['<c:out value="${focus}"/>'];<br />
+ try{<br />
+ if (focusControl.type != "hidden")<br />
+ focusControl.focus();<br />
+ }catch(er){<br />
+ //Fix for IE focus problem.<br />
+ }<br />
+//--><br />
+</script><br />
<br />
Now the error goes off...<br />
</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-70808940690984899462011-06-14T05:01:00.000-07:002011-06-14T05:06:09.317-07:00Slowly Changing dimension (type - 2) on Pentaho Kettle<div dir="ltr" style="text-align: left;" trbidi="on">Recently a'm in a need to use SCD Type -2 on the datamart design, and kettle spoon as the tool...<br />
<br />
<span style="background-color: #cccccc;">SCD management methodologies referred to as Type 0 through 6. Type 6 SCDs are also sometimes called Hybrid SCDs.</span><br />
<br />
<span style="background-color: #cccccc;">The Type 2 method tracks historical data by creating multiple records for a given natural key in the dimensional tables with separate surrogate keys and/or different version numbers. With Type 2, we have unlimited history preservation as a new record is inserted each time a change is made.</span><br />
Courtesy : <a href="http://en.wikipedia.org/wiki/Slowly_changing_dimension">http://en.wikipedia.org/wiki/Slowly_changing_dimension</a><br />
<br />
For example, take an employee table, if the employee moves to other company, the table could look like this, with incremented version numbers to indicate the sequence of changes:<br />
<br />
emp_key | name | emp_code | company | version <br />
---------+-------+----------+-----------------------------+---------<br />
1 | Ashik | emp01 | WhiteHouseBusinessSolutions | 0<br />
2 | Ashik | emp01 | NetEd Learning Solutions | 1<br />
3 | Ashik | emp01 | MindTree Ltd | 2<br />
4 | Ashik | emp01 | CollabNet Inc | 3<br />
<br />
In Pentaho, Lets see how.,<br />
I created two tables Employee and the Employee_detail., <br />
<br />
select * from employee;<br />
emp_key | emp_id<br />
---------+--------<br />
1 | emp01 <br />
<br />
select* from employee_detail;<br />
emp_detail_key | name | code | company | version | date_from | date_to<br />
----------------+------+------+---------+---------+-----------+---------<br />
(0 rows)<br />
<br />
Pentaho Will read the emp_id from the eployee table by a TableInput step, and I add the remaining fields as constants for the sake of this sample., and add Dimension Lookup/Update step from kettle to perform SCD -Type2<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_vgRyapBWUwKJh6CSNswsvxrqJi43T5rPR7NTtCQIHZLQOfOVlnN6hjZKffZibCBztj85Tx5O6OZWBn4FMl2KjwBHMUn5Fdp7NLFc-cZOL9AcuNBINpZ9iHC7UC4ZmnpAcZ974J_YhwRC/s1600/Screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="258" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_vgRyapBWUwKJh6CSNswsvxrqJi43T5rPR7NTtCQIHZLQOfOVlnN6hjZKffZibCBztj85Tx5O6OZWBn4FMl2KjwBHMUn5Fdp7NLFc-cZOL9AcuNBINpZ9iHC7UC4ZmnpAcZ974J_YhwRC/s640/Screenshot.png" width="640" /></a></div><br />
<br />
<br />
<br />
I just read the emp_id from employee using TableInput step.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY1IKBRYlk5v9PR9od3ukr_gTDaxAQJ5eEgUw411GiE4gePOWlETjhDt3qXKFJlg_wKAxZnAu0yHovia2n4mJpjFP3hoHZErS0URsfB5A-bTAorybPRiz1xv-sjuwQNd6DyCBbzdaHxpG7/s1600/Screenshot-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="254" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY1IKBRYlk5v9PR9od3ukr_gTDaxAQJ5eEgUw411GiE4gePOWlETjhDt3qXKFJlg_wKAxZnAu0yHovia2n4mJpjFP3hoHZErS0URsfB5A-bTAorybPRiz1xv-sjuwQNd6DyCBbzdaHxpG7/s640/Screenshot-1.png" width="640" /></a></div><br />
<br />
and am trying to change the employee company name for every run.,<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKWuqGLyzjSNRY9_tRFIEaZoJXFeKbuPDnswr9aPmksEw0eY7xTuqRq-1hah60b9Ac19dwtoAswpQPztcIU8FTg_sCqHJvH6j1YGDndsMlYois0ZwRYXAFiz6MpSFXTpZK8_Rbb1czv7SP/s1600/Screenshot-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="254" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKWuqGLyzjSNRY9_tRFIEaZoJXFeKbuPDnswr9aPmksEw0eY7xTuqRq-1hah60b9Ac19dwtoAswpQPztcIU8FTg_sCqHJvH6j1YGDndsMlYois0ZwRYXAFiz6MpSFXTpZK8_Rbb1czv7SP/s640/Screenshot-2.png" width="640" /></a></div><br />
the Dimension Lookup/Update step used to do the SCD here, this componenet requires the date range fields and the version field on the SCD table. and I had specified emp_id as the lookup field.<br />
<br />
and it changes the version accordingly., i.e whenever a change in the company pentaho creates a new version and the older ones remain as versioned.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwrbjLBODqPJNRz1M7PbcHyBeNZsR-xXqbWxp6MDexrWFO_LL3-fWn-yfNvbNNh5AbLgatrge7XTdsXAiN6V9z6R_aSiniIoJsKQwKY0ccIm2dwzowmZCi8G5c7Iy3vV74ydM7DOFICZo7/s1600/Screenshot-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="574" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwrbjLBODqPJNRz1M7PbcHyBeNZsR-xXqbWxp6MDexrWFO_LL3-fWn-yfNvbNNh5AbLgatrge7XTdsXAiN6V9z6R_aSiniIoJsKQwKY0ccIm2dwzowmZCi8G5c7Iy3vV74ydM7DOFICZo7/s640/Screenshot-3.png" width="640" /></a></div><br />
<br />
and I had given the "type of update" as update for name and insert for company., thus name will get update if there any change in same and the company get versioned upon change.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBnVmSCYEiHuxiEH46nxGbpcN6cyxaaGpV84DeILlh_3cWjGm-PavosEbgWHZWycqaTo7GC9A3VOL9YhxcOMqTSRcTLze9RVh4r-DxFRaysn5SCgg4PIUOdeFJ8AxDfVVwyGp1lapS1gEo/s1600/Screenshot-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBnVmSCYEiHuxiEH46nxGbpcN6cyxaaGpV84DeILlh_3cWjGm-PavosEbgWHZWycqaTo7GC9A3VOL9YhxcOMqTSRcTLze9RVh4r-DxFRaysn5SCgg4PIUOdeFJ8AxDfVVwyGp1lapS1gEo/s640/Screenshot-4.png" width="640" /></a></div><br />
<br />
So now the setup is ready to perform SCD., lets run the transformation.<br />
<br />
After the first run...<br />
<br />
select* from employee_detail;<br />
emp_detail_key | name | code | company | version | date_from | date_to<br />
----------------+-------+-------+-----------------------------+---------+------------+------------<br />
0 | | | | 1 | |<br />
1 | Ashik | emp01 | WhiteHouseBusinessSolutions | 1 | 2011-06-17 | 2199-12-31<br />
<br />
Again am changing the company name..<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnVoYsLmOH1qBhy_ANINWj_fFur5n5dBLrdmNmAuRyoYaPNRlnA9B2N5k8hGkjsJEJufuNZNBrsptnBxmY0oXzt7vFTC1GwOodEwDcbjwjbzzHjC8gsCmHlngJHbOq1cl0SJsOpEHoR0Tn/s1600/Screenshot-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="252" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnVoYsLmOH1qBhy_ANINWj_fFur5n5dBLrdmNmAuRyoYaPNRlnA9B2N5k8hGkjsJEJufuNZNBrsptnBxmY0oXzt7vFTC1GwOodEwDcbjwjbzzHjC8gsCmHlngJHbOq1cl0SJsOpEHoR0Tn/s640/Screenshot-5.png" width="640" /></a></div><br />
now lets see the table..,<br />
<br />
select* from employee_detail;<br />
emp_detail_key | name | code | company | version | date_from | date_to<br />
----------------+-------+-------+-----------------------------+---------+------------+------------<br />
0 | | | | 1 | |<br />
2 | Ashik | emp01 | NetEd Learning Solutions | 2 | 2011-06-17 | 2199-12-31<br />
1 | Ashik | emp01 | WhiteHouseBusinessSolutions | 1 | 2011-06-17 | 2011-06-17<br />
<br />
You can see the version update.. on change of company name.. after several runs..<br />
<br />
select* from employee_detail;<br />
emp_detail_key | name | code | company | version | date_from | date_to<br />
----------------+-------+-------+-----------------------------+---------+------------+------------<br />
0 | | | | 1 | |<br />
1 | Ashik | emp01 | WhiteHouseBusinessSolutions | 1 | 2011-06-17 | 2011-06-17<br />
3 | Ashik | emp01 | MindTree Ltd | 3 | 2011-06-17 | 2199-12-31<br />
2 | Ashik | emp01 | NetEd Learning Solutions | 2 | 2011-06-17 | 2011-06-17<br />
<br />
select* from employee_detail;<br />
emp_detail_key | name | code | company | version | date_from | date_to<br />
----------------+-------+-------+-----------------------------+---------+------------+------------<br />
0 | | | | 1 | |<br />
1 | Ashik | emp01 | WhiteHouseBusinessSolutions | 1 | 2011-06-17 | 2011-06-17<br />
2 | Ashik | emp01 | NetEd Learning Solutions | 2 | 2011-06-17 | 2011-06-17<br />
4 | Ashik | emp01 | CollabNet Inc | 4 | 2011-06-17 | 2199-12-31<br />
3 | Ashik | emp01 | MindTree Ltd | 3 | 2011-06-17 | 2011-06-17<br />
<br />
Thats it.., to get the latest version or the current record.,<br />
<br />
select * from employee_detail where name = 'Ashik' and now() between date_from and date_to;<br />
emp_detail_key | name | code | company | version | date_from | date_to<br />
----------------+-------+-------+---------------+---------+------------+------------<br />
4 | Ashik | emp01 | CollabNet Inc | 4 | 2011-06-17 | 2199-12-31<br />
<br />
<br />
<br />
</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com4tag:blogger.com,1999:blog-4010557758803296647.post-54285524548253497542011-05-18T02:57:00.000-07:002011-05-18T04:04:30.937-07:00AutoIncrement sequences in Oracle/Postgres/MySql<div dir="ltr" style="text-align: left;" trbidi="on">Came across a situation, where I need to perform Auto insert on a Table's column in a sequential key order, and that too be compatible with Oracle, Postgres and MySql.<br />
<br />
And here is how the comparision goes....,<br />
<br />
Ofcourse we can use TableMax + 1, but the overhead is the performance...So decided to go with Pseudo columns..<br />
<br />
Pseudocolumn behaves like a table column, but is not actually stored in the table. You can select from pseudocolumns, but you cannot insert, update, or delete their values.<br />
Thus suggests the use of Sequences., that can generate unique sequential values. These values are often used for primary and unique keys. You can refer to sequence values in SQL statements with these pseudocolumns:<br />
<br />
* CURRVAL: Returns the current value of a sequence<br />
* NEXTVAL: Increments the sequence and returns the next value<br />
<br />
A sequence can be accessed by many users concurrently with no waiting or locking.<br />
<br />
Create Sequence:<br />
<br />
<span style="background-color: #b6d7a8;">CREATE SEQUENCE sequence_name</span><br />
<span style="background-color: #b6d7a8;"> MINVALUE value</span><br />
<span style="background-color: #b6d7a8;"> MAXVALUE value</span><br />
<span style="background-color: #b6d7a8;"> START WITH value</span><br />
<span style="background-color: #b6d7a8;"> INCREMENT BY value</span><br />
<span style="background-color: #b6d7a8;"> CACHE value;</span><br />
<br />
Sequences are independant of any table and can be used to keep a value unique across a number of tables.<br />
<br />
MySQL doesn't currently support sequences. There is no sequence number generator (CREATE SEQUENCE) in MySQL. However it does have an auto increment value which can be applied to a primary key of a table. This is done during the table creation.<br />
<br />
<span style="background-color: #b6d7a8;">mysql> create table seq_test</span><span style="background-color: #b6d7a8;"> (id int primary key auto_increment</span><span style="background-color: #b6d7a8;">, name varchar(21));</span><br />
<span style="background-color: #b6d7a8;"></span><br />
As the name suggests the value is assigned automatically, this is in contrast to Oracle where we have to call the nextval function of the sequence to return the value when we need it.<br />
<br />
In Postgres you can use type SERIAL during table creation which is similar to AUTO_INCREMENT in MySQL.<br />
<br />
<span style="background-color: #b6d7a8;">pgsql=> CREATE TABLE seq_test (id SERIAL PRIMARY KEY);</span><br />
<br />
Is just shorthand notation for:<br />
<br />
<span style="background-color: #b6d7a8;">pgsql=> CREATE SEQUENCE XXX START 1;</span><br />
<span style="background-color: #b6d7a8;">pgsql=> CREATE TABLE seq_test (id integer PRIMARY KEY DEFAULT nextval('XXX'));</span><br />
<br />
Well the shorthand notation has a minor gotcha: you cannot drop the sequence that has been created automatically. You need to drop the column itself. <br />
Thats the difference between a SERIAL PRIMARY KEY definition and the "verbose" mode.<br />
<br />
Verbosely you can have even more control over the sequence. With SERIAL the default is something like<br />
<br />
<span style="background-color: #b6d7a8;">CREATE SEQUENCE XXX</span><br />
<span style="background-color: #b6d7a8;"> INCREMENT BY 1</span><br />
<span style="background-color: #b6d7a8;"> NO MAXVALUE</span><br />
<span style="background-color: #b6d7a8;"> NO MINVALUE</span><br />
<span style="background-color: #b6d7a8;"> CACHE 1;</span><br />
<br />
By hand you can define e.g.<br />
<br />
<span style="background-color: #b6d7a8;">CREATE SEQUENCE XXX</span><br />
<span style="background-color: #b6d7a8;"> START n</span><br />
<span style="background-color: #b6d7a8;"> INCREMENT BY n</span><br />
<span style="background-color: #b6d7a8;"> MAXVALUE n</span><br />
<span style="background-color: #b6d7a8;"> MINVALUE n</span><br />
<span style="background-color: #b6d7a8;"> CACHE 1;</span><br />
<br />
Insert in to Table using seq nextVal.<br />
<br />
<span style="background-color: #b6d7a8;">pgsql=> insert into seq_test(id, name)values(nextval('s1'), test1);</span><br />
<span style="background-color: #b6d7a8;">INSERT 0 1</span><br />
<span style="background-color: #b6d7a8;">pgsql=> select * from seq_test;</span><br />
<span style="background-color: #b6d7a8;"> id name</span><br />
<span style="background-color: #b6d7a8;">------------------</span><br />
<span style="background-color: #b6d7a8;"> 1 test1</span><br />
<span style="background-color: #b6d7a8;">(1 row)</span><br />
<br />
We can also insert value of our choosing.<br />
<br />
<span style="background-color: #b6d7a8;">pgsql=> insert into seq_test(id, name)values('100', 'test2');</span><br />
<span style="background-color: #b6d7a8;">INSERT 0 1</span><br />
<span style="background-color: #b6d7a8;">pgsql=> select * from seq_test;</span><br />
<span style="background-color: #b6d7a8;"> id name</span><br />
<span style="background-color: #b6d7a8;">-------------------------</span><br />
<span style="background-color: #b6d7a8;"> 1 test1</span><br />
<span style="background-color: #b6d7a8;"> 100 test2</span><br />
<span style="background-color: #b6d7a8;">(2 rows)</span><br />
<br />
Thus, the sequence will start from its own higher point, not related to any of the table values.<br />
<br />
<span style="background-color: #b6d7a8;">pgsql=> insert into seq_test(id, name)values(nextval('s1'), 'test3');</span><br />
<span style="background-color: #b6d7a8;">INSERT 0 1</span><br />
<span style="background-color: #b6d7a8;">pgsql=> select * from seq_test;</span><br />
<span style="background-color: #b6d7a8;"> id name</span><br />
<span style="background-color: #b6d7a8;">---------------------------</span><br />
<span style="background-color: #b6d7a8;"> 1 test1</span><br />
<span style="background-color: #b6d7a8;"> 100 test2</span><br />
<span style="background-color: #b6d7a8;"> 2 test3 </span><br />
<span style="background-color: #b6d7a8;">(3 rows)</span><br />
<br />
However the same is different in MySQL If you use AUTO_INCREMENT.<br />
<br />
<span style="background-color: #b6d7a8;">mysql> insert into seq_test (name) values ('test1');</span><span style="background-color: #b6d7a8;"></span><br />
<br />
<span style="background-color: #b6d7a8;">mysql> select * from seq_test;</span><br />
<span style="background-color: #b6d7a8;">+----+------+</span><br />
<span style="background-color: #b6d7a8;">| id | name |</span><br />
<span style="background-color: #b6d7a8;">+----+------+</span><br />
<span style="background-color: #b6d7a8;">| 1 | test1 |</span><br />
<span style="background-color: #b6d7a8;">+----+------+</span><br />
<span style="background-color: #b6d7a8;">1 row in set</span><br />
<br />
However you can override the value of your choice.<br />
<br />
<span style="background-color: #b6d7a8;">mysql> insert into seq_test (id,name) values (100,'test2');</span><br />
<span style="background-color: #b6d7a8;">Query OK, 1 row affected</span><br />
<br />
<span style="background-color: #b6d7a8;">mysql> select * from seq_test;</span><br />
<span style="background-color: #b6d7a8;">+-----+------+</span><br />
<span style="background-color: #b6d7a8;">| id | name |</span><br />
<span style="background-color: #b6d7a8;">+-----+------+</span><br />
<span style="background-color: #b6d7a8;">| 1 | test1 |</span><br />
<span style="background-color: #b6d7a8;">| 100 | test2 |</span><br />
<span style="background-color: #b6d7a8;">+-----+------+</span><br />
<span style="background-color: #b6d7a8;">2 rows in set </span><br />
<br />
The sequence will then start from this new higher point.<br />
<br />
<span style="background-color: #b6d7a8;">mysql> insert into seq_test (name) values ('test3');</span><br />
<span style="background-color: #b6d7a8;">Query OK, 1 row affected </span><br />
<br />
<span style="background-color: #b6d7a8;">mysql> select * from seq_test;</span><br />
<span style="background-color: #b6d7a8;">+-----+-------+</span><br />
<span style="background-color: #b6d7a8;">| id | name |</span><br />
<span style="background-color: #b6d7a8;">+-----+-------+</span><br />
<span style="background-color: #b6d7a8;">| 1 | test1 |</span><br />
<span style="background-color: #b6d7a8;">| 100 | test2 |</span><br />
<span style="background-color: #b6d7a8;">| 101 | test3 |</span><br />
<span style="background-color: #b6d7a8;">+-----+-------+</span><br />
<span style="background-color: #b6d7a8;">3 rows in set </span><br />
<br />
Also you can reset the sequence using an alter table command.<br />
<br />
<span style="background-color: #b6d7a8;">mysql> alter table seq_test auto_increment = 100;</span><br />
<span style="background-color: #b6d7a8;">Query OK, 1 row affected </span><br />
<span style="background-color: #b6d7a8;"></span><br />
This method could also be used to assign a higher number to start the sequence rather than starting with 1 by calling the alter table straight after the table creation.<br />
<br />
So in summary auto_increment offers a great way of assigning a unique value automatically to a table. However what it doesn't allow when compared with an Oracle Sequence is different increment values, ability to use across a number of tables.<br />
Oracle doesn't provide either AUTO_INCREMENT like MySQL or SERIAL like Postgres., <br />
If you look an equivalent to AUTO_INCREMENT in MySQL.,<br />
<br />
-- Won't work:<br />
<span style="background-color: #b6d7a8;">Sql>CREATE TABLE (seq_id NUMBER(1) DEFAULT xxx_seq.NEXTVAL);</span><br />
<span style="background-color: #b6d7a8;">ORA-00984: column not allowed here</span><br />
<br />
Then how to create an autoincrement field in a table with a sequence ...<br />
<br />
-- Will work:<br />
<br />
<span style="background-color: #b6d7a8;">Sql> create table seq_test(id number , name varchar2(21));</span><br />
<span style="background-color: #b6d7a8;">Statement processed.</span><br />
<br />
<span style="background-color: #b6d7a8;">First create a sequence</span><br />
<span style="background-color: #b6d7a8;">Sql> create sequence xxx ;</span><br />
<span style="background-color: #b6d7a8;">Statement processed.</span><br />
<br />
<span style="background-color: #b6d7a8;">Then create the trigger.</span><br />
<span style="background-color: #b6d7a8;">create trigger yyy before insert on seq_test</span><br />
<span style="background-color: #b6d7a8;">for each row</span><br />
<span style="background-color: #b6d7a8;">when (new.id is null)</span><br />
<span style="background-color: #b6d7a8;">begin</span><br />
<span style="background-color: #b6d7a8;"> select xxx.nextval into :new.id from dual;</span><br />
<span style="background-color: #b6d7a8;">end;</span><br />
<span style="background-color: #b6d7a8;">/</span><br />
Also, the below works as like Postgres...<br />
<br />
<span style="background-color: #b6d7a8;">Sql>INSERT INTO seq_test(id, name)VALUES(xxx_seq.nextval, 'test1');</span><br />
<br />
However, setting the default vlaue while Table creation to sequence.nextVal in Postgres will work unlike in Oracle.<br />
<br />
<span style="background-color: #b6d7a8;">pgsql=>create sequence xxx;</span><br />
<br />
<span style="background-color: #b6d7a8;">pgsql=>create table seq_test(id INT DEFAULT nextval('s1') NOT NULL, name varchar(21));</span><br />
<br />
<span style="background-color: #b6d7a8;">pgsql=> insert into seq_test(name)values('test4');</span><br />
<br />
<div style="background-color: #b6d7a8;">pgsql=> select * from seq_test;<br />
</div><span style="background-color: #b6d7a8;">id | name</span><br />
<span style="background-color: #b6d7a8;">----+-------</span><br />
<span style="background-color: #b6d7a8;"> 3 | test4</span><br />
<span style="background-color: #b6d7a8;">(1 row)</span><br />
<br />
<br />
Decided to use sequences while Insert.........<br />
<br />
<br />
</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-90687097895576707922011-05-17T05:43:00.000-07:002011-05-17T05:43:27.761-07:00Execute PL/SQL on Oracle from Python Script<div dir="ltr" style="text-align: left;" trbidi="on">I had a chance to write a Python script that connects to Oracle and do some SQL..<br />
I thought of bloging the basic steps., that I followed..<br />
<br />
Install Oracle client (below packages), If the box doesn't have Oracle Instance., to run the script.<br />
<br />
The local box requires the below packages to be installed., to run the script.<br />
<br />
oracle-instantclient11.2-basic-11.xxxxx.xx.zip<br />
cx_Oracle-5.xxx.tar.gz <br />
<br />
Once the installation is done, write the script to connect to Oracle and do process.<br />
<br />
<span style="background-color: #b6d7a8;">#The Script uses the cx_Oracle to connect to the Oracle instance.. It can be of a local or remote instance. </span><br />
<br />
def grantSelectOnOracle():<br />
try:<br />
import cx_Oracle<br />
#Checking for the presence of database<br />
con = cx_Oracle.connect(u"%s/%s@%s:%s/%s" % ('DATABASE_USERNAME',<br />
'DATABASE_PASSWORD',<br />
'DATABASE_HOST',<br />
'DATABASE_PORT',<br />
'DATABASE_NAME'))<br />
<br />
<br />
<div style="background-color: #b6d7a8;">#Once the connection is established, the script tries to run a PL/SQL block, here #it grant select permission on the User passed.</div><br />
try:<br />
#Granting select privilege to read only user on all reports tables<br />
cur = con.cursor()<br />
grantCommand = """<br />
begin<br />
for i in (select table_name from user_tables)<br />
loop<br />
execute immediate 'grant select on '|| i.table_name||' to %s';<br />
end loop;<br />
end;""" % ( 'DATABASE_READ_ONLY_USER')<br />
cur.execute(u"%s" % grantCommand)<br />
cur.close()<br />
logger.info("Done.")<br />
except:<br />
logger.warn("Some Problem Occured in Granting Read Only Permission to User. Please set permissions Manually")<br />
pass<br />
con.close()<br />
except Exception, e:<br />
logger.error("Connection problem occured with Oracle")<br />
pass<br />
<br />
Here it is., now enjoy with the Python script to execute PlSql's remotely on Oracle......!<br />
<br />
<br />
<br />
<br />
<br />
</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-47840424365994940052011-05-17T05:10:00.000-07:002011-05-17T05:29:23.757-07:00Setup remote debugging in Eclipse<div dir="ltr" style="text-align: left;" trbidi="on"><div class="separator" style="clear: both; text-align: center;"></div>Debugging is always an interesting way to understand better how a system works internally.<br />
<br />
And will happen with most of opensource projects.<br />
<br />
So how can we easily debug ? The answer is : use remote debugging.<br />
<br />
To be able to attach your Eclipse debugger to a running Java process you need to start that process with the following Java options…It can be any of your startup files for the application.. may be run.sh..etc<br />
<br />
<div style="background-color: #b6d7a8;">export JAVA_OPTS=</div><br />
<span style="background-color: #b6d7a8;">"-Xdebug -Xrunjdwp:transport=dt_socket,address=9000,server=y,suspend=n"</span><br />
<br />
Once you have done this restart the server.<br />
<br />
From Eclipse go to the Debug Configuration.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4POeayxLVFUSY5KyVVda_MD8LUgYRj7mwPv22SZ4YnSOFcqOpS2s73zeoJ-dhA_-aXY8WGN6frJ0-1mG6HvAyIDJ41dMi7c4EksmWFHFVH6UVrgHqOd3_0Ns_XFOPfhZpi61iVDlmFXiQ/s1600/Screenshot-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4POeayxLVFUSY5KyVVda_MD8LUgYRj7mwPv22SZ4YnSOFcqOpS2s73zeoJ-dhA_-aXY8WGN6frJ0-1mG6HvAyIDJ41dMi7c4EksmWFHFVH6UVrgHqOd3_0Ns_XFOPfhZpi61iVDlmFXiQ/s640/Screenshot-1.png" width="640" /></a></div><br />
<br />
<br />
<br />
<br />
create a new Remote Java Application configuration for the process you want to connect to and set the port number to xxxx(eg - 9000), the same as that of the options.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix2uBm9vb6J3jzpg5ypsX6VGIUT7odxwwBaD0aQ8vSeA3Dwrh6oBrLvYwid1aLxxt7n8RZjoRvIHXG7wU7vrASTUa5R75MPe-_mHT7MLgbTzeZr0JaP9oEpDdc88CwwYxdfEG60jRvSw3k/s1600/Screenshot-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix2uBm9vb6J3jzpg5ypsX6VGIUT7odxwwBaD0aQ8vSeA3Dwrh6oBrLvYwid1aLxxt7n8RZjoRvIHXG7wU7vrASTUa5R75MPe-_mHT7MLgbTzeZr0JaP9oEpDdc88CwwYxdfEG60jRvSw3k/s640/Screenshot-2.png" width="640" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"></div><br />
<br />
Enter the hostname for the machine running the Java process. Thats it…you are on debug mode...</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-85742107400174655182011-05-17T04:39:00.000-07:002011-05-17T04:39:31.640-07:00Java Program to run Pentaho kettle ETL Job<div dir="ltr" style="text-align: left;" trbidi="on">I got to use Kettle spoon as the ETL tool, and created Jobs and transformations.<br />
<br />
and used the Pentaho provided API's to run the Transformations/Jobs from java.<br />
<br />
An ETL Job file can be created and get stored in a disk.. the API requires the file path to get passed.<br />
<br />
<br />
Provided the system has the required Pentaho libraries and the class path.,<br />
<br />
The Jars can be downloaded from the Pentaho Site.<br />
<br />
<span style="background-color: #b6d7a8;">First Initialize the Kettle Environment from Java.,</span><br />
<br />
/** static block that initializes the kettle environment */<br />
static {<br />
try {<br />
KettleEnvironment.init(false);<br />
} catch (KettleException e) {<br />
smLogger.error("Pentaho.Job.Run.Error.Initialization :" + e.getMessage());<br />
}<br />
}<br />
<br />
<br style="background-color: #93c47d;" /><span style="background-color: #b6d7a8;">And now pass the Kettle Job File Path to the API.</span><br />
<br />
/**<br />
* Executes the Job<br />
* @param jobPath jobFile<br />
* @throws ETLRunException<br />
*/<br />
public void executeJob(String jobPath) throws ETLRunException {<br />
<br />
<span style="background-color: #b6d7a8;"># Initialize the Job Meta. </span><br style="background-color: #b6d7a8;" /><br />
try {<br />
JobMeta jobMeta = new JobMeta(jobPath, null, null);<br />
<br />
<span style="background-color: #b6d7a8;">// Create the Job Instance</span><br />
Job job = new Job(null, jobMeta);<br />
job.getJobMeta().setInternalKettleVariables(job);<br />
<br />
job.setLogLevel("BASIC");<br />
<br />
job.setName(Thread.currentThread().getName());<br />
<br />
<span style="background-color: #b6d7a8;">// Start the Job, as it is a Thread itself by Kettle.</span><br />
job.start();<br />
job.waitUntilFinished();<br />
<br />
if (job.getResult() != null && job.getResult().getNrErrors() != 0) {<br />
smLogger.error("Pentaho.Job.Run.Error.FinishedWithErrors");<br />
throw new ETLRunException("Pentaho.Job.Run.Error.FinishedWithErrors");<br />
}<br />
<br />
<span style="background-color: #b6d7a8;">// Now the job task is finished, mark it as finished.</span><br />
job.setFinished(true);<br />
<br />
<span style="background-color: #b6d7a8;">// Cleanup the parameters used by the job. Post that invoke GC.</span><br />
jobMeta.eraseParameters();<br />
job.eraseParameters();<br />
<br />
} catch (Exception e) {<br />
smLogger.error("Pentaho.Job.Run.Error.FinishedWithErrors :" + e.getMessage());<br />
throw new ETLRunException(e);<br />
}<br />
<br />
}<br />
</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com3tag:blogger.com,1999:blog-4010557758803296647.post-13283080997223474892011-05-17T03:26:00.000-07:002011-05-17T03:28:34.520-07:00Log memory utilization before and after a Java process run<div dir="ltr" style="text-align: left;" trbidi="on"><br />
<br />
<br />
Sometimes running a Java process requires .. <br />
to calculate the amount of memory it needs/takes to run.<br />
<br />
And sometimes logging the same is useful when the job grows in future.<br />
<br />
Java API provides a mechanism to log the memory..<br />
In such a way.., We can calculate memory available, used memory, total memory.. etc by using the API provided methods..<br />
<br />
Method to get the Total memory available...<br />
<br />
<span style="background-color: #b6d7a8;">Runtime.getRuntime().totalMemory()</span><br />
<br />
Similarly, we can get the available memory by...<br />
<br />
<span style="background-color: #b6d7a8;">Runtime.getRuntime().freeMemory()</span><br />
<br />
and by doing some calculation, we get the memory used by a Process...<br />
<br />
<span style="background-color: #b6d7a8;">Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()</span><br />
<br />
Combining all these data, conclude a method that will log the Total memory allocated and the memory used by the Process.<br />
<br />
<br />
Now time to log the memory usage before and after a job/process run.<br />
<br />
# Write a LogUtil class which has the below method.<br />
<br />
Log memory utilization before and after a job run<br />
<br />
/**<br />
* returns the total memory and used memory.<br />
*<br />
* @return total and used memory<br />
*/<br />
<span style="background-color: white;"> </span><span style="background-color: white;"> public static String getMemoryUsageLog() {</span><br />
<div style="background-color: #b6d7a8;"><br />
</div><div style="background-color: #b6d7a8;"> return MEMORY_ALLOCATED + "[" + String.valueOf(Runtime.getRuntime().totalMemory()) + "] " + MEMORY_USED + "[" +</div><div style="background-color: #b6d7a8;"> String.valueOf(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + "]";</div><div style="background-color: #b6d7a8;"> }</div><br />
<br />
# Call the method from the Invoker class.<br />
<br />
# Write a pre-execute method to call the Memory log util.<br />
<br />
/**<br />
* pre-execute method, initializes the pre-execute data<br />
*<br />
* @throws XXXRunException<br />
*/<br />
protected void preExecute() throws XXXRunException {<br />
//memory usage before job run<br />
mLogger.debug("Memory Usage before Job Run : " + LogUtil.getMemoryUsageLog()); <br />
}<br />
<br />
# Run the Process/Job<br />
<br />
# Write a pre-execute method to call the Memory log util.<br />
<br />
/**<br />
* post-execute method, initializes the post-execute data<br />
*<br />
* @throws XXXRunException<br />
*/<br />
protected void postExecute() throws XXXRunException {<br />
//memory usage after job run<br />
mLogger.debug("Memory Usage after Job Run : " + LogUtil.getMemoryUsageLog()); <br />
}<br />
<br />
Now look for the memory logs in your loggers....! </div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-61351924338155899862011-05-17T02:43:00.000-07:002011-05-18T03:16:15.486-07:00Connect Oracle from Shell and execute SQL.<div dir="ltr" style="text-align: left;" trbidi="on">A shell script that connects to Oracle and execute SQL..<br />
The script will run on Oracle installed box, and do authentication against the username, password..<br />
<br />
and verifies the Oracle_sid passed is valid. Once the authentication passes it executes the sql on Oracle.<br />
<br />
The skeleton of the Script looks similar to the below...<br />
<br />
Begin your main method.<br />
<br />
<span style="background-color: #b6d7a8;">main() {main() {</span><br />
<br />
Make sure the User executing the script is Oracle user.<br />
<br />
<span style="background-color: #b6d7a8;">#1. Make sure only user oracle can run the script </span><br />
<span style="background-color: #b6d7a8;">if [ "$(whoami)" != "oracle" ]; then </span><br />
<span style="background-color: #b6d7a8;"> echo "This script must be run as oracle user" 1>&2 </span><br />
<span style="background-color: #b6d7a8;"> exit 1 </span><br />
<span style="background-color: #b6d7a8;">fi</span><br />
<br />
<span style="background-color: white;">#2. Read the Oracle System User Name</span><br />
<br />
<span style="background-color: white;">oracle_system_user=$PARAM(system)</span><br />
<br />
<span style="background-color: white;">#3. Read the Oracle System User Password</span><br />
<br />
<span style="background-color: white;">oracle_system_password=$PARAM(password)</span><br />
<br />
<span style="background-color: white;">#4. Prompt for ORACLE_SID</span><br />
<span style="background-color: white;">database_name=$PARAM(test_db)</span><br />
<br />
#5. Check the given credentials are valid and that the SID exists.<br />
checkAuth # Have this function created outside of main method.<br />
<br />
#6. If, the Authentication suceeds, Export the Oracle SID<br />
export ORACLE_SID=database_name<br />
<br />
#7. Execute the Query you need to.<br />
echo "Processing..............please do not exit.........."<br />
executeProcess # Have this metod outside of main method.<br />
echo "Execution Success........!"<br />
<br />
# Close the main method.<br />
<br />
<span style="background-color: #b6d7a8;">}</span><br />
<br />
<span style="background-color: #b6d7a8;">main</span><br />
<br />
<br />
# function to check if the given SID is valid.<br />
<span style="background-color: #b6d7a8;">checkAuth() {</span><br />
<span style="background-color: #b6d7a8;"> #Connect to oracle</span><br />
<span style="background-color: #b6d7a8;"> sqlplus -s -l $oracle_system_user/$oracle_system_password@$database_name << STD_IN</span><br />
<span style="background-color: #b6d7a8;"> exit;</span><br />
<span style="background-color: #b6d7a8;">STD_IN</span><br />
<span style="background-color: #b6d7a8;"> errorCode=$? </span><br />
<span style="background-color: #b6d7a8;"> if [ ${errorCode} -ne 0 ];then</span><br />
<span style="background-color: #b6d7a8;"> echo "Unable to connect the DB with the supplied credentials."</span><br />
<span style="background-color: #b6d7a8;"> exit 1</span><br />
<span style="background-color: #b6d7a8;"> fi </span><br />
<span style="background-color: #b6d7a8;">}</span><br />
<br />
<br />
# function to execute process, DATABASE_USERNAME.<br />
<span style="background-color: #b6d7a8;">executeProcess() {</span><br />
<span style="background-color: #b6d7a8;"> #Connect to Oracle</span><br />
<span style="background-color: #b6d7a8;"> sqlplus -s -l $oracle_system_user/$oracle_system_password@$database_name << STD_IN </span><br />
<span style="background-color: #b6d7a8;"> select sysdate from dual; </span><br />
<span style="background-color: #b6d7a8;"> exit;</span><br />
<span style="background-color: #b6d7a8;">STD_IN</span><br />
<span style="background-color: #b6d7a8;">}</span><br />
<br />
<br />
For reading the Params we can prompt the User to enter the valuse dynamically., by writing a readParam function., <br />
instead of hardcoding the values for username, password and sid values.. In that way the script can be uses as dynamic.<br />
<br />
Similarly, the executeProcess method can be used to execute Plsql blocks.<br />
<br />
The command that goes between "<< STD_IN" and "STD_IN" will be directly interpretted by Oracle, so we can have <br />
only the valid sql commands inside... and can give any user defined name for "STD_IN"..it can be "XXXYYZZ".<br />
<br />
that is only to say the interpreter the commands inside will get directly executed....!</div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0tag:blogger.com,1999:blog-4010557758803296647.post-57537422942687268342011-01-27T00:01:00.000-08:002011-05-17T01:35:06.227-07:00Browser - Server Communication(Http Caching)<div dir="ltr" style="text-align: left;" trbidi="on"><div style="font-family: "Trebuchet MS",sans-serif;">Hi, Recently I had faced a very common web app problem...and there are few interesting things to share with..</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">User Profile page - where User can upload an Image and can change If he wants.. and to upload the Image we made an Action class to respond.. and the thing is the Url is static.. thus causes the Problem..</div><div style="font-family: "Trebuchet MS",sans-serif;">Even though the User is Changing the Picture the browser displays the Old picture(Edit -> View)..Post to Get ..the url to fetch the Image is static the actual request to Fetch the Picture is not Fired..the browser simply fetches the Image from the Cache..Unless If we manually refresh the page..</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">Two things to note down here.. Either</div><div style="font-family: "Trebuchet MS",sans-serif;">a) we can tell the browser not to use the Cache or</div><blockquote><blockquote style="font-family: "Trebuchet MS",sans-serif;"> * 1 Option One: Use a Cache-Control Header with 's-maxage'<br />
* 2 Option Two: Use a Query String<br />
* 3 Option Three: Use a "Zero" Expires Header<br />
* 4 Option Four: Use a Cache-Control Header with 'private'<br />
* 5 Option Five: Use a Cache-Control Header with 'no-cache'<br />
* 6 Option Six: Use a Cache-Control Header with 'no-store'</blockquote></blockquote><div style="font-family: "Trebuchet MS",sans-serif;">b) use the cache for may be for some period of time.. </div><blockquote style="font-family: "Trebuchet MS",sans-serif;">* 1 Use a Cache-Control Header with 'maxage' with time period specified.</blockquote><div style="font-family: "Trebuchet MS",sans-serif;">Problem with option a) is the performance hit..Every time a new request made eventhough the picture is not actually changed by the User.</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">Problem with option b) If user changes the Picture the Browser uses the Cached version. Oops...</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">Is there a way to tell the Browser, If the Picture modified at the Server side use the updated one If not use the one in Cache..Oh this is been the perfect solution, If it can be achieved.. !</div><div style="font-family: "Trebuchet MS",sans-serif;"> So we need to communicate with the browser.. the only way via through the HTTP response.. </div><div style="font-family: "Trebuchet MS",sans-serif;">The Browser will normally Interpret the response and initiate a new request.. </div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">The Http Response Header which suits very close is "max-age=0" </div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">max-age=0 implies that the content is considered stale (and must be re-fetched) immediately</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">It can be used by either by the Browser(via Request) or by the Server (via Response)</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;"><b>When "max-age=0" added in Response Header by the Server</b> It simply tells caches/Browsers the response is stale from the get-go and so they <b>SHOULD</b> revalidate the response before using a cached copy. ( I tried this option with Mozilla/IE8 but both are not performing any Validation and it causes a new request Every time..It's simply behaves like not using the Cache.)</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;"><b>When "max-age=0" added in Request Header by the Browser</b> , then each cache along the way will revalidate its cache entry (eg. with the "If-Not-Modified" header) all the way to the origin server. If the reply is then 304 (Not Modified), the cached entity can be used.Else If it's 200(Modified), Use the Updated one.</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;"><b>When sent by the Browser: Request Header with "max-age=0"</b></div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBUAs1ClaCMlp1IZsBSy57Xzx3zeHN4JxvQw-lpr2nvQeXBJBgBuEIbirg2NkjmNDC7MaJ1UTencWVv2u1VoFMruGWElIMjoImsjpTTNTa6tHnv9Ss9X0pCKt8889dPHSi9fmdj7EzEBWh/s1600/Blog1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBUAs1ClaCMlp1IZsBSy57Xzx3zeHN4JxvQw-lpr2nvQeXBJBgBuEIbirg2NkjmNDC7MaJ1UTencWVv2u1VoFMruGWElIMjoImsjpTTNTa6tHnv9Ss9X0pCKt8889dPHSi9fmdj7EzEBWh/s640/Blog1.JPG" width="640" /></a></div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
<b>Response From Server with Status Code :</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDbsV1iEkpP35Iz4d3NJTEizFIsd1yJiCJT_ltiXIj7X8mTjLpS8RRewRKA9FTiA5zjYUtxDZcZZhGo8eFScF6C0s5EkDeqQcXcdGVe_6dH9TvxIaLo4D74uacL10stLgvYcVyrAXAgTfy/s1600/Blog2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="359" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDbsV1iEkpP35Iz4d3NJTEizFIsd1yJiCJT_ltiXIj7X8mTjLpS8RRewRKA9FTiA5zjYUtxDZcZZhGo8eFScF6C0s5EkDeqQcXcdGVe_6dH9TvxIaLo4D74uacL10stLgvYcVyrAXAgTfy/s640/Blog2.JPG" width="640" /></a></div><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;"><br />
</div><div style="font-family: "Trebuchet MS",sans-serif;">So...the Browser uses "max-age=0" in the Request Header the Server responds with proper status code(304/200) and the Cache entries are used accordingly.<br />
<br />
If Server uses "max-age=0" in response Header "response.setHeader("Cache-control","max-age=0");" the Browser always makes a new request to the Server...may be can avoid the actual download cost by using server side logic, but not the overhead an additional Http Call to the Server!!!<br />
</div><div style="font-family: "Trebuchet MS",sans-serif;"></div></div>Ashikhttp://www.blogger.com/profile/05806613710688235706noreply@blogger.com0