Custom ROM a Samsung Galaxy S2

Už je to rok a půl, co jsem majitelem v nadpise uvedené hračky. Mimo úvodních pár měsíců, kdy jsem byl nadšen rychlostí systému (v porovnání s G1), jsem na telefon permanentně nadával a v poslední době to gradovalo do takových extrémů (touha rozmlátit telefon, komunitu i google), že jsem se konečně přinutil věnovat odpoledne nahrání … Pokračovat ve čtení „Custom ROM a Samsung Galaxy S2“

Už je to rok a půl, co jsem majitelem v nadpise uvedené hračky. Mimo úvodních pár měsíců, kdy jsem byl nadšen rychlostí systému (v porovnání s G1), jsem na telefon permanentně nadával a v poslední době to gradovalo do takových extrémů (touha rozmlátit telefon, komunitu i google), že jsem se konečně přinutil věnovat odpoledne nahrání Custom ROMky, zprovoznění ROOTa a image s recovery. A protože jsem ne všechny potřebné informace a nástroje našel na jednom místě a navíc se potýkal s drobnými problémy, přijde mi vhod udělat tímto postem takový malý rozcestník.

Když jsem svého času přehrával systém v G1 (CyanogenMod a později SuperD), byly všechny kroky popsány pěkně pod sebou přímo na stránkách s danou ROMkou, u Toshiby se daly rozumně dohledat po fórech. Co se však týká SG2, našel jsem víceméně kulové. Všechny návody (oficiální) počítají s tím, že už požívám nějakou jinou ROMku a přestože někdy úvod a nadpis vypadá, že bude dále řeč o vanilla Samsung paskvilu androidu, nebylo tomu tak. Mimo jiné se tyto návody specializují spíše na Windows, já však potřeboval update provést z Macka (a obecně by to mělo fungovat i pro GNU Linux).

Miui
Miui

Pokusím se tyto kroky sepsat níže, spolu se zdrojem, odkud jsem čerpal. Uváděná čísla verzí (pokud je uvedu) se samozřejmě časem změní, takže v mnoha případech zůstanu pouze u uvedení názvu daného nástroje a strýček google jistě poradí. Ještě bych na úvod poznamenal, že nenesu žádné riziko, pokud si někdo následujícím postupem s telefonem něco provede.

1. Instalace obrazu zavaděče – postupoval jsem podle howto ve wiki Cyanogen modu, který mě nasměroval na Heimdal Suite, což je multiplatformní nástroj pro flashování obrazů do Samsung Galaxy S zařízení, jak tvrdí oficiální FAQ. Z výše uvedené wiki jsem se dostal i ke ClockworkMod Recovery. Heimdal jsem stáhnul ve formě instalačního balíčku a hned tady jsem měl první problém – instalátor umně skryl okno s informací o potřebě restartu pod všechna okna a já se chvíli trápil s tím, proč se mi nedaří aplikace použít. Po restartu však vše šlo jako po másle a bylo možné nahrát image do zařízení (nejprve nutno zapnout USB debugging a nastartovat telefon do „download režimu“ pomocí trojhmatu volume down, home a power):

[code]sudo heimdall flash –kernel zImage[/code]

2. Povolení superuživatele – spočívající v nahrání binárky su a aplikace Superuser. Na tuto aplikaci mě odkázal miculog, kterého jsem se ale zase tak moc nedržel a ze stránek stáhl zip archivy, které jsem nahrál na kartu, nabootoval do recovery (tentokrát volume up, home a power) a odtud je pomocí „Install zip from sdcard“ nainstaloval.

3. Provedení zálohy – stále v recovery režimu je možné provést zálohu systémových oddílů v nabídce „backup and restore“.

4. Stažení vlastního obrazu s ROM – a následné nahrání na kartu (nebo interní úložiště) v telefonu. Já se rozhodl pro MIUI ROM, takže jsem stahoval nejnovější build a snažil se postupovat podle oficiálního návodu. Bohužel jsem v rámci minulého kroku zároveň začal mazat i cache, data a další místa, takže mi telefon odmítl nabootovat a nemohl jsem tak do něj nahrát zip soubor s updatem. Po chvíli googlení jsem problém vyřešil a pomocí adb z Android SDK namountoval „ramdisk“ jako externi úložiště (telefon v recovery režimu) a nahrál tam požadovaný image:

[code]shmoula $ adb shell
# mount -t tmpfs none /sdcard
# exit
shmoula $ adb push miuiandroid_GT-I9100_ics-2.11.23.zip /sdcard/
. . . . .[/code]

5. Instalace custom ROM – po předchozím vymazání „wipe data/factory reset“, „wipe cache partition“, „advanced – Wipe Dalvik Cache“ a konečně „Mounts and Storage – Format/System“ následuje opět „Install zip from SD card“ a výběr v předchozím kroku nahraného zazipovaného image. Po chvíli akce a napětí následuje restart telefonu pomocí výběru „restart system now“ a s největší pravděpodobností naběhnutí nového systému (a drobné rozčarování z jiného rozložení a následujícího kroku).

6. Instalace Google aplikací – protože nesmějí být bundlovány s custom ROMkama. MIUI už však části těchto aplikací obsahuje, navíc má vlastní market a další drobnosti, takže je nutné tyto služby nejprve povypínat, jak radí začátek tohoto vlákna.

[code]shmoula $ adb shell
# mount -o remount,rw -t ext4 /dev/block/mmcblk0p1 /system
# mv /system/app/NetworkLocation.apk /system/app/MiuiNetworkLocation.bak
# pm disable com.miui.cloudservice
# pm disable com.xiaomi.xmsf
# exit[/code]

Teď už je možné stáhnout balíček s google apps a nahrát jej na kartu v telefonu. Instaluje se opět jako update v recovery režimu volbou „Install zip from SD card“.

7. Aktivování Mass usb režimu – od jisté verze tydlidrojdu není povolen USB Mass storage režim, takže je nutné jej aktivovat ručně:

[code]shmoula $ adb shell
# setprop persist.sys.usb.config mass_storage[/code]

8. Závěrečný restart telefonu a přihlášení se ke svému google účtu a následný komentář s pochvalou pod tímto návodem.

Kralický a jesenický vandr

Už nějakou dobu koukám na Evropské dálkové trasy vedoucí přes Českou republiku, ale pořád jsem se nějak nedokopal k tomu, abych vyrazil. Nuže letos jsem se přiměl jít aspoň část E3 (která vede ze španělského Santiaga de Compostella do Cape Emine u Černého moře). Nejprve jsem chtěl přejít její poslední českou část – od Králiků … Pokračovat ve čtení „Kralický a jesenický vandr“

Už nějakou dobu koukám na Evropské dálkové trasy vedoucí přes Českou republiku, ale pořád jsem se nějak nedokopal k tomu, abych vyrazil. Nuže letos jsem se přiměl jít aspoň část E3 (která vede ze španělského Santiaga de Compostella do Cape Emine u Černého moře). Nejprve jsem chtěl přejít její poslední českou část – od Králiků až na Bumbálku, ale nakonec díky nedostatku času (jen 5 dní) jsem šel z Mladova u Králiků do Nového Malína u Šumperka.

Trasa na Everytrail

Králíky byly fajn – téměř nikde ani človíčka, na Sněžníku jsem se brzy ráno objevil sám-samotinký, čím blíže jsem ale byl k Jeseníkům, tím více lidí se objevovalo. Po Ramzovou se to ještě dalo, ale potom nastal hotový Václavák, pod Pradědem tuplovanej. Tam už nikdy více! Někdy v budoucnu dojdu tuhle etapu E3 (pokud tam nebude přelidněno) a v Jeseníkách mě zajímá nejvíc así část Rychlebů, tam by ještě nemuselo být tolik národa. Chjo, proč prostě člověk nemůže mít poblíž místo, kde celý den jde a nepotká ani živáčka :(.

Nějaké fotky z výpravy třeba tutok, pospojovaná panorámata až úúúúplně jsou na konci galerie.

Pozadí ubytovníčku: validace

Co mám: mnoho nápadů Výstup: nejjednodušší možné řešení, kterým jsem schopen vyřešit zákazníkův problém Co budu dělat: mluvit s potenciálními zákazníky, pivotovat, hledat nejjednodušší možnou funkcionalitu Co pro to použiju: lean canvas (případně jiný canvas či způsob validace, pokud je vhodnější) Minule jsem psal o něčem, co by se dalo nazvat blitím nápadů. Podstatnější než … Pokračovat ve čtení „Pozadí ubytovníčku: validace“

Co mám: mnoho nápadů
Výstup: nejjednodušší možné řešení, kterým jsem schopen vyřešit zákazníkův problém
Co budu dělat: mluvit s potenciálními zákazníky, pivotovat, hledat nejjednodušší možnou funkcionalitu
Co pro to použiju: lean canvas (případně jiný canvas či způsob validace, pokud je vhodnější)

Minule jsem psal o něčem, co by se dalo nazvat blitím nápadů. Podstatnější než to je však validace těchto nápadů, v tom pomůže svatá trojice. Tou se rozumí trojice uživatel, uživatelův problém a moje řešení tohoto problému. Nejlepší situace je ta, když sám vidím v něčem problém a chci jej řešit (neboli sám sobě jsem zákazníkem). Potom můžu přistoupit k onomu blití nápadů z minula a mám nápadů na řešení přehršel, protže vím, o čem mluvím. Ovšem i přesto můžu být sám sobě zákazníkem jediným a to je špatně, proto přichází na řadu výše zmíněná validace.

Validace problému/řešení
Validace problému/řešení (zdroj: http://market-by-numbers.com)

Proč jsem zvýraznil spojení uživatelův problém? Protože musím vycházet z problému, který uživatele opravdu trápí, ne z problému, o kterém si myslím já, že uživatele pálí. Proto je třeba se ptát, ne někde v ústraní dumat nad možnými problémy. Je potřeba sehnat potencionální zákazníky, lidi, kteří do dané problematiky vidí a věčné skeptiky (mě :-)) a předložit jim různé scénáře s problémovými situacemi a způsoby jejich řešení a postupně se s nimi dopracovávat k jedinému konkrétnímu výsledku, nebo klidně i několika různým důležitým cílům, z nichž vykrystalizuje výsledný produkt (lépe řečeno Minimal Viable Product).

Postupná krystalizace nápadu (kombinace problém/řešení)
Postupná krystalizace nápadu (kombinace problém/řešení) (zdroj: http://theagni.com/)

Tohle dopracovávání se k výslednému nápadu, který bude mít cenu realizovat, by mělo probíhat iterativně – tedy postupně by se měl nápad doplňovat připomínkami, validovat, doplňovat, validovat…třeba i v několika paralelních větvích, které budou validovány dále ještě jinými způsoby, aby byla vybraná ta nejlepší. Nejlepším výběrem je myšlen kompromis mezi užitnou hodnotou pro uživatele a mojí schopností tuto věc co nejrychleji realizovat. Tím myslím především finanční stránku věci, protože když je čas a peníze, realizovat se dá cokoliv :-).

Lean canvas
Lean canvas (zdroj: http://www.ashmaurya.com/)

V hledání tohoto kompromisu pomůže nástroj zvaný lean canvas. Lépe řečeno – mnoho canvasů, protože co canvas, to kombinace uživatel/problém. Není možné napasovat stejné uživatele na různé problémy, protože je prostě nemusí vůbec pálit. Canvas má několik polí, která se vyplňují postupně a samozřejmě canvas samotný se dále vyvíjí na základě komunikace s potenciálními klienty/zákazníky, díky čemuž mohou nevyhovující canvasy zanikat i vznikat nové. Prvotní vytvoření canvasu zabere okolo 20 minut, delší koumání je ztráta času, jak píše autor této techniky, Ash Maurya, v knize Running lean. Když nevím, nevyplňuju, nebo ani vyplnit nemůžu, protože to zatím není jasné – též budeme iterovat a dopracovávat se k nejvhodnějšímu řešení. Lean canvas vychází z Business model canvasu a vhodně jej rozšiřuje pro začínající projekty, dle slov autora: Business canvas na příkladech ukazoval zajeté instituce, jako apple, nebo skype… Lean canvas se soustředí na začínající projekty a na mnoha příkladech ilustruje tuto techniku na vlastním rozjížděném projektu. Nebudu to dále rozmazávat, stojí to za přečtení – druhé vydání je k sehnání na Amazonu za nějakých $16, první vydání bylo možné předplatit v rámci psaní knihy a dostávat je na etapy (nebo zkuste napsat přímo autorovi, je to člověk otevřený ;-)).

Pivotování
Pivotování (zdroj: http://steveblank.com/)

Ale zpátky k validacím: v této fázi se bude určitě hodně pivotovat, protože mám představu, kdo jsou mými zákazníky, oni jimi však většinou nebudou, musí sami sebe identifikovat s mým produktem. Proto se budou cíle velmi často měnit a v této fázi je to výhodné a také velice vhodné, protože nic reálného zatím neexistuje a měním pouze myšlenky a nahlížení na ně. Jen se k tomu musím přinutit, je to jako u zubaře – včasný zásah bolí mnohem méně. Časem se to nějak ustálí a najednou bude vše „nad slunce jasné“.

Teď už by mohl být pomalu čas na nákup domény, dočkali jsme se! Taky je možné začít vytvářet profil pro aplikaci na sociálních sítích, (nejenom) abychom se dostali uživatelům pod kůži a vytvořili si nějaké publikum. Ale o tom zase až příště, konečně se to stočí k techničtějším věcem, o kterých měla tato série původně celá být!

(Repost z blogu Ubytovníčku.)

Import scripts architecture, pt. II

First post of this series ended with code snippet, which invokates importAction itself. It’s not still the importing itself, but we are almost there. This post will be about what happenes, what hapened before and what is going to happen :-). But first things first, let’s have a look on Factory and Proxy pattern, which … Pokračovat ve čtení „Import scripts architecture, pt. II“

First post of this series ended with code snippet, which invokates importAction itself. It’s not still the importing itself, but we are almost there. This post will be about what happenes, what hapened before and what is going to happen :-). But first things first, let’s have a look on Factory and Proxy pattern, which are used while calling imports.

Factory pattern is based on idea instead of creating a new object with new operator, factory is asked, which returns a new instance. This is way standard Spring’s bean factory works. It may looks like following diagram (image is from oodesign.com):

Factory Pattern
Factory Pattern (click for source - oodesign.com)

To hide newly created instance behind an interface facade Proxy pattern is used. This functionality is imho basic of AOP programming, because instead of calling original object, something else may be called. Have a look on following diagram (again from oodesign.com):

Proxy Pattern
Proxy Pattern (click for source - oodesign.com)

Now it’s possible to put those patterns together and get real importAction functionality: importAction is just an interface, which hides importActionImpl newly created for every request. This can be seen on following snippet of application context configuration.

[code lang=“xml“]
<bean id=“ImportAction“ class=“org.springframework.aop.framework.ProxyFactoryBean“>
<property name=“targetName“ value=“importAction“/>
<property name=“singleton“ value=“false“/>
<property name=“proxyInterfaces“>
<list>
<value>cz.shmoula.imports.ImportAction</value>
</list>
</property>
</bean>

<bean id=“importAction“ class=“cz.shmoula.imports.ImportActionImpl“ scope=“prototype“ />
[/code]

As I wrote a while before – all of this is done due to posibility of calling something else. But back to implementation, for details refer to some AOP documentation. Method importAction.invocateImportScript() is a pointcut, on which two advisors are bound. Have a look at following diagram.

ImportActionAdvice
ImportActionAdvice

This configuration can be achieved by NameMatchMethodPointcutAdvisor, see following snippet for ImportContentAdvisor bean definition and updated ImportAction itself:

[code lang=“xml“]
<bean id=“importContentAdvice“ class=“cz.shmoula.imports.ImportContentAdvice“ />

<bean id=“ImportContentAdvisor“ class=“org.springframework.aop.support.NameMatchMethodPointcutAdvisor“>
<property name=“advice“ ref=“importContentAdvice“/>
<property name=“mappedNames“>
<list>
<value>invocateImportScript</value>
</list>
</property>
</bean>

<bean id=“ImportAction“ class=“org.springframework.aop.framework.ProxyFactoryBean“>
<property name=“targetName“ value=“importAction“/>
<property name=“singleton“ value=“false“/>
<property name=“proxyInterfaces“>
<list>
<value>cz.shmoula.imports.ImportAction</value>
</list>
</property>
<property name=“interceptorNames“>
<list>
<value>ImportContentAdvisor</value>
<value>DeployContentAdvisor</value>
</list>
</property>
</bean>

<bean id=“importActionTask“ class=“cz.shmoula.imports.ImportActionTask“ scope=“prototype“>
<property name=“importAction“ ref=“ImportAction“/>
</bean>
[/code]

Last few lines is definition of importActionTask I wrote in previous post. In this class importAction.invocateImportScript() method is called. This method has one parameter – map of properties of config file (in format <QName, Serializable>) to read them once and have them all the time and not need to call nodeService.getProperties() again and again – in advisors or importAction class. When this method is called, proxy of importAction catches this calling and calls invoke() method on ImportContentAdvice and DeployContentAdvice. Let’s have a look on invoke() method in ImportContentAdvice:

[code lang=“java“]
public Object invoke(MethodInvocation invocation) throws Throwable {
Object[] objects = invocation.getArguments();
Object result = null;
Map<QName, Serializable> propertiesMap = __resolve_arguments_to_map_(objects);

String lockToken = (String) propertiesMap.get(ContentModel.PROP_NODE_UUID);
setTokenOnTarget(invocation, lockToken);

try {
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
misLockingService.createLock(lockToken);

object = invocation.proceed();

misLockingService.releaseLock(lockToken);
} finally {
AuthenticationUtil.clearCurrentSecurityContext();
}

return result;
}
[/code]

All what ImportActionAdvice does can be seen on previous snippet. It locks used config file to prevent access from another thread, using node uuid as a locking token. Then it authenticates a system user (mainImportThread runs outside any security context) and invocates next chain (DeployContentAdvisor and after then invocateImportScript(), if everything goes ok). After return from invocation lock is released and security context is cleared. If anything happens inside, lock release is skipped, so some rescue mechanisms are automaticaly initiated after some time. Simple, yet effective, i hope.

Import scripts architecture

It has been some time since last post about import scripts from external sources to alfresco repository, I was busy with debugging and threading, but now I have some little time to write some notes on that topic again. I’d like to introduce „architecture“ of my solution, at least a first part of it, so … Pokračovat ve čtení „Import scripts architecture“

It has been some time since last post about import scripts from external sources to alfresco repository, I was busy with debugging and threading, but now I have some little time to write some notes on that topic again. I’d like to introduce „architecture“ of my solution, at least a first part of it, so lets start with an image.

Calling ImportAction
Calling ImportAction

MainImportingThread is Runnable implementation, which is initiated on init. Main thread sleeps some time and searches for config files in repository, while not sleep. If found some, ImportActionTask instance is created for each config in separate thread. ImportActionTask checks, if is it possible to run script described by config. If so, run script method on ImportAction is executed. That’s in few words functionality of the first part. Now in more details.

First version used quartz jobs triggered by org.alfresco.util.CronTriggerBean every 10 seconds, but that was temporarily solution, too heavy in my eyes. In second version I tried to use BatchProcessor, but spent few days without got it worked as I want :-(. Finally I used ThreadPoolTaskExecutor with combination of java.lang.Runnable in main thread and it worked like a charm! As I wrote a while ago, MainImportingThread has its own separate thread, which cyclically looks for configuration files in repository. It looks like following snippet:

[code lang=“java“]
public void init() {
taskExecutor.execute(new Runnable() {
@Override
public void run() {
while(keepAlive) {
Thread.sleep(threadSleepMs);

for(ResultSetRow row : resultSet) {
Object oTarget = pool.makeObject();

if(oTarget instanceof ImportActionTask) {
ImportActionTask importActionTask = (ImportActionTask) oTarget;
importActionTask.setScriptRef(row.getNodeRef());
taskExecutor.execute(importActionTask);
}
pool.destroyObject(oTarget);
}
}
taskExecutor.shutdown();
pool.destroy();
}
});
}
[/code]

For every row in result set PoolableObjectFactory creates a new instance of ImportActionTask and pass it to ThreadPoolTaskExecutor to execute. After all that – object is destroyed (destroyObject method is there just for sure – in time this method is called action still runs).

ImportActionTask is Runnable implementation, so every action runs its own thread. After execution isTimeToRun() method is called and action runs only when it’s its time and action is enabled, as it defined in data model illustrated on following diagram.

Config model
Config model

isTimeToRun() method uses fields importCron, importLastAction and isImportActive for its work. Its body looks similar to following simplified code snippet:

[code lang=“java“]
private boolean isTimeToRun() {
if(!model.isImportActive())
return false;

String cronString = model.getImportCron();
Date lastAction = model.getLastAction();

CronExpression cronExpression = new CronExpression(cronString);
if(cronExpression.getNextValidTimeAfter(lastAction).getTime() > System.currentTimeMillis())
return false;

return true;
}
[/code]

Previous snippet checks cron expression against time of last successfully executed action using org.quartz.CronExpression and in case of success (ie lastAction+cron < current time) importAction itself may be triggered. But more on this next time, right now just another snippet from ImportActionTask, which calls importAction (received from ProxyFactoryBean):

[code lang=“java“]
public void run() {
if(isTimeToRun) {
try {
importAction.invocateImportScript();
} finally {
importAction.clean();
}
}
}
[/code]

Custom configs in Alfresco

While creating import services I want some configuration mechanism for (not just) BatchProcessor. I like Alfresco’s *-config-custom.xml files (aka Spring framework XMLConfigService), so I did simple custom config service. Anyone, who customized Alfresco, knows format of this file (web-client-config-custom.xml). I put that structure to new file – mis-config-custom.xml: [code lang=“xml“] <alfresco-config> <config evaluator="string-compare" condition="imports"> <processor> … Pokračovat ve čtení „Custom configs in Alfresco“

While creating import services I want some configuration mechanism for (not just) BatchProcessor. I like Alfresco’s *-config-custom.xml files (aka Spring framework XMLConfigService), so I did simple custom config service.

Anyone, who customized Alfresco, knows format of this file (web-client-config-custom.xml). I put that structure to new file – mis-config-custom.xml:

[code lang=“xml“]
<alfresco-config>
<config evaluator="string-compare" condition="imports">
<processor>
<worker-threads-num>3</worker-threads-num>
<batch-size-num>6</batch-size-num>
<split-transactions>false</split-transactions>
<logging-interval>1000</logging-interval>
</processor>
<token>
<time-refresh>1000</time-refresh>
<time-expiration>20000</time-expiration>
</token>
</config>
</alfresco-config>
[/code]

Please note those constants are just for ilustration purposes now, more on them in scripting series. My MisConfigService extends org.springframework.extensions.config.xml.XMLConfigService, but uses nothing from that class (except init method in bootstrap), I just added some statics inside to keep things together:

[code lang=“java“]
public class MisConfigService extends XMLConfigService {
public MisConfigService(ConfigSource configSource) {
super(configSource);
}

public static String getChildValue(Config config, String elementId, String childName, String defaultValue) {
ConfigElement myConfig = config.getConfigElement(elementId);
String result = myConfig.getChildValue(childName);

return (result==null ? defaultValue : result);
}

public static Integer getChildValue(Config config, String elementId, String childName, Integer defaultValue) {
Integer result = defaultValue;
try {
String number = getChildValue(config, elementId, childName, defaultValue.toString());
result = Integer.parseInt(number);
} finally { /* O:-) */ }
return result;
}

. . .
}
[/code]

To instantiate this service just add bean definition with UrlConfigSource(-s), which defines path to custom xml configuration:

[code lang=“xml“]
<bean id="misConfigService" class="cz.mis.service.MisConfigService" init-method="init">
<constructor-arg>
<bean class="org.springframework.extensions.config.source.UrlConfigSource">
<constructor-arg>
<list>
<value>classpath:alfresco/extension/mis-config-custom.xml</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
[/code]

Now it’s possible to access those properties in code like this:

[code lang=“java“]
org.springframework.extensions.config.Config config = configService.getConfig(imports");
Boolean splitTransactions = MisConfigService.getChildValue(config, "processor", "split-transactions", false);
[/code]

It’s also possible to access attributes of nodes in that config:

[code lang=“java“]
ConfigElement myConfig = config.getConfigElement("config-element");
for(ConfigElement child : myConfig.getChildren()) {
// child.getAttributes()
// child.getName()
// child.getValue()
}
[/code]

That’s all! And now for something completely different! I also spent some time trying to get properties files working and putting those properties into Spring configurations. There’s nice solved thread in Alfresco forums. Prefix helps:

[code lang=“xml“]
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:alfresco/extension/mis-settings.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="placeholderPrefix" value="${mis:" />
<property name="placeholderSuffix" value="}" />
</bean>

<!– and I can use stuff from properties file with prefix –>
<bean class="foo.bar.Example">
<property name="fooAttribute">
<value>${mis:some.sample.property}</value>
</property>
</bean>
[/code]

More on invoking scripts in Alfresco

In my last blogpost on this topic I wrote about invoking rhino scripts programatically from Alfresco and about passing its model into freemarker templates. After that I realized that alfresco templateService is unable to render more complex models (nested classes and collections) if called this way (please note on Alfresco webscripts invoked standard way this … Pokračovat ve čtení „More on invoking scripts in Alfresco“

In my last blogpost on this topic I wrote about invoking rhino scripts programatically from Alfresco and about passing its model into freemarker templates. After that I realized that alfresco templateService is unable to render more complex models (nested classes and collections) if called this way (please note on Alfresco webscripts invoked standard way this works correctly!). I think the problem is – no wrapper is used while passing model. I spent lot of time trying many things, read a lot of posts and topics on that, played with built-ins, trying to find that part in Alfresco source, but still no result, just exceptions. But solution is VERY SIMPLE – to write own templateService :-). Well, maybe this post is more about Freemarker and Rhino than Alfresco, sorry about that!

So, what I want to achieve? Suppose some simple script like this one:

[code lang=“javascript“]
model.bar="foo";
model.foo = "bar";
model.obj = {q:1, w:3, e:9};

model.arr = new Array();
model.arr[0] = {a:1, b:8, c:9, d:{d:66}};
model.arr[1] = {a:2, b:7, c:10, d:{d:66}};
model.arr[2] = {a:3, b:6, c:11, d:{d:66}};
model.arr[3] = {a:4, b:5, c:12, d:{d:66}};
[/code]

and a Freemarker template simple as that:

[code lang=“plain“]
q attribute: ${obj.q}

<#list arr?keys as value>
– ${value} = ${arr[value].a} = ${arr[value].b} = ${arr[value].c}
<#assign obj_d = arr[value].d>
+ d: ${obj_d.d}
</#list>
[/code]

Now after calling templateService.processTemplate( . . . ) the output is Exception this and Exception that… That happens, because Rhino creates NativeObjects for those objects and after passing those objects to Freemarker (through Java – without any ObjectWrapper), which doesn’t know how to handle them. The same situation happens when passing a collection with generics like <String, Object> into model – those Objects are standard Java objects and in Freemarker acts like them, they really don’t have attributes like q, a, b, etc. :-). Similar situation can happen with collections – they needs to be wrapped too. But stop with theory, back to practise. For those interested in wrapping and so, follow Freemarker documentation on wrappers.

Before wrapping – when I printed out keys of model, there were’nt my attributes defined in Javascript. What I found inside is on following listing, note my attributes are also there – bar, foo, arr, obj:

[code lang=“plain“]
<#assign keys = model?keys>
<#list keys as key>
+ ${key}
</#list>

(** output **)
+ getClass
+ clone
+ put
+ remove
+ get
+ equals
+ entrySet
+ class
+ hashCode
+ foo
+ keySet
+ size
+ clear
+ isEmpty
+ containsKey
+ values
+ arr
+ containsValue
+ empty
+ toString
+ putAll
+ bar
+ obj
[/code]

Those properties belongs to hashMap and is possible to get rid of them using simpleMapWrapper, this technique is described well in Rizwan Ahmed post.

What about my templateService implementation? First I tried to use JSONtoFmModel and convert javascript objects to strings, but that was no way forward. That was before I discovered wrappers :-). There is RhinoWrapper class in Freemarker and it solves all my problems! It can be used as simple as this:

[code lang=“java“]
Configuration cfg = new Configuration();
RhinoWrapper rhinoWrapper = new RhinoWrapper();

rhinoWrapper.setSimpleMapWrapper(true);
cfg.setObjectWrapper(rhinoWrapper);
[/code]

Previous listing creates new Freemarker configuration instance and rhino wrapper instance and binds them together. With that configuration it’s possible to create template instance, which takes reader with template content, let’s read it from repository:

[code lang=“java“]
NodeRef nodeRef = new NodeRef("workspace://SpacesStore/56ba2237-f776-494a-939b-d259b68c021a");

ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
InputStream inputStream = contentReader.getContentInputStream();

Template template = new Template("my_template", new InputStreamReader(inputStream), cfg);
[/code]

That’s all we need to render. Rendering takes one more parameter – model map, which is HashMap defined in my previous blogpost. To keep it simple (and post a little shorter) I’m writing output to sysout:

[code lang=“java“]
OutputStreamWriter streamWriter = new OutputStreamWriter(java.lang.System.out);
template.process(rootMap, streamWriter);
[/code]

Those few lines and everything works fine now! But I got some silver hairs with trying to solve that, now I can feel like a boss, so I achieved new „Alfresco guru badge“ :-). Also I’m thinking about some public GIT repository with my Alfresco inventions, what you think about it?

Like an Alfresco guru!
Like an Alfresco guru!

PostScriptum: Can anyone explain me why Alfresco uses Rhino version 1.6R7 which is from year 2007? :-O

BFU vyzva: Zkuste setrit cas svuj i druhych

Vcera jsem se nehorazne nasral vytocil a vyustilo to v potrebu odpovedet na vsechny mejlove adresy uvedene v dotycnem mejlu a budu to delat znovu a znovu, dokud mi neco bude chodit. Text tohoto mejlu prikladm nize, treba to nekoho inspiruje. Jako takovou maturitu pro pokrocilejsi muzu jeste doporucit deset bodu – 10 Rules to … Pokračovat ve čtení „BFU vyzva: Zkuste setrit cas svuj i druhych“

Vcera jsem se nehorazne nasral vytocil a vyustilo to v potrebu odpovedet na vsechny mejlove adresy uvedene v dotycnem mejlu a budu to delat znovu a znovu, dokud mi neco bude chodit. Text tohoto mejlu prikladm nize, treba to nekoho inspiruje. Jako takovou maturitu pro pokrocilejsi muzu jeste doporucit deset bodu – 10 Rules to Reverse the Email Spiral.

Dobry den,

jmenuji se Vaclav Balak a vubec Vas neznam. Proc Vam pisu? Protoze mi davate kontaktni informace na Vas, prestoze jsem si je vubec nevyzadal a nemam o ne zajem. Ale klidne o Vas muzou mit zajem moji klienti, kterym Vase kontaktni udaje velmi rad prodam!! A proto pisu – NAUCTE SE CHRANIT SOUKROMI SVYCH KONTAKTU!!! Kdyz uz mate potrebu preposilat mejly nadepsane „Fwd: FW: FWD:……Tohle musis videt!!!!“, zkuste udelat tu malickost a SMAZAT OBSAH ZPRAVY, nebo jej aspon upravit, at nepredavate adresy lidi, kterym to prislo pred Vami dal a dal a dal a dal, dokud je nekdo nezneuzije!! A vubec – jeden znamy na toto tema napsal pekny clanek, zkuste se na nej podivat: BFU výzva: Rádi e-mailujete? Má to však své zásady!

A proc pisu jeste? Reaguju na mejl samotny – na clanek o Kosteleckych Uzeninach. Za clanek dekuji, byl velmi zajimavy, jeste zajimavejsi vsak byla forma, v jake jsem clanek obdrzel – dokument Wordu! Proc? Pokud odmyslim fakt, ze nekdo tento program vubec nema (tudiz tento dokument ani neotevre a neprecte si jej), nemuzu pochopit, proc kopirujete clanek volne pristupny na internetu dovnitr nejakeho dokumentu? Dochazi ke ztrate informace – k puvodnimu clanku pribyl odkaz na video, pribyly k nemu nove informace… O tohle vsechno ctenari tohoto .doc dokumentu prisli! Posudte nasledujici dva zpusoby poslani informace:

Zpusob jedna:
1) Prectu si clanek, ktery se mi libi
2) zkopiruju adresu na tu stranku a
3) vlozim ji do noveho mejlu a odeslu

Zpusob dva:
1) Prectu si clanek, ktery se mi libi
2) zkopiruju cely clanek a
3) otevru word a protoze neumim vytvorit novy dokument, tak
4) otevru nejaky jiny dokument
5) dam ulozit jako a ulozim ho
6) smazu obsah toho dokumentu a (postupne oznacim vse mysi samozrejme)
7) pro jistotu ho ulozim
8 ) vlozim do tohoto prazdneho dokumentu obsah toho clanku
9) ulozim a dlouho hledam na disku, kam jsem ten dokument vubec ulozil
10) prilozim tento dokument k mejlu a odeslu

Proc proboha volite zpusob dva? Nehlede na to, ze v 90% pripadu to stejne zkopirujete blbe, takze tam polovina informaci vubec neni, nejsou tam obrazky, nejsou tam nova zjisteni, ktere autor publikoval po odeslani zmineneho souboru mejlem, ma to rozhazene formatovani… a predevsim – NENI TAM DISKUZE, kde jsou mnohdy podstatnejsi informace, nez v clanku samotnem (pokud to teda neni kopie z novinky.cz, kde se nadava na ods, nebo z idnes.cz, kde se nadava na komunisty) a navic se teda nemuzete do teto diskuze zapojit s vlastnim nazorem!!

Zkuste se nad tim prosim aspon zamyslet a pripadne tento mejl predat dal svym znamym – ovsem na zaklade vyse zminenych informaci – nemam zajem, aby moje emailova adresa kolovala svetem az do 21.12.2012.

Dekuji za cas a preji pekny den
Vaclav Balak

PS. puvodni adresa onoho clanku je
http://www.bizwiz.cz/2012/02/11/kostelecke-uzeniny-tvurce-hororovych-zazitku/

Invoking scripts in Alfresco programatically

I’m working on universal CRON runner, which runs scripts saved somewhere in repository (more on this next time). Running webscripts isn’t so easy, as I thought, there is no scriptService.runScriptWithThisDescription(path) method, so I did some research and got some results, which I’d like to share. It’s possible to run java backed actions from js-webscripts. Everything you … Pokračovat ve čtení „Invoking scripts in Alfresco programatically“

I’m working on universal CRON runner, which runs scripts saved somewhere in repository (more on this next time). Running webscripts isn’t so easy, as I thought, there is no scriptService.runScriptWithThisDescription(path) method, so I did some research and got some results, which I’d like to share.

It’s possible to run java backed actions from js-webscripts. Everything you need is just a bean extending ActionExecuterAbstractBase with definition parent=“action-executer“ and it’s possible to access that bean from webscript like actions.create(„that_bean_id“). But not in reverse, at least not so simple.

First think I found was post on forum and advice to look at unit tests of webscripts, but everything I found were invocations through http. So I went deeper and figured out that Alfresco uses Rhino JavaScript implementation for scripts. At Rhino pages are good examples, so I had first working script soon. At following codelist I firstly load content of node (script body) from repository and then invocate script itself.

[code lang=“java“]
NodeRef nodeRef = new NodeRef("workspace://SpacesStore/4a96aaaa-bb80-eeee-aaaa-800a43fcddb8");
ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
InputStreamReader isr = new InputStreamReader(contentReader.getContentInputStream());
BufferedReader reader = new BufferedReader(isr);

Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
Object result = cx.evaluateReader(scope, reader, "scriptInvocation", 1, null);
} finally {
Context.exit();
}
[/code]

But I wanted something more – some model in and out and something simplier. So I found ScriptService. Now all those long lines can be ommited, because there is executeScript method!

[code lang=“java“]
Map<String, Object> model = new HashMap<String, Object>();
model.put("foo", "bar");

Object result = scriptService.executeScript(scriptRef, model);
[/code]

Template rendering (I’m using FreeMarker’s ftls) is done the same way, but through TemplateService and processTemplate(nodeRef, model) method, which accepts the same model (altered by script running).

The most beautiful thing is ability of script (and also template) to call java methods, which classes are „injected“ through model, let’s see an example.

[code lang=“java“]
Map<String, Object> model = new HashMap<String, Object>();

model.put("something", new Something());
model.put("bar", "foo");

Object result = scriptService.executeScript(scriptRef, model);
String templateResult = templateService.processTemplate("workspace://SpacesStore/noderef", model);

. . .

public class Something {
public String text = "aaa";

public String getText() {
return "bbb";
}
}
[/code]

Now it’s possible to use script / template:

[code lang=“javascript“]
(** script **)
bar = something.text;

(** template **)
${something.text} ?=? ${bar}
[/code]

What is output? Is in templateResult „aaa ?=? aaa“? Nope! Script accesses class attributes directly, on the other side template accesses methods. So output is „bbb ?=? aaa“!!!

One more important thing: this JavaScript implementation is just a stub, so every alfresco service needs to be „injected“ through model, so for example it’s impossible to run stuff like this without modifications:

[code lang=“javascript“]
var connector = remote.connect("http");
var result = connector.call("http://www.google.com/");
[/code]

Remote object has to be injected (this script is from Share/Surf), more on this in this blogpost. You have to add ScriptRemote to model and create spring-webscripts-config-custom.xml with endpoints defined, see that post. Also another services needs to be injected – to get access to repository, search service… good starting point is Alfresco Wiki.

Edit: Native Java API Access described in wiki is wrong, IT IS POSSIBLE to access maps in JavaScript like this:

[code lang=“javascript“]
var value = javaMap.key;
[/code]

BrMo poprvé

Včera jsem se zúčastnil BrMa (dle srazů „prvního neformálního pokecu na téma točící se kolem mobilních zařízení a jejich platforem“) a dojem z této akce byl více, než kladný. Původně jsem na akci šel čistě ze zvědavosti s představou, že to bude zase nějaká tuctovka ve stylu študáků lepících na koleně „ty webovky v PéHáPé … Pokračovat ve čtení „BrMo poprvé“

Včera jsem se zúčastnil BrMa (dle srazů „prvního neformálního pokecu na téma točící se kolem mobilních zařízení a jejich platforem“) a dojem z této akce byl více, než kladný. Původně jsem na akci šel čistě ze zvědavosti s představou, že to bude zase nějaká tuctovka ve stylu študáků lepících na koleně „ty webovky v PéHáPé protože to je teďka in a dělám to na tom…é…nette, protože v tom dělají všichni“, ale opak byl pravdou! Převažovali opravdoví vývojáři, jimž kódování je denním chlebem. A vývojáři ne tak ledajací – opět to nebylo 90% Android a 10% iOS, ale byly tu zastoupeny i další platformy – Symbian, Windows a dokonce i Bada v několika exemplářích (a aby se u RedHatů a další podobně proNokiácky smíšlející havěti nezlobili, musím zmínit i „kjůt“ přívržence Meega).

BrMo
BrMo

Ale pojďme od začátku. Hned po příchodu jsem vyfasoval pěknou visačku a šáhnul si do krabice pro marcipánového maskota své oblíbené platformy – zeleného mužíčka. Večeři mi to sice nenahradilo, ale bylo to fajn! Ještě bylo možné si vzít jablko, ale protože bylo nakousnuté, tak jsem se k němu nějak neměl. Na srazech bylo přihlášeno okolo dvaceti lidí a mám dojem, že všichni opravdu dorazili a k tomu ještě někteří navíc, takže se Karel Souček za pořadatele (firma Trinerdis) chopil slova a celou akci uvedl. Z jeho slov bylo vidět zapálení pro celou věc a bylo cítit, že si to opravdu užívá a „myslí to upřímně“, velký dík!

Z celkového průběhu akce (jak bylo uvedeno výše – jednalo se o neformální pokec) si odnáším dva zásadní poznatky:

  • Nikdy nechci dělat pro Badu – je to systém tak příšerný, nedotažený a s chováním SDK velmi podivným, až mě nesmírná hrůza jímala.
  • Nokie stále neví, co chce (ale tak nějak tuším, že se chce soustředit na business sféru, jen je otázka, se kterou platformou a jak dlouho).
Ve sklépku
Ve sklépku

Čas utekl jako voda a když jsem prchal na vlak tak jsem při loučení zjistil, že výše zmíněný Karel Souček bude zřejmě ten Karel Souček, se kterým jsme v páté třídě v biologickém kroužku trhali mouchám nožičky, koukali mikroskopem na cibuli a šikanovali učitelku; a další člověk z TrinerdisuPavel Žák – se mnou sdílel navštěvování páté třídy základní školy. Svět je malej a rok na jedno BrMo moc dlouhej!