tag:blogger.com,1999:blog-60863238938895445872024-02-18T18:27:24.667-08:00Whatever Gets You UpKindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.comBlogger11125tag:blogger.com,1999:blog-6086323893889544587.post-83646315716816162912013-03-28T02:31:00.001-07:002013-03-28T02:31:10.328-07:00<h2>
HTTP Session attributes considered evil</h2>
<div>
I've been dealing with a Struts 1.x based application in a Java 1.4 environment.</div>
<div>
<br /></div>
<div>
Struts actions, where the majority of the business logic is implemented, are not capable of returning results. Hence the predominant anti-pattern used when one needs to return a value from one action to another, is setting parameters in the request or session scope.</div>
<div>
<br /></div>
<div>
This quickly gets out of hand and the code becomes what I'd term "Java with a mixture of the JavaScript bad parts" - a global object with untypized access, where all actors read and write as they deem appropriate.</div>
<div>
<br /></div>
<div>
Here are the most notable disadvantages to this approach:</div>
<div>
<br /></div>
<div>
- Unconvenient (raw) object access</div>
<div>
- Complete reliance on side effects as opposed to returning values completely inhibits the functional coding style, and makes your application hard to reason about.</div>
<div>
- The fact that one actor relies on another to have written data in session/request so that the other can read it, introduces strong temporal coupling between the actors.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
I would go far as to argue that any framework which relies on session and request objects to pass data between actors is evil.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-11149177576782647652012-01-19T04:23:00.000-08:002012-01-19T04:23:55.301-08:00Central workflow with Git<b>Abstract: </b>This post will describe the characteristics of a development workflow in a traditional software enterprise, and prescribe a Git workflow which implements it. <br />
<br />
More and more large software enterprises are basing their development infrastructure on Git. (The possible reasons will not be discussed in this post.)<br />
<br />
An interesting thing about this phenomenon is that tranditional software development in these companies is done with a very centralized process. More precisely, there is one central repository per project. Everybody "submits" and "syncs" from one and the same "development" / "stream" branch in that repo.<br />
Furthermore, people do not want to isolate themselves from others' changes, or isolate others from their changes, when in the "stream" branch. On the contrary, they want to share changes with others as often as possible, and always work with the latest and greatest from other team members, effectively practicing what is known as "Continuous Integration".<br />
<br />
On the other hand, Git beginners do not know how to implement this wokflow with Git. They work ineffectively with it and abuse it. This is so because Git is very complex - the author's humble opinion is that it is much more complex than it should be.<br />
A typical sign of this inefficiency is people merging their own changes, or, in general, commits which contain no diff but only merge histories.<br />
<br />
<u><b><br />
</b></u><br />
<u><b>The Workflow</b></u><br />
This was first suggested to me by Borislav Kapukaranov.<br />
<u><b> </b></u><br />
<b>Peter wants to develop feature "Adding validation logic to editor"</b><br />
Steps:<br />
<b> </b><br />
1. Create a branch "validation"<br />
2. Develop Commit develop Commit develop Commit. Done, ready to submit<br />
3. Pull master to obtain latest changes from the team<br />
4. Rebase "validation" on top of master. <i>This replays your local work on top of the new origin/master history. It rewrites your commits as if you were always developing on top of the latest version of the remote. <b>This is exactly your intention in a centralized / continuous integration workflow.</b></i><br />
<i>Please make sure your working tree is clean when doing a rebase, otherwise it won't work. Working tree clean means you have committed all your local changes.</i><br />
<br />
<i><b>5</b></i><i><b>. <span style="color: red;">(Optional) Rebase fails because of conflicts with remote history</span></b></i><br />
6. (Optional) Peter resolves conflicts through IDE or text editor. Peter adds the modified files to the staging area by performing "<span style="font-family: "Courier New",Courier,monospace;">git add</span>" on the modified files<br />
7.(Optional) Peter continues rebase.<br />
<span style="font-family: "Courier New",Courier,monospace;">git rebase --continue</span><br />
<span style="font-family: inherit;">Rebase finishes successfully.</span><br />
<br />
<span style="font-family: inherit;">8. Peter is ready to push their work to the remote master:</span><br />
<span style="font-family: inherit;"><span style="font-family: "Courier New",Courier,monospace;">git push origin validation:refs/heads/master</span></span><br />
<span style="font-family: inherit;">(push my local branch "validation" to the remote branch "master" of remote "origin") </span><br />
<br />
<u><b>The lazy Workflow</b></u><br />
This is "the fucking short version" for people who do not wanna bother with creating local branches for everything they develop. Okay. Working on the master branch is not a good practice, and I do it too.<br />
<br />
<i>Why working on master branch is inferior from the above described approach:</i><br />
<i>- If you create dedicated branch for every task you do, you can work on many pieces of work independently by just switching branches;</i><br />
<i>- If you work on local dedicated branch, when you <span style="font-family: "Courier New",Courier,monospace;">git pull master</span>, <span style="font-family: inherit;">there will<b> never </b>be conflicts</span>.</i><br />
<br />
Anyways, here is the lazy workflow<br />
<i> <on branch="" master=""><br />
</on></i><br />
1. Develop commit develop commit develop commit<br />
2. <span style="font-family: "Courier New",Courier,monospace;">git pull --rebase</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: inherit;">3. (optional)</span> </span> Rebase fails. Resolve conflicts, add files to index, continue rebase<br />
4. git pushKindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-54614204686501157472011-01-16T14:05:00.000-08:002011-01-16T14:11:34.996-08:00Why not to use intristic lockingpackage crap;<br />
<br />
public class Deadlock {<br />
<br />
public static void main(String[] args) throws InterruptedException {<br />
Deadlock deadlock = new Deadlock();<br />
deadlock.go();<br />
}<br />
<br />
private void go() throws InterruptedException {<br />
ImplicitLock lock = new ImplicitLock();<br />
FooRunnable r1 = new FooRunnable(lock);<br />
// ExplicitLock lock = new ExplicitLock();<br />
// FooRunnable r1 = new FooRunnable(lock);<br />
<br />
synchronized (lock) {<br />
Thread t1 = new Thread(r1);<br />
t1.start();<br />
t1.join();<br />
}<br />
}<br />
<br />
class ImplicitLock implements Fooable {<br />
public synchronized void foo() {<br />
int x = 2;<br />
x++;<br />
}<br />
}<br />
<br />
class ExplicitLock implements Fooable {<br />
private final Object monitor = new Object();<br />
<br />
public void foo() {<br />
synchronized (monitor) {<br />
int x = 2;<br />
x++;<br />
}<br />
}<br />
}<br />
<br />
interface Fooable {<br />
void foo();<br />
}<br />
<br />
class FooRunnable implements Runnable {<br />
private Fooable fooable;<br />
<br />
public FooRunnable(Fooable fooable) {<br />
this.fooable = fooable;<br />
}<br />
<br />
@Override<br />
public void run() {<br />
fooable.foo();<br />
}<br />
<br />
}<br />
}KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-91894323200388729302010-12-15T10:03:00.000-08:002010-12-15T10:07:32.986-08:00TDD AntipatternsThis time I just want to link to something really rich I encountered - <a href="http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/">a catalogue of TDD antipatterns</a>.<br />
<br />
It really relates to my current work on a legacy system, where a lot of tests exhibit "The Liar" and "The Greedy Catcher". This way, even a successful run of the full suite (which happens quite rarely) gives you a sense of false safety.KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-14404121999371336272010-09-25T05:38:00.000-07:002010-09-25T05:38:19.654-07:00<span class="Apple-style-span" style="font-family: Arial; font-size: medium;"><span class="Apple-style-span" style="font-size: 16px;">It's surprising how simple concepts could evade one's mind for incredible amounts of time. I'd never explained to myself fully what a static class or interface means. Never took the time to read about it.</span></span><div><br />
</div>KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-37845839387356872372010-09-01T03:55:00.000-07:002010-09-01T03:57:27.367-07:00Open-source and code-qualityOver the past few months, at the Eclipse demo camp in Sofia first, I've been hearing about the <a href="http://www.eclipse.org/virgo/">Virgo</a> server,<br />
part of the EclipseRT project.<br />
Although the main current usecase of Virgo is deploying and running JEE Web modules and Spring apps, in its core it is just an OSGi runtime. This means that it can be distributed without the Tomcat servlet container, and since it has the OSGi modularity, you can potentially create different 'servers' out of it to suit your needs, provided you deploy the bundles you need for the job.<br />
To be honest, I haven't given it a try yet. The reason I am writing this is that last night I took a quick glimpse of the <a href="http://wiki.eclipse.org/Virgo/Source#Virgo_git_Repositories">source code</a> of the kernel, and... Well, first-looks show the code is really high-quality. It leaves you with the impression that if you check it out, you can start working with it immediately, having little to no knowledge of the domain area. To a person with no knowledge in the product, stuff seems cleanly designed, hidden behind interfaces, documented, unit and integration tested, so forth. I don't think I encountered a class > 500 LOC, which was quite a pleasant surprise.<br />
The only thing that really puzzled me and turned me off a bit :) "<span class="Apple-style-span" style="font-family: Arial, sans-serif; font-size: small; line-height: 19px;">The use of the 'final' qualifier on method parameters and local variables is strongly discouraged.</span><span class="Apple-style-span" style="font-family: Arial, sans-serif; font-size: small; line-height: 19px;"> </span>" (from the product's <a href="http://wiki.eclipse.org/Virgo/Committers">code convention</a>) Ah well. Question of taste...<br />
Anyways, at a first glimpse, at least the internal quality of this product seems to showcase what open-source can achieve. (Well, it's been open-source for some time now, <a href="http://en.wikipedia.org/wiki/SpringSource_dm_Server">the initial contribution was made by SpringSource</a>, so kudos you guys).<br />
<br />
You can take a look what is being planned for the product <a href="http://wiki.eclipse.org/Virgo/Future">here</a>. If you are interested to know about the Virgo community, or interested to become a contributer, <a href="http://wiki.eclipse.org/Virgo/Community">check this out</a>.<br />
<br />
I'll take the time to <a href="http://www.eclipse.org/virgo/download/">take her for a spin</a> as soon as I get some school work off my back this week...<br />
With relation to one of my previous blogs (in Bulgarian), one should be able to use Virgo as a nice RAP runtime, if they deployed the RAP platform bundles on Virgo. No servlet bridge or stuff.KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com1tag:blogger.com,1999:blog-6086323893889544587.post-82552955562481529212010-08-29T07:19:00.000-07:002010-12-15T10:16:59.253-08:00<div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;">UnsupportedOperationException controversy</span></div><div style="text-align: center;"><span class="Apple-style-span" style="font-size: x-large;"><br />
</span></div><div style="text-align: justify;">In my view, some controversy can be seen concerning the existence of the UnsupportedOperationException class.</div><div style="text-align: justify;">The <a href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">LSP</a>, one of the <a href="http://en.wikipedia.org/wiki/Solid_(object-oriented_design)">SOLID</a> principles of object-oriented designs, suggests that correct OO systems should exhibit behavioural subtyping. This informally means that replacing occurences of e.g. Dog objects inside your program with e.g. Pitbull objects should result in your program functioning the way it did before, provided Pitbull is a subtype of Dog.</div><div style="text-align: justify;">If a Dog class correctly implements the bark() method, and Pitbull throws an UnsupportedOperationException in its bark() method, behavioural subtyping is no longer the case. Clients of Dog relied it is perfectly safe to invoke its bark() method, and replacing occurences of Dog with Pitbull will make client code fail everywhere the method in question is being invoked.</div><div style="text-align: justify;">Such an OO system obviously exhibits bad design. When the behavioural is-a relationship between parent and child class is broken, polymorphism cannot be utilized to its full power.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">An extreme opinion can be stated that the very existence of UnsupportedOperationException gives the tool for junior programmers to violate the LSP.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">So why does UnsupportedOperationException exist in the first place, and what is its correct usage?</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">Good insight can be gained by studying <a href="http://download-llnw.oracle.com/javase/1.4.2/docs/guide/collections/designfaq.html#1">what the Java Collection Framework designers have to say</a>. They chose to create fairly generic interfaces with <i>optional operations </i>instead of a large set of specific intefaces, to circumvent two problems:</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><i>- There could be interfaces for lots of specific stuff - modifiability, mutability, etc. Combining each new Interface (concept) with the already existing ones creates twice as much artifacts in the hieararchy. It also creates a parallel hierarchy, which is an OO antipattern (ModifiableLinkedList on the one side, ImmodifiableLinkedList at the other). Repeating the process e.g. a second time makes, it the worst case, four times more artifacts and two parralel hierarches each consisting of a set of two parralel hierarchies in their turn. Ultimately the size of the hierarchy blows up exponentially. Needless to say, implementing code reuse through the parallel branches becomes a major pain in the ass, since Java has no multiple inheritance. You will have to introduce delegation to circumvent this, but when you add one more concept to the framework, you will again end up with a parallel hierarchy in the delegate objects, and you will need one more layer of delegation to circumvent the problem again. Such design soon becomes unmanageable, even for simple enough domain areas.</i></div><div style="text-align: justify;"><i>- There was no way to envision all the sets of concepts (interfaces) that will have to be introduced to suit all possible real-world Collection implementers.</i></div><div style="text-align: justify;"><i><br />
</i></div><div style="text-align: justify;">So the approach of generic interfaces with optional operations was selected. Implementors to which a certain method was not relevant, would throw an UnsupportedOperationException.</div><div style="text-align: justify;">Such an approach possibly violates the <a href="http://en.wikipedia.org/wiki/Interface_segregation_principle">Interface Segregation Principle</a> on the provider side, but it is a compromise against an exponential size of the hierarchy. In OO Design, one always needs to compromise - this is the tricky part of it.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">To sum up, it is permissible in some cases to use the UnsupportedOperationException, to signal that a certain method in the interface is not relevant for an implementation in question. However, throwing this exception in a method which is correctly implemented in the parent class, is always a mistake and a marker of bad design, and must be avoided. Strengthening the throws clause of a child ( <i>@sigals UnsupportedOperationException if(true)</i> ) is a violation of the LSP and hinders use of polymorphism.<br />
<br />
The described above child classes most likely realize the more general (anti)-pattern of implementation inheritance. This means they do not have a easily reasonable is-a relationship with the parent class, but rather extend it to reuse some of its code. Implementation inheritance is almost always a bad phenomenon , signalling that the domain was not object-modeled in the best way possible. Implementation inheritance has fallen out of favor for the same reason UnsupportedOperationException can be considered controversial - polymorphism cannot be safely used in such cases.<br />
<br />
</div>KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-4229284434031544962010-06-21T12:06:00.000-07:002010-06-21T14:06:23.034-07:00Кратък поглед върху Rich Ajax Platform<div style="text-align: center;"><div style="text-align: justify;"><br />
<span style="font-size: medium;"><span style="font-size: large;">Първо прочетете това :)</span><span style="font-size: small;"><br />
Тази статия </span></span>е прекалено кратка и няма за цел да запознава читателя с Eclipse Plug-in Development или Rich Client Platform Development. Предполагат се общи познания по Eclipse и Eclipse разработка. <a href="http://www.eclipse.org/articles">http://www.eclipse.org/articles</a> и <a href="http://www.vogella.de/articles/Eclipse">http://www.vogella.de/articles</a><a href="http://www.vogella.de/articles/Eclipse">/Eclipse</a> са две добри места за "входна точка" в Eclipse. Започнете първо с навлизане в Eclipse откъм потребителска гледна точка, после се запознайте с архитектурната концепция на платформата, след което можете да се ориентирате към Plugin Development и RCP development спецификите. Ако вече сте направили това, тази статия ще ви даде непретенциозно въведение в RАP.<br />
<span style="font-size: medium;"><br />
<span style="font-size: large;"> Web Application Development + Java ?</span><br />
<span style="font-size: small;">Изглежда, че към днешна дата, по-големият процент Java програмисти, включително опитните такива, не са способни да разработят Web-базирано приложение, било то богато, или не. Аз самия се вписвам в този процент. Причината е</span></span><span style="font-size: medium;"><span style="font-size: small;">, че този тип работа изисква специфични познания - в миналото HTML, а сега - JavaScript, CSS, Flash, ... Въпросните Java програмисти пък нямат огромно желание да учат тези технологии, и така продължават да странят от света на Уеб приложенията. Това е проблем за софтуерната индустрия - голямо количество програмисти, част от които доста способни, не могат да бъдат оползотворени да работят точно по това, което най-много се харчи в днешно време - Web Frontend-ове. От друга страна е много трудно да си намерим качествен Web Developer - такъв, който е едновременно добър софтуерен инженер, и владее добре въпросния спектър от технологии.</span><span style="font-size: small;"></span></span><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: small;">Един наш шанс за Ajax в Java света, или по-точно Java EE света, е JSF. Това обаче, поне откъм начин на писане на кода, не е чиста Java технология, и отново създава бездна между Java програмирането и Web разработката. Разбира се, JSF има и големи плюсове, които няма да обсъждаме тук.</span></div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"><span style="font-size: large;">Алтернативи... </span></div><div style="text-align: justify;"></div><div style="text-align: justify;">Като говорим за RIA разработка на чиста Java, първото за което всички сме дочули и се сещаме е GWT. Вече се появяват и server-side Java технологии за RIA разработка, използващи GWT за рендериране на страницата при клиента. Така или иначе, вече се налага схващането, че е полезно да можем да пишем уеб приложения на чиста Java.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><span style="font-size: large;">Какво ви трябва, за да пишете за RAP...</span></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;">Средата е невзискателна. Всякакъв Servlet Container (напр. Tomcat 6 инсталация), и Eclipse дистрибуция, на която да си инсталирате RAP инструментариум. След малко ще научим , че разработката на RAP приложения е много подобна на RCP/Plugin Development, следователно ще са ви нужни нещата от PDE (Plugin Development Environment) дистрибуцията. Силно препоръчително е да позлвате най-новата версия на Eclipse - за момента Galileo, след две седмици ще излезе Helios. RAP инструментариума се инсталира от <a href="http://download.eclipse.org/rt/rap/1.2/update">този Updatesite</a>.</div><div style="text-align: justify;">За да се компилират вашите RAP приложения, ви е нужна RAP target platform-а - как се инсталира тя пише <a href="http://www.eclipse.org/rap/downloads/runtime-install.php">тук</a>.</div><div style="text-align: justify;"></div><div style="text-align: justify;"></div><div style="text-align: justify;"><i>RAP инструментариума предлага вграден Web Server (Jetty), така че Tomcat не ви е нужен задължително. В случай, че ползвате Jetty, или друга OSGi-базирана разработка, например DMServer/Virgo, <a href="http://felix.apache.org/site/index.html">Felix</a>, ще можете да се възползвате от много добра интеграция с еклипс, и лесен build/deployment. В противен случай ще ви се наложи да пакетирате приложението си в WAR архив и да използвате така наречения <a href="http://www.eclipse.org/equinox/server/http_in_container.php">OSGi Servlet Bridge</a>, за да пуснете OSGi фреймуърк в сървъра, който ползвате.</i><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: medium;"></span><br />
<span style="font-size: medium;"><br />
</span><br />
<span style="font-size: large;">Какво е RAP</span><br />
RAP е платформа за разработка на RIA, емулираща RCP в уеб среда.<br />
Тази картинка ще видите на <a href="http://eclipse.org/rap/introduction.php">http://eclipse.org/rap/introduction.php</a>, както и на всяка презентация/статия, въвеждаща в темата:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwO7UV-TMV-el2F3iZF8Fl3Bxg4_LOzcbkbc5jYMdntiN-EdjG7XjFOqbc3mm7EgTduvuc2_zk4bBcwAjKo5JL1AAHqg6GlLyAmynQcz09oRQOalCj6Vcq_mZPtbYyFRmRyP91FCwQo4Q/s1600/architecture.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="133" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwO7UV-TMV-el2F3iZF8Fl3Bxg4_LOzcbkbc5jYMdntiN-EdjG7XjFOqbc3mm7EgTduvuc2_zk4bBcwAjKo5JL1AAHqg6GlLyAmynQcz09oRQOalCj6Vcq_mZPtbYyFRmRyP91FCwQo4Q/s400/architecture.png" width="400" /></a></div><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Основната цел на RAP е да позволи разработка на богати интернет приложения, преизползвайки Eclipse / RCP програмния модел. Така, на теория, един Eclipse разработчик може веднага да почне да пише Уеб приложения за RАP, без нужда от допълнително обучение.<br />
<br />
Основните разлики са две - вместо SWT (Standard Widget Toolkit) - Eclipse-ката Native UI библиотека, се използва RWT (RAP Widget Toolkit). RWT на свой ред, вместо викания към операционната система, прави викания към Servlet контейнера, в който работи, който на свой ред обслужва клиента - уеб браузър, поддържащ JavaScript.<br />
<br />
Нека да налегнем че RAP е платформа за писане на проложения, а не на уеб сайтове. С преизползването на RCP модела могат с не много код да се имплементират бизнес приложения, поддържащи задачи, които са свързани с комплексна интеракция с потребителя. <br />
<br />
Пример за такова приложение е <a href="http://ondemand.yoxos.com/geteclipse/start">http://ondemand.yoxos.com/geteclipse/start</a>. Инструмента позволява да си свалим "тунингована" по наша преценка Eclipse дистрибуция, с всички нужни ни функционалнисти, като има филриране по операционни системи, Plugin/Feature Dependencies се установяват автоматично, има възможност за запазване на Download шаблони, и така нататък. <br />
<br />
<span style="font-size: large;">За какво можем, и за какво - не бива, да използваме RAP</span><br />
Тук ще скицираме кратки критерии, които можете да 'огледате' преди да направите архитектурния избор да ползвате RAP за вашето RIA приложение.<br />
<br />
<b>М</b><b>ожем</b><br />
- Да показваме по структуриран начин различни набори информация/артефакти, живеещи на мрежата <br />
- Да управляваме комплексни взаимодействия с потребителя<br />
- Да изпълняваме бавни операции асинхронно, запазвайки responsiveness на интерфейса<br />
- Да имплементираме WYSIWYG редактор... <br />
<br />
Ако голяма част от изброените функционалности не съществуват като изисквания към приложението ни, RAP може би <b>не е добър избор</b>.<br />
<b>Например - </b><br />
- Ако информацията, която обработваме е чист текст, който няма нужда да бъде структуриран.<br />
- Ако само визуализираме артефакти в приложението си, но не се налага да се редактират.<br />
- Ако взаимодействията ни с потребителя не са сложни<br />
<br />
-Ако пишем сайт - бил то и такъв, в който се споделя съдържание<br />
- Ако не се изисква висока responsiveness на интерфейса - тогава трябва да се запитаме, нужен ли ни е Ajax фреймуърк изобщо.<br />
<br />
<span style="font-size: large;">Разширяемост и управление на жизнения цикъл</span><br />
<span style="font-size: large;"><span style="font-size: small;">Ако вашето богато уеб проложение има изискване за разширяемост (лесно добавяне на нова функционалност)</span> </span>и обширна конфигурация (промяна на начина, по който работи съществуващата такава), RAP може да се окаже много добър избор на платформа. Тези две функционалности идват наготово от еклипс програмния модел - а именно plugin компонентния модел и runtime модела - OSGi.<br />
Новата функционалност, която сме внедрили, на практика би представлявала просто един нов plugin във Equinox(OSGi) инстанцията, в която върви приложението. Това означава, че при добри условия, крайния потребител ще я види, след като рестартира браузър сесията си.<br />
Бихме могли и да напишем функционалност, която е абстрахирана от конкретните начини, по които ще бъде разширявана, четейки от <a href="http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IExtensionRegistry.html">Extension Registry</a> имплементации на добре дефинирани Java интерфейси. <br />
Обновяването на съществъваща функционалност също може да мине напълно безболезнено за цялото приложение, стига въпросната нова промяна да не е <a href="http://wiki.eclipse.org/Version_Numbering#When_to_change_the_major_segment">счупваща </a>за останалия свят, и Plugin-ите които зависят на функционалността, да са толерантни към промяна на малките сегменти на версията.<br />
<br />
<span style="font-size: large;">Основни потребителски сценарии на RAP</span><br />
Основният сценарий, заради който е разработена RAP платформата, се нарича Single-Sourcing. Целта е да може от една и съща код база да се създават богати клиентски и уеб приложения. (RAP & RCP) На практика ще има известна разлика между крайните версии на двете кодбази, но тя ще е доста ограничена, а и съществуват техники да се управлява лесно, както е подробно обяснено в прикачената към библиографията книга.<br />
Single-sourcing техниката може да бъде използвана, когато искаме да таргетираме по-голяма клиентска група с приложението си, когато искаме да и осигурим по-всеобхватен достъп към него, или пък защото сме в начален стадий на разработка и не желаем все още да вземем решение, дали ще продаваме приложението си като "дебел клиент" или уеб клиент. <br />
<br />
Друг сценарий би бил мигриране на съществуващо клиентско приложение към Уеб среда. Представете си например даден мейл клиент, който се използва в корпорация X като десктоп приложение. Всеки update на клиента,който трябва да се наложи на всички машини в корпоративната мрежа, е скъпо, времеемко и сложно занимание. С мигриране към Wеb интерфейс напълно се губи нуждата от такава процедура, тъй като софтуера се обновява само на едно място - на приложния сървър.<br />
Обикновено е възможно да се мигрират съществуващи Eclipse/RCP приложения към RAP <b>без голямо усилие</b>. Статистически около 75% от кодбазата не се нуждае от никаква модификация.<br />
Въпросното мигриране е добре изследвана територия, и е описано в книгата, спомената в библиографията долу. Сериозни проблеми бихме имали с приложения, които разчитат много силно на наличието на локална файлова система, работейки с org.eclipse.core.resources интерфейса.<br />
<br />
Не на последно място, съществува и сценария, в който бихме желали да изградим чисто ново WEB приложение с RAP, защото сме решили че платформата е подходяща за задачата.<br />
<br />
</div><div style="text-align: justify;"></div><div style="text-align: justify;"><span style="font-size: large;">Ограничения на RAP</span><br />
Тъй като до сега бяхме изцяло оптимистични, е добре да споменем няколко ограничения на RAP с цел "управляване на очакванията".<br />
<br />
- Неразполагането с операционна система, към която да се обърнем, ни лишава от някои възможности, които бихме очаквали в RCP. Например, не можем свободно да рисуваме произволни наши форми върху интерфейса. (Това може да се промени в близко бъдеще с навлизането на <a href="http://en.wikipedia.org/wiki/Canvas_element">HTML 5 Canvas</a>)- Разпределеният характер на мрежовите приложения, латентността в мрежовата инфраструктура, и изискванията за скалируемост на цялото уеб приложение, налагат промени и рестрикции върху RCP програмния модел. Както се вижда от архитектурната картинка, тези промени са основно в <a href="http://wiki.eclipse.org/WidgetToolkit">RWT API-то спрямо SWT</a><br />
- Участниците в RAP проекта са работили доста върху скалируемостта на платформата. Въпреки това не е добра идея да се използва RAP за имплементиране на тежка бизнес логика, защото приложението, за разлика от RCP света, може да бъде достъпвано от мног клиенти едновременно. Добре е подобна логика да се изнася в бизнес компоненти, които могат да скалират добре, например EJB, и RAP да се ползва за презентационен слой и "леки" обработки на данни, с цел показване в интерфейса, и подаване обратно на бизнес логиката. <br />
<br />
<i><b>бибиография:</b></i><br />
1. <a href="http://www.eclipse.org/articles">http://www.eclipse.org/articles</a><br />
2. <a href="http://help.eclipse.org/">http://help.eclipse.org/ </a><br />
3. <a href="http://www.eclipse.org/rap">http://www.eclipse.org/rap</a> <br />
4. Eclipse Rich Ajax Platform - bringing Rich Clients to the Web - Fabian Lange.<br />
<br />
<br />
<br />
<span style="font-size: large;"><br />
</span><br />
<br />
<br />
</div></div>KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-58453167017597625092009-11-30T00:32:00.000-08:002009-11-30T01:07:43.113-08:00<span style="font-size:130%;">Quick tip to reduce hierarchical complexity.<br /><br /><span style="font-size:100%;">This week it is going to be a short and simple one.<br /><br />Say we build a set of classes implementing a particular interface. For the sake of example we can imagine these classes represent abstractions of test applications used in integration testing fixtures. We may have various implementers - web service provider applications, consumer ones, EJB applications, web applications, so forth.<br />Inevitably at one point in time we will realize there is common logic between the implementers - for example, creating some j2ee modules and assigning them to an ear module; deploying the application; cleaning up after the application on the fixture teardown, etc.<br />We may be tempted to remove the code duplication with polymorphism. This is a very common approach, but not a sound one in my opinion.<br />So, we create a base class to hold the common logic, and the implementers extend it. Why not cool? Well, the problem is that the classes extending this base class have little in common in terms of domain modeling. The only thing in common they have is code. Even the name of the base class will suck, which reveals a code smell. Such classes are usually called (in our example) "BaseApplication", "AbstractApplication" or something like that.<br />So, what to do? Simple, create utulity class or classes to hold the common code and refer them inside your hierarchy. Now, if certain implementers have special stuff to do to extend the common tasks logic, the utilities might also follow some hierarchy.<br />Some may say that utility objects are also a code smell, but this approach is cleaner in terms of Object modeling and simpler in terms of type hierarchy, than having a "BaseApplication". Also, it is more flexible, because Java does not support multiple inheritance (thank goodness), so when not extending the 'base application', you retain the option to extend some real class from your domain model.<br /></span></span>KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-79529588045111228902009-06-19T01:25:00.000-07:002009-09-12T02:46:57.508-07:00<span style="font-weight: bold;">Integration Testing vs Unit Testing</span><br /><br />In my last blog I described an approach to testing which i called 'Integration Unit Testing'. The basic idea was to unit-test a class against its real-life environment rather than relying on a mocking infrastructure. I cannot tell whether and when this approach is good to follow - i guess that it is a question of personal taste and intuition, as are many aspects of programming.<br />Another approach i would like to address, this time briefly, is the one of 'Integration Testing instead of Unit Testing'.<br />In a nutshell, here's what this is about:<br />You are given some tack to accomplish. First you write classes ImportantClass and classes B and C. Class ImportantClass does some importantStuff(), and with the aid of classes B and C seems to get the job done. Being a TDD guy, you of course beforehand write the unit tests for B, C and ImportantClass.<br />A few hours/days later, you discover that what you came up with is bullcrap and decide to revert your changelist. You write some new Aid classes D and E, and alter class ImportantClass to use them. Of course, BTest and CTest are no longer relevant, and you have to write DTest and ETest in their place.<br />Now the real bad thing is that ImportantClassTest is also no longer valid. The problem is that, being a unit test, in its bulk it does not assert that some high level business functionality is achieved. Rather, it very much asserts that e.g. some methods of B and C are called with some parameters.<br />Being a patient guy, you say: 'O.K. Patience my young padawan'. (You say it to yourself, knowing it is healthy for a programmer to talk to themselves.) So you rewrite the ImportantClassTest.<br />Of course, a day later you have another ephiphany and discover that the concept of classes D and E is bullcrap also. So you do another revert and end up in the same ordeal, rewriting again ImportantClassTest, and very much wanting to bite off your arms now.<br />If you end up in such a situation, where classes B,C,D, E and so forth are likely to change/drop with each ephiphany you have or each new developer working on the code, it is likely an indication that classes B,C and so forth are not key aspects to the business functionality to begin with. If they are not, perhaps the effort is not worth to unit-test them at all. So what you could do is write an ImportantClassIntegrationTest, test only thru the ImportantClass's public API and assert some business objectives are really achieved. Then you measure the test's code coverage against the code of ImportantClass and the current B, C and so forth classes. If the metric is OK, you also have done OK.<br />To further emphasise on the small importance and transient-ness of the B and C and so forth classes, you can either:<br /><ul><li>nest them inside the ImportantClass</li><li>create them inside the package of ImportantClass and give them package visibility.</li></ul>KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0tag:blogger.com,1999:blog-6086323893889544587.post-44257536319027799502009-06-07T09:02:00.000-07:002009-06-07T10:08:10.512-07:00A few words on Unit Testing, Integration Testing and MocksThe last week at work, I was given the task to rewrite an IMHO crappy piece of code dealing with deletion of objects in some Eclipse-based editor. Actually, I just had to fix a bug and add a small new feature, but things turned out FUBAR, so after 2 changelist reverts i realized refactoring just wont do the job and tests and productive code have to be rewritten.<br />Half of the task went down to implementing a 'Delete' command for some Metamodel module upon which our editor, and our tool, is based.<br />So, trying to be a TDD guy, i sat down and started to devise a test. I devised an elaborate test fixture mocking all the functionality i had to interact with in the Metamodel module. This way, i figured, I would abstract my code from this Module as much as possible, and protect from failures in it - even if it failed, i figured, my test would still pass, and my code would still be correct.<br />So I spent 3 hours mocking this Metamodel, 1 hour more to devise my tests, then i implemented the functionality in the Command; and everything was tip-top - or so i thought.<br />The next day I got me a cup of coffee, sat at the desk and tried to manually test my code against the productive Metamodel module. Surprisingly to me, my command worked for only 20% of the input data. It turned out that the branches of the class responsible for the remaining 80% of the cases invoked the Metamodel in such manners that it would always throw exceptions. And still my low-granular, carefully devised JUnits were passing.<br />So all the JUnits i had written really were a prerequisite for my code to be correct, but they still gave little value to the product.<br />I sat down and wrote integration tests of my class with the real Metamodel module. Almost all of them would fail on the existing code. The assertions were all based on problems i saw in the live behaviour of the editor. After i get them all green tomorrow morning, the code will be next to rocket-stable.<br />Unit-testing, and Mocking for that matter, is rarely always the answer. Think about it - why would you want to abstract your Command from module 'A', when this Command exists for the sole purpose of interacting with module 'A', and not module 'B', 'C' or some other? Having your Unit tests pass and the integration fail is just having code that is correct from your point of view, but incorrect from the System / Customer / User point of view.<br />Furthermore, this Metamodel module was so widely reused throughtout our application stack, that the test fixture for the Integration test consisted of just calling a couple of handy lookup and creation services - it took me less than 20 mins to write, and the value for the product was times the value I achieved with 3 hours of EasyMocking. (what's so easy about it anyway?!).<br />Unit testing in isolation has real value for units which are low in the Application Stack. Mocking has real value when real object interactions are VERY expensive or VERY hard to setup. A Module high in the Application Stack of usages often has no real meaning without the Modules it relies upon; it was likely designed with these Modules in mind, and so it should - we shouldnt abstract for the sole purpose of abstraction. Likewise, tests abstracted from these modules can become meaningless too.<br />So, if you have a Class relying on lots of other functionality, do not rush to mock this functionality when testing. If it is not hard and expensive, setup the real functionality, feed it to the Class, and feed the Test with lots of test data. Data drive it, giving it boundary cases, normal cases, illegal inputs and anything in between. If the test passes then, you'll get real confidence the class gets the job done.<br />The only remaining problem is that if the modules which you rely upon fail, your Integration test will fail, too. Then, you will have harder root cause analysis and misleading test run reports. On the other hand, if A is designed as something on top of B and C, and B and C fail, does it really make sense for A's tests to pass?<br />The described approach has one more advantage.All the tests of classes high in the usage stack also test the low-level modules in various different circumstances equal with their real requirements, so as a result, as the project evolves, the low-level modules should become very robust.<br />So perhaps Unit-testing a class against its live environment is not such a bad idea. Hesitantly, i will call this approach 'Integration Unit Testing'. I shall derive more practical experience and give it some more thought, and perhaps write about it in my later blogs.KindofSickButCoolStillhttp://www.blogger.com/profile/09980054385655483167noreply@blogger.com0