“sudo pmset -a hibernatemode 1″ = suspend to disk only
“sudo pmset -a hibernatemode 3″ = suspend to disk + RAM (default on laptops)
Dieses Blog ist in erster Linie ein öffentliches Notizbuch für mich selber: ich notiere darin kleine Dinge, die ich immer wieder vergesse. Oder Entwicklungen, die ich spannend finde.
Rémy Schumm, 4. 5. 2012
Die Klasse FakeRecorder kann Testdatenpaar von Suchkriterium und Resultat von einem Realen Service aufnehmen und als XML-Files ablegen.
Die Klasse FakeUnmarshaller kann diese XML-Daten aufgrund der Suchkriterien wieder rekonstruieren und über ein FakeBean ausliefern.
Zur Verwendung siehe den UnitTest FakeTest.
Technische Details: Zur Umwandlung von und zu XML wird JAXB verwendet. Zur Identifikation der Suchkriterien wird ein md5-Hash über die toString() Methode des Suchkriteriums gemacht.
Das Root-Element der Resultatklasse muss mit @XmlRootElement annotiert sein. Alle beiteiligten Klassen müssen Serializable sein.
package ch.schumm.fakeservice.bean; | |
import ch.schumm.fake.FakeUnmarshaller; | |
import ch.schumm.fakeservice.model.Kunde; | |
import ch.schumm.fakeservice.model.Suchkriterium; | |
/** | |
* Ein Fake-Bean, das den FakeUnmarshaller benutzt. | |
* @author C709360 | |
* | |
*/ | |
public class FakeBean implements KundeBean { | |
public class KundeBeanException extends RuntimeException { | |
private static final long serialVersionUID = 1L; | |
public KundeBeanException(Exception e) { | |
super(e); | |
} | |
} | |
public Kunde getKundeForKriterium(Suchkriterium kriterium) { | |
try { | |
FakeUnmarshaller<Suchkriterium, Kunde> fakeUnmarshaller = new FakeUnmarshaller<Suchkriterium, Kunde>(); | |
return fakeUnmarshaller.unmarshallResultForKriterium(kriterium, Kunde.class); | |
} catch (Exception e) { | |
throw new KundeBeanException(e); | |
} | |
} | |
} |
package ch.schumm.fake; | |
import java.io.File; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.JAXBException; | |
import javax.xml.bind.Marshaller; | |
import javax.xml.bind.PropertyException; | |
import org.apache.commons.codec.digest.DigestUtils; | |
import ch.schumm.fakeservice.bean.RealBean; | |
import ch.schumm.fakeservice.model.Kunde; | |
import ch.schumm.fakeservice.model.Suchkriterium; | |
/** | |
* Nimmt Fake-Daten von einem realen Service auf und legt sie mittels JAXB als XML-Dateien ab. <br> | |
* Die Dateinamen der XML-Dateien werden aus dem md5-Hash der Suchkriterien erzeugt. Dazu müssen sie eine eindeutige toString-Methode | |
* implementieren. | |
* | |
* @author C709360 Rémy Schumm | |
* | |
* @param <S> Typ des Suchkriteriums | |
* @param <R> Typ des Resultats. | |
*/ | |
public class FakeRecorder<S,R> { | |
/** | |
* Nimmt einen Testdatensatz auf. | |
* @param suche das Suchkriterium | |
* @param result das Resultat des realen Service. | |
* @throws JAXBException | |
* @throws PropertyException | |
*/ | |
public void record(S suche, R result) | |
throws JAXBException, PropertyException { | |
JAXBContext context = JAXBContext.newInstance(result.getClass()); | |
// Marshalling... | |
Marshaller m = context.createMarshaller(); | |
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | |
String md5key = generateHashKey(suche); | |
m.marshal(result, new File(md5key + ".xml")); | |
} | |
public static String generateHashKey(Object suche) { | |
String key = DigestUtils.md5Hex(suche.toString()); | |
return key; | |
} | |
} |
package ch.schumm.fakeservice.bean; | |
import static org.junit.Assert.*; | |
import org.junit.Test; | |
import ch.schumm.fake.FakeRecorder; | |
import ch.schumm.fakeservice.model.Kunde; | |
import ch.schumm.fakeservice.model.Suchkriterium; | |
/** | |
* Testet die FakeRecorder und FakeUnmarshaller Infrastruktur. | |
* @author C709360 | |
* | |
*/ | |
public class FakeTest { | |
@Test | |
public void testRecorder() throws Exception { | |
RealBean bean = new RealBean(); | |
Suchkriterium hufnagelsuche = RealBean.generateHufnagelSuche(); | |
Kunde hufnagel = bean.getKundeForKriterium(hufnagelsuche); | |
Suchkriterium schummsuche = RealBean.generateSchummSuche(); | |
Kunde schumm = bean.getKundeForKriterium(schummsuche); | |
FakeRecorder<Suchkriterium, Kunde> recorder = new FakeRecorder<Suchkriterium, Kunde>(); | |
recorder.record(schummsuche, schumm); | |
recorder.record(hufnagelsuche, hufnagel); | |
} | |
@Test | |
public void testFakeImplSchumm() { | |
KundeBean fakebean = new FakeBean(); | |
Kunde kunde = fakebean.getKundeForKriterium(RealBean | |
.generateSchummSuche()); | |
assertEquals("Josef Schumm wohnt an Schlossgasse in Coburg", | |
kunde.toString()); | |
} | |
@Test | |
public void testFakeImpl() { | |
KundeBean fakebean = new FakeBean(); | |
Kunde kunde = fakebean.getKundeForKriterium(RealBean | |
.generateHufnagelSuche()); | |
assertEquals("Barbara Hufnagel wohnt an Hanauer Strasse in Alzenau", | |
kunde.toString()); | |
} | |
} |
package ch.schumm.fake; | |
import java.io.File; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.Unmarshaller; | |
/** | |
* Reproduziert die mit dem FakeRecoder aufgenommen Testdaten. | |
* | |
* @author C709360 | |
* | |
* @param <S> Typ des Suchkriteriums | |
* @param <R> Typ des Resultats. | |
*/ | |
public class FakeUnmarshaller<S, R> { | |
/** | |
* Rekonstruiert die Testdaten für ein Suchkriterium. | |
* @param kriterium das Suchkriterium. | |
* @param rootClassResult Der Klassentyp des Resultats. | |
* @return | |
* @throws Exception | |
*/ | |
public R unmarshallResultForKriterium(S kriterium, Class<R> rootClassResult) throws Exception { | |
String key = FakeRecorder.generateHashKey(kriterium); | |
JAXBContext context = JAXBContext.newInstance(rootClassResult); | |
// Unamarshalling... | |
Unmarshaller u = context.createUnmarshaller(); | |
@SuppressWarnings("unchecked") | |
R kunde = (R) u.unmarshal(new File(key + ".xml")); | |
return kunde; | |
} | |
} |
package ch.schumm.fakeservice.model; | |
import java.io.Serializable; | |
public class Adresse implements Serializable { | |
/** | |
* | |
*/ | |
private static final long serialVersionUID = 1L; | |
String strasse; | |
String ort; | |
public String getStrasse() { | |
return strasse; | |
} | |
public void setStrasse(String strasse) { | |
this.strasse = strasse; | |
} | |
public String getOrt() { | |
return ort; | |
} | |
public void setOrt(String ort) { | |
this.ort = ort; | |
} | |
@Override | |
public String toString() { | |
return "an " + strasse + " in " + ort; | |
} | |
} |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |
<kunde> | |
<adresse> | |
<ort>Coburg</ort> | |
<strasse>Schlossgasse</strasse> | |
</adresse> | |
<name>Schumm</name> | |
<vorname>Josef</vorname> | |
</kunde> |
package ch.schumm.fakeservice.model; | |
import java.io.Serializable; | |
import javax.xml.bind.annotation.XmlRootElement; | |
@XmlRootElement | |
public class Kunde implements Serializable { | |
/** | |
* | |
*/ | |
private static final long serialVersionUID = 4539708693456998133L; | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
public String getVorname() { | |
return vorname; | |
} | |
public void setVorname(String vorname) { | |
this.vorname = vorname; | |
} | |
public Adresse getAdresse() { | |
return adresse; | |
} | |
public void setAdresse(Adresse adresse) { | |
this.adresse = adresse; | |
} | |
String name; | |
String vorname; | |
Adresse adresse; | |
@Override | |
public String toString() { | |
return vorname + " " + name + " wohnt " + adresse.toString(); | |
} | |
} |
package ch.schumm.fakeservice.main; | |
import java.io.File; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.Marshaller; | |
import javax.xml.bind.Unmarshaller; | |
import ch.schumm.fakeservice.model.Adresse; | |
import ch.schumm.fakeservice.model.Kunde; | |
/** | |
* Marshallt einen Kunden in ein XML-File, unmarshallt ihn grad wieder und zeigt ihn an. | |
* | |
*/ | |
public class Main { | |
public static void main(String[] args) throws Exception { | |
JAXBContext context = JAXBContext.newInstance(Kunde.class); | |
//Marshalling... | |
Marshaller m = context.createMarshaller(); | |
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | |
Kunde kunde = generateModel(); | |
m.marshal(kunde, new File("josef.xml")); | |
//Unamarshalling... | |
Unmarshaller u = context.createUnmarshaller(); | |
Object object = u.unmarshal(new File("josef.xml")); | |
System.out.println(object.toString()); | |
} | |
private static Kunde generateModel() { | |
Kunde kunde = new Kunde(); | |
kunde.setName("Schumm"); | |
kunde.setVorname("Josef"); | |
Adresse adresse = new Adresse(); | |
adresse.setOrt("Coburg"); | |
adresse.setStrasse("Schlossgasse"); | |
kunde.setAdresse(adresse); | |
return kunde; | |
} | |
} |
<?xml version="1.0" encoding="UTF-8"?> | |
<bestellung> | |
<vollerName>Hans Muster</vollerName> | |
<strasseNr>Muster. 31</strasseNr> | |
<plzOrt>8406 Winterthur-Töss</plzOrt> | |
<telephon>052 222 2222</telephon> | |
<email>hans.muster@gmx.ch</email> | |
<bemerkung>Diese Bestellung kommt von XML.</bemerkung> | |
<bilder> | |
<bild> | |
<anzahl>1</anzahl> | |
<format>17x6</format> | |
<name>Bild_3.tiff</name> | |
<preis>6.00</preis> | |
</bild> | |
<bild> | |
<anzahl>1</anzahl> | |
<format>12x3</format> | |
<name>am_See.jpg</name> | |
<preis>123.00</preis> | |
</bild>ame | |
<bild> | |
<anzahl>1</anzahl> | |
<format>17x6</format> | |
<name>hans.jpg</name> | |
<preis>6.00</preis> | |
</bild> | |
<bild> | |
<anzahl>2</anzahl> | |
<format>12x45</format> | |
<name>chorA.jpg</name> | |
<preis>6.00</preis> | |
</bild> | |
</bilder> | |
</bestellung> |
-(NSManagedObject *)bestellung { | |
if (bestellung != nil) { | |
return bestellung; | |
} | |
NSManagedObjectContext * moc = [self managedObjectContext]; | |
NSError * fetchError = nil; | |
NSArray * fetchResults; | |
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init]; | |
@try { | |
NSEntityDescription * entDesc = [NSEntityDescription entityForName:@"Bestellung" inManagedObjectContext:moc]; | |
[fetchRequest setEntity:entDesc]; | |
fetchResults = [moc executeFetchRequest:fetchRequest error:&fetchError]; | |
} | |
@finally { | |
[fetchRequest release]; | |
} | |
if ((fetchResults != nil) && ([fetchResults count] == 1) && (fetchError == nil)) { | |
//Dept. versorgen und zurückgeben. | |
[self setBestellung:[fetchResults objectAtIndex:0]]; | |
return bestellung; | |
} | |
//error Zeugs ausgelassen, siehe Seite 31 | |
//Das ObjC ist halt doch auch nicht so Elegant. NSError und NSException? Was jetzt? | |
return nil; |
//Mail wird auf das Fenster gedragt. | |
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender | |
{ | |
//NSLog(@"draging ausführen"); | |
NSPasteboard * pg = [sender draggingPasteboard]; | |
NSString * input = [pg stringForType:NSStringPboardType]; | |
NSError * err = nil; | |
//NSLog(input); | |
NSXMLDocument * dom = [[NSXMLDocument alloc] initWithXMLString:input options:NSXMLDocumentTidyXML error:&err]; | |
//NSLog([dom XMLStringWithOptions:NSXMLNodePrettyPrint]); | |
//Daten: | |
[self addDomValue:@"name" toValueKey:@"vollerName" fromDom:dom]; | |
[self addDomValue:@"adresse" toValueKey:@"strasseNr" fromDom:dom]; | |
[self addDomValue:@"ort" toValueKey:@"plzOrt" fromDom:dom]; | |
[self addDomValue:@"telefon" toValueKey:@"telephon" fromDom:dom]; | |
[self addDomValue:@"email" toValueKey:@"email" fromDom:dom]; | |
NSData * bemerkung = [[[[[dom rootElement] nodesForXPath:@"//bemerkungen" error:&err] objectAtIndex:0] stringValue] dataUsingEncoding:NSUTF8StringEncoding]; | |
[self setValue:bemerkung forKeyPath:@"bestellung.bemerkung"]; | |
//Bilder: | |
//iterieren | |
//neues Bild-Objekt herstellen | |
//hinzufügen | |
//Alle <format> durchgehen, dann im Parent schauen, was für ein Bild es war. | |
NSArray * bilderNodeList = [[dom rootElement] nodesForXPath:@"//format" error:&err]; | |
int i; | |
NSManagedObject * bild; | |
NSXMLElement * bildElement; | |
NSNumberFormatter * nf = [[NSNumberFormatter alloc] init]; | |
NSString * bertFormat; | |
for (i = 0; i < [bilderNodeList count]; i++) { | |
bildElement = [bilderNodeList objectAtIndex:i]; | |
bild = [NSEntityDescription insertNewObjectForEntityForName:@"Bild" inManagedObjectContext:[self managedObjectContext]]; | |
[bild setValue:[[[[bildElement nodesForXPath:@"../../name" error:&err] objectAtIndex:0] stringValue] stringByDeletingPathExtension]forKeyPath:@"name"]; | |
bertFormat = [[[bildElement nodesForXPath:@"name" error:&err] objectAtIndex:0] stringValue]; | |
//Tokenzier für Auseinandernehmen der Werte von <format/name>. | |
NSArray * tokens = [bertFormat componentsSeparatedByString:@" "]; | |
[bild setValue:[tokens objectAtIndex:0] forKeyPath:@"format"]; | |
[bild setValue:[tokens objectAtIndex:1] forKeyPath:@"farbe"]; | |
[bild setValue:[tokens objectAtIndex:2] forKeyPath:@"papier"]; | |
[bild setValue:[nf numberFromString: [[[bildElement nodesForXPath:@"anzahl" error:&err] objectAtIndex:0] stringValue] ] forKeyPath:@"anzahl"]; | |
[bild setValue:[nf numberFromString:[[[bildElement nodesForXPath:@"preis" error:&err] objectAtIndex:0] stringValue] ] forKeyPath:@"preis"]; | |
[bild setValue:[[[bildElement nodesForXPath:@"../../galerie" error:&err] objectAtIndex:0] stringValue] forKeyPath:@"galerie"]; | |
[bild setValue:[self bestellung] forKeyPath:@"bestellung"]; | |
} | |
//Optionen zur Bestelleung: | |
//iterieren | |
//TODO das gleiche nochmals für die <optionen>. | |
[nf release]; | |
[dom release]; | |
return YES; | |
} | |
//utility für mich. | |
// sucht "//domkey" im DOM und fügt den Wert als bestellung.cdkey dem Dokument hinzu. | |
- (void)addDomValue:(NSString*) domkey toValueKey:(NSString*) cdkey fromDom:(NSXMLDocument*) dom | |
{ | |
NSError * err; | |
NSString * xpath = [@"//" stringByAppendingString:domkey]; | |
NSString * wert = [[[[dom rootElement] nodesForXPath:xpath error:&err] objectAtIndex:0] stringValue]; | |
//NSLog(wert); | |
if (err != nil) { | |
NSLog(@"fehler in xpath: %@", err); | |
} | |
![]() |
das zugehörige Modell |
NSArray * accusativPluralFeminin = [NSArray arrayWithObjects:@"Ianuarias", @"Februarias", @"Martias", | |
@"Apriles", @"Maias", @"Iunias", @"Iulias", @"Augustas", @"Septembres", @"Octobres", @"Novembres", @"Decembres", nil]; |