tanulj meg fejleszteni. Nem a platform alkalmatlan, hanem te.
A tesztelések során az derült ki, hogy bár különböző készülékeken és verziókon jöttek elő a problémák, mégsem a fragmentáció volt a gond, hanem klasszikus szoftverfejlesztési hibák. Ezek pedig nyilván nem jöttek elő pl. az 1GB RAM-al bíró Galaxy S II és hasonló erőműveken. Tanultak a hibáikból, jobb kódot készítettek és jobb fejlesztők lettek. Arról nem beszélve, hogy a leckét is megtanulták: nem a fragmentáció, hanem a programozói tudás hiánya a probléma.
A legnagyobb probléma pedig végig az volt, hogy egyszerűen nem ismerték a platformot és nem szenteltek neki elegendő időt.
>
Instead of
$("#example").text("Hello, world!");
$("#example").css("color", "red");
user
var $example = $("#example");
$example.text("Hello, world!");
$example.css("color", "red");
Missing parts of selectors
Repetitive Selectors
Unused Shortcuts

Nagyszerű könyv. Lean elvek. Agile fejlesztés. És nem az elmélet, hanem az, hogyan ténylegesen megvalósították egy állami projekten a svéd rendőrségnél.
Többszörösen is aktuális számomra, mert én is közszférában létező projekten dolgozom. Az ottani tapasztalatok és problémákat én is látom a saját szervezetemben. És ha ezek a módszerek ott beváltak, akkor itt az EU más intézményeiben is működnie kell.
Gyakorlatilag semmi elmélet. Csak a véres valóság.
"Bezzeg én megmondtam" oktatókönyv.

Bezzeg: Gazdag papa próféciáiban igen hosszan kifejtett egy jövőképet, amit már gazdag apja vezetett fel. Azt a jövőképet, ami a "Monopoly-pénz" bevezetése hozott magával. Az egésznek van egy "bezzeg én megmondtam" stílusa.
Oktató:
Egyik rész sem mondott újat. Egyszerűen a 2008-as gazdasági válság tükrében sokkal reálisabbnak látszanak az építőkövek.
3 szabály:
Vad kicsapongásom a mindennapi olvasnivalók között.

Tetszett, hiszen érthető és logikus megismerhettem a legmagasabb fizikai modelleket és elméleteket.
De bevallom, hogy még nem olvastam el és nem is biztos, hogy el fogom valaha is. Egyszerűen azért, mert nagyon nagy részét már ismertem korábbról. Még az egyetemen tanultam amolyan szabadon választható tantárgyak keretei között. Szinte egy az egyben ugyan az.
Persze lehet, hogy a kedvetlenségbe a töménység is beleszólt. Luxemburgból Budapestre menet olvastam el és egy teljes napig bizony nem köt le a téma.
Hogyan kapcsolódik a német bombázás, egy éhes kacsa és az adrenalin felfedezése? Egy vegetáriánus nő és a mesterséges lélegeztetés? A Morse-jel és egy művészeti akadémia és a pszichoszomatikus szó eredete. Vesekő az égetett mész és a skót whisky? És a Gőzmozdony?
Érdekesnek érdekes. A felfedezések között bizony voltak kapcsolatok. Gyakran olyanok, amire nem is gondoltál volna. De azért nem egy egységes mű. Úgy csapong, ahogy a kapcsolatok jönnek. Ez nem baj, hiszen ez a világ. A gond az, hogy emiatt nem is fogod sajnálni ha leteszed vagy félbehagyod és fél év múlva folytatod.

There is a notion - a wrong notion - that the critical path is the path through the network of activities with zero slack. If you have a zero slack network, you're late before you start.
All IMPORTANT items in your project need protection. That protect is called schedule margin.
In the scheduling world, NEVER have the important work on the critial path without schedule margin.
Don't Use Slack Time for Anything Other Than Deliverables Protection
So In The End
Az abszolút minimum amit tudni kell a hash függvényekről, digitális aláírásról és jelszavakról.
If you are a user:
Make sure all your passwords are 12 characters or more, ideally a lot more. I recommend adopting pass phrases, which are not only a lot easier to remember than passwords (if not type) but also ridiculously secure against brute forcing purely due to their length.
If you are a developer:
Use bcrypt or PBKDF2 exclusively to hash anything you need to be secure. These new hashes were specifically designed to be difficult to implement on GPUs. Do not use any other form of hash. Almost every other popular hashing scheme is vulnerable to brute forcing by arrays of commodity GPUs, which only get faster and more parallel and easier to program for every year.
Igazából pollozó megoldást nem kell nagyon programkóddal igazolni, mert alapvetően újgyakorlatnak kell lennie:
Előnye, hogy
Hátránya:
Ami viszont érdekesnek találtam az a jpoller. Főleg azért érdekes, mert olyan rémületes ábrája van, hogy még átnézni sem volt kedvem, nem hogy megérteni :)

Igazából ide nincs milyen példát felhozni, hiszen csaknem ugyan az, mint a JPathwatch. Szinte csak az importokat kell lecserélni.
Jellemzője:
Ugyan alapbül nem támogatja, de azért természetesen megoldható. A WatchDir.java példában következő módon:
/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (trace) {
Path prev = keys.get(key);
if (prev == null) {
System.out.format("register: %s\n", dir);
} else {
if (!dir.equals(prev)) {
System.out.format("update: %s -> %s\n", prev, dir);
}
}
}
keys.put(key, dir);
}
/**
* Register the given directory, and all its sub-directories, with the
* WatchService.
*/
private void registerAll(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
További kapcsolódó linkek:
Példaprogram a következő:
public class JPathwatchSample {
public static void main(String[] args) throws Exception {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path watchedPath = Paths.get("c:/temp");
WatchEvent.Kind[] eventsToWatch = new WatchEvent.Kind[] {
StandardWatchEventKind.ENTRY_CREATE,
StandardWatchEventKind.ENTRY_DELETE,
StandardWatchEventKind.ENTRY_MODIFY,
StandardWatchEventKind.OVERFLOW,
ExtendedWatchEventKind.ENTRY_RENAME_FROM,
ExtendedWatchEventKind.ENTRY_RENAME_TO,
};
WatchKey key = null;
try {
key = watchedPath.register(watchService, eventsToWatch, ExtendedWatchEventModifier.FILE_TREE);
}
catch (UnsupportedOperationException uox) {
System.err.println("file watching not supported!");
}
catch (IOException iox) {
System.err.println("I/O errors");
}
for (;;) {
// take() will block until a file has been created/deleted
WatchKey signalledKey;
try {
signalledKey = watchService.take();
}
catch (InterruptedException ix) {
continue;
}
catch (ClosedWatchServiceException cwse) {
System.out.println("watch service closed, terminating.");
break;
}
// get list of events from key
List<WatchEvent<?>> list = signalledKey.pollEvents();
// VERY IMPORTANT! call reset() AFTER pollEvents() to allow the
// key to be reported again by the watch service
signalledKey.reset();
System.out.println("--------------------------------");
// we'll simply print what has happened; real applications
// will do something more sensible here
for (WatchEvent e : list) {
String message = "";
if (e.kind() == StandardWatchEventKind.ENTRY_CREATE) {
Path context = (Path) e.context();
message = context.toString() + " created";
}
else if (e.kind() == StandardWatchEventKind.ENTRY_DELETE) {
Path context = (Path) e.context();
message = context.toString() + " deleted";
}
else if (e.kind() == StandardWatchEventKind.ENTRY_MODIFY) {
Path context = (Path) e.context();
message = context.toString() + " modified";
}
else if (e.kind() == ExtendedWatchEventKind.ENTRY_RENAME_FROM) {
Path context = (Path) e.context();
message = context.toString() + " renamed from";
}
else if (e.kind() == ExtendedWatchEventKind.ENTRY_RENAME_TO) {
Path context = (Path) e.context();
message = context.toString() + " renamed to";
}
else if (e.kind() == StandardWatchEventKind.OVERFLOW) {
message = "OVERFLOW: more changes happened than we could retreive";
}
System.out.println(message);
}
}
}
}
Jellemzői:
StandardWatchEventKinds: Milyen elbaszott egy névENTRY_CREATE: Minden esemény ENTRY-vel kezdődik. De mivel egy konstans család tagjai teljesen felesleges. Persze a CREATE, MODIFY, stb szavak könnyen ütközhetnek más API-val, de akkor miért pont az ENTRY-vel prefixelt. Prefix sem kell, ha nem az elbaszott StandardWatchEventKinds osztályba tartozik.take()) és nem blokkolva (poll()) is használható.take() hívásra egyszerre több eventet is kapunk, amiből lehet következtetni a végrehajtandó műveletre.dir createddir deletedfile.txt createdfile.txt modifiedfile.txt renamed fromfile2.txt renamed tofile2.txt deletedstylesheet.css createdstylesheet.css modifiedstylesheet.css modifiedalcatraz.101.hdtv-lol.avi createdalcatraz.101.hdtv-lol.avi modifiedalcatraz.101.hdtv-lol.avi modifieddir\subdiir createddir modifieddir\subdiir\subfile.txt createddir\subdiir modifieddir\subdiir\subfile.txt modifiedFile másolás kezelésére itt készítő tanácsa (FAQ):
Can I use jpathwatch to find out when a program finished writing to a file?
This is a bit tricky, but doable. First of all the problem is that most operating systems do not provide events for that, all they report is if a file has been created, modified or deleted in a directory, which is what jpathwatch passes back to your application code.
So effectively, you can find out when a program writes to a file, but you can’t find out when it’s done with it.
What I recommend in this case is to use heuristics: Assume that a process is done writing to a file when the file isn’t modified for a while (say, ten seconds or so).
Assume you watch a directory on an FTP server, and you want to pick up files after they have finished uploading to that directory.
In a loop, you
poll()on the WatchService for events on that directory. For each file that is created or modified (ENTRY_CREATE/ENTRY_MODIFY), you store the time of the last event on that file. Maintain a sorted list of these times; the oldest file in the list is the one that is going to expire first. Remove files that have expired from your sorted list (calculate if their last event time plus the timeout is less than the current time) and flag them as ‘done’.For the now oldest file in your list that hasn’t expired yet, calculate the time until it will expire, and call
poll()with that duration. If you have no more files in your list, calltake()instead ofpoll()to wait for new files to appear.
Az én kis problémámra a következő implementációt hoztam össze.
Másodjára implementálva a megoldást kiderül, hogy a JNotify esetén mely kódrészleteket tudom újra felhasználni. A JNotify-ban megismert BatchExecutor nagyon jól ki lehet emelni. A szemafor váltogatást interfész mögé teszem és már egy csomó mindent nem kell újra megírnom.
public interface IWorkerSemafor {
public void working();
public void notWorking();
}
class BatchExecutor implements Runnable {
private final IWorkerSemafor semafor;
BatchExecutor(IWorkerSemafor semafor) {
this.semafor = semafor;
}
@Override
public void run() {
this.semafor.working();
try {
Thread.sleep(1 * 1000); // 1 sec
}
catch (InterruptedException e) {
return;
}
finally {
this.semafor.notWorking();
}
try {
System.out.println("executing command... "+new Date());
Runtime.getRuntime().exec("cmd /c build.bat ");
}
catch (IOException e) {
e.printStackTrace();
}
}
}
És a következő egyszerű kódot írtam, hogy meghajthassam a funkcionalitást:
public class JPathwatchSample2 {
public static void main(String[] args) throws Exception {
Thread watcherT = new Thread(watcher);
watcherT.start();
System.out.println("JPathwatch: Press CTR+C to stop");
for (;;) {
Thread.sleep(1000);
}
}
static volatile boolean workerRunning = false;
static Runnable watcher = new BackgroundWatcher();
static class BackgroundWatcher implements Runnable, IWorkerSemafor {
public void run() {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path watchedPath = Paths.get("c:/temp");
setup(watchService, watchedPath);
for (;;) {
WatchKey signalledKey;
try {
signalledKey = watchService.take();
}
catch (InterruptedException ix) {
continue;
}
catch (ClosedWatchServiceException cwse) {
System.out.println("watch service closed, pls exit");
break;
}
List<WatchEvent<?>> list = signalledKey.pollEvents();
signalledKey.reset();
if (workerRunning) {
continue;
}
if (null == list || 0 == list.size()) {
continue;
}
Thread worker = new Thread(new BatchExecutor(this));
worker.start();
}
}
private void setup(WatchService watchService, Path watchedPath) {
WatchEvent.Kind<?>[] eventsToWatch = new WatchEvent.Kind[] {
StandardWatchEventKind.ENTRY_CREATE,
StandardWatchEventKind.ENTRY_DELETE,
StandardWatchEventKind.ENTRY_MODIFY,
StandardWatchEventKind.OVERFLOW,
ExtendedWatchEventKind.ENTRY_RENAME_FROM,
ExtendedWatchEventKind.ENTRY_RENAME_TO,
};
WatchKey key = null;
try {
key = watchedPath.register(watchService, eventsToWatch, ExtendedWatchEventModifier.FILE_TREE);
}
catch (UnsupportedOperationException uox) {
uox.printStackTrace();
}
catch (IOException iox) {
iox.printStackTrace();
}
}
@Override
public void working() {
workerRunning = true;
}
@Override
public void notWorking() {
workerRunning = false;
}
};
}
Nativ megoldás. Az operációs rendszer értesít a változásokról.
A websiton található sample minimális átirat.
public class JNotifySample {
public static void main(String args[]) throws Exception {
// path to watch
String path ="c:/TEMP";
// watch mask, specify events you care about,
// or JNotify.FILE_ANY for all events.
int mask = JNotify.FILE_CREATED |
JNotify.FILE_DELETED |
JNotify.FILE_MODIFIED |
JNotify.FILE_RENAMED;
// watch subtree?
boolean watchSubtree = true;
// add actual watch
int watchID = JNotify.addWatch(path, mask, watchSubtree, new Listener());
// sleep a little, the application will exit if you
// don't (watching is asynchronous), depending on your
// application, this may not be required
Thread.sleep(1000000);
// to remove watch the watch
boolean res = JNotify.removeWatch(watchID);
if (!res) {
// invalid watch ID specified.
}
}
static class Listener implements JNotifyListener {
public void fileRenamed(int wd, String rootPath, String oldName,
String newName) {
print("renamed " + rootPath + " : " + oldName + " -> " + newName);
}
public void fileModified(int wd, String rootPath, String name) {
print("modified " + rootPath + " : " + name);
}
public void fileDeleted(int wd, String rootPath, String name) {
print("deleted " + rootPath + " : " + name);
}
public void fileCreated(int wd, String rootPath, String name) {
print("created " + rootPath + " : " + name);
}
void print(String msg) {
System.err.println(msg);
}
}
}
Működésének jellemzői:
created c:/TEMP : notify\dir az új könyvtár létrehozásamodified c:/TEMP : notify szülő könyvtár módosulása(!)deleted c:/TEMP : notify\dirmodified c:/TEMP : notifycreated c:/TEMP : notify\file.txtmodified c:/TEMP : notifymodified c:/TEMP : notify\file.txtrenamed c:/TEMP : notify\file.txt -> notify\file2.txtmodified c:/TEMP : notifymodified c:/TEMP : notify\file2.txtmodified c:/TEMP : notify\file2.txtdeleted c:/TEMP : notify\file2.txtmodified c:/TEMP : notifycreated c:/TEMP : notify\alcatraz.101.hdtv-lol.avimodified c:/TEMP : notifymodified c:/TEMP : notify\alcatraz.101.hdtv-lol.avimodified c:/TEMP : notify\alcatraz.101.hdtv-lol.avi-Djava.library.path= jvm argumentummalElég körülményes egy buildelő rendszert ráépíteni, mert a file módosításon kívül igen komplex értesítéseket kapunk. Amire lehet egy kicsit építeni az az, hogy bármi is történik vagy a file vagy egy könyvtár módosul. A gond az, hogy többször is. Ha több fájlról van szó, akkor meg állományonként, ami igen nagy.
Igen részletesen lehet az fájlrendszer értesítéseit elkapni, de pont emiatt nagyon nehéz egy olyan egyszerű funkcionalitást is ráépíteni mint az én kis dokumentum fordításom.
A probléma megoldása lehet az, hogy ha értesülünk az eseményről:
A triggerelt esemény végrehajtásának hosszától függően a 3. és 4. esemény fel is cserélhető.
Konkrétan:
Csupaszítsuk le egy kicsit az alapprogramot:
public class JNotifySample2 {
public static void main(String args[]) throws Exception {
String path = "c:/TEMP/notify";
int mask = JNotify.FILE_CREATED |
JNotify.FILE_DELETED |
JNotify.FILE_MODIFIED |
JNotify.FILE_RENAMED;
boolean watchSubtree = false;
JNotify.addWatch(path, mask, watchSubtree, new JNotifyExecutorListener());
System.out.println("Press CTR+C to stop...");
for (; true;) {
Thread.sleep(10000);
}
}
}
És a listenert is készítsük el:
package public class JNotifyExecutorListener extends JNotifyAdapter {
volatile boolean executing = false;
static void p(String s){
System.out.println(s);
}
@Override
public void fileModified(int wd, String rootPath, String name) {
p("modification is notified");
if(!name.endsWith(".txt")){
return;
}
p("executing something? "+executing);
if (!executing) {
p("start the BatchExecutor thread");
Thread t = new Thread(new BatchExecutor());
t.start();
}
p("modification handler method end");
}
class BatchExecutor implements Runnable {
@Override
public void run() {
p("setting to execution...");
// Indicating that it make no sense to
executing = true;
p("executing something (BatchExecutor)? "+executing);
try {
p("sleep a bit...");
Thread.sleep(1 * 1000); // 1 sec
}
catch (InterruptedException e) {
p(e.toString());
return;
}
finally {
p("setting back flag");
// ready to catch the next event
executing = false;
}
p("executing something (BatchExecutor 2)? "+executing);
try {
p("Start command line...");
Runtime.getRuntime().exec("cmd /c Path_to/build.bat ");
}
catch (IOException e) {
e.printStackTrace();
}
p("command is ready");
}
}
}
Miért java-val kezdem?
Egyrészt ebben van a legnagyobb tapasztalatom. Másrészt ha ez megvan, akkor megvan minden olyan nyelvben is, amit kedvelek (Ruby/JRuby, Scala) egy jó kis wrapper API-val.

Az ami a Programming in Scala könyv nem. Rövid gyors. Ugyan nem teljesen részletekbe menő, de napi szinten használható hátteret ad és útmutatást ahhoz, hogyan keresd elő a részleteket.
Persze nem teljes. De kezdésnek már tökéletesen elegendő. Sokkal hamarabb kedvet kapsz hozzá, mint mondjuk a 800 oldalnyi referencia könyv olvasása során.