neděle 10. září 2023

Jak ušetřit platbou v bitcoinech

 Bitcoin jsem nechtěl nikdy utrácet. Příjem mám korunách a navíc i u mě funguje Grashamův zákon. Jaký smysl by mělo převádět koruny na bitcoin abych jím následně zaplatil, pročež si restauratér přijaté satoshi ihned nechá směnit zpět na koruny? Nic než další transakční náklady.

Jednou za rok až dva jsem si na chaincampu za pár stovek koupil satoshi do lightning peněženky. Obvykle jsem to pak hned utratil za občerstvení nebo nějaké to tričko. Letos jsem měl ještě nějaké satoshi z nákupu před dvěma lety. Když jsem v peněžence ale viděl, že jsem tenkrát bitcoin kupoval za dvojnásobnou cenu, nechtělo se mě je utrácet vůbec - vždyť bych, vyjádřeno v korunách, platil dvojnásobnou cenu než je na cenovce. Silně jsem tak pocítil negativní stránku volatility bitcoinu. Negativní zkušenost lze ale vždy chápat jako příležitost. Tentokrát jsem si uvědomil, že je to příležitost nakoupit bitcoin za nízkou cenu. Ne proto abych v něm spořil na důchod/nemovitost/..., ale proto abych ho utratil za běžnou spotřebu v době, kdy bude opět za vysoké ceny. Nejen že budu moci nakupovat levněji, zároveň svým malým dílem přispěji ke snižování dlouhodobé volatility bitcoinu.

Představa, že dnes nakoupím bitcoin na svoji roční útratu v restauracích a za dva roky budu platit např.  přes qerko poloviční ceny, je hodně lákavá. Přitom pravděpodobnost takového scénáře mi nepřipadá malá.

Nejsem už tak mladý abych nevěděl, že jakoukoli moji myšlenku už měl někdo přede mnou. Pokud jste na takovou úvahu již narazili, dejte prosím link do komentářů.

pátek 21. července 2023

Použítí oneOf v YAML + openapi generator

 Definice problému

Mám Spring Boot aplikaci REST JSON služby. Služba má definované API pomocí YAML. Z YAML souboru jsou generovány Java třídy pomocí openapi-generator-maven-plugin verze 6.6.0 nebo novější.

Pokud je v YAML

Addressee:
oneOf:
- $ref: '#/components/schemas/AddresseeBranch'
- $ref: '#/components/schemas/AddresseeHome'

Je při generování vytvořeno rozhraní Addressee a třídy AddresseeBranch a AddresseeHome implementující rozhraní Addressee.
Při volání služby a následném pokusu o deserializaci nastane chyba

Cannot construct instance of `...Akddressee` (no Creators, like default constructor, exist).

Ta je způsobena tím, že deserializer neví na kterou instanci mapovat. Použije proto (nevhodně) samotné rozhraní, které však nelze instanciovat.
Tento problém je na githubu ve stavu otevřený.

Řešení

V některých případech (kdy je jedna z implementací pouhým rozšířením druhé) je možné pomocí SimpleAbstractTypeResolver napevno definovat na kterou z uvedených instancí se mají příchozí data deserializovat. Takové řešení je popsáno zde.

Obecné řešení však spočívá v použití diskriminátoru v YAML definici:

Addressee:
oneOf:
- $ref: '#/components/schemas/AddresseeBranch'
- $ref: '#/components/schemas/AddresseeHome'
discriminator:
propertyName: objectType

Následně je třeba v každém z referencovaných objektů definovat property objectType typu String. Ta by měla mít hodnotu odpovídající názvu reference (tj. názvu implementace rozhraní). Klient tak v requestu vlastně sám uvede, který z možných objektů posílá. objectType zároveň musí být povinná. Příklad pro první z uvedených instancí:

AddresseeBranch:
description: Office
type: object
required:
- objectType
properties:
objectType:
type: string
pattern: '^AddresseeBranch$'

Uvádět pattern není povinné. Je však vhodné ho definovat, aby v případě chybné hodnoty zadané klientem nevznikla chyba během zpracování. Takto bude případná chyba odchycena již při validaci requestu.

Pokud chceme aby hodnota property objectType neodpovídala názvu objektu, je třeba definovat mapování mezi hodnotou a názvem objektu. Podrobněji v sekci Mapping Type Names.




úterý 21. června 2022

Využití propertyExchange v todD

 Toto nefunguje:

 .setProperty("my_queue").jsonpath("path_to_my_queue_name")
 .setProperty("jmsAddress").simple("queue:${exchangeProperty.my_queue}")
 .toD(jms("${exchangeProperty.jmsAddress}").disableReplyTo(true))

Ani toto:

 .setProperty("my_queue").jsonpath("path_to_my_queue_name")
 .toD("jms:queue:${exchangeProperty.my_queue}?disableReplyTo=true")

Ale pouze toto:

 .setProperty("my_queue").jsonpath("path_to_my_queue_name")
 .setProperty("jmsAddress").simple("jms:queue:${exchangeProperty.my_queue}?disableReplyTo=true")
 .toD("${exchangeProperty.jmsAddress}")

Do metody toD lze umisťovat pouze jednoduchý Simple výraz, do metody jms pouze obyčejný String, který nebude vyhodnocován.

úterý 2. června 2020

Několik tipů k WSO2 BPS

Vzhledem k tomu, že dokumentace k WSO2 BPS není dokonalá a někdy je třeba pátrat delší čas, rozhodl jsem se pár postřehů z práce s tímto produktem zapsat sem.
Více o BPMN na wikipedii.

BPS má REST API pomocí kterého lze řídit (spouštět, získávat info,...) procesy. Podrobněji v Activiti dokumentaci
Jak lze koordinovat human tasky je zdokumentováno.
Stav procesů lze nahlédnout na /bpmn-explorer který defaultně běží na portu 9445.

TimerStartEvent - spouštění procesu v požadovaný čas nebo opakovaně

  • V tomto prvku je možné definovat 3 property. Je třeba vždy definovat právě jednu, ostatní jsou totiž ignorovány. 
  • Výrazy musí být dle ISO 8601, to lze zvalidovat.
  • Pokud se ve výrazu definuje časové pásmo (např. +02:00) nefunguje dle očekávání
  • Tipy jak definovat výrazy: https://github.com/cylc/cylc-flow/wiki/ISO-8601 (příklad spuštění každý den v 9 hodin: R/T09:00/P1D
MessageStartEvent
REST Task - spouštění REST requestů
Script Task
(Exclusive) Gateway

čtvrtek 23. dubna 2020

Transport error: 401 Error: Unauthorized ve WSO2

Při použití Basic auth a patřičném nastavení property Authorization může přesto nastat výjimka
 org.apache.axis2.AxisFault: Transport error: 401 Error: Unauthorized  
      at org.apache.axis2.transport.http.HTTPSender.handleResponse(HTTPSender.java:371)  
      at org.apache.axis2.transport.http.HTTPSender.sendViaGet(HTTPSender.java:109)  

To může být způsobeno firewallem, za kterým WSO2 produkt je a který vyžaduje onu autorizaci. Řešením může být nastavení property Proxy-Authorization. Více v dokumentaci.

středa 22. dubna 2020

Peer not authenticated + SSL peer failed hostname validation for name: null

Při volání serveru přes https, v tomto případě volání endpointu z WSO2, nastala chyba:
 HTTPSender Unable to sendViaGet to url[https://myserver/context]  
 javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname validation for name: null  
      at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.verifyHostname(TLSProtocolSocketFactory.java:233)  
      at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:186)  

Zkusil jsem spustit znovu s parametrem
 -Djdk.internal.httpclient.disableHostnameVerification=true  

a následovala výjimka:
 HTTPSender Unable to sendViaGet to url[https://myserver/context]  
 javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated  
      at sun.security.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)  
      at org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory.verifyHostName(SSLProtocolSocketFactory.java:276)  

Pokud byl request proveden pomocí curl, tak fungoval
 curl -X GET --cacert /C/certs/root_cert.cer 'https://myserver/context ' -H 'Authorization: Basic QVBJOk1vbmV0YTIwMjA=' -v  

Zkusil jsem openssl
 OpenSSL> s_client -connect myserver:443  
 Loading 'screen' into random state - done  
 CONNECTED(000008C0)  
 write:errno=10054  
 ---  
 no peer certificate available  
 ---  
 No client certificate CA names sent  
 ---  
 SSL handshake has read 0 bytes and written 317 bytes  
 ---  
 New, (NONE), Cipher is (NONE)  
 Secure Renegotiation IS NOT supported  
 Compression: NONE  
 Expansion: NONE  
 No ALPN negotiated  
 ---  
 error in s_client  
Zde je vidět, že server nevrátil certifikát.

Zkusil jsem znovu s parametrem -servername
 openssl s_client -connect myserver:443 -servername myserver  

Také je vhodné ověřit, že server podporuje TLSv1.2:
 openssl s_client -connect myserver:443 -tls1_2 -servername myserver  

V obou případech bylo vidět, že server certifikát vrací. Parametr -servername nastavuje TLS SNI (Server Name Indication) extension v ClientHello message během handshake, jak je popsáno v dokumentaci.
Při následném prohlédnutí ClientHello zprávy pomocí wiresharku bylo patrné, že neobsahuje server_name extension (více o tématu zde). Tato je pro některé servery nutná (i když dle RFC6066 nepovinná) - jeden webový server může sloužit více doménám a jaký certifikát použít se rozhoduje právě na základě obsahu server_name.
V javě lze vypnout nebo povolit (defaultní stav) SNI pomocí parametru
 -Djsse.enableSNIExtension=true  

Naštěstí jsem narazil na informaci, že Java nastavuje extension server_name pouze pro FQDN, což myserver není (podobně jako např. localhost).
Pokud tedy chceme z javy volat server, který v handshake protokolu Client Hello vyžaduje extension server_name, je třeba aby tento měl FQDN a samozřejmě odpovídající certifikát, který máme umístěn i v našem truststore.

pátek 10. ledna 2020

WSO2: Paralelní zpracování v multitenantním prostředí

Pokud chceme na WSO2 provádět paralelní zpracování, lze k tomu, v závislosti na naší potřebě, využít jednoho z dvou mediátorů: clone a iterate. Oba mediátory obsahují element target, který má v obou případech shodnou implementaci třídou org.apache.synapse.mediators.eip.Target. Z tohoto důvodu také oba mediátory umožňují použít shodné elementy sequential (pomiňme nyní, že v dokumentaci mediatoru clone není uveden) a continueParent.
Atribut sequential uvádí, zda mají být jednotlivé targety zpracovávány sekvenčně. Pokud však použijete sequential="false" (defaultní hodnota) jsou mediatory v targetu spouštěny nečekaně pod carbon.super tenantem. To může způsobit nemožnost volání endpointů, protože k nim carbon.super tenant nemusí být subscribnutý (subscribnout carbon.super v multitenantním prostředí není dobrý nápad) nebo nevlastní patřičný autentizační token.
Atribut continueParent určuje, zda má být zachován původní payload. Další, pro nás klíčovou, vlastností použití tohoto atributu je i zpracování mediatoru bez čekání na jeho výsledek. Více uvedeno v Example 2.

Předpokládejme, že jsme chtěli paralelně volat dvě sekvence (obsahujících např. volání endpointů):

<clone sequential="false">
    <target>
        <sequence key="firstSequence"/>
    </target>
    <target>
        <sequence key="secondSequence"/>
    </target>
</clone>

Z důvodu uvedeného výše to však v multitenantním prostředí není možné. Požadovanou funkci ale bude mít následující řešení:

<clone sequential="true" continueParent="true">
    <target>
        <sequence key="firstSequence"/>
    </target>
</clone>
<sequence key="secondSequence"/>

Pokud budeme chtít takto paralelně volat více sekvencí, je tedy třeba každou umístit do vlastního clone.
Pokud má být sekvence v clone volána způsobem fire-and-forget, pak je třeba na konec clone umístit mediator
<drop/> - jinak by následující flow bylo voláno vícenásobně (z každého clone znovu).