Salesforce: rendering of Visual Force pages and context

Visits: 478

In a simple, straightforward programming situation, you would assume that functions are executed when they are seemingly called. When you are rendering a Visual Force page in Salesforce, things seem to be a bit mixed up. Though it seems counter-intuitive, it is understandable what happens.

Let’s take a simple example of a Visual Force page with a controller.

Page (atest):

<apex:page controller="Atest">
    Parameter: {!parameter} <br />
    Parameter2: {!parameter2} <br />
    Parameter: {!parameter} <br />
    Parameter2: {!parameter2} <br />
    Parameter: {!parameter} <br />
    Parameter2: {!parameter2} <br>
</apex:page>

Controller:

public class Atest {
    private Integer parameter;

    public Atest () {
        System.debug ('Constructor called.');
        parameter = 1;
    }

    public Integer getParameter () {
        System.debug ('getParameter');
        parameter = parameter + 1;
        return parameter;
    }

    public Integer getParameter2 () {
        System.debug ('getParameter2');
        parameter = parameter + 1;
        return parameter;
    }

    public PageReference nextPage () {
        PageReference page1 = new PageReference('/apex/atest2');
        Blob b = page1.getContentAsPDF();
        PageReference page2 = new PageReference('/apex/atest2');  
        b = page2.getContentAsPDF();
        PageReference page3 = new PageReference('/apex/atest2');  
        return page3;
    }
}

{!parameter} is a reference to the method getParameter () and {!parameter2} a reference to getParameter2 (). Ignore the method nextPage () for now…

So what you might expect is that the Visual Force renderer calls getParameter (). This increases the variable parameter by 1 and returns its new value. We do see the output “Parameter: 2” – as expected. Then the renderer calls getParameter2 (). This again increases the variable parameter by 1 and returns its new value. We do see the output “Parameter2: 3” – as expected.

Next, we want “parameter” again – seemingly a call to the method getParameter (). But now the method is not actually executed; parameter is not increased anymore. We get the outputs “Parameter: 2” and “Parameter2: 3” again and again, no matter how many times we think the method is called.

Now for the second part in the controller above, we need the VF page atest2, which is virtually the same, except that it is a different file. Also, for convenience to call the method in our class, add

<apex:form>
    <apex:commandButton action="{!nextPage}" value="next page" />
</apex:form>

to the page atest. When you now click on “next page”, the page atest2 is created 3 times. To make sure that it is actually rendered, we get the content of the page as a pdf, and the third time, the page is returned as a PageReference. Therefore you are transported to the page atest2.

What you now see is “Parameter: 4” and “Parameter2: 5”. Even though we have rendered the page atest2 3 times, the variable parameter has only been increased by 1 two times.

This is because the renderer works in the same context for all 3 times it renders atest2. getParameter () and getParameter2 () are both called exactly once, and that only, because we are rendering a page in a new context – the call to the method nextPage (). You could even create the pages atest3 and atest4, have them rendered after each other (in one method), and “Parameter” and “Parameter2” will be the same value for each rendered page.

Any output of a method is directly cached, and the method is not called again, except for if you force a rerender – because that is what re-rendering is for. If you know that values will change, you have to instruct Visual Force to do a new rendering.

To get around this, make sure that you create a new context for a new page with changing information. The easiest way to do so is IMHO to create a controller instance for every page that needs to be rendered, and this in turn can be done by having a different controller for the subsequent pages.

tl;dr:

Do not change the information of a variable or method during one rendering context. Have all information be calculated before anything is actually rendered, and do not change information when using a get-method. If information changes due to user input, have the sections that show information based on the input rerendered when necessary.

Overall: within one context the VF renderer will always call any getter only once.

Salesforce – Vergleich von 2 orgs

Visits: 403

Das ist mal ein gutes Tool, da bin ich gerade drauf gestoßen: Salesforce Org Compare. Damit kann man die Metadaten von 2 Orgs herunter laden lassen, und die Differenzen (einfach ein Diff auf den heruntergeladenen Daten) anzeigen lassen. Das ist wunderbar, wenn man mal wieder vergessen hat, welche Teile alle für ein Deployment notwendig sind, oder wenn die Orgs unterschiedlich reagieren, und man keinen Anhaltspunkt hat, warum das so ist.

Es sollte eigentlich nicht notwendig sein, schließlich weiß man genau, was man geändert hat, der Kollege hat auch nicht auf einer anderen Sandbox widersprüchliche Sachen geändert…

Thermische Probleme mit Raspberry Pi 4

Visits: 516

Der Raspberry Pi 4 sieht mit seinem Gigabit-Ethernet und den USB-3 Ports genau danach aus, was mir die ganze Zeit fehlte. Einer meine 3er-Pis spielt seit geraumer Zeit mit 3 externen Festplatten NAS. Das natürlich nur unbefriedigend, da mit USB 2 und 100 MBit bei dmcypt-verschlüsselten Platten in guten Fällen 6 MB/s per Samba übertragen werden. Das reicht für vieles, aber längst nicht alles, was ein NAS so tun soll.

Der Pi4 kopiert von dmcrypt zu dmcrypt mit etwa 70 MB/s (über Samba kommt bei mir dann etwa 50 MB/s an – bei großen Dateien), das ist die richtige Richtung. Allerdings lief er beim Kopieren von 2 TB nicht durch, sondern stürzte mit einer Kernel Panic ab. Wenn der als NAS funktionieren soll, muss er auch stabil sein, schnell alleine reicht nicht.

Mit fiel schon beim ersten Starten auf, dass vor allem die USB-Anschlüsse sehr warm werden, die CPU war auch fast nicht mehr anfassbar. Spätestens in einem Gehäuse kann die Wärmeableitung nicht mehr reichen. Bei Tom’s Hardware wird berichtet, dass eine neue Firmware den USB-Controller kühler laufen lassen soll, Tests zeigten 2°C Unterschied, was aber bei weitem noch nicht für den Betrieb in einem Gehäuse ausreicht. Meine Messungen (billigstes Infrarot-Thermometer) des CPU- und RAM-Chips zeigten Oberflächentemperaturen bis 62°C, wenn auf dem Bildschirm das Thermometer erscheint, und auch Thermal Throttling hinweist.

Mit

vcgencmd measure_temp

kann der CPU-Sensor ausgelesen werden. Bei 80°C auf dem Sensor zeigt sich das Thermometer. Meine Versuche bestätigen, dass bei Last nach etwas mehr als 3 Minuten der Prozessor bremst – kopiert der Pi verschlüsselte Dateien, haben alle Chips gut zu tun.

Mit aktiver Kühlung durch einen Lüfter (120mm auf 5 V statt 12 V, so nicht hörbar, und so aufgestellt, dass etwas Luft über die Chips zieht) bleibt der Sensor zwischen 50°C und 60°C, die Oberfläche liegt dann bei akzeptablen 36°C. Im Moment habe ich keine weiteren Abstürze, vielleicht ist die Temperatur das einzige Problem. Auf jeden Fall lasse ich den nur dann als NAS laufen, wenn ich einen Lüfter dran stellen kann, andernfalls ist mir das eine zu heiße Sache.

Salesforce – Assets with or without Contact and/or Account

Visits: 1572

In test classes it is always a good thing if you are not just going through your code, but also to actually test if it is working according to design. To make your test shine, you would test both kinds of cases, working examples as well as those where you expect an error to occur.

To test a trigger, that ensures Contact and Account to be set on an Asset (as long as certain parameters are fulfilled), I added a test case where the trigger could not work properly.

According to the Apex documentation, an Asset must have Contact and/or Account set, otherwise you will run into an Exception (FIELD_INTEGRITY_EXCEPTION: Every asset needs an account, a contact or both).

Screenshot of Asset Object Reference - AccountId must be set

So, my Test class includes

Asset a = new Asset (Name = 'Test asset');
try {
    insert a;
    System.assert (false, 'Should not reach this, an Asset needs an Account or Contact');
}
catch (Exception e) {
}

However, in the project I work at currrently, this assertion fails, as our Assets can exist without neither Contact nor Account.

And this is where the documentation is plainly wrong, as it depends on the Organization-Wide Defaults for sharing. If you set access to anything except the default (Controlled by Parent), you can create Assets without Account or Contact. So the documentation is wrong 75% of the time, as a setting of “Private”, “Public Read Only” and “Public Read/Write” allows Assets without.

This was a pitfall for me, as my test class worked on one box, but not on another. And soometimes failing tests hint at an issue with the org itself. But only sometimes, most times it is because a developer did not set up the test correctly.

Atmel Atmega ISP Programmierung schlägt fehl

Visits: 1288

Von Pollin gibt es für 2,99 € ein ‘Entwicklungsboard’ mit einem Atmega 168PA drauf. Die Beschreibung ist dürftig, insbesondere, wie der In System Programmiert werden kann. Bei mir lag so ein Board etwas länger rum, weil einfach die Zeit fehlte.

Durchpiepen der Leitungen zeigte dann, dass der Programmer an folgende Pins am Pollinboard angeschlosen werden musste:

ICSP 1 (MOSI) – 11
ICSP 2 (Vcc) – Vcc
ICSP 4 (Gnd) – GND
ICSP 5 (RST) – RST
ICSP 7 (SCK) – 13
ICSP 9 (MISO) – 12

Vergleiche dazu das Anschlusslayout des Olimex Programmers.

Aber alles, was ich versuchte, die Kommunikation funktionierte nicht. Die Fehlermeldung war:

Timestamp: 2018-10-29 18:08:45.435
Severity: ERROR
ComponentId: 20100
StatusCode: 1
ModuleName: TCF (TCF command: Device:startSession failed.)

Failed to enter programming mode. ispEnterProgMode: Error status 
received: Got 0xc0, expected 0x00 (Command has failed to 
execute on the tool)

Da keine meiner Ideen fruchtete, bin ich diesem Troubleshooting gefolgt. Oszilloskop an der RESET Leitung zeigte dann nicht einen, sondern mehrere (ich glaube 5) Reset-versuche durch den Programmer. Das sollte so nicht sein. Das Lesen der Gerätesignatur in Atmel Studio schickt einen Reset, wenn alles gut geht.

Letztlich stellte sich heraus, dass ich zwar die Pins richtig raus gesucht hatte, aber einfach meiner eigenen Anleitung nicht gefolgt bin. MOSI hatte ich mit GND verbunden, und schon ging nichts mehr.

Ich habe noch weiter getestet mit allen Signalen: bei diesem Fehler kann es ganz einfach sein, dass ein Kabel nicht richtig angeschlossen ist. Am falschen Pin oder ein Kabelbruch – der Fehlercode ist immer derselbe wenn eins der ISP Signale fehlt, egal ob MOSI, MISO, SCK oder RST.

TBS5520SE – Multi-standard TV Tuner USB Box

Visits: 1484

Die kleine Box in schwarzen Metall verspricht, alles an Fernsehsignalen empfangen zu können, was so hier verfügbar ist.

An der Frontseite 2 LEDs, einmal in blau, um zu zeigen, dass das Gerät arbeitet, und einmal in grün, damit wird angezeigt, dass auf der aktuellen Frequenz ein Sender gefunden wurde. Aber davon sagt die nicht wirklich vorhandene Anleitung nichts. Außerdem sind die LEDs viel zu hell, reine Funktionsindikatoren sollten leicht sichtbar, aber nicht grell sein. Neben einem Monitor aufgestellt stört die Helligkeit massiv. 

Die Box hat einen Antenneneingang sowie einen LNB-Anschluß und ein USB-2.0 Interface.

Das Gehäuse ist aus Metall und hat ein paar Lüftungsschlitze. Die Beschreibung sagt: “Neben der guten Ausstattung und dem modernen Interface wurde auch auf eine geringe Leistungsaufnahme und damit eine niedrige Abwärme, für geräuschlosen Betrieb, geachtet.”. Ein Lüfter ist sicher auch nicht notwendig, aber das Gerät wird schon warm, die Lüftungsschlitze sind gerechtfertigt und gut.

Empfangbar seien DVB-S / S2 / S2X, DVB-C / C2, DVB-T / T2 und ISDB-T. Und das passt auch wunderbar zum zentralen Chip in dem Gerät, dem Si21832B. Dieser dekodiert all diese Standards für uns.

Daneben gibt es noch einen CY7C68013A für die USB-Kommunikation. Außerdem ein paar Verstärker- und Logikchips, aber keine Überraschung.Kommen wir zu den Treibern. Diese können von der Webseite herunter geladen werden, im Lieferumfang ist kein Datenträger – der sowieso nur veraltete Versionen haben würde.

Ich habe zum Ansehen des Video-Streams den kostenlosen TBS-Viewer ausprobiert. Der Sendersuchlauf hatte mich direkt demotiviert. Der Empfangstyp war auf Satellit voreingestellt.

Ein Wechsel auf DVB-T (‘Terrestrisch’) setzt immer die Transponderliste “DMB-TH Hong Kong” als Standard.Ein Suchlauf (Korrekt auf Transponderliste: DVB-T2 Deutschland gestellt) lieferte keinen Sender, über die gesamte Frequenzliste hinweg. Spezifisches Scannen einzelner Frequenzen, die hier definitiv empfangbar sind und unverschlüsselte Sender übertrage (514 MHz und 706 MHz), lieferten auch 0 Sender. Den Grund dafür liefert die etwas schwache Anleitung: absolut wichtig ist TBS 5520 SE ChangeMode-Tool v1.0.0.2, mit diesem kann der Empfangsmodus geändert werden. Bei mir war der auf Satellit voreingestellt; nur mit diesem Tool kann auf DVB-T2 umgestellt werden.

Danach funktionierte dann auch der Suchlauf im TBS-Viewer. Allerdings ist die Empfindlichkeit des Empfängers eher mittelmäßig. Eine mickrige Antenne, die auch an einem Fernseher hier eher schwierigen Empfang liefert, die aber an einer Xoro-Box sehr gut funktioniert, liefert auch am TBS-5520 keinen befriedigenden Empfang. Vermutlich braucht man mit dem Gerät meistens eine aktive Antenne.

Zum Test wollte ich auch den kostenpflichtigen DVB-Viewer installieren – TBS Viewer lief noch – und erhielt die Fehlermeldung, dass DVB-Viewer noch liefe. Meine Vermutung ist da klar, dass der TBS-Viewer eine verfiesemantuckte Version des DVB-Viewers ist.

Also: es funktioniert alles, aber die Dokumentation stinkt schon anrüchig, und die Lösung des Empfangsumschaltens mittels des Change-Mode-Tools ist suboptimal. Technisch Interessierte werden keine Schwierigkeiten damit haben, aber es ist nicht gut.

Ein für mich wichtiger Kaufgrund war die Zusage “Treiber für sämtliche 32- und 64-bit Betriebssysteme befinden sich im Lieferumfang: Win XP, Vista, Win 7/8/10 und Linux.” “Sämtliche 32- und 64-bit Betriebssysteme” … MacOS ist schon einmal nicht dabei, somit ist sämtliche eine leere Versprechung. Linux ist ein verlockendes Versprechen für mich; ich will immer noch den Empfang von Fernsehen auf einem Raspberry Pi machen, und das Dekodieren dann auf anderen Clients. Leider unterstützt der Raspberry Pi h.265 nicht. Linux und sämtliche klingt wunderbar. ARM-Linux scheint da aber nicht wirklich drunter zu fallen.

Mehr zum Linux-Treiber später….

Salesforce API documentation could be better with API versions

Visits: 418

Once again I found a nice, no, necessary feature. When searching for RecordType-IDs in Apex, you could either query for them with SOQL (burning away the precious number of statements) OR you could just ask the schema.

With List<RecordTypeInfo> contactRTs = Schema.SObjectType.Contact.getRecordTypeInfos() you can get all available RecordTypes for this Object. With contactRTs[0].getName() you can get the label of the record type.

The label. This may be dependent on the language of the user, so it’s utterly useless in code. But there is also contactRTs[0].getDeveloperName() – yay! However, the documentation never states which is the minimum API needed for a function call, and this is absolute crap. Why not just add a line with the API-version? Otherwise you may get errors, which contradict the documentation.

Yes, I know that the Summer`18 release is not far now, so in this case it means that it was a bit more than a week before I can use this needed feature. But it cost me quite some time – checking if I had a typo, if I misread … and then finally a search for the release notes with this function. With the API version in the docs, this would have been a matter of minutes…

Simple script for setting allowed IP address in tinyproxy

Visits: 870

I’m using a dynamic IP address, but for Salesforce development I need a fixed one. As a proxy with a fixed IP address is enough, I use tinyproxy for that. Of course I cannot afford to have an unrestricted open proxy. Now this small script helps me achieve this in a half-automated way. I ssh in to my proxy-server, call this script, and it takes care of producing an appropriate line in the configuration:


#! /bin/bash
#
# Find the ip address of the calling ssh
callerId=`echo $SSH_CONNECTION | awk '{print $1}'`
#
# Find the allow line and set it to the ip address
sudo sed -i "/# set from script/!b;n;cAllow ${callerId}" /etc/tinyproxy.conf
sudo systemctl restart tinyproxy

In order for this to work, you need a marker in your tinyproxy.conf. This script looks for “# set from script”, and replaces the following line with an Allow-entry.

Was soll das Gerede in Warteschleifen?

Visits: 323

Jetzt bin ich wieder in so einer Hotline-Warteschleife (ich möchte doch mal wissen, was Medion zu den widersprüchlichen Angaben zu dem Rechner zu sagen hat), und werde mit Musik genervt.

Aber die Unsitte, dass alle paar Sekunden eine Bandansage mich darüber informiert, dass immer noch niemand zur Verfügung steht, ist extrem störend. Wenn einfach nur Musik läuft, dann schalte ich halt den Lautsprecher an, lasse das Telefon neben mir liegen, und kann weiter arbeiten. Eine menschliche Stimme allerdings reisst mich dann raus – es gibt mir Hoffnung, dass jemand mit mir reden, ja vielleicht sogar mein Problem lösen könnte.

Wirklich: lasst diese sinnlosen Ansagen. Es glaubt doch sowieso niemand, was da geredet wird. “Nur noch ein paar Augenblicke..”, “Der nächste freie Mitarbeiter…” Blah blah blah. Es nervt!