Vicky Cristina Barcelona
Is Woody Allen at his best.
The story and cinematic style are reminiscent of Rohmer's Pauline à la plage and Conte d'été.
Is Woody Allen at his best.
The story and cinematic style are reminiscent of Rohmer's Pauline à la plage and Conte d'été.
Posted by
Jorge
at
Wednesday, March 04, 2009
0
comments
Java Puzzlers is a worthy complement to Effective Java. It provides a collection of puzzles that illustrate obscure (and some times bizarre) behavior of the Java programming language and its core libraries.
I've highlighted some of the book's puzzles below, grouped by Numerical, API, and OOP-related traps & solutions.
§ Numerical Traps
i % 2n = i & (2n - 1), which I first read about in Hacker's Delight. The bitwise expression on the right is likely faster and works safely with negative operands. [#1, page 5]200 - 110 = 90 instead of 2.00 - 1.10 = 0.89999...final long MICROS_PER_DAY = 24L * 60 * 60 * 1000;. [#3, page 9]Integer.MAX_VALUE. All integers are <= Integer.MAX_VALUE and when Integer.MAX_VALUE is incremented it silently wraps around to Integer.MIN_VALUE. [#26, page 57]Math.abs(Integer.MIN_VALUE); If the argument is Integer.MIN_VALUE or Long.MIN_VALUE, Math.abs returns its argument. [#64, page 149]§ API Traps
String.replaceAll(Pattern.quote("."), ","); instead of String.replaceAll("\\."), ",");, for example, to prevent the accidental omission of escape characters. [#20, page 43]finally block is always executed, even if the try block returns. Err...except if the try block calls System.exit(0). @See #39 below. I first read about this in Peter Norvig's Java Infrequently Answered Questions. [#36, page 77]finally blocks to execute.
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello");
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
System.out.println(" world");
}
});
System.exit(0);
}
}
[#39, page 83]close method of input & output streams can throw IOException too; from JSE5, input and output streams implement the Closeable interface. This lets us write a generic close method for streams, e.g.:
...
InputStream in = null;
try {
in = new FileInputStream(src);
...
} finally {
closeAndIgnoreException(in);
}
private static void closeAndIgnoreException(Closeable c) {
if (c != null) {
try {
c.close();
} catch(IOException ioe){ // ignored }
}
}
[#41, page 87]Method m = <object variable>.class.getMethod(<method name>); instead of Method m = <object variable>.getClass().getMethod(<method name>);to prevent possible runtime exceptions caused by the runtime type returned by reflection. [#79, page 189]§ OOP Traps
void f(float) { ... }
void f(double) { ... }
f with f(42), the parameter types for the first method are assignable to the parameter types of the second method through a widening primitive conversion. That is, double = float. By contrast, float = double is not valid without a cast. Thus, f(double) is removed from the set of possible methods to call and f(float) is called. [#46, page 105]
public class SynchronizedStatic {
private static Long counter;
public SynchronizedStatic() {
synchronized (SynchronizedStatic.class) {
counter++;
}
}
}
[#55, page 127]
public class ShadesOfGray {
public static void main(String[] args){
System.out.println(X.Y.Z); // prints 'White'
}
}
class X {
static class Y {
static String Z = "Black";
}
static C Y = new C(); // variable Y takes precedence over type Y
}
class C {
String Z = "White";
}
[#68, page 163]final modifier means different things on methods & fields: final instance methods cannot be overridden; final static methods may not be hidden; final fields may not be assigned more than once. [#72, page 171]Resources:
Labels: book, idioms, java, programming, puzzle, quirks, review
Posted by
Jorge
at
Tuesday, October 21, 2008
0
comments
London Tube Journey Planner (TubeJP) offers detailed public transport maps for London
On NY Turf offers routes for New York City
On both services, a layer with the geographic transport routes (not their equivalent schematic diagrams) overlays the city maps.
Related:
A Taste of a Moscow Tube Station
Tube Map variations (great stuff!)
Moscow Tube
LondonTown Tube - The Real Thing
Labels: map, transport, tube, underground
Posted by
Jorge
at
Saturday, June 07, 2008
0
comments
"Each part of your face is not perfect but when you put them all together
is not bad"
Posted by
Jorge
at
Saturday, May 24, 2008
1 comments
My Complete Far Side (50% off the original price!) arrived Friday morning and made my weekend in a way that can only be described by Zita Swoon's wonderful Bananaqueen below
Posted by
Jorge
at
Sunday, April 13, 2008
0
comments
Another gem for my growing list of Resources For Learning Mandarin:
Nciku contains a neat dictionary, conversations (text-to-speech, optionally annotated with hànzì and pīnyīn), Q & A's, tools, an awesome handwriting recognition tool that let's you draw Chinese characters, and plenty more cool stuff!
Related:
Resources For Learning Mandarin
Labels: chinese, hanzi, language, learning
Posted by
Jorge
at
Thursday, April 10, 2008
0
comments
I found the article by Emeritus Professors Robert B.K. Dewar and Edmond Schonberg anecdotal and weakly supported.
I describe the argument flaws by quoting and commenting in-line in the rest of this post.
"For example, Bjarne Stroustrup reports from Texas A & M University that the
industry is showing increasing unhappiness with the results of this approach."
"It [Texas A&M] did [teach Java as the first language]. Then I started
teaching C++ to the electrical engineers and when the EE students started to
out-program the CS students, the CS department switched to C++. [5]"
Hmmm, the conclusion here may be wrong due to many types of selection bias:
It is conceivable that a Computer Science department where Guido van Rossum teaches Python will attract and create lots of above average Python programmers, beating the crap out of their C++ and Java colleagues (the company of 'stars' raises the level of everyone else).
"At AdaCore, we are certainly aware of many universities that have adopted Ada
as a first language because of similar concerns."
"Ada is the language of software engineering par excellence. Even when it is not
the language of instruction in programming courses, it is the language chosen to
teach courses in software engineering. This is because the notions of strong
typing, encapsulation, information hiding, concurrency, generic programming,
inheritance, and so on, are embodied in specific features of the language."
"From our experience and that of our customers, we can say that a real
programmer writes Ada in any language."
"For example, an Ada programmer accustomed to Ada’s package model, which
strongly separates specification from implementation, will tend to write C in a
style where well-commented header files act in somewhat the same way as package
specs in Ada. The programmer will include bounds checking and consistency checks
when passing mutable structures between subprograms to mimic the strong-typing
checks that Ada mandates [6]. She will organize concurrent programs into tasks
and protected objects, with well-defined synchronization and communication
mechanisms."
Labels: computer science, education, engineering, software
Posted by
Jorge
at
Tuesday, January 08, 2008
1 comments
My previous brute-force approach to list all the 4-dimension combinations of S = {(latent, manifest), (static, dynamic), (weak, strong), (nominal, structural)} typing -by filtering the desired tuples from the powerset of S - wasn't very efficient.
It was a perfect example of The Danger of Naïveté: an inefficient algorithm that was nevertheless simple to devise and implement.
Moreover, quoting The eight rules of Dean Carney, I should have observed that "the first place to look for a solution is within the problem itself". If the problem involves 4 tuples of 2 elements each to begin with, the efficient solution is not likely to require the powerset of all 8 elements.
Thus, I figured I should give it another go.
First, for simplicity and better readability, let me replace the values of S with the character-tuples on the left hand side:
(A, B) = (latent, manifest)
(C, D) = (static, dynamic)
(E, F) = (weak, strong)
(G, H) = (nominal, structural)
S = {(A, B), (C, D), (E, F), (G, H)}
Next, I'll write down some 4-size combinations. After a short while, a pattern emerges:
ACEG
ACEH
ACFG
ACFH
ADEG
ADEH
...
The pattern becomes clear when I look at the elements like this:
X1=(A, B) x X2=(C, D) x X3=(E, F) x X4=(G, H)
This is a Cartesian product of X1 x X2 x ... x Xn, the set containing all possible combinations of one element from each set.
With this knowledge, implementing an efficient algorithm is easy. First, a method to return the Cartesian product of two sets:
# arguments must be lists, e.g.: cartesian([1, 2], [3, 4])
def cartesian(list1, list2):
result = []
for el in list1:
for item in list2:
if isinstance(el, list):
result.append(el + [item])
else:
result.append([el] + [item])
return result
Testing the method with input X=[1, 2] x Y=[3, 4] yields:
[[1, 3], [1, 4], [2, 3], [2, 4]]
Which looks good.
Next, a method to process the Cartesian product of any number of sets. Here's a candidate implementation:
def types_cartesian(aList):
head = aList[0]
tail = aList[1:]
for item in tail:
head = cartesian(head, item)
return head
The output of method types_cartesian(aList) given input aList=S is:
S={['static', 'dynamic'], ['weak', 'strong'], ['latent', 'manifest'], ['nominal', 'structural']}
S1 x S2 x ... Sn =
['static', 'weak', 'latent', 'nominal']
['static', 'weak', 'latent', 'structural']
['static', 'weak', 'manifest', 'nominal']
['static', 'weak', 'manifest', 'structural']
['static', 'strong', 'latent', 'nominal']
['static', 'strong', 'latent', 'structural']
['static', 'strong', 'manifest', 'nominal']
['static', 'strong', 'manifest', 'structural']
['dynamic', 'weak', 'latent', 'nominal']
['dynamic', 'weak', 'latent', 'structural']
['dynamic', 'weak', 'manifest', 'nominal']
['dynamic', 'weak', 'manifest', 'structural']
['dynamic', 'strong', 'latent', 'nominal']
['dynamic', 'strong', 'latent', 'structural']
['dynamic', 'strong', 'manifest', 'nominal']
['dynamic', 'strong', 'manifest', 'structural']
This is the same list as the one obtained earlier with the powerset approach.
The big difference is that with the powerset implementation the number of possible combinations explodes exponentially ( O(2n) ), whereas the Cartesian product is a quadratic ( O(n2) ) algorithm.
Here's the code.
Related:
Types and Programming Languages
http://en.wikipedia.org/wiki/Cartesian_product
http://programming-misc.googlecode.com/svn/trunk/python/types_cartesian.py
Labels: maths, programming, python, types
Posted by
Jorge
at
Sunday, December 30, 2007
1 comments
From Quote of the week: Why brainstorming is a bad idea:
"To their surprise, the researchers found that virtual groups, where
people brainstormed individually, generated nearly twice as many
ideas as the real groups.
The result, it turned out, is not an anomaly. In a 1987 study,
researchers concluded that brainstorming groups have never
outperformed virtual groups.
Of the 25 reported experiments by psychologists all over
the world, real groups have never once been shown to be
more productive than virtual groups.
In fact, real groups that engage in brainstorming consistently
generate about half the number of ideas they would have
produced if the group's individuals had worked alone."
Labels: brainstorm, economics, psychology
Posted by
Jorge
at
Saturday, December 29, 2007
0
comments
This excellent overview of type systems by nostrademons @ reddit describes latent, manifest, static, dynamic, weak, strong, nominal, and structural typing, and places a number of popular programming languages on some of these dimensions.
I got curious about listing all the possible combinations and then find programming languages to fill in the slots.
I cooked up some quick and dirty Python code to do the job.
Let's call S = {static, dynamic, latent, manifest, weak, strong, nominal, structural}
The powerset of S has 28 = 256 combinations, only some of them useful but I can fix that later.
# return a list with 2len(l) elements (definition of powerset)
def powerset(l):
r = []
if l:
head = l[:1]
tail = l[1:]
for item in powerset(tail):
r.append(item)
r.append(head + item)
else:
r.append([])
return r
The powerset of S returns the complete list of subsets of S from sizes 1 to 8 plus the empty set {}.
This means subsets {static}, {dynamic}, {static, dynamic}, ..., all the way to {static, dynamic, latent, manifest, weak, strong, nominal, structural} are included too.
However, there are 4 pairs of [kind of] mutually exclusive types and I am only interested in the subsets of size 4. Thus, I need only consider the unique 4-dimension combinations.
The maths tells me I should expect n!/k!(n-k)! = 8!/4!(8-4)! = 70 such combinations (see binomial coefficient or look up the middle number of the 8th row of Pascal's triangle).
I can obtain this by keeping only those subsets of size 4 from the list returned by the powerset. The function below will help:
# filter list keeping only n-tuples where n=size
def len_filter(l, size=1):
return [item for item in l if len(item) == size]
Still, the set of 70 size-4 combinations contain lots of invalid members because of the 4 pairs of [kind of] mutually exclusive types (read original comment for details):
("static", "dynamic"),
("latent", "manifest"),
("weak", "strong"),
("nominal", "structural")
A simple solution is to just remove any combination that contains these "invalid" pairs. The 2 functions below will do that:
# retain n-tuples that don't contain any of the invalid pairs
def invalidpairs_filter(l):
return [item for item in l if not is_invalid(INVALID_PAIRS, item)]
# returns True if any of the invalid tuples is contained in the given n-tuple,
# False otherwise
def is_invalid(invalid_pairs, ntuple):
s = set(ntuple)
for invalid_pair in invalid_pairs:
si = set(invalid_pair)
if si.issubset(s):
return True
return False
Et voilá! Running the Python program prints the 16 combinations we want:
['latent', 'nominal', 'static', 'weak']
['dynamic', 'latent', 'nominal', 'weak']
['manifest', 'nominal', 'static', 'weak']
['dynamic', 'manifest', 'nominal', 'weak']
['latent', 'nominal', 'static', 'strong']
['dynamic', 'latent', 'nominal', 'strong']
['manifest', 'nominal', 'static', 'strong']
['dynamic', 'manifest', 'nominal', 'strong']
['latent', 'static', 'structural', 'weak']
['dynamic', 'latent', 'structural', 'weak']
['manifest', 'static', 'structural', 'weak']
['dynamic', 'manifest', 'structural', 'weak']
['latent', 'static', 'strong', 'structural']
['dynamic', 'latent', 'strong', 'structural']
['manifest', 'static', 'strong', 'structural']
['dynamic', 'manifest', 'strong', 'structural']
Should there be a programming language for all the above combinations? nostrademons and grauenwolf already filled in a few of these slots (apologies if my copy & paste introduced errors):
latent, nominal, static, weak =
dynamic, latent, nominal, weak = PHP
manifest, nominal, static, weak = C, Access SQL, Java, C++, C#,
dynamic, manifest, nominal, weak =
latent, nominal, static, strong = Haskell
dynamic, latent, nominal, strong = Scheme
manifest, nominal, static, strong = Java, C++, C#, VB.NET (strict), T-SQL
dynamic, manifest, nominal, strong =
latent, static, structural, weak = VB.NET, VBScript
dynamic, latent, structural, weak = JavaScript, PHP, Assembly
manifest, static, structural, weak =
dynamic, manifest, structural, weak =
latent, static, strong, structural = Ocaml, Haskell
dynamic, latent, strong, structural = Erlang, Scheme, Common Lisp, Python, Ruby
manifest, static, strong, structural = Haskell, C++ Templates
dynamic, manifest, strong, structural = Common Lisp
Repetitions occur because most languages support multiple forms of typing.
My next task (for another night, another post) is to clean-up the table and find languages that fit in the 5 unused dimensions from the list above:
['latent', 'nominal', 'static', 'weak'] =
['dynamic', 'manifest', 'nominal', 'weak'] =
['dynamic', 'manifest', 'nominal', 'strong'] =
['manifest', 'static', 'structural', 'weak'] =
['dynamic', 'manifest', 'structural', 'weak'] =
Possible Questions:
Labels: maths, powerset, programming, python, types
Posted by
Jorge
at
Friday, December 28, 2007
1 comments
(original theme by the-one-and-only Niniane)
"Don't let your beard get in the way of your typing"
Labels: lol
Posted by
Jorge
at
Friday, December 07, 2007
0
comments
From The Economist Plenty of blame to go around, Sep 27th 2007:
"On September 5th Mattel had told an American Congressional committee that
its recall of 17.4m toys containing a small magnet that could be swallowed by
children was due to a flaw in the toys' design, rather than production
flaws in China."
"The boss of a Chinese toy firm involved in a huge safety recall has committed suicide, Chinese media has said. Zhang Shuhong, who co-owned the Lee Der Toy Company, was reportedly found dead at his factory in southern China.
About 1.5 million toys made for Fisher Price, a subsidiary of US giant Mattel, were withdrawn from sale earlier this month. Many were made by Lee Der."
Posted by
Jorge
at
Sunday, September 30, 2007
0
comments
From the [very entertaining] book Ugly Americans by Ben Mezrich, the tale of Ivy League traders playing the Asian markets:
Vaguely Related:
Quotes Entirely Relevant to Software Engineers
Posted by
Jorge
at
Saturday, September 29, 2007
0
comments
Share my ISA (of dubious legality)
On the one hand there is a number of people constrained by the ISA limits but keen on investing their money in a reduced risk, tax efficient way; on the other hand, only a small number of people uses or maximises their ISA.
I see an opportunity to restore the supply-demand balance: investors could agree to "lend" money to the non-investors and maximize their ISAs every year, effectively using the ISA allowance on their behalf. The "borrower" would take a (very) small commission for his ISA allowance rental, a lot better than the nothing they would have received had their ISA allowance remained unused.
As the amount accumulates, the compounded interest may allow a renegotiation more favorable to the borrower.
Twitter Services
Services on the move in an area (e.g.: plumbers) update their status and location; interested parties subscribe to the service they need and request a visit if the professional is available and in their neighbourhood.
Rent My Garage
A garage rental marketplace: matchmaking between people with a garage and no car, and people with a car but no garage.
Paper-Holding Notebook Screen
Computer lids that also serve as page holders by either having a pressure system on the side of the screen to hold the pages in between, or having a supporting frame that slides outwards and becomes a vertical paper tray.
This would simplify tremendously the process of typing while reading from paper notes.
The Chosen 1
Unlike current online dating and matchmaking services, The Chosen 1 "game" goes beyond simple string matching.
Male and female participants are plotted on the screen as blue and pink dots resembling stars in the sky. The game challenges participants over time with questions, puzzles, activities, and preferences, some of which are introduced by the participants themselves, others are created by the system. Traits, tastes, preferences, characteristics, and relationships, can change and evolve throughout a season.
Participants can decide whether they want to attract opposites or like-minded personalities.
The Chosen 1 clusters participants creating "galaxies" and "systems" of attraction. Once certain thresholds are broken, participants within a constellation are allowed to chat online. If two participants of opposite sexes (or same sex if they so selected) are so close that they end up in a cluster of two, they are each others' Chosen 1 and given each others' contacts.
Game Over.
Because ideas are easy, doing stuff is hard. True, true.
Vaguely Related:
Business need: Digg for Consumer Electronics
Posted by
Jorge
at
Thursday, September 20, 2007
0
comments
Morning chat I overheard on the tube:
...
Little Girl 1: I wanna get into facebook
Little Boy: yeah, everyone's in it
Little Girl 2: wha' is it?
Little Girl 1: it's kinda like myspace...my sister's in it too. I really want to join
...
Labels: facebook, myspace, tube
Posted by
Jorge
at
Wednesday, September 19, 2007
1 comments
From the BBC Business News, 31 August 2007:
Barclays says that a "technical breakdown" in the UK's clearing system forced it to borrow £1.6bn from the Bank of England.
It is the second time this month that the bank has tapped into the central bank's emergency credit line, sparking fears it is facing a cash crisis.
...
On 20 August, Barclays was forced to take out a £314m loan from the Bank of England after HSBC was unable to process a last-minute request for the money.
Labels: barclays, bofe, finance, northern rock
Posted by
Jorge
at
Saturday, September 15, 2007
0
comments
A few weeks ago I (accidentally) found out that a pet-project of mine, the cross-lingual instant messaging, wasn't working anymore. Debugging showed the "3rd party translation sub-system" was always returning an empty translation.
The fix revealed my development and maintenance process was flawed:
public interface ITranslationEngine {
/**
* Translates 'text' from a given 'fromLanguage' into a given 'toLanguage'
* @param fromLanguage
* @param toLanguage
* @param text
* @return
* @throws Exception
*/
public String translate(String fromLanguage, String toLanguage, String text) throws TranslationException;
}
...
<aop:config>
<aop:aspect id="translationEngineInterceptor" ref="translationEngineAdvice">
<aop:after-returning
method="notify"
returning="translatedString"
pointcut="execution(* translate(String, String, String)) and args(from, to, text)" />
</aop:aspect>
</aop:config>
<bean id="translationEngineAdvice" class="org.jiim.translation.TranslationEngineAdvice">
<property name="mailSender" ref="mailSender"/>
<property name="mailMessage" ref="mailMessage"/>
</bean>
...
public class TranslationEngineAdvice {
protected final Log logger = LogFactory.getLog(getClass());
private MailSender mailSender;
private SimpleMailMessage mailMessage;
public static final String NEW_LINE = System.getProperty("line.separator");
...
public boolean notifyByEmail(JoinPoint jp, Object translatedString, String from, String to, String text) {
String s = (String) translatedString;
// if message string returns empty translation
if (!"".equals(text) && "".equals(s.trim())) {
SimpleMailMessage msg = new SimpleMailMessage(this.mailMessage);
StringBuffer txt = new StringBuffer(msg.getText());
txt.append(NEW_LINE).append("Date [").append(new Date()).append("]");
txt.append(NEW_LINE).append("From language [").append(from).append("]");
txt.append(NEW_LINE).append("To language [").append(to).append("]");
txt.append(NEW_LINE).append("Original Text [").append(text).append("]").append (NEW_LINE);
msg.setText(txt.toString());
try {
this.mailSender.send(msg);
return true;
} catch (MailException me) {
logger.error("Error sending email notification ", me);
}
}
return false;
}
}
...
Labels: aop, programming, tdd
Posted by
Jorge
at
Wednesday, September 05, 2007
0
comments
AJAX-based Rich Internet Applications (RIAs) make heavy use of JavaScript. To improve the user experience of RIAs we can minimize the number of (JavaScript) file requests and reduce their size.
These concatenation and compression operations can be done on-the-fly or at build time.
On-the-fly techniques intercept requests and perform merging and/or compression in real-time. They are simple(r) to implement, often involving URL rewriting combined with a compress script, but:
<dependency>
<groupId>rhino</groupId>
<artifactId>custom_rhino</artifactId>
<version>0.1</version>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<!-- destination for the compressed JavaScript files -->
<j:set var="js.compression.dir" value="${typically_the_war_src_dir}"/>
<j:set var="js.compression.skip" value="false"/> <!-- enable/disable compression -->
<j:set var="js.compressor.lib.path" value=""/> <!-- path to the compressor jar -->
<goal name="get-compressor" description="Set path to JavaScript compressor library">
<!-- get compressor from dependencies, to allow standalone use of goal -->
<j:if test="${empty(js.compressor.lib.path)}">
<ant:echo message="Fetching JavaScript compressor from repository" />
<j:forEach var="lib" items="${pom.artifacts}">
<j:if test="${lib.dependency.artifactId == 'custom_rhino'}">
<lib dir="${maven.repo.local}">
<include name="${lib.path}"/>
<j:set var="js.compressor.lib.path" value="${lib.path}"/>
</lib>
</j:if>
</j:forEach>
</j:if>
<ant:echo>
Path to JavaScript compressor library is ${js.compressor.lib.path}
</ant:echo>
</goal>
<goal name="compress-js" description="Compress JavaScript across all site components">
<!-- Different sections might have different compression requirements -->
<j:if test="${js.compression.skip == 'false'}">
<attainGoal name="compress-site-js"/>
<attainGoal name="compress-forum-js"/>
...
</j:if>
</goal>
<goal name="compress-site-js" prereqs="get-compressor" description="Compress JavaScript files for 'site'">
<ant:echo message="Compressing JavaScript files for 'site'" />
<j:set var="stripLinebreaks" value="true" />
<j:set var="js.dir" value="${path_to_javascript_files}"/>
<concat destfile="${js.dir}/site-concat.temp" force="yes">
<filelist dir="${js.dir}"
files="file_to_merge.js, another_file_to_merge.js, etc.js"/>
</concat>
<ant:java
jar="${js.compressor.lib.path}"
failonerror="true"
fork="yes"
output="${js.dir}/site-breaks.temp">
<ant:arg value="-c"/>
<ant:arg value="${js.dir}/site-concat.temp"/>
</ant:java>
<!-- move compressed files back from dest to src dir -->
<ant:move file="${js.dir}/site-breaks.temp" tofile="${js.dir}/site_c.js" filtering="true">
<j:if test="${stripLinebreaks == 'true'}">
<ant:echo message="Removing line breaks" />
<filterchain>
<striplinebreaks/>
</filterchain>
</j:if>
</ant:move>
<delete file="${js.dir}/site-concat.temp"/>
</goal>
<goal name="compress-forum-js" prereqs="get-compressor" description="Compress JavaScript files for 'forum'">
<j:set var="stripLinebreaks" value="true"/>
<j:set var="src.dir" value="${path_to_javascript_files}"/>
<!-- Delete previously compressed files -->
<ant:delete>
<ant:fileset dir="${src.dir}" includes="*_c.js"/>
</ant:delete>
<ant:echo message="Compressing 'forum' JavaScript files from ${src.dir}" />
<!-- create temp dir for compressed files -->
<ant:mkdir dir="${src.dir}/_compressedjs"/>
<j:set var="dest.dir" value="${src.dir}/_compressedjs"/>
<!-- compile JavaScript files to compress -->
<ant:fileScanner var="forumJSFiles">
<ant:fileset dir="${src.dir}" casesensitive="yes">
<ant:include name="file_to_compress.js"/>
<ant:include name="another_file_to_compress.js"/>
<ant:exclude name="*_c.js"/>
</ant:fileset>
</ant:fileScanner>
<!-- loop through files and compress using compressor set in 'get-compressor' goal -->
<j:forEach var="jsFile" items="${forumJSFiles.iterator()}">
<ant:echo message="Compressing ${jsFile.name}" />
<ant:java
jar="${js.compressor.lib.path}"
failonerror="true"
fork="yes"
output="${dest.dir}/${jsFile.name}">
<ant:arg value="-c"/>
<ant:arg value="${src.dir}/${jsFile.name}"/>
</ant:java>
</j:forEach>
<!-- move compressed files back from dest to src dir -->
<ant:move todir="${src.dir}" filtering="true">
<ant:fileset dir="${dest.dir}" casesensitive="yes">
<ant:include name="*.js"/>
</ant:fileset>
<j:if test="${stripLinebreaks == 'true'}">
<ant:echo message="Removing line breaks" />
<filterchain>
<striplinebreaks/>
</filterchain>
</j:if>
<ant:mapper type="glob" from="*.js" to="*_c.js"/>
</ant:move>
<!-- delete temp dir -->
<ant:delete dir="${dest.dir}"/>
</goal>
...
var xmlhttp=false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
// JScript gives us Conditional compilation, we can cope with old IE versions.
// and security blocked creation of the objects.
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
xmlhttp = false;
}
}
@end @*/
...
<goal name="conditional-compress-js" prereqs="get-compressor"
description="Conditional compression of JavaScript files">
<j:set var="src.dir" value="${path_to_javascript_files}"/>
<!-- check timestamps to see if compression is required -->
<ant:echo message="Checking timestamps of JavaScript files from ${src.dir}" />
<ant:fileScanner var="jsFiles">
<ant:fileset dir="${src.dir}" casesensitive="yes">
<ant:include name="**/*.js"/>
<ant:exclude name="**/*_c.js"/>
</ant:fileset>
</ant:fileScanner>
<j:forEach var="jsFile" items="${jsFiles.iterator()}">
<ant:echo message="Checking last-modified-date of ${jsFile.name}" />
<uptodate property="js.modified" targetfile="${src.dir}/${jsFile.name}">
<srcfiles dir="${src.dir}" includes="**/*_c.js" />
</uptodate>
</j:forEach>
<j:if test="${js.modified}">
<j:set var="compression.required" value="true"/>
</j:if>
<j:if test="${compression.required}">
<ant:echo message="Modified JavaScript files. Compression required." />
<!-- insert compression block here -->
...
</j:if>
</goal>
Labels: compression, javascript, maven, performance, rhino
Posted by
Jorge
at
Sunday, September 02, 2007
6
comments
Besides encapsulating behaviors that affect multiple classes into reusable modules, AOP can also be used for unobtrusive notification tasks.
Instead of browsing application logs looking for exceptions, for example, I like to have the application itself email me the exception stack-trace and the arguments that caused the error when something goes wrong.
Here's an example advice that sends an email after an exception is thrown. The date of occurrence and the exception stack-trace are sent in the body of the email.
The Java code for the advice:
...
public class ThrowableAdvice {
private MailSender mailSender;
private SimpleMailMessage mailMessage;
public static final String NEW_LINE = System.getProperty("line.separator");
// Setters and getters omitted for clarity
...
/**
* Send email notification with exception stacktrace
*
* @param throwable
* @return boolean frue if email message is sent, false otherwise
*/
public boolean notifyByEmail(Throwable throwable) {
SimpleMailMessage msg = new SimpleMailMessage(this.mailMessage);
StringBuffer txt = new StringBuffer(msg.getText());
txt.append(NEW_LINE).append(new Date());
txt.append(NEW_LINE).append(ExceptionUtils.stackTraceToString(throwable));
msg.setText(txt.toString());
try {
this.mailSender.send(msg);
return true;
} catch (MailException me) {
logger.error("Error sending email notification ", me);
}
return false;
}
}
...
...
public static String stackTraceToString(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
...
...
<aop:config>
<aop:aspect id="appThrowableInterceptor" ref="throwableAdvice">
<aop:after-throwing
method="notifyByEmail"
throwing="throwable"
pointcut="execution(* some.package.*.*(..))" />
</aop:aspect>
</aop:config>
<bean id="throwableAdvice" class="ThrowableAdvice">
<property name="mailSender" ref="mailSender"/>
<property name="mailMessage" ref="throwableMessage"/>
</bean>
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="localhost"/>
</bean>
<bean id="throwableMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="subject" value="ALERT! Exception thrown @ some app module"/>
<property name="text" value="ALERT! Exception thrown from some module"/>
<property name="from" value="the.notifier@application.org"/>
<property name="to">
<list>
<value>developers@application.org</value>
</list>
</property>
</bean>
...
(Vaguely) Related Posts:
Better Living Through (Unit) Testing and Advice
Reusable Advice - Part I
References:
AOP@Work: Dependency injection with AspectJ and Spring
AOP@Work: Check out library aspects with AspectJ 5
AOP@Work: AOP myths and realities
Improve modularity with aspect-oriented programming
Spring AOP Reference documentation
Labels: aop, aspect, programming
Posted by
Jorge
at
Friday, August 31, 2007
0
comments
A few weeks ago, I approached a TfL staff member (let's call him SM) after failing to exit the gate with my Oyster card. Chat transcript below.
Me: "Hi, I can't get out and the machine keeps telling me to seek attention..."
SM (grinning and slowly printing each word in my head): "seek attention? Are you sure the machine told you to seek attention"?
Me (mildly annoyed and a tad confused): "Yes, it displayed the words seek attention in flashing red letters!"
This goes on a few more rounds until SM, visibly amused, opens the gate and let's me through.
...
Labels: foolish
Posted by
Jorge
at
Saturday, August 18, 2007
0
comments