wtorek, kwietnia 30, 2013

How get real JDBC connection in BW Java

com.tibco.plugin.java.JavaConnectionAccessor ac = (com.tibco.plugin.java.JavaConnectionAccessor) conn;
java.sql.Connection wrappedConn = ac.getDBConnection();
java.sql.PreparedStatement ps = null;
java.sql.Clob val_binary = null;
try {
    java.lang.reflect.Field connField = null;
    for (java.lang.reflect.Field f : wrappedConn.getClass().getDeclaredFields()) {
if (java.sql.Connection.class.isAssignableFrom(f.getType()))
connField = f;
    }
    if (connField==null)
throw new RuntimeException("Cannot get original JDBC connection from Tibco Wrapper");
    connField.setAccessible(true);
    java.sql.Connection oraConn = (java.sql.Connection) connField.get(wrappedConn);
    /* do the work */
}
catch (java.sql.SQLException sqle) {
sqle.printStackTrace();
ac.releaseConnection(sqle);
wrappedConn = null;
throw sqle;
}
catch (Throwable t) {
t.printStackTrace();
ac.releaseConnection();
wrappedConn = null;
throw new RuntimeException("Unexpected Java code error", t);
}
finally {
if (ps!=null) {
try {
ps.close();
}
catch (Exception e) {}
}
if (wrappedConn!=null)
ac.releaseConnection();
}

niedziela, kwietnia 28, 2013

Java file names encoding

public static void main(String[] args) throws IOException {
Path p1 = Files.newDirectoryStream(Paths.get("/home/user/jdk/test"))
.iterator().next();
System.out.println("API 1.7: "+p1.toUri().getPath());
System.out.println("API 1.7: "+p1.toFile().getAbsolutePath());
String p2 = new java.io.File("/home/user/jdk/test").list()[0];
System.out.println("API 1.4: "+p2);
}

user@user-Aspire-4530:~/workspace/TestApp/bin$ LC_ALL="pl_PL.cp1250" java -Dfile.encoding=utf8 TestApp
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.4: g��eg������ka-����������
user@user-Aspire-4530:~/workspace/TestApp/bin$ LC_ALL="pl_PL.cp1250" java -Dfile.encoding=cp1250 TestApp
API 1.7: /home/user/jdk/test/g�eg���ka-����
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.4: g??eg??????ka-??????????
user@user-Aspire-4530:~/workspace/TestApp/bin$ LC_ALL="pl_PL.utf-8" java -Dfile.encoding=cp1250 TestApp
API 1.7: /home/user/jdk/test/g�eg���ka-����
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.4: g�eg���ka-����
user@user-Aspire-4530:~/workspace/TestApp/bin$ LC_ALL="en_US" java -Dfile.encoding=utf8 TestApp
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.4: g��eg������ka-����������
user@user-Aspire-4530:~/workspace/TestApp/bin$ LC_ALL="en_US" java TestApp
API 1.7: /home/user/jdk/test/g?eg???ka-?????
API 1.7: /home/user/jdk/test/g??eg??????ka-??????????
API 1.4: g??eg??????ka-??????????

user@user-Aspire-4530:~/workspace/TestApp/bin$ LC_ALL="pl_PL.utf8" java -Dfile.encoding=utf8 TestApp
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.7: /home/user/jdk/test/gżegżółka-ąęćśń
API 1.4: gżegżółka-ąęćśń

Why there are differences?

sun.nio.fs.UnixDirectoryStreamIterator creates String from bytes with default JVM encoding:

private Path readNextEntry() {
            assert Thread.holdsLock(this);

            for (;;) {
                byte[] nameAsBytes = null;

                // prevent close while reading
                readLock().lock();
                try {
                    if (isOpen()) {
                        nameAsBytes = readdir(dp);
                    }
                } catch (UnixException x) {
                    IOException ioe = x.asIOException(dir);
                    throw new DirectoryIteratorException(ioe);
                } finally {
                    readLock().unlock();
                }
...

File.list() is a native C/C++ method (suprise!):

JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
                                 jobject file)
{
    DIR *dir = NULL;
    struct dirent64 *ptr;
    struct dirent64 *result;
    int len, maxlen;
    jobjectArray rv, old;

    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
        dir = opendir(path);
    } END_PLATFORM_STRING(env, path);
    if (dir == NULL) return NULL;

    ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
    if (ptr == NULL) {
        JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
        closedir(dir);
        return NULL;
    }

    /* Allocate an initial String array */
    len = 0;
    maxlen = 16;
    rv = (*env)->NewObjectArray(env, maxlen, JNU_ClassString(env), NULL);
    if (rv == NULL) goto error;

    /* Scan the directory */
    while ((readdir64_r(dir, ptr, &result) == 0)  && (result != NULL)) {
        jstring name;
        if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
            continue;
        if (len == maxlen) {
            old = rv;
            rv = (*env)->NewObjectArray(env, maxlen <<= 1,
                                        JNU_ClassString(env), NULL);
            if (rv == NULL) goto error;
            if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
            (*env)->DeleteLocalRef(env, old);
        }
        name = JNU_NewStringPlatform(env, ptr->d_name);
...

JNIEXPORT jstring JNICALL
JNU_NewStringPlatform(JNIEnv *env, const char *str)
{
    jstring result;
    result = nativeNewStringPlatform(env, str);
    if (result == NULL) {
        jbyteArray hab = 0;
        int len;

        if (fastEncoding == NO_ENCODING_YET)
            initializeEncoding(env);

        if ((fastEncoding == FAST_8859_1) || (fastEncoding == NO_ENCODING_YET))
            return newString8859_1(env, str);
        if (fastEncoding == FAST_646_US)
            return newString646_US(env, str);
        if (fastEncoding == FAST_CP1252)
            return newStringCp1252(env, str);

        if ((*env)->EnsureLocalCapacity(env, 2) < 0)
            return NULL;

        len = (int)strlen(str);
        hab = (*env)->NewByteArray(env, len);
        if (hab != 0) {
            (*env)->SetByteArrayRegion(env, hab, 0, len, (jbyte *)str);
            if (jnuEncodingSupported(env)) {
                result = (*env)->NewObject(env, JNU_ClassString(env),
                                           String_init_ID, hab, jnuEncoding);
            } else {
                /*If the encoding specified in sun.jnu.encoding is not endorsed
                  by "Charset.isSupported" we have to fall back to use String(byte[])
                  explicitly here without specifying the encoding name, in which the
                  StringCoding class will pickup the iso-8859-1 as the fallback
                  converter for us.
                 */
                jmethodID mid = (*env)->GetMethodID(env, JNU_ClassString(env),
                                                    "", "([B)V");
                result = (*env)->NewObject(env, JNU_ClassString(env), mid, hab);
            }
            (*env)->DeleteLocalRef(env, hab);
            return result;
        }
    }
    return NULL;
}

Conclusions: When you get file names of everything other than native filesystem, you may have damaged strings. It they are not damaged, but in different UTF-8 representations you can translate them with Normalizer.

czwartek, kwietnia 25, 2013

Tibco Software Support is a failure

8 months of diagnosing  and fixing XA issue and... Tibco Software cannot fix XA transactions in their ESB stack. If you deal with money you shouldn't use EMS + BW, otherwise you will end up with inconsistent balances, stucked transactions, missing data and fatal manual recovery cases - everything without proper help from vendor. Leaving clients alone with serious problems can be the just that one argument to switch to different vendor. This bird won't fly. Where is 2 second advantage?

czwartek, kwietnia 18, 2013

Płatności mobilne

Dwa lata prac, więcej niż kilka milionów wydatków i 10000* użytkowników w 5 miesięcy - to płatności mobilne T-Mobile (PTC na chwilę obecną nie jest w stanie ustalić ilu jest realnych użytkowników płatności, 10000 to tylko liczba sprzedanych usług). 10000 użytkowników w niecałe 3 tygodnie i dalszy dynamiczny wzrost - to aplikacja IKO od PKO B.P. Czemu PTC odniosło taką porażkę a PKO sukces?

PTC:
  • wymagana dedykowana karta SIM z zapisaną aplikacją i kluczami kryptograficznymi
  • niewiele wspieranych telefonów z NFC, w zasadzie tylko Samsung Galaxy S3, skomplikowany dewelopment, brak własnych ekspertów
  • skomplikowana architektura systemu
  • specjaliści od bezpieczeństwa twierdzą, że NFC ma luki '0 day/in the wild'
  • usługa wymaga kooperacji z bankiem: Raiffeisen, mBank, Getin.
  • drogi abonament
  • wysokie koszty operatora (infrastruktura własna, T-Mobile DE, opłaty operatora transakcji Gemalto)
PKO:
  • aplikacja IKO na dowolna komórkę z Androidem, iOS-em, Blackberry (ale wymaga internetu)
  • bezpieczeństwo oparte na kryptografii i tokenach
  • usługa jest darmowa
  • można płacić w sklepach i robić wypłaty z bankomatów
  • można sprawdzić bieżący stan konta i historię wpływów/wydatków
Latem mało wygodnie jest nosić ze sobą portfel albo karty luzem, a komórkę ma się zawsze. PKO pokazuje, że jest bankiem otwartym na młodych ludzi i zależy im na wygodzie klientów.

Dla myWallet powinny zostać zrobione badania rynkowe: przepytanie grupy docelowej przez SMG/KRC co potencjalni klienci sądzą o usłudze, czy by chcieli jej używać, jakie widzą plusy a jakie minusy. Wyniki badania powinny zostać przeanalizowane i stanowić podstawę do nowej bardziej szczegółowej ankiety a potem warsztatów grupy testowej z makietą. Właścicieli biznesowi powinni wcześniej odrobić pracę domową jak wygląda proces kreacji produktu, wybrać do tego metodykę uwzględniającą strategiczną konieczność synergii między biznesem a NT/IT. Czy biznes plan był naciągany ze względu na naciski z Niemiec i wszystkie te rzeczy pominięto? Nie wiadomo. Jedno jest jednak pewne - każdy duży projekt powinien mieć dobrze sprawdzone uzasadnienie biznesowe.

czwartek, kwietnia 11, 2013

Massively parallel BW XML processing

1. Create Java Code parsing XML and finding appropriate nodes, with asynchronous requests creation and response handling:

Document doc = XmlHelper.getDocumentBuilder().parse(new InputSource(new StringReader(xml)));

List<Node> matchingParentNodes = XmlHelper.getNestedElementsByTagNs(doc, "http://namespace.example.com", "EntityNode");
int cnt = matchingParentNodes.size();
final CountDownLatch gate = new CountDownLatch(cnt);

for (Node node : matchingParentNodes) {
    final Message m = new Message();
    m.param1 = XmlHelper.getNodeValueNs(node, "Param1");
    m.cookie = node;
    m.requestReply(timeoutMillis, new MessageCallback() {
        @Override
        public void onMessage(Message resp) {
            Node owner = (Node) resp.cookie;
    XmlHelper.setNodeValueNs(owner, "Param2", resp.param2);
    gate.countDown();
        }
    });
}

gate.await(timeoutMillis, TimeUnit.MILLISECONDS);
if (gate.getCount() != 0)
    throw new RuntimeException("Not all subrequests "+gate.getCount()+" of "+matchingParentNodes.size()+" has been handled in given time "+timeoutMillis+"ms");

return XmlHelper.nodeToString(doc);

2. Java Event Starter reads Messages from LinkedBlockingQueue and handles single record.