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]