Caluga
all about java, coding and hacking

Caluga - The Java Blog

this blog will cover topics all around Java, opensource, Mongodb and alike. Especially Morphium - THE POJO Mapper for mongodb


found results: 78

<< 1 ... 2 ... 3 ... >>


category: Computer

using Qnap as GIT-Server - SSH Problems

2013-11-06 - Tags: git qnap

Linux really rules, especially if you consider the possibilities you get, adding functionalities to linux based gadgets or fix / add missing functionalities.

The latter one is something that also can be said about the Qnap storage system. I use git for my own little software projects. This is cool as a version control system and easy to use - better than SVN or CVS.

So I have my repositories on a share on my qnap. You can mount the share and use git to synchronize stuff, that works fine so fare. But causes problems, when working remotely where you do not want to mount the share in order to be able to push things.

Luckily you can install additional tools on the qnap, git is one of them. And it works fine out of the box, you can use ssh to access git and the repositories.

Unfortunately is the sshd that comes with the qnap somewhat works strange, you can alter the /etc/sshd to what you want, it won't be possible to log in as something else than admin.

I do not want to open some root login to this qnap, no way. So I digged a bit deeper and found out, that the sshd is altered to only allow logins as admin.

But I am root on this machine, so lets hack.

  1. install openssh via the Optware installation frontend. unfortunately this alone does not work, as the installation is not replacing the existing one. So we need to go further
  2. rename original in /usr/sbin: mv sshd sshd.qnap
  3. create link: ln -s /opt/sbin/sshd
  4. alter your sshd_config to your needs
  5. restart sshd (either via the GUI by disabling and re-enabling remote login or via kill the SSHD - Attention, this might and often will kick you out)

ok, now you should be able to log in as someone else than admin.

now you only need to create your repositories to your liking. I created one special share for it (which can also be mounted).

in my case it would be something like: git clone user@qnap:/share/development/git/repo

if you think this might be a security risk, you could set the login shell for that user to git-shell to avoid direkt access of this user.

Happy Hacking


category: Java --> programming --> Computer

New Version of Morphium Mongodb POJO Mapper V2.0.23

2013-11-06 - Tags:

sorry, no english version available


category: Computer --> programming --> Objective-C

RSA Implementierung in Objective-C

2013-11-04 - Tags: objective-c security encryption

Still work in progress, does not 100% reflect the German Version

I already wrote about my effort in creating a completely independent RSA implementation here. I posted it also on github-RSALib, so maybe somebody wants to use it... or wants to give feedback.

There is also a byte-compatible java version here. Thing is, it is not very easy to use encryption on different operating systems, e.g. iOS and Linux, and assume things are working fine. Actually, usually it is quite the opposite. But the most important Reason for those implementations was to have an insight in how encryption can be done without the os, so to exactly know what happens. No OS-backdoor could break into that... at least, not as easily.

The proof of concept is a very complicated Messaging System, that runs on iOS using the RSAImplementation mentioned above, connecting to a server implemented in java using the same implementation in java. Works quite fine...

what now

I was thinking about creating something useful with that code. I used to write a private diary on my mac using the App DayOne. This is a very cool app, runs on iOS and OSX, synchronizes you entries between different installations and is very beautiful to use - and is not encrypted at all! For a diary App! How can that be? Even the simplest physical diary usually has some kind of lock on it or somathing... But with DayOne all entries are store in plain text on the disk drive of all machines you use DayOne and synchronize it.

So, if your MacBook is lost - all your private Thoughts are plain to see there... Congratulations.

There is actually no good encrypted Diary in the App Store. Some encrypt good, but are very uncomfortable. Some are beautiful, but do not encrypt good... And so on...

So... a new Diary App was needed (at least for me):

For my Eyes Only

This is the name of the App, and this is the name the app will be published with. The Idea is quite easy, use the AES/RSA Implementations above to create a securely encrypted Diary... Great Idea!

What features do we need:

  • RSA Encryption. But where to put the Key? Obviously not a good idea to store the key in the app itself, but how about storing it hidden in a picture (steganography)? or on an usb stick? Both options should be possible
  • The keys are of course AES encrypted
  • It should be able to be synchronized using iCloud (maybe dropbox)
  • It should be possible to add photos and such to your entries, but those need to be encrypted as well
  • you need to be able to search for things

Problem is: using CoreData is not working, as the structure would still be visible, and searching would not work any way, as everything would be encrypted...

So... We need to create our own storage system.

Implementation Diary

I will put in some things I did during the implementation here, even if the app will never be published, it might be worth having this info around somehow...


category: Apple --> Computer

von Java zu Objective-C

2013-10-24 - Tags:

sorry, no english version available


category: Java --> programming --> Computer

Morphium: Minor feature release avaliable V2.0.22

2013-10-16 - Tags: java-2 morphium-2 programming

sorry, no english version available


category: global

Morphium - MongoDb Object Mapper now on GitHub

2013-10-15 - Tags:

sorry, no english version available


category: Computer --> programming --> Objective-C

VerschlĂŒsselung auf iOS mit Objective-C

2013-10-02 - Tags: ios objective-c-2 rsa

no english version available yet

Im Zuge der ganzen NSA/Prism/Snowden Diskussion und auch weil es mich interessiert, hab ich mich mal rangesetzt und mir ein paar Gedanken zum Thema "privacy & Security" im Internet gemacht.

Was momentan fehlt ist ein Weg, wirklich sicher zu kommunizieren. Die DE-Mail ist ja nur ein besserer Witz (sicherheitstechnisch zumindest) und GPG / PGP ist viel zu kompliziert zu benutzen und vor allem - man benötigt eine IdentitĂ€tsprĂŒfung, "komische" Zertifikate und anderes, was der Otto-Normal-User so nicht wirklich versteht.

Das ist alles viel zu weit davon entfernt, wirklich brauchbar zu sein oder auch nur annĂ€hernd Email den Rang abzulaufen. Eigentlich mĂŒsste es vollkommen transparent laufen und alles VerschlĂŒsselt werden - von End-To-End. D.h. von einem Client werden die Daten so verschlĂŒsselt, dass nur der EmpfĂ€nger diese Daten entschlĂŒsseln kann.

Im Zuge dessen habe ich versucht, auf IOS eine Public-Key-VerschlĂŒsselung hin zu bekommen, um zu sehen, was man damit noch so anstellen kann. Das ganze hat mich doch vor mehr Probleme gestellt, als gedacht.

ZunĂ€chst mal ein Symmetrisches VerschlĂŒsselungsverfahren - auch wenn das fĂŒr das Ziel einer End-To-End-VerschlĂŒsselung nicht wirklich ausreichend ist (symmetrische SchlĂŒssel kann man immer per Brute-Force angreifen), so ist es doch wichtig, dass die Keys nicht irgendwo unverschlĂŒsselt rumliegen, sondern, wenn ĂŒberhaupt, dann mit einem guten Passwort verschlĂŒsselt werden.

AES in Objective-C

Das war erstaunlicherweise gar nicht so kompliziert, es gibt da wirklich eine Menge Beispiele im Netzt zu, die auch wirklich funktionieren. Hier der Code zum VerschlĂŒsseln von NSData:

[codesyntax lang="objc"]

- (NSData *)AES256EncryptWithKey:(NSString *)key{
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
    bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc( bufferSize );

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted );
    if( cryptStatus == kCCSuccess )
    {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free( buffer ); //free the buffer
    return nil;
}

[/codesyntax]

Das funktioniert tadellos und ist einfach zu verwenden. Da man symmetrisch verschlĂŒsselt, muss man auch nicht mit 2 Keys rumhantieren und die evtl. sogar im KeyStore ablegen.

Die EntschlĂŒsselung ist auch recht simpel:

[codesyntax lang="objc"]

- (NSData *)AES256DecryptWithKey:(NSString *)key{

    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc( bufferSize );

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesDecrypted );

    if( cryptStatus == kCCSuccess )
    {
        NSLog(@"Decrypt success");
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

    free( buffer ); //free the buffer
    return nil;
}

[/codesyntax]

RSA Implementierung

Standardweg mit Bordmitteln

ZunĂ€chst hab ich mal die Bordmittel von iOS 6 (und spĂ€ter 7) benutzt, um daten VerschlĂŒsseln zu können. Das hat zunĂ€chst auch recht gut funktioniert - hier ein code-Beispiel:

[codesyntax lang="objc" lines="fancy" lines_start="1"]

NSData *wrappedSymmetricKey = data;
SecKeyRef key = yes ? self.publicKeyRef : self.privateKeyRef;

size_t cipherBufferSize = SecKeyGetBlockSize(key);
size_t keyBufferSize = [wrappedSymmetricKey length];

NSMutableData *bits = [NSMutableData dataWithLength:keyBufferSize];
OSStatus sanityCheck = SecKeyDecrypt(key,kSecPaddingPKCS1,
(const uint8_t *) [wrappedSymmetricKey bytes],cipherBufferSize,[bits mutableBytes],&keyBufferSize);
NSAssert(sanityCheck == noErr, @"Error decrypting, OSStatus == %ld.", sanityCheck);

[bits setLength:keyBufferSize];

return bits;

[/codesyntax]

 

Das ist der Code, der einen gegebenen Datenblock (NSData*) verschlĂŒsselt und die verschlĂŒsselten Daten wiederum als NSData zurĂŒckgibt. (die Basis fĂŒr diesen Code findet sich hier). Das funktioniert so weit auch wunderbar.... solange man bei jedem Start der Application, das SchlĂŒsselpaar neu generiert.

Aber eins nach dem anderen. Was macht der Code? Er nutzt die auch unter OSX bekannte "SchlĂŒsselbundverwaltung" bzw. dessen Pendant von iOS. Dort mĂŒssen die SchlĂŒsselpaare abgelegt werden um sie entsprechend nutzen zu können.

Die Erstellung und speicherung eines SchlĂŒsselpaares geht z.B. so (auch wieder von hier):

[codesyntax lang="objc"]

OSStatus sanityCheck = noErr;
    publicKeyRef = NULL;
    privateKeyRef = NULL;

    // First delete current keys.
    [self deleteAsymmetricKeys];

    // Container dictionaries.
    NSMutableDictionary *privateKeyAttr = [NSMutableDictionary dictionaryWithCapacity:0];
    NSMutableDictionary *publicKeyAttr = [NSMutableDictionary dictionaryWithCapacity:0];
    NSMutableDictionary *keyPairAttr = [NSMutableDictionary dictionaryWithCapacity:0];

    // Set top level dictionary for the keypair.
    [keyPairAttr setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id) kSecAttrKeyType];
    [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:kSecAttrKeySizeInBitsLength] forKey:(__bridge id) kSecAttrKeySizeInBits];

    // Set the private key dictionary.
    [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecAttrIsPermanent];
    [privateKeyAttr setObject:privateTag forKey:(__bridge id) kSecAttrApplicationTag];
    // See SecKey.h to set other flag values.

    // Set the public key dictionary.
    [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecAttrIsPermanent];
    [publicKeyAttr setObject:publicTag forKey:(__bridge id) kSecAttrApplicationTag];
    // See SecKey.h to set other flag values.

    // Set attributes to top level dictionary.
    [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id) kSecPrivateKeyAttrs];
    [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id) kSecPublicKeyAttrs];

    // SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
    sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef) keyPairAttr, &publicKeyRef, &privateKeyRef);

    if (publicKeyRef == NULL) {
        NSLog(@"did not get keys");
    }
    LOGGING_FACILITY( sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL, @"Something really bad went wrong with generating the key pair." );

[/codesyntax]

 

Dabei wird ein neues SchlĂŒsselpaar generiert und die Referenzen darauf lokal abgelegt (Variablen publicKeyRef und privateKeyRef).

So weit so gut, das klappt wunderbar, hat nur einige EinschrĂ€nkungen: so kann man keine SchlĂŒssel erzeugen, die grĂ¶ĂŸer sind als 4096 bit, außerdem mĂŒssen an stelle von dem SchlĂŒssel selbst ja auch noch einige Zusatzinformationen abgespeichert werden, wie z.B. unter welchem "Namen" die keys abgelegt werden etc.

Das, was mich am meisten daran gestört hat, ist aber, dass es eine sehr unhandliche Schnittstelle ist, wo zwischen Objective-C und C/C++ hin und hergemapped werden muss. (Anm. ich hab die letzten Jahr(zehnt)e hauptsÀchlich Java programmiert, da ist alles etwas "sauberer"). Eine schönere Objective-C implementierung hab ich nicht gefunden. (falls ihr eine kennt, immer her damit...)

Aber was mich wirklich genervt hat, ist, dass ich es ums verrecken nicht hin bekommen habe, diese dĂ€mlichen RSA-Keys z.B. aus den UserDefaults wieder zu lesen. No chance... man kann sich die Bits zwar holen, kann aus den Bits auch wieder Keys erstellen lassen... allerdings landen die dann irgendwo im Storage und ich bekomme den Key nicht mehr da raus. Entweder er ist nil oder er kann nicht zum entschlĂŒsseln benutzt werden.

Also, hier der letzte Code-Stand, evtl. habt ihr ja ne Idee, was da klemmen kann:

[codesyntax lang="objc"]

 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *privKey = [defaults dataForKey:@"privateKey"];
    NSData *pubKey = [defaults dataForKey:@"publicKey"];
    RSA *rsa = [RSA shareInstance];
    [rsa deleteAsymmetricKeys];
    if (privKey == nil) {
        NSLog(@"No key stored");

        NSLog(@"Generating new keys... please wait");
        self.aesPwdTF.text = @"generating keys...please wait";
        [self.textTF setEnabled:false];
        [self.aesPwdTF setEnabled:false];
        [rsa generateKeyPairRSACompleteBlock:^{
            NSLog(@"Keys prepared");
            self.aesPwdTF.text = @"";
            [self.textTF setEnabled:true];
            [self.aesPwdTF setEnabled:true];
            [self.aesPwdTF becomeFirstResponder];
            [defaults setObject:[rsa privateKeyBits] forKey:@"privateKey"];
            [defaults setObject:[rsa publicKeyBits] forKey:@"publicKey"];
        }];
    } else {
        [rsa setPrivateKey:privKey];
        [rsa setPublicKey:pubKey];
        self.publicKeyTF.text = [[rsa publicKeyBits] base64EncodedString];
        NSLog(@"Private key %@", [[rsa privateKeyBits] base64EncodedString]);
        NSLog(@"read keys from defaults");
    }

[/codesyntax]

 

Das ist der Teil, der entscheided, ob der Key noch mal eingelesen werden soll. Klar, man könnte das auch im Keystore drin lassen, aber leider kann ich die daten ja nicht auslesen, denn hier klemmt es jedes Mal in der einen oder anderen Form:

[codesyntax lang="objc"]

- (void)setPrivateKey:(NSData *)privateKey {
    privateKeyRef = NULL;
    OSStatus sanityCheck = noErr;
    SecKeyRef peerKeyRef = NULL;

    LOGGING_FACILITY( privateKey != nil, @"Private key parameter is nil." );

    NSMutableDictionary *peerPrivateKeyAttr = [[NSMutableDictionary alloc] init];

    [peerPrivateKeyAttr setObject:(__bridge id) kSecClassKey forKey:(__bridge id) kSecClass];
    [peerPrivateKeyAttr setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id) kSecAttrKeyType];
    [peerPrivateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [peerPrivateKeyAttr setObject:privateTag forKey:(__bridge id) kSecAttrApplicationTag];
    [peerPrivateKeyAttr setObject:privateKey forKey:(__bridge id) kSecValueData];
    [peerPrivateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecReturnPersistentRef];

    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) peerPrivateKeyAttr, (CFTypeRef *) &peerKeyRef);
    if (sanityCheck) {
        if (sanityCheck != errSecDuplicateItem)
            return;
        // Already have a key with this digest, so look it up to get its ref:
        [peerPrivateKeyAttr removeObjectForKey: (__bridge id)kSecValueData];
        [peerPrivateKeyAttr setObject: privateTag forKey: (__bridge id)kSecAttrApplicationLabel];//??
        [peerPrivateKeyAttr removeObjectForKey: (__bridge id)kSecReturnPersistentRef];
        [peerPrivateKeyAttr setObject: (__bridge id)kCFBooleanTrue forKey: (__bridge id)kSecReturnPersistentRef];
        SecItemCopyMatching((__bridge CFDictionaryRef)peerPrivateKeyAttr, (CFTypeRef*)peerKeyRef);
    }

    NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];

    // Set the private key query dictionary.
    [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    SecItemCopyMatching
    ((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&peerKeyRef);
    privateKeyRef = peerKeyRef;

}

[/codesyntax]

Das blöde ist, dass nach der AusfĂŒhrung dieses Codes, sowohl im Simulator als auch auf dem GerĂ€t die Variable privateKeyRef entweder nil ist, oder nicht fĂŒr die EntschlĂŒsselung verwendet werden kann.

Aber ich wollte ja eh eine "schönere" Schnittstelle bauen, die das ganze etwas entmystifiziert...

You don't own it, 'till you make it

Also, wie funktioniert eigentlich RSA. So kompliziert ist das eigentlich gar nicht, allerdings handelt es sich dabei um sehr große Primzahlen. Hier eine Beispielimplementierung in Java:

[codesyntax lang="objc"]

public class RSA {
   private final static BigInteger one      = new BigInteger("1");
   private final static SecureRandom random = new SecureRandom();

   private BigInteger privateKey;
   private BigInteger publicKey;
   private BigInteger modulus;

   // generate an N-bit (roughly) public and private key
   RSA(int N) {
      BigInteger p = BigInteger.probablePrime(N/2, random);
      BigInteger q = BigInteger.probablePrime(N/2, random);
      BigInteger phi = (p.subtract(one)).multiply(q.subtract(one));

      modulus    = p.multiply(q);
      publicKey  = new BigInteger("65537");     // common value in practice = 2^16 + 1
      privateKey = publicKey.modInverse(phi);
   }

   BigInteger encrypt(BigInteger message) {
      return message.modPow(publicKey, modulus);
   }

   BigInteger decrypt(BigInteger encrypted) {
      return encrypted.modPow(privateKey, modulus);
   }

   public String toString() {
      String s = "";
      s += "public  = " + publicKey  + "n";
      s += "private = " + privateKey + "n";
      s += "modulus = " + modulus;
      return s;
   }

   public static void main(String[] args) {
      int N = Integer.parseInt(args[0]);
      RSA key = new RSA(N);
      System.out.println(key);

      // create random message, encrypt and decrypt
      BigInteger message = new BigInteger(N-1, random);

      //// create message by converting string to integer
      // String s = "test";
      // byte[] bytes = s.getBytes();
      // BigInteger message = new BigInteger(s);

      BigInteger encrypt = key.encrypt(message);
      BigInteger decrypt = key.decrypt(encrypt);
      System.out.println("message   = " + message);
      System.out.println("encrpyted = " + encrypt);
      System.out.println("decrypted = " + decrypt);
   }
}

[/codesyntax]

(Gefunden hier)

Eigentlich ist es ja nur nötig, ein paar Primzahlen zu generieren, die man dann potenziert und den modulo berechnet, sowohl zum Ver- als auch EntschlĂŒsseln.

Klingt eigentlich total simpel, aber die Herausforderung liegt darin, mit Zahlen zu rechnen, die mehrere 1000 bit lang sind - man muss also ne eigene Arithmetik abbilden.

Das kann beliebig komplex werden, weshalb ich mich bei meiner Implementierung an die GNU-Java BigInteger Implementierung gehalten habe und diese mehr oder minder auf Objective-C portiert habe (Source Code dafĂŒr ist hier).

Die Portierung ist nicht wirklich einfach gewesen, vor allem weil Java und Objective-C / C++ eine andere Vorstellung von primitiven Datentypen haben, aber zumindest lÀuft die BigInteger Arithmetik erst mal so weit.

Nachdem ich eine BigInteger Implementierung in Objective-C zur VerfĂŒgung hatte, ist die VerschlĂŒsselung Ă€hnlich simpel wie in java:

[codesyntax lang="objc"]

- (BigInteger *)encryptBigInteger:(BigInteger *)message {
    if (message.bitLength > self.bitLen) {
        NSLog(@"Encrypting impossible: Message is too long for key! Key is %d bits wide, message is %d", self.bitLen, message.bitLength);
        //splitting it? how?
        return nil;
    }
    return [message modPow:self.e modulo:self.n];
}

- (BigInteger *)decryptBigInteger:(BigInteger *)message {
    return [message modPow:self.d modulo:self.n];
}

[/codesyntax]

Wobei da sicherlich noch ein paar Optimierungen passieren könnten, wie z.B. das Splitten zu großer Eingaben in mehrere BigIntegers mit der richtigen GrĂ¶ĂŸe fĂŒr die VerschlĂŒsselung.

Ich denke, das Prinzip von RSA ist bekannt und kann so Anwendung finden. Ich werde hier sicherlich noch mehr Code posten der euch dann vielleicht auch ne Menge Arbeit erspart.

Was jetzt noch fehlt sind mehr oder minder 2 Dinge: Das umwandeln von NSData in eine Menge von BigIntegers sowie der zugehörige umgekehrte Weg. Wenn das funktioniert, kann man jeden BigInteger-Wert einzeln ver- bzw. entschlĂŒsseln und kann somit beliebige Daten sicher ĂŒbertragen oder ablegen.

Aber darĂŒber ein andern mal mehr...

Warum das ganze?

Naja, zum einen hat es mich stark interessiert, mal so ein verschlĂŒsselungsverfahren umzusetzen - und da ich in Objective-C keine passende Implementierung gefunden habe (einen Haufen Implementierungen in c/c++ ja, aber keine wirklich objektorientiert, die man als Java-Entwickler leichter versteht), lag es doch besonders Nahe, das gleich mal in Objective-C zu implementieren.

Eine Anwendung dafĂŒr schwebt mir auch schon vor, mal sehen, in wie weit dass dann wirklich seinen Weg in den App-Store findet... so "stay tuned" ;-)

 


category: Linux/Unix --> Computer

MongoDB Erfahrungen / Tips und Tricks

2013-06-17 - Tags:

sorry, no english version available


category: Computer --> programming

Objective-C vs Java - Memory Management

2013-05-29 - Tags: java memorymanagement objective-c

no english version available yet

Bei meinen Entwicklungen in Java und Objective-c musste ich mich zwangslĂ€ufig auch mit dem Memory Management auseinandersetzen. Das ist in Java meistens ja etwas, das insbesondere seit JDK 1.4 mehr oder minder im Hintergrund stattfindet. Das war eine der großen Neuerungen von Java und viele andere Sprachen haben es ĂŒbernommen. Aber gerade das aufkommen der neuen mobilen EndgerĂ€te mit doch mehr oder minder beschrĂ€nkten Resourcen machen das MemoryManagement der eigenen Applikationen wieder etwas spannender.

Warum ĂŒberhaupt Speichermanagement?

FrĂŒher war doch alles besser - im guten alten Assembler hat man einfach so im Speicher rumgeschrieben, ohne sich einen Dreck um irgendein Betriebssystem oder so zu kĂŒmmern. Das ist heute natĂŒrlich nicht einfach so möglich, denn das Betriebssystem benutzt ja auch RAM und man ist ja nicht das einzige Programm, das gerade lĂ€uft. So gesehen, war die gute alte 8-Bit Ära schon besser, weil einfacher! Heute muss man erst mal das System fragen, wenn man Speicher benutzen will, denn sonst wird das Programm schnell einfach beendet (Unter Unix ist das gerne mal ein „Segmentation Fault“) - schon aus SicherheitsgrĂŒnden - man könnte ja sonst wichtige Systemfunktionen oder ~daten ĂŒberschreiben. Speicher allokiert man z.B. in C/C++ mit der Funktion malloc. Ihr gibt man einfach eine GrĂ¶ĂŸenangabe mit und als Ergebnis erhĂ€lt man einen Zeiger auf den fĂŒr uns zur VerfĂŒgung gestellten Speicherbereich. Das GegenstĂŒck zu mallocist free- damit wird zuvor allokierter Speicher wieder frei gegeben. Alles eigentlich doch recht simpel. Naja
 da könnte man doch fragen, was denn daran so kompliziert ist, den Speicher, den man sich vorher „geholt“ hat, dann auch wieder frei zu geben, wenn man ihn nicht mehr braucht. Im Grunde ist das richtig, vor allem fĂŒr sehr einfache Programme kann man den Speicher so „verwalten“. Aber heutige Anwendungen haben schon andere Anforderungen an das Speichermanagement. Das fĂ€ngt schon damit an, dass ich zum Erstellungszeitpunkt gar nicht genau weiß, wie viel Speicher ich eigentlich benötige. Person p=new Person(); p.setName(„Ein Name“); p.setAge(new Integer(44)); sieht simpel aus, oder? aber genaugenommen passiert folgendes: es wird Speicher fĂŒr ein Objekt „Person“ allokiert. Die Person ist noch „leer“. Jetzt wird ein Name hinzugefĂŒgt. D.h. zum Zeitpunkt der Erstellung der Person wusste ich nicht, wie groß das Objekt eigentlich werden sollte. Ich kann also gar nicht wissen, wie viel Speicher ich bereit stellen muss.

Retain Cycle

Gleiches gilt fĂŒr die Freigabe. Wann soll ich den Speicher wieder frei geben? Wann wird die Person pnicht mehr benötigt? Das wird besonders dann komplex, wenn man auch komplexere Datenstrukturen benötigt. Ein einfaches Beispiel dafĂŒr ist eine Baumstruktur:

 Class Parent extends Node{
      List children;
    }
    Class Child extends Node {
       Parent parentNode;
    }

solch eine Struktur ist normalerweise im Vorhinein nicht klar in der GrĂ¶ĂŸe festzulegen. Außerdem ergeben sich sog. Retain Zyklen. Hier hat jeder Knoten einen Zeiger auf seine Vaterknoten und dieser wiederum einen Zeiger auf alle Kindknoten. Das bedeutet, jeder dieser Knoten ist von irgendwoher erreichbar, es gibt ja eine Referenz darauf. Das Hauptprogramm:

root = new Parent();
//add children
// do something with root and the Data
Root = new Parent()

Das Hauptprogramm erzeugt hier einen Parent-Knoten mit alle seinen Kindknoten. Der der so belegte Speicher ist noch vom Hauptprogramm aus erreichbar, da wir ja die Referenz root haben. Wird diese allerdings ersetzt (letzte Zeile), ist der ganze Baum nicht mehr erreichbar und der Speicher könnte theoretisch freigegeben werden. Allerdings ergibt sich ein Retain Zyklus, da ja jeder knoten von einem anderen referenziert wird => Dilemma.

Wie funktioniert das in java - der Garbage Collector

Den Garbage Collector (oder kurz GC) kann man sich vorstellen, wie die Mutter eines Teenagers, die immer hinter ihm herlĂ€uft und auf- bzw. wegrĂ€umt. Genauso ist der Garbage Collector. Der GC erkennt die Objekte, die vom Programmthread (bzw von allen Programmthreads) aus erreichbar sind. Alle anderen werden gelöscht. DafĂŒr erstellt der GC einen sog. Erreichbarkeitsgraphen aller erstellten Objekte. Und der Speicher aller Objekte, die vom einem laufenden Thread aus nicht mehr erreichbar sind, wird freigegeben. Klingt im ersten Moment recht simpel, wird aber insbesondere im Hinblick auf Retain Cycle etwas undurchsichtig. Kurz gesagt, der GC kann auch komplexere Datenstrukturen einfach aus dem Speicher rĂ€umen, da er ja die Erreichbarkeit von den Programmthreads aus berechnet und nur die Objekte abrĂ€umt, die nicht mehr erreichbar sind. => auch Retain Cycle werden abgerĂ€umt. Manchmal wird geschrieben, dass der GC Retain Cycle erkennt, da er ja alle darin befindlichen Objekte im Speicher hĂ€lt, wenn es auch nur eine Referenz auf das Objekt gibt.

Wie obiges Beispiel: Baumstruktur. Vaterknoten hĂ€lt Referenzen auf Kindknoten und umgekehrt. Wenn der Vater abgerĂ€umt wird, weil er nicht mehr erreichbar ist, werden auch alle Knoten, die daran hĂ€ngen mit abgerĂ€umt (es sei denn, das Hauptprogramm hĂ€lt noch eine referenz auf einen der Kind-Knoten. Dann wĂŒrde zumindest dieser Baumteil nicht mit abgerĂ€umt).

Die erste Instanz von Parent() mit all seinen abhÀngigen Kindknoten sind am Ende des Beispiels zwar noch im RAM, aber vom den Programmthreads aus nicht mehr erreichbar. D.h. Der GC wird sie beim nÀchsten Lauf abrÀumen.

Manchmal ist es auch wichtig, diese Cyclen vom Programmseite aus zu verhindern bzw. zu vermeiden. DafĂŒr gibt es in Java z.B. noch spezielle Referenzobjekte WeakReference und PhantomReference. Diese verhalten sich etwas anders beim berechnen des Erreichbarkeitsgraphen und beeinflussen so den GC. Eine Normale Variable ist in dem Zusammenhang immer als strong Referenz zu sehen.

Wie klappt das in Objective-C?

In Objective-C wird seit geraumer Zeit ein etwas anderer Ansatz gewĂ€hlt, das Reference Counting. Dabei hĂ€lt jedes Objekt einen ZĂ€hler, wie viele Zeiger darauf verweisen. An sich eine gute Idee, jedoch muss der Programmierer leider selbst dafĂŒr sorgen, dass diese ZĂ€hler erhöht oder verringert werden. DafĂŒr stellt Objective-C die beiden Methoden retain und release bereit, die von NSObject aus vererbt werden. Mit retain wird der Reference Counter (auch Retain Counter) erhöht, mit release um eins reduziert. Ist der Reference Counter gleich 0, kann das Objekt gelöscht und der zugehörige Speicher freigegeben werden. Das war fĂŒrchterlich mĂŒhsam und auch fehleranfĂ€llig. Man musste höllisch aufpassen, wann man retain und wann release aufruft. Wurde letzteres zu frĂŒh aufgerufen, hatte man irgendwo einen nil-Pointer. Das verursacht zwar nicht unbedingt eine Fehlermeldung in Objective-C (zugriff auf nil pointer tut meistens einfach nix), aber tut eben auch nicht das, was man erwartet. Wurde ein release jedoch vergessen, hatte man ein Memoryleak produziert - auch nicht gerade wĂŒnschenswert.

ARC

In den neuen Versionen von Obective-C (OSX und IOS6) wurde das „Automatic Reference Counting“ eingefĂŒhrt. Das einem diese ganze lĂ€stige Arbeit abnehmen sollte. Das funktioniert eigentlich wirklich super - wenn man ein wenig Hintergrundinfos hat. Im Endeffekt wird das Hauptprogramm nur in einem @autoreleasepool { } gestartet, was der Laufzeitumgebung sagt: „Hey, alles in diesen Geschweiften klammern bitte zĂ€hlen“ Das funktioniert weitgehend zur Compiletime, d.h. der Compiler fĂŒgt Code ein, der die Referenzcounter erhöht oder verringert! Zur Laufzeit findet nur noch eine PrĂŒfung auf reference_counter==0 statt. Der Ansatz ist also grundlegend anders als der des Garbage collectors. Auch birgt er so seine TĂŒcken.

So kann mit dieser Methode ein Retain Cycle nicht erkannt werden bzw. die so belegten Speicherbereiche werden nicht freigegeben => Speicherloch! Um das zumindest von Programmiererseite verhindern zu können, kann man auch in Objective-C schwache weak referenzen verwenden. Nehmen wir die Baumstruktur noch mal als Beispiel, hier in Objective-C. Parent.h:

 @interface Parent: Node
 @property (strong, nonatomic) NSArray *children;
 @end

Child.h:

@interface Child: Node
@property (weak, nonatomic) Parent *parent;
@end

wichtig ist, dass der Child-Knoten nur eine weak-Referenz auf den Vaterknoten hat. Damit kann der ganze Baum abgerÀumt werden, wenn der Vaterknoten nicht mehr erreichbar ist. im Programm:

Parent *p=[[Parent alloc]init];
//add children
//do something
p=[[Parent alloc]init];

Eine weak-Referenz erhöht den Reatain-Counter nĂ€mlich nicht. Wenn also das Hauptprogramm keinen Zeiger mehr auf den ursprĂŒnglich erzeugten Objektbaum hat, kann alles abgerĂ€umt werden. Bei meinen Tests mit IOS7 ist mir aufgefallen, dass es sehr wohl sinnvoll sein kann, Objektpointer auf nil zu setzen. Wenn man das tut, wird der Speicher sofort wieder freigegeben. Und in einigen FĂ€llen ist es das, was man braucht. (zur Erinnerung, das meiste beim ARC wird als Code automagisch eingefĂŒgt! Wenn ich irgendwas auf nil setze, weiß der Compiler auf jeden Fall, dass ich das Objekt nicht mehr brauche).

Aber das ist leider nicht alles. Das ARC wird von den Grenzen des Code-Blocks von @autoreleasepool bestimmt. Das bedeutet, erst wenn man den Block verlĂ€sst, wird sicher alles abgerĂ€umt, was noch mit einem Retain-Counter von 0 im RAM rumidelt. So kann es nötig sein, dass man von Zeit zu Zeit mal einen neuen Autoreleasepool anlegt, damit die darin erzeugten Zwischenergebnisse vom Heap entfernt werden. Es ist schwer zu sagen, wann genau man das machen muss, aber Rekursionen und Schleifen sind heiße Kandidaten ;-)

Leider kommt man in Objective-C nicht immer nur mit den Objekten aus, die einem die Laufzeitumgebung zur VerfĂŒgung stellt. Da Objective-C eine Obermenge von C ist, kann man auch auf die gesamte C FunktionalitĂ€t zurĂŒckgreifen und das ist leider manchmal nötig. Einige Systemaufrufe liefern als Ergebnis leider C-Structs und keine Objekte, die mit ARC wieder abgerĂ€umt werden. Manchmal muss man solche Datenstrukturen auch anlegen, was dann natĂŒrlich am ARC „vorbei“ passiert. So kann man sich sehr einfach ein Speicherloch zĂŒchten, wenn man nicht aufpasst. Im Zweifel am besten immer auf die Objekte der Laufzeitumgebung setzen.

Bewertung beider Mechanismen

GC

  • Nachteil: die Laufzeianalyse ist evtl aufwĂ€ndig, kann zu kurzzeitigem "einfrieren" fĂŒhren insbesondere bei speicherintensiven Anwendungen, lĂ€uft irgendwann - der Zeitpunkt ist nicht klar
  • Vorteil: simpel fĂŒr den Programmierer, Retain Cycle werden erkannt

ARC

  • Vorteil: keine aufwĂ€ndige Laufzeitanalyse, alles zur Compiletime (Set Nil deswegen manchmal sinnvoll)
  • Nachteil: es wird automatisch Code erzeugt, das kann zu unerwartetem Verhalten fĂŒhren, keine Erkennung von cyclen Problem: Memory allocation an dem Arc vorbei!


category: Computer

MongoDB Problem

2013-04-22 - Tags:

sorry, no english version available


category: global

Who is Stephan Bösebeck?

2013-04-19 - Tags: about

originally posted on: https://boesebeck.name

Whose blog is this? Yes, who am I ... this question is amazingly difficult to answer, foolishly. My IT career can be read here on the blog under My IT History.

There is not much more to say. There will be very little private life here, so my professional career will be in a nutshell:

  • After the Abi (and a rather stupid episode at the Bundeswehr - but that is another story) I studied computer science at the University of Passau
  • Degree with diploma
  • Since I had little or no BaFöG and my parents were not able to contribute much, I started to work while I was studying
  • Apart from a few student jobs (for example at the computer discounter ESCOM - unfortunately broke meanwhile, but since I learned a lot) I was from 1995 as an IT consultant
  • I have also made some certifications during this time: Sun Certified Trainer, Certified Java Programmer, LPI Certification, Certified C ++ Programmer, Certified Macro4 Trainer, etc. - I could presumably cover entire rooms with the receipt
  • Customers of mine were among others: IBM, Sun, Dresdner Bank, Deutsche Bank, HP, Unilog Integrata, Ixos Training, Goethe Institute ...
  • I have specialized in the technologies Linux / Unix and Java. Therefore also the certifications in the areas
  • I did Java training, especially for SUN and Integrata, from Beginner to Advanced Java Programmer Certification Training
  • It looked similar with Linux training, u.A. For IBM. From beginner training courses to advanced clustering and firewalling topics
  • Of course there were also training courses in other areas around Unix: Perl, TCL / Tk and a few other exotics (Python was still really exotic: smirk :)
  • The knowledge for the Schlulungen I have me in many, unfortunately usually quite small projects appropriated. There was a lot of small and medium-sized companies, for which I developed software. Or worked on larger projects.
  • Since I often can not realize the projects very often, I was quickly pushed into the role of the project manager / manager. As contact for customers, as subcontractor, etc.
  • Since I as a freelancer alone had hardly a chance to lead a large project or realize I have decided to work hard. In this case, I started as a project manager at Softlab
  • There I was able to realize some major projects (well, at least bigger than that before). Among other things for MAN and BMW
  • I wanted to gain more management experience and was then "sued" by Acronis. I started there as "Manager Training & Consulting EMEA". And had to build the training department there. First only for Germany, later for EMEA, then worldwide.
  • The training management and the training management has then more or less moved to USA, which is why I became "Head of Engineering" at Holidayinsider.com. Unfortunately HRS has bought us and now there are Holidayinsider also no more
  • I was then little more than a year Head of Technology at Simplesystem GmbH & Co Kg - but that was unfortunately not quite the correct
  • the next job is already fixed, but I will only post here, when I started there: smile:


category: Computer

PostgreSQL => MongoDb => Morphia => Morphium

2013-04-18 - Tags: java mongodb morphium

no english version available yet

Das war ein langer Weg... Bei holidayinsider.com nutzen wir mittlerweile MongoDB (neben Lucene-SolR als primÀre SearchEngine) als primÀres BackEnd. Das brachte ein paar Probleme aber auch tolle Lösungen mit sich.

ZunĂ€chst mal, warum ĂŒberhaupt eine NoSQL-Db und warum dann gerade Mongo? Die Frage ist natĂŒrlich berechtigt und wurde bei uns auch heiß diskutiert. FĂŒr das, was wir vor hatten, war PostgreSQL (so toll die Datenbank auch sonst ist) leider nicht ganz das richtige. Die Vorteile (Transaktionen, Berechtigungen und Relationen) wĂ€ren fĂŒr unseren Ansatz nur hinderlich gewesen.

Was haben wir gemacht? Wir loggen nahezu alles, was auf der Plattform passiert und werten diese Daten dann spÀter in einem Offline-Prozess aus (wobei auch eine Menge Daten live in Graphite mit verfolgt werden können). Das war so mit PostgreSQL nicht möglich - wir hatten zwischenzeitlich Collections mit mehr als 100 Millionen DatensÀtzen deren Verarbeitung mit PostgreSQL mehr als 24h gedauert hÀtte. Da diese Daten keinen Relationen zuzuordnen sind, brachte eine Relationale Datenbank mit Transaktionen etc keine besonderen Vorteile. Deswegen der Gedanke (auch, weil das Mapping den Java-Objekten recht nahe kommt) auf einen Dokumentenbasierten Storage und in dem Fall Mongo zu setzen. Und: wir sind ein hippes Startup Unternehmen, da muss man auf neue Technologien setzen!

Warum Mongo? Wir wollten auf ein möglichst flottes und einfaches Modell setzen, Mongodb als primĂ€re Datenbasis war das erklĂ€rte Ziel, PostgreSQL "nur" noch im Hintergrund fĂŒr einige nicht lastabhĂ€ngige Daten. Mongo schien uns als erste Wahl in dem Zusammenhang... da könnte man in der Retrospektive sicherlich noch mal drĂŒber diskutieren ;-)

holidayinsider.com ist eine 100% pure Java Anwendung ohne viel Overhead. Deswegen war es auch wichtig, eine gute Java Anbindung der NoSQL-Datenbank zu haben. FĂŒr Mongo gab es 1. einen guten Java-Treiber, und 2. ein Mapping Framework namens Morphia.

Wir haben morphia einige Zeit lang produktiv in der Anwendung gehabt, aber insbesondere mit der Erweiterbarkeit hatten wir Probleme. Als dann die ersten Bugs in morphia zu Datenverlusten und DownTimes gefĂŒhrt hatten, mussten wir uns was einfallen lassen. Insbesondere, weil Morphia seit bestimmt einem Jahr auf der Version 0.99-1 festhĂ€ngt und nicht mehr weiter entwickelt wird. Wir hĂ€tten bei dem Projekt ja sogar mitentwickelt, haben das auch angeboten, aber der einzige Entwickler von Morphia wollte nicht, dass irgendjemand mit macht.

Wir wollten das Projekt forken, haben es ausgecheckt und ich hab mir den Code mal genauer angesehen. Sagen wir es so - es war einfacher, es neu zu machen.

Das war die Geburtsstunde von Morphium (http://code.google.com/p/morphium) - der Name war an Morphia angelegt, ich wollte so auch die Vorarbeit von Scott Hernandes wĂŒrdigen. Das Projekt wird rege weiter entwickelt und ist mittlerweile in der Version 2.0.6 verfĂŒgbar. Entweder von der Projektseite, oder von SonatypeOSS / Maven.

Morphium ist ein ObjectMapper fĂŒr Java mit einer Menge Features (die meiner Meinung nach in Morphia gefehlt haben). Hierbei bezeichnen Entities Objekte (POJOs), die in Mongo gespeichert werden sollen.:

  • sehr robustes, flexibles und performantes Mapping von Entities/POJOs zu mongo Objekten: So ist es in Morphium möglich, seinen eigenen Mapper zu definieren. Das ist z.B. sinnvoll, wenn man das Mapping mocken muss fĂŒr z.B. JUnit-Tests oder dergleichen.
  • Flexible API: Es ist möglich, fast alle Kernkomponenten selbst zu implementieren bzw. die Default-Implementierungen abzuleiten und um eigene FunktionalitĂ€ten erweitern. so können ObjectMapper, Cache, Writer und Query durch eigene Implementierungen ersetzt werden (zur Laufzeit).
  • UnterstĂŒtzung von Vererbung und Objekthierarchien: Morphium untersucht fĂŒr das Mapping den gesamten Ableitungsbaum, wodurch es möglich ist, Entities von anderen abzuleiten und entsprechend zu erweitern.
  • Deklarative definition von Indices: Mongo ist nur dann performant, wenn indices verwendet werden. Diese sollten gut zu den Suchanfragen passen. Ohne Index hit ist eine Query um leicht das 10-100 fache langsamer (query auf eine Collection mit ca. 500.000 Entries ohne Index hit dauerte ca. 45 Sek - mit < 1Sek). In Morphium können die Indices mit Hilfe von Annotations definiert werden.
  • Deklaratives mapping: Feld- und Collectionnamen können deklarativ festgelegt werden. Es ist auch möglich, Aliase anzugeben, wordurch ein Feld evtl. unter mehreren Namen ansprechbar ist. Das ist insbesondere bei Datenmigration hilfreich. Es ist auch möglich, daten von verschiedenen Typen in eine Collection zu speichern - insbesondere bei Vererbung sinnvoll, weshalb das Flag auch @polymorph heißt.
  • transparente Referenzen (werden von Morphium aufgelöst), incl. lazy loading: In Morphium kann man sehr leicht objekt-Referenzen definieren. Das funktioniert ĂŒber die Java-Annotation @Reference. Diese wird direkt auf dem entsprechenden Feld angewendet, wodurch nicht das ganze
  • deklaratives Caching: jedes entity bestimmt, ob es per default gecachted werden soll oder nicht. Das bedeutet, dass Ergebnisse von Suchanfragen an diese Entity im Ram gehalten werden. Dabei wird auch bestimmt, wie groß dieser Cache sein soll, ob er bei Schreibzugriffen geleert wird, wie lange Cache-EintrĂ€ge gĂŒltig sind und was passieren soll, wenn der Cache "voll" ist.
  • asynchrones und gepuffertes Schreiben: gerade die Schreibzugriffe im Cluster (wenn man darauf wartet, dass die Knoten die einzelnen Schreiboperationen bestĂ€tigen) können lĂ€nger dauern. Das kann zu Problemen mit der Performance fĂŒhren. Eine Asynchrone API war da sehr sinnvoll. Gepuffertes Schreiben legt zunĂ€chst einen Puffer im RAM an und schreibt die Daten dann erst nach einer gewissen Zeit, oder wenn eine Mindestanzahl erreicht ist. Das ist insbesondere deswegen sinnvoll, weil Bulk-Inserts deutlich effizienter funktionieren als einzelne.
  • Validation: Validieren der Daten vor jedem Schreibzugriff mit javax.validation
  • Partial updates: gerade bei großen Dokumenten sinnvoll - sendet Änderungen an die Datenbank, nicht das gesamte Objekt
  • Threadsafety und Cluster awareness bzw. ClusterfĂ€higkeit: Wir nutzen Mongo im cluster von mehreren Knoten. Es ist wichtig, das Morphium UnterstĂŒtzung dafĂŒr hat. Einerseits um die Sicherheit zu gewĂ€hrleisten (warte darauf, dass alle verfĂŒgbaren Knoten den Schreibzugriff bestĂ€tigen) andererseits um z.B. richtig zu reagieren (wenn ein knoten ausfĂ€llt etc.). Das ganze wird innerhalb Morphiums auch ĂŒberwacht
  • Montoring und profiling: durch ein cleveres Listener-Konzept ist es möglich, jeden Schreib- oder Lesezugriff zu messen und die Daten fĂŒr spĂ€tere Auswertung abzulegen (z.B. in Graphite).
  • Lifecycle Methods: Es ist möglich, Callbacks innerhalb eines Entity zu definieren fĂŒr "preStore" oder "postLoad". Des Weiteren ist es noch möglich, globale Listener fĂŒr diese Events zu definieren, d.h. bei jedem Schreibzugriff bzw. Lesezugriff unbabhĂ€ngig vom Typ informiert werden.
  • automatische Werte: wie z.B. lastChanged oder lastAccessed - Timestamps
  • einfaches Messaging: messaging ĂŒber MongoDB. Die Messages werden persistiert und in einer Transaktions-Ă€hnlichen Art und Weise verarbeitet.
  • UnterstĂŒtzung fĂŒr Geospacial-Suche: selbsterklĂ€rend, oder?
  • UnterstĂŒtzung von Map-Reduce bzw. dem Aggregation Framework: Das ist die Antwort von Mongo auf Joins und komplexe Anfragen in der "Relationalnen" Welt. Kurz gesagt: damit lassen sich auch schwierigere Auswertungen einigermaßen performant lösen. Das Konzept dahinter ist aber dennoch etwas schwer zu verstehen.
all diese Features sind zur Zeit in der aktuellen Version von Morphium verfĂŒgbar und sind auch schon produktiv in einigen Projekten im Einsatz.

Weitere Doku ist auf der Projektseite oder unter www.caluga.de/morphium zu finden.

Aber zurĂŒck zum Thema mongoDB. So einfach, wie ich es hier geschildert habe, war es auch nicht. Wir hatten auch ein paar (u.a. schmerzhafte) learnings.

Wir hatten 10Gen zu uns eingeladen, uns die Vorteile von Mongo mal zu erklĂ€ren und uns das in ihren Augen korrekte Setup zu empfehlen. Wir nutzten zu diesem Zeitpunkt mongo schon fĂŒr einige kleinere Logs, hatten noch keinen Cluster. Die Empfehlung ging auch von 10gen eindeutig in Richtung Sharding und jeden Shard in einem replicaSet zu Clustern. An sich ja eine gute Idee, allerdings nicht ganz seiteneffektfrei.

Wir sind ziemlich schnell drauf gekommen, dass Sharding auch overhead produziert - nicht zu knapp! So ist die Wahl des shard Key von essentieller Bedeutung beim Einsatz von Sharding. Der Einsatz von timestamps fĂŒhrte bei uns leider nicht zum gewĂŒnschten Effekt (das fĂŒhrt nĂ€mlich auch dazu, dass quasi minutenweise der Shard gewechselt wird, da nicht timestamp modulo Anzahl Maschinen benutzt wird um den Knoten rauszufinden, sondern immer 64K-Blöcke auf den selben Knoten landen). Außerdem war es so, dass requests auf eine geshardete Collection Extreme Last produziert hat. Teilweise wurden dadurch globale locks angelegt -> downtime!!!! Das war untragbar. Mal abgesehen davon, dass das setup extrem kompliziert ist (mongod fĂŒr jeden Knoten, der daten hĂ€lt, mit entsprechender Konfiguration, mongos auf jedem Knoten, der auf den shard zugreifen will und dann noch mongo-config-Server, die auch erreichbar sein mĂŒssen. Bei uns fĂŒhrte das zu mehr Problemen, als die, die es lösen sollte. So wurde immer mal wieder die Last auf den mongo Knoten extrem hoch nur durch irgendwelche Sharding- oder ReplicaSet-Sync-Operationen. Dummerweise haben die dazu gefĂŒhrt, dass alle anderen requests auch lahm wurden, teilweise so langsam, dass sie in Timeouts gelaufen sind.

Zu allem Überfluss lief auch eine ganze zeit lang der Failover nicht - zumindest nicht in Java. Wenn ein mongo Knoten ausgefallen ist, oder zur Wartung runtergefahren wurde, versuchte der Java Treiber dennoch darauf zuzugreifen.... Untragbar! Wir haben der mongo dennoch eine Chance gegeben, eine Menge von den learnings in Morphium einfließen lassen. Im Moment sind wir wieder auf dem Stand: Simplicity wins! Ein Cluster von 4 mongo Knoten, im replicaSet konfiguriert, kein Sharding. Und momentan keine Probleme!

Dabei ist auch wichtig, die Knoten korrekt zu dimensionieren. Wichtig sind: flotte Festplatte und genug RAM. Da ist man schnell etwas verwirrt, da der MongoD selbst nicht wirklich viel RAM nutzt, aber er nutzt "Memory Mapped files", d.h. der RAM-Bedarf des Systems steigt deutlich an. Wichtig ist, dass die Indices ins RAM passen. Sobald das nicht mehr der Fall ist, sollte man ĂŒber Sharding noch mal nachdenken. Wir sind da aber noch weit von entfernt bei unseren Datenmengen.

En Problem mit den Datenmengen hatten wir dennoch: die eine der primĂ€ren Datenbanken ist zwischenzeitlich auf eine GrĂ¶ĂŸe von mehr als 250GB im Filesystem angewachsen, obwohl reine Nutzdaten nur in etwa 100GB umfassten. Das liegt daran, wie mongo das Storage organisiert: einzelne Dateien werden angelegt, je nach dem, was gerade benötigt wird - aber nie gelöscht, selbst, wenn man die Collections entfernt. Der einmal belegt Platz wird wieder verwendet. Allerdings scheint es da zumindest bei unserem Setup ein Problem gegeben zuhaben, denn der Platzbedarf im Filesystem lag deutlich ĂŒber den wirklichen Daten, obwohl nicht so viel gelöscht wurde... Die einzige Lösung fĂŒr dieses Problem ist ein repairDatabase auszufĂŒhren. Das fĂŒhrt aber dazu, dass der entsprechende Knoten fĂŒr die Dauer der Reparatur nicht erreichbar ist - Super, wenn auch der Failover nicht richtig funktioniert. Außerdem darf das Datenverzeichnis nur max. zu 50% belegt sein, will sagen: es muss noch genug Platz auf dem Filesystem sein, damit die Daten ein mal komplett umkopiert werden können.

Das konnten wir erst in letzter Zeit lösen, sind dann auch einen anderen Weg gegangen:

  • alle 2 Wochen wird ein Knoten im Cluster runtergefahren. Wenn es der Primary node war, wartet man zudem noch auf den Failover
  • auf diesem Knoten wird das Datenverzeichnis gelöscht
  • der Knoten wird im replicaset wieder hochgefahren
  • er synchronisiert alle Daten neu, minimaler platzbedarf auf der Platte.
Das funktioniert mittlerweile recht problemlos (der Failover funktioniert, und Morphium ĂŒberwacht ja den Cluster Status). So haben wir den Platzbedarf auf den Platten um ca. 50% senken können.

Also als Tip: Jeder, der plant mongoDB einzusetzen, sollte auch diese Downtime fĂŒr Repairdatabase oder das Syncen im Replicaset einplanen! Ich wĂŒrde aus diesem Grund Mongo immer in einem ReplicaSet einsetzen!

Ein anderes Problem mit mongo hat zu grösseren hickups gefĂŒhrt, ein mal zu einem kompletten Datenbankcrash. Das muss man etwas ausfĂŒhrlicher erklĂ€ren:

In mongo ist es möglich, komplett asynchron zu schreiben. D.h. Der schreibzugriff ist beendet, obwohl kein Knoten die Operation schon ausgefĂŒhrt hat. Das ist natĂŒrlich etwas sub-optimal, wenn man diese Daten wieder einlesen möchte (z.B. FĂŒr Messaging oder Session Replikation). Deswegen ist es in mongo und in allen zugehörigen Treibern möglich, anzugeben,

  • darauf zu warten, dass der Knoten die Operation bekommen hat
  • ob darauf gewartet wird, dass die Schreiboperation auf der Platte persistiert wurde (fsync)
  • und auf wie viele Knoten man denn da warten will. Da kann man mittlerweile auch so was wie majority angeben.
  • beim lesen vomier mongo kann Management, welche Knoten im replicaSet benutzt werden sollen. Ob primĂ€rknoten, sekundĂ€rknoten oder irgendeiner.
Wir dachten natĂŒrlich, ok, bei wichtigen Daten warte auf alle Knoten. Das hat immer wird zu hĂ€ngenden schreibprozessen und timeout exceptions in der Anwendung gefĂŒhrt. Auch nachklĂ€nge Diskussion mit 10Gen konnte mir keiner erklĂ€ren, was da los ist. Die hi verwendete Option ist bestimmt, wie viele schreibvorgĂ€nge auf verschiedenen Knoten erfolgreich sein mĂŒssen. Wenn ich das auf die Anzahl der Knoten stelle, hĂ€ngt der request (auch in der mongo Shell) reproduzierbar. FĂŒr alle Werte kleiner als die maximale Anzahl an Knoten funktioniert es.

in der Shell sieht dass dann so aus:

hi1:PRIMARY> db.testreplication.insert({_id:new Date()});printjson(db.runCommand({getLastError:1, w:4,  wtimeout:180000}));
{
        "n" : 0,
        "lastOp" : Timestamp(1347957897000, 17),
        "connectionId" : 431946,
        "wtimeout" : true,
        "waited" : 180000,
        "err" : "timeout",
        "ok" : 1
}

 

Das bedeutet, der Zugriff auf dieses Replicaset mit 4 Knoten (ohne Arbiter, eigentlich sind es 5) dauerte 180 Sekunden(!) in lief dann in einen Timeout. Sobald man irgend einen Wert kleiner 4 angibt, funktioniert es wunderbar. heute, 18.04.2013 ist dieser saublöde Fehler wieder aufgetreten, diesmal war allerdings nicht eingestellt, schreib auf alle Knoten, sondern nimm die Majority (also etwa die HÀlfte). Sowas ist wirklich unnötig

Obwohl ich von 10Gen keine BestĂ€tigung bekommen habe, denke ich, es werden bei diesen SchreibvorgĂ€ngen, Hidden nodes (solche nodes, die nur die Daten bekommen, aber von denen nicht gelesen wird) dabei nicht mit gezĂ€hlt werden. Ist nur eine Theorie, funktioniert aber.... Morphium unterstĂŒtzt eine deklarative Festlegung sowohl der ReadPreference, also welche Cluster Knoten fĂŒr den Lesezugriff erlaubt sind, als auch auf wie viele Knoten gewartet werden sollte bei Schreibzugriffen. Da das mit dem Schreiben auf alle knoten nicht funktioniert hat und die Anwendung immer wieder Dirty Reds bekommen hat (ich musste mir anhören, dass es ja keine Dirty Reds geben kann, da es das Konzept auf mongo gar nicht gibt - klugsch... Dann eben stale Data!) haben wir momentan alles auf primĂ€r umgestellt.. D.h. Wichtige Collections werden nur auf den primĂ€ren Knoten geschrieben, und auch nur von dem gelesen... Widerspricht irgendwie dem lastverteilungsgedanken der replicasets.

Aber ok... Momentan haben wir noch keine Probleme mit der Last, sollte das kommen, muss sich 10Gen da was einfallen lassen.... Der Bug ist ĂŒbrigens auch in der aktuellen V2.4.1 von mongodb vorhanden... (wenn man von einem Bug reden kann, evtl. ist es nur ein Fehler in der Doku)

Fazit: Nichts desto Troz kann ich MongoDb weiterempfehlen, wenn auch nicht uneingeschrĂ€nkt. Ich habe Mongo jetzt mit Hilfe von Morphium auch in anderen Projekten eingesetzt (z.B. CalugaMed). Wenn man weiß, was man tut, kann mongo wirklich einen Großen Vorteil bringen. Da man nicht so fix an Strukturen gebunden ist, wie in einer Relationalen Datenbank, hat man viel mehr Möglichkeiten. Als "DatenmĂŒlleimer" ungeschlagen! Allerdings kann man auf Grund des fehlenden Zwangs zur Struktur auch mehr falsch machen! Datenmigrationen sind relativ leicht zu machen, die Anfragesprache (JavaScript) ist gut und mĂ€chtig (auch wenn immernoch ein brauchbares FrontEnd fehlt) und mit Morphium gibt es auch ein recht gutes Mapping-Framework (ich weiß, Eigenlob stinkt ;-) ).

Dennoch wĂŒrde ich Mongo nicht fĂŒr alles und immer benutzen. Man muss sich ĂŒberlegen, was man benötigt. Wenn man z.B. Joins benötigt bzw. die Daten eben nicht so document based ablegen kann, so sind diese Anfragen in Mongo eher schwierig bis unmöglich abzubilden. Abhilfe schaft da evtl. auch der "Mut zur Redundanz". D.h. evtl. daten mehrfach ablegen. Komplexe Anfragen funktionieren zwar, können aber zu echten Problemen und Downtimes fĂŒhren, wenn man nicht aufpasst (Indizes!!! Indizes!!! Indizes!!!).

Alles in Allem ist Mongo eine echte Alternative zu SQL-Datenbanken und kann in bestimmten FÀllen echte Performance und flexibilitÀtsvorteile bringen. Einen Blick ist es auf jeden Fall wert.

mongo gibt es hier: www.mongodb.org

Morphium Projektseite: code.google.com/p/morphium

Morphium deutsch: www.caluga.de/morphium.html


category: global

new Version of #Morphium (#M...

2012-06-17 - Tags: java morphium mongodb

new Version of Morphium and Morphium GUI: #Morphium (#MongoDb Object Mapper)

MorphiumGUI V0.6.8 - http://t.co/YRIVAUgL


category: Computer

Morphum Object Mapper fĂŒr MongoDB (noSQL DB)

2012-04-03 - Tags:

sorry, no english version available


category: Tweet --> global

New Java Object-Mapper for #Mongodb

2012-04-02 - Tags: tweet

sorry, no english version available


category: global --> Tweet

Saying that Java is nice...

2011-09-21 - Tags: tweet java

originally posted on: https://boesebeck.name

“Saying that Java is nice because it works on all OSes is like saying that anal sex is nice because it works on all genders.” @NIxiePixel


category: Computer --> programming --> Java

Multithreaddingprobleme in Java

2011-08-10 - Tags: java-2 programming

no english version available yet

Da mir das jetzt schon so oft passiert ist, und ich immer wieder wieder solche Probleme lösen muss, möchte ich hier mal kurz ein paar Tipps zum Thema Multithreadding in Java und den damit verbundenen Problemen kommen.

Allgemeines

Worum geht es eigentlich und warum  gibt’s da Probleme? Multithreadding ist eine Programmiertechnik, die es möglich macht, dass in einem Programm mehrere Dinge (mindestens zwei) gleichzeitig passieren bzw. ausgefĂŒhrt werden. Im Zuge der Desktopanwendungen nutzt jeder Anwendungen, die mehrere Threads nutzen, denn meistens ist die OberflĂ€che ein Thread, und die Anwendung "selbst" mindestens ein anderer. Das ist ja an sich kein Problem sondern eher wĂŒnschenswert... Aber es bringt auch Probleme mit sich... Schauen wir uns das mal am Beispiel von Java etwas genauer an.

Das Problem ist, dass mehrere Threads quasi gleichzeitig auf einen gemeinsamen Speicherbereich bzw. eine gemeinsame Variable zugreifen. Das kann unproblematisch sein, wenn es sich um reinen Lesezugriff handelt. Sobald einer der beteiligten Threads jedoch schreibt, können seltsame Dinge passierten.

Klassenbibliothek und Externa

Java wird gerne in EJB oder allgemeiner J2EE-Umgebungen eingesetzt und dort muss das eigene Programm nicht selten mit vielen gleichzeitigen Zugriffen (concurrent usern) umgehen können. Dummerweise wird das bei der Programmierung oft einfach "vergessen" bzw. nicht beachtet. Der beliebteste Fehler ist vermutlich, dass Teile aus der Klassenbibliothek oder aus Bibliotheken von Drittanbietern verwendet werden, die nicht threadsicher (threadsafe) sind. Gerade mit JDK1.5 wurden einige neue und vermeintlich "bessere" Klassen weingefĂŒhrt, wie zum Beispiel die der StringBuilder fĂŒr den StringBuffer.

Nahezu der einzige Unterschied zwischen StringBuilder und StringBuffer ist, dass der StringBuilder nicht threadsafe ist, der StringBuffer dagegen schon. Es ist also nicht ratsam, in J2EE-Umgebungen "blind" ĂŒberall den StringBuilder einzusetzen, weil Java sagt, der ist flotter. Ist er auch, allerdings kann das zu sehr seltsamen Effekten bei der Requestberarbeitung fĂŒhren.

Weitere Beispiele, die evtl. noch aufgelistet werden sollten sind ArrayList (nicht threadsafe) und Vector (threadsafe), HashMap (nicht threadsafe) und Hashtable (threadsafe). Klar... man sollte nun aber auch nicht blind die threadsafen Varianten verwenden, sondern sich einfach klar darĂŒber sein, was man tut und was man in welcher situation braucht.

Singletons und deren Probleme

Schlimmer wird es noch, wenn man z.B. in einem Container eingebettet ist wie z.B. einer Servletengine oder eben einem EJB-Application-Server. Dann werden die eigenen Klassen evtl. von mehreren Threads gleichzeitig aufgerufen, selbst wenn man sich selbst da keine Gehangen darĂŒber gemacht hat. Insbesondere, wenn man irgendwelche static-Variablen verwendet (auch schreibend) kann das sehr nach hinten los gehen. So wird eigentlich fast immer, das Singleton Pattern falsch implementiert. Hier eine Implementierung, die man auch oft liest und die in Single-threadded Environments sicherlich ok ist:

[codesyntax lang="java" lines="fancy" lines_start="1"]
public class Singleton {
     private static instance;
     private Singleton() {
          //initialisierung
     }
     public static Singleton get() {
         if (instance==null) { instance=new Singleton(); }
         return instance;
     }
}

[/codesyntax]

Ich höre euch quasi schon sagen: "Was soll denn daran falsch sein, so mach ich es doch auch immer..."... Naja... wo fangen wir an.... (String genommen ist das ja auch eine Factory ;-))

Gehen wir mal davon aus, dass dieser Singleton in einer multithreadded Umgebung verwendet wird. Wenn nun mehrere Thread gleichzeitig auf get() zugreifen, kann es vorkommen, dass der Konstruktor 2x aufgerufen wird, was evtl. ja fatal ist. (ich weiß, die meisten nutzen Singleton, nicht weil das Objekt nur einmal instanziiert werden darf, sondern um einfach darauf zugreifen zu können.) Wenn im Konstruktor nun Dinge passieren, die nur ein Mal vorkommen dĂŒrfen (Files öffenen, Sockets öffnen etc), dann knallst... es ist nicht gesagt, ob der Singleton dann richtig instanziiert ist.

Gleiches gilt, wenn in dem Constructor eine Exception geworfen wird, dann ist gar nicht klar, was passieren wird und ob/welche Instanz man dann bekommt, wenn man get() aufruft.

eine "Verbesserung" wÀre die Instanz gleich zu initialisieren:

 

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Singleton {
       private static instance=new Singleton();

      private Singleton() {

           //initialisierung

      }

     public static Singleton get() {

           return instance;

     }

}

[/codesyntax]

 

Das ist zwar schon besser, aber nicht wirklich gut. Denn: der Construktor des Singleton wird vom ClassLoader aufgerufen, was zunĂ€chst mal gut klingt, jedoch gleich wieder relativiert wird, wenn man bedenkt, dass es durchaus mehr als einen ClassLoader in einer Application (insbesondere in J2EE-Containern oder Ă€hnlichem) geben kann. Dann kann es wiederum vorkommen, dass mehrere Instanzen des Singleton erstellt werden. Siehe dazu auch: Programming Singletons (Sun). Evtl. erinnert man sich dann ja an das SchlĂŒsselwörtchen synchronized:

 

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Singleton {
      private static instance=null;

      private Singleton() {

           //initialisierung

      }

     public synchronized static Singleton get() {

if (instance==null) {

instance=new Singleton();

}

           return instance;

     }

}

[/codesyntax]

 

An sich hĂ€tte man damit alle Probleme umgangen, jedoch ist es nicht wirklich wĂŒnschenswert, Singletons so zu implementieren. Denn das synchronized sorgt dafĂŒr, dass immer nur ein Thread auf get() zugreifen kann. Damit hat man sich einen ziemlichen Flaschenhals gebaut, der die Performance stark negativ beeinflussen kann.

Eine wesentlich bessere Lösung ist folgende:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Singleton {
      private static instance=null;

      private Singleton() {

          try {
              //initialisierung
          } catch (Throwable e) {} //geeignete Fehlerbehandlung!!!!!!!
      }

      public static Singleton get() {
          if (instance==null) {
              synchronized(Singleton.class) {
                   if (instance==null) {
                        instance=new Singleton();
                   }
              }
          }

          return instance;
     }
}

[/codesyntax]

Damit ist sichergestellt, dass man in den Synchronized-Block nur dann kommt, wenn wirklich eine neue Instanz erstellt werden muss. Damit leidet die Performance nicht so erheblich wie im Beispiel vorher. NUR so hat man eine Singleton-Implementierung, die wirklich threadsafe ist. (und ich bin mir selbst da nicht zu 10000% sicher...;-)

Static im Allgmeinen

GrundsĂ€tzlich sollte man es sich verkneifen in einer multithreadded Umgebung irgendwo das Wörtchen "static" zu verwenden, es sei denn, man ist sich seiner Sache sehr sicher. Die falsche Verwendung von Singletons ist nur ein Problem. HĂ€ufig werden auch Datenstrukturen statisch abgelegt um "einfacher" darauf zugreifen zu können. Das birgt natĂŒrlich Gefahren. Beispiel:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Servlet extends HttpServlet {
private static MyLogger l=new MyLogger();

       .....

       public void get(HttpServletRequest req, HttpServletResponse resp) {
             l.setSessionId(req.getSession(true).getId());
             //do something
             l.setSessionId(null); //request finished!
      }

}

[/codesyntax]

 

Aus falschem Ehrgeiz wurde hier wohl versucht, den Speicherbedarf zu reduzieren. Die Idee war es, die SessionIDs mitzuprotokollieren um bei den Logausgaben die TĂ€tigkeiten eines Users/Browsers verfolgen zu können. Das ging aber gehörig schief, denn: Ein Servlet wird (normalerweise) nur ein Mal instanziiert aber von mehreren Thread (je nach Konfiguration des Containers) verwendet. Das fĂŒhrte dazu, dass man die Requests in keiner Weise verfolgen konnte, da sehr hĂ€ufig mitten in den Log ausgaben (null) ausgegeben wurde... Weil Thread 1 schon in der Bearbeitung recht weit fortgeschritten ist, und Thread 2 gerade beginnt doGet() aufzurufen.

Problem mit Member-Variablen

Wenn man sich die AusfĂŒhrungen vom letzten Beispiel noch mal ansieht, dann sollte klar sein, dass der selbe Fehler auch auftritt, selbst wenn de Variable nicht static ist! Der Grund: eine Instanz der Klasse "Servlet" wird von verschiedenen Threads verwendet. Somit existiert auch die Variable l (MyLogger) nur ein Mal, selbst wenn sie nicht static ist! Das kann man nur umgehen, indem man lokale Variablen erstellt, (sofern das nicht zu anderen Problemen fĂŒhrt - Performance z.B.).

Dummerweise erkennt man nicht wirklich oft, ob eine Klasse  bzw. deren Instanz von "Außen" von mehreren Thread verwendet wird, oder nicht. Da hilft nur, RTFM!

Aber sowas kann man sich auch selbst zaubern, insbesondere wenn man seine Elemente in Datenstrukturen packt, die evtl. von verschiedenen Klassen/Threads verwendet werden wollen. Die Beispiele dafĂŒr kann man sich wohl sehr einfach vorstellen, jedoch ist es hĂ€ufig nicht so einfach zu erkennen, selbst im eigene Code nicht. Es bleibt wichtig, sich immer klar darĂŒber zu sein, was die threads in der eignen Anwendung tun und wer, wie und mit wie vielen Threads auf meine Klassen zugreift.

"Synchronized ist doch die Lösung fĂŒr alle Multithread-Probleme"

Naja, das wÀre ein wenig zu einfach. Mit synchronized kann man sicherlich einige Probleme lösen, allerdings könnte das sehr gut sein, dass man sich damit mehr Probleme einhandelt, als einem lieb ist. Die Performance leidet stark unter der Verwendung von synchronized, da diese Blöcke nur von einem Thread verwendet werden können. Besonders schlimm wird es, wenn man nicht das richtige Objekt zur Synchronisierung verwendet:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public void doSomething() {
      synchronized(Object.class) {
           //do Something weird
       }
}
[/codesyntax]

Hier synchronisiert man gegen das Class Objekt von der Klasse Object, weiter oben in der Hierarchie geht gar nicht... wenn man das an verschiedenen Stellen der Anwendung macht, kann jeweils nur ein Thread auf einen dieser Blöcke zugreifen, selbst wenn sie auf völlig unterschiedliche Daten zugreifen und gerne auch parallel laufen dĂŒrften. Es ist besser, auf das "kritische" Objekt zu synchronisieren, dann wĂŒrden sogar verschiedene Instanzen dieser Klasse parallel laufen können:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class MyClass {
    private Vector myData;
    ......
    public void doSomething() {
          synchronized(myData) {
             myData.add(....);
          }
    }
}

[/codesyntax]

hier werden nur Threads synchronisiert, die auf das selbe Objekt zugreifen. Falls es mehrere Instanzen von MyClass gibt, dĂŒrfen auch mehrere Threads gleichzeitig die Methoden in den jeweils anderen Instanzen ausfĂŒhren. Sowas passiert z.B. gerne bei der Verwendung von EJBs.

Es ist aber auch durchaus möglich, einen Deadlock mit dem Synchronized  statement zu erzeugen.

 

[codesyntax lang="java" lines="fancy" lines_start="1"]

public void a() {

        synchronized(this) {

              b();

              //do something

        }

}

public void b() {

        synchronized (this) {

              //do something!

        }

}

[/codesyntax]

Das sieht doch auf den ersten Blick recht gut aus, jedoch bei genauerem hinsehen, kann das nicht funktionieren: Wenn in Methode a() die Methode b() aufgerufen werden soll, ist man schon innerhalb eines synchronized Blocks. in Methode B befindet sich wiederum  ein Sychronized Block (auf das selbe Object) und der kann erst dann betreten werden, wenn der erste Block verlassen wird, was ja nicht passiert, solange b() nicht fertig wird... Klassischer Deadlock!

Oft ist das natĂŒrlich nicht so plakativ wie hier der Fall, sondern ĂŒber 3 oder Mehr "Ecken" (Klassen bzw. Objekte)... Das ist zwar nicht unbedingt ein Multithreadding-Only-Thema, denn Deadlocks können auch singlethreadded auftauchen (s.o.), aber bei mehreren Threads sind sie um so schwieriger zu entdecken. Manchmal gibt es auch keine echten Deadlocks sondern nur Verzögerungen, wo ein Thread einfach mal 1-2 Minuten "Pause" macht, biss alle anderen irhre Synchronized-Blöcke verlassen haben.

Static fĂŒr Threads: Threadlocal

Wenn man allerdings doch etwas statisches einem Thread zur VerfĂŒgung stellen will (bzw. allen Objekten, die in diesem Thread instanziiert werden), dann kann man sich mit einer neuen Klasse behelfen: Threadlocal (auch wenn das nicht wirklich die Idee fĂŒr diese Klasse war):

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Test {
     private static ThreadLocal instance=null;
     private Test() {
        //do something
     }

     //remember to add synchronized blocks for Singleton!!!
     public static Test get() {
          if (instance==null || instance.get()==null) {
               instance=new ThreadLocal();
               instance.set(new Test());
          }
          return instance.get();
     }
}

[/codesyntax]

Bitte nicht vergessen, hier noch die passenden synchronized-Blöcke einzufĂŒgen - der Übersichtlichkeit wegen habe ich die hier mal weggelassen.

Beans, Servlets und so weiter

Diese Dinger sind ja momentan fĂŒrchterlich in und werden ĂŒberall verwendet, meistens ohne sich wirklich Gedanken dazu zu machen. Mal abgesehen, dass man in den Meisten fĂ€llen mit EJB sich nur Nachteile einkauft und die Vorteile nicht wirklich nutzt. Aber das ist ein anderes Thema...

Was die meisten nicht bedenken (wieso auch, ist ja so einfach einzusetzen), dass Beans und Servlets einigen Regeln unterliegen, die man - insbesondere im Hinblick auf Multithreadding - besser kennen sollte.

Bei den Servlets verursacht es immer wieder Schwierigkeiten das wenige Instanzen des Servlet von vielen Threads verwendet wird. D.h. in diesem Zusammenhang können auch Member-Variablen zu Problemen werden!

Bei Beans ist es Àhnlich, insbesondere Stateless beans... dort ist vielen nicht bekannt, dass, selbst wenn man eine Referenz auf eine Bean hat und selbst wenn es sich um eine Lokal vorhandene Bean handelt, es nicht gesagt ist, dass man bei jedem Methodenaufruf die selbe Bean erhÀlt - oft ist das eben nicht so. Und meistens wird dieses eigentliche Default Verhalten eben erst unter Last gezeigt.

Man kann das natĂŒrlich umgehen, in dem man einfach Stateful Session Beans verwendet, die "kleben" quasi am Client. Da darf man dann auch gerne was zwischen Methodenaufrufen speichern.

Aber auch hier ergeben sich Probleme, denn unter hoher last, werden auch ein Haufen solcher Beans erzeugt. Das kann zu Speicher- oder allgemeiner Resourcenproblemen fĂŒhren. Klar, man kann die Anzahl der Beans auf ein Maximum begrenzen... Dann warten die zusĂ€tzlichen Anfragen eben hĂ€ngen, bis eine Bean freigegeben wurde.

Und ach ja: bitte vergesst nicht, irgendwann der Bean zu sagen, dass sie nicht mehr benötigt wird... Eine Destroy-Methode oder so ist fĂŒr Stateful Beans wirklich nötig! (Siehe Auch: EJB-Lifecycle).

Abschließende Tipps

Ganz ehrlich, ich hab schon viel zu viel Zeit damit zugebracht, lustige Fehler in Java-Programmen zu suchen, die nur auftraten, wenn Last auf dem System ist. Diese Fehler sind in nahezu 100% der FĂ€lle auf Probleme der Trennung zwischen den Threads zurĂŒckzufĂŒhren.

So etwas ist leider nicht so einfach zu debuggen, denn es tritt ja nur unter Last auf. Manche dieser Fehler kann man mit JMeter finden und somit dann auch beheben, aber da JMeter naturgemĂ€ĂŸ nur immer wieder das selbe tut, bzw. die gleiche Anfrage an den Server stellt, kann man damit auch nicht 100% alle Fehler dieser Art finden.

Eine nahezu unverzichtbare Hilfe beim lösen dieser Probleme bieten Profiling-Tools wie z.B. JProfiler. Aber die wirklich nĂŒtzlichen Tools sind leider kostenpflichtig, auch wenn sich die Anschaffung sicherlich auszahlen wird.


category: global

Procmail - mark messages as read

2008-05-24 - Tags: linux email

originally posted on: https://boesebeck.name

Das funktioniert allerdings nur, wenn man die Emails serverseitig in einem Maildir ablegt. Der Trick ist, procmail dazu zu bewegen, die Emaildatei mit dem Suffix :2,S zu versehen.

hier ein Auszug aus meiner Procmailrc:

:0
* From:.*Add your rule here.*
{
foldername=${DEFAULT}/.DEFAULTFOLDER_HERE

:0c
$foldername/ # stores in .$foldername/new/

:0
* LASTFOLDER ?? /\/[^/]+$
{ tail=$MATCH }
TRAP=”mv $foldername/new/$tail* $foldername/cur/$tail:2,Sa”

:0
/dev/null
}

Ihr solltet natĂŒrlich die regel oben und den Defaultfolder anpassen. die Variable ${DEFAULT} zeigt bei mir direkt auf die Inbox. Das ganze mit dem zusĂ€tzlichen “a” an dem Dateinamen liegt an Dovecot (denke ich). die Letzte Regel sorgt dafĂŒr, dass es zu keiner Mailverdoppelung kommt.

found results: 78

<< 1 ... 2 ... 3 ... >>