XPages: Optimized Partial Refreshs

Inspired by the last post of Mark, I have created a small CSJS snippet which allows to optimize the behaviour of a Partial Refresh. Normally, if you execute a Partial Refresh, all elements of a form are sent to the server. Take a look at this XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:inputText
        id="inputText01"
        value="#{sessionScope.inputText01}" />
    <xp:inputText
        id="inputText02"
        value="#{sessionScope.inputText02}" />
    <xp:inputText
        id="inputText03"
        value="#{sessionScope.inputText03}" />
    <xp:inputText
        id="inputText04"
        value="#{sessionScope.inputText04}" />
    <xp:inputText
        id="inputText05"
        value="#{sessionScope.inputText05}" />
    <xp:inputText
        id="inputText06"
        value="#{sessionScope.inputText06}" />
    <xp:inputText
        id="inputText07"
        value="#{sessionScope.inputText07}" />
    <xp:inputText
        id="inputText08"
        value="#{sessionScope.inputText08}" />
    <xp:inputText
        id="inputText09"
        value="#{sessionScope.inputText09}" />
    <xp:inputText
        id="inputText10"
        value="#{sessionScope.inputText10}" >
    </xp:inputText>

    <xp:div id="refreshMe">
        <xp:label
            value="#{javascript:java.lang.System.currentTimeMillis()}"
            id="labelNow" />
    </xp:div>

    <xp:button
        value="Normal Refresh"
        id="buttonNormal">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial" refreshId="refreshMe">
        </xp:eventHandler>
    </xp:button>

</xp:view>

The button refreshes only a small portion of the XPage, the DIV element refreshMe. It does not require any of the posted field values, it just refreshes the DOM element in the frontend. But when clicking the button, the posted data to the server contain all the fields and their values, which is not necessary in this case.

This is how the request looks like in Firebug (I have prefilled all fields with the values 1..9):

The response of the server is – as expected – the actual HTML code for the refreshed DOM element:

The optimized  version adds the option clearForm to partial refreshs. When using this option, only the XPages internal fields are sent to the server, but DOM will be refreshed correctly:

<xp:button
        value="Cleaned Refresh"
        id="buttonCleaned">
        <xp:eventHandler
            event="onclick"
            submit="false">
            <xp:this.script>
                <![CDATA[
                    XSP.partialRefreshPost(
                        '#{id:refreshMe}',{
                            clearForm: true,
                        }
                    );]]>
            </xp:this.script>
        </xp:eventHandler>
    </xp:button>

Now, the POST looks like this:

You may think „This is useless, you can do a XSP.partialRefreshGet instead!“

That’s why there is the second option additionalFields. This allows to define all fields you want to update during the refresh:

<xp:button
        value="Cleaned Refresh"
        id="buttonCleaned">
        <xp:eventHandler
            event="onclick"
            submit="false">
            <xp:this.script>
                <![CDATA[
                    XSP.partialRefreshPost(
                        '#{id:refreshMe}',{
                            clearForm: true,
                            additionalFields: ['#{id:inputText01}',
                                              '#{id:inputText02}' ],
                        }
                    );]]>
            </xp:this.script>
        </xp:eventHandler>
    </xp:button>

When clicking the button now, the specified fields are added to the POST request (and will be updated in the JSF tree:

Here is the snippet (Tested on IE 11, Chrome 33 & FF 27 with ND9 & ND 8.5.3 )

<xp:scriptBlock id="scriptBlockPROptimized">
        <xp:this.value><![CDATA[
XSP.addOnLoad(function(){

    // hijack the existing partial refresh method
    if( !XSP.__partialRefresh ){
        XSP.__partialRefresh = XSP._partialRefresh;
    }

    // add the new one to the XSP object
    XSP._partialRefresh = function x_prfh(method, form, refreshId, options){

        // clear the form?
        if( options.clearForm ){

            // create a new HTML form...
            var newForm = document.createElement( "form" );
            newForm.setAttribute( "method", form.method );
            newForm.setAttribute( "action", form.action );

            // ... and loop all existing fields
            for( var i = 0; i<form.length; i++ ){
                var field = form[i];
                var fieldName = field.name;
                var includeField = false;

                try{

                    // check for addition fields
                    if( options.additionalFields ){
                        includeField = dojo.indexOf(options.additionalFields, fieldName)!=(-1)?true:false;
                    }

                    // only add XPages relevant fields and addtional fields
                    if( fieldName == form.id || fieldName.substr(0,2) == '$$' || includeField ){

                        var newField = null;
                        if( field.options ){
                            // special handling for fields with options
                            for( var j=0; j<field.length; j++ ){
                                if( field.options[j].selected ){
                                    newField = document.createElement( "input" );
                                    newField.setAttribute( "type", "hidden" );
                                    newField.setAttribute( "name", fieldName );
                                    newField.setAttribute( "value", field.options[j].value );
                                    newForm.appendChild( newField );
                                }
                            }
                        }else{
                            // default field handling: just clone the DOM element
                            newField = field.cloneNode( true );
                            newForm.appendChild( newField );
                        }
                    }
                }catch(e){
                    console.log(e);
                }
            }

            // call the original refresh method with the new form
            return XSP.__partialRefresh(method, newForm, refreshId, options);
        }

        XSP.__partialRefresh(method, form, refreshId, options);
    };
});]]></xp:this.value>
    </xp:scriptBlock>

Just add the script block above to your XPage, or move it into a CSJS library. But keep in mind that you have to think twice when removing data from the request. It can lead in an inconsistence between the data in the client and the data stored in the component tree on the server.

Veröffentlicht unter Dojo Toolkit, Java Script, Performance, Web, XPages, XSP | Verschlagwortet mit , , , , , , , , , | 6 Kommentare

XPages: SSJS & How you can have fun at the office

This article is a demonstration of what harmful things you can do when using the SSJS & prototyping wrong (described here by the great Mark Roden). Don’t do that! Especially not on a productive server!
 

Wanna have a lot of fun in the office with the other developers? Just overwrite some global SSJS functionality! They will never find out what happend to their applications!

 

This is the application we destroy

Our demonstration application uses a small Java class which has only a single method:

package ch.hasselba.xpages;

public class Demo {

    public String foo() {
       return "bar";
    }
 }

This class is used for a label on our XPage and is loaded with the importPackage functionality of SSJS:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
    xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:label
        id="labelDemo">
        <xp:this.value>    
            <![CDATA[#{javascript:
                importPackage( ch.hasselba.xpages );
                return new ch.hasselba.xpages.Demo().foo();}]]>
        </xp:this.value>
    </xp:label>

</xp:view>

When opening the XPage, nothing special happens, the label is filled with the value „bar“:

Now let’s destroy it!

Add a button on the XPage, containing a single line of SSJS code…

<xp:button
        value="Click Me"
        id="button1">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:prototype.importPackage = null;}]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>

… and reopen the XPage:

Question: What will happen if you click the button?

Answer:

Exactly: The method is destroyed on the whole server! Every XPage which imports a Java package with SSJS will not work anymore. Only a complete server restart helps now. The error message you will receive won’t help you, because it looks like the Java classes could not be found. Now „help“ the other developers with tips like „Maybe a build problem? Have you tried to clean the application?“.

P.S. You can execute the code on other ways, f.e, with an eval statement, or a method binding of your choice.

Tested on ND 8.5.3 & ND 9.0

Veröffentlicht unter ServerSide JavaScript | Verschlagwortet mit , , , , , | 5 Kommentare

XPages: Events, ActionListeners & Parameters

To access the event parameters of an event within your actionListener, you have to access the source object of your actionEvent object:

package ch.hasselba.xpages;

import java.util.List;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import com.ibm.xsp.complex.Parameter;
import com.ibm.xsp.component.xp.XspEventHandler;

public class MyActionListener implements javax.faces.event.ActionListener {

    public void processAction(ActionEvent event)
            throws AbortProcessingException {
        XspEventHandler eventHandler = (XspEventHandler) event.getSource();
        List<Parameter> params = eventHandler.getParameters();
        for (Parameter p : params) {
            System.out.println(p.getName() + " -> " + p.getValue());
        }
    }
}

Here is an example XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:button
        value="Label"
            id="buttonAction">
        <xp:eventHandler
            event="onclick"
            submit="true"
            immediate="true" refreshMode="norefresh">
            <xp:this.parameters>
                <xp:parameter
                    name="param"
                    value="#{javascript:java.lang.System.currentTimeMillis()}">
                </xp:parameter>
            </xp:this.parameters>

                <xp:this.actionListeners>
                    <xp:actionListener type="ch.hasselba.xpages.MyActionListener" />
                </xp:this.actionListeners>
            </xp:eventHandler>

    </xp:button>

</xp:view>

You can see the result on the console when the button is clicked:

When doing the same for an action, you have to access the UIComponent from the actionEvent:

package ch.hasselba.xpages;
import java.util.List;
import javax.faces.event.ActionEvent;
import com.ibm.xsp.complex.Parameter;
import com.ibm.xsp.component.xp.XspEventHandler;

public class MyActionListener {
public void execute(ActionEvent event) {
        XspEventHandler eventHandler = (XspEventHandler) event.getComponent();
        List<Parameter> params = eventHandler.getParameters();
        for (Parameter p : params) {
            System.out.println(p.getName() + " -> " + p.getValue());
        }
     }
}

Here is an example XPage, the method execute is contained by the managed bean myAction:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:button
        value="Label"
            id="buttonAction">
        <xp:eventHandler
            event="onclick"
            submit="true"
            immediate="true"
            refreshMode="complete"
            actionListener="#{myAction.execute}">
            <xp:this.parameters>
                <xp:parameter
                    name="param"
                    value="#{javascript:java.lang.System.currentTimeMillis()}">
                </xp:parameter>
            </xp:this.parameters>
        
        </xp:eventHandler>
            
    </xp:button>
    
</xp:view>
Veröffentlicht unter Java, JSF, XPages | Verschlagwortet mit , , , , , | Ein Kommentar

Quick-n-Dirty: Disable Validation for FileDownload control (2)

Because my old snippet does not work anymore for ND9 (the IBM changed the internal methods / objects) I had to create a new way to disable the validation of the FileDownload control. Now I have switched to a PhaseListener which waits for to any XspEventHandler and checks if this event handler is a child of the FileDownload control. If so, it skips the validation.

Here is the code for the PhaseListener:

package ch.hasselba.xpages;

import java.util.Iterator;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import com.ibm.xsp.component.UIViewRootEx;
import com.ibm.xsp.component.xp.XspEventHandler;
import com.ibm.xsp.component.xp.XspFileDownload;
import com.ibm.xsp.context.FacesContextExImpl;
import com.ibm.xsp.util.FacesUtil;

public class FileDownloadPhaseListener implements PhaseListener {

    /**
     * FileDownloadPhaseListener
     * 
     * by Sven Hasselbach, 19.12.2014
     */
    private static final long serialVersionUID = 1L;

    public void afterPhase(PhaseEvent arg0) {}

    public void beforePhase(PhaseEvent arg0) {
        try{
            FacesContextExImpl fc = (FacesContextExImpl) FacesContext.getCurrentInstance();

            // get client id
            String clientId = FacesUtil.getHiddenFieldValue(fc); 

            // extract last id 
            String[] ids = clientId.split(":");
            String id = ids[ ids.length  - 1 ];

            // search for the component
            UIViewRootEx uiRoot = (UIViewRootEx) fc.getViewRoot();
            UIComponent cmp = FacesUtil.getComponentFor(uiRoot,  id );

            if( cmp instanceof XspEventHandler ){

                while( cmp != null ){

                    // found the download control?
                    if( cmp instanceof XspFileDownload ){
                        // disable validation & quit the process
                        fc.setDisableValidators( true );
                        break;
                    }
                    // climb up the component tree    
                    cmp = cmp.getParent();
                }
            }

        }catch(Exception e){}
    }

    public PhaseId getPhaseId() {
        return PhaseId.PROCESS_VALIDATIONS;
    }
}

When using it on a XPage…

… you can now delete a File …

… but cannot submit a form with the required field:

This is the required faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <lifecycle>
    <phase-listener>ch.hasselba.xpages.FileDownloadPhaseListener</phase-listener>
  </lifecycle>
</faces-config>

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter Java, JSF, XPages | Verschlagwortet mit , , , , | Schreib einen Kommentar

Quick-n-Dirty: Use „Old-School“ Javacode in your XPages-Application

Because I often have read that it is not possible to access „Old School“ Java elements in XPages, but never understood the reason why, I have written my own class loader for fun to demonstrate how to do this:

1. I have created a simple Java library:

2. The library contains a single class „LoadedClass„:

The class is very simple. When it is instantiated, it just prompts the current timestamp to the server console:

package ch.hasselba.test;

public class LoadedClass { 

    public LoadedClass(){
        System.out.println("Time: " + System.currentTimeMillis() );
    }
}

3. If you inspecting the Java design element, you will notice a attachment with the name %%object.jar%%. That’s the compiled library:

And here is the UNID of the design element for accessing it

4. Now it is time for the class loader (the modified code originally came from Laurentiu Cristofor):

package ch.hasselba.demo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

/**
 * 
 * This class implements a simple class loader that can be used to load at
 * runtime classes contained in a JAR file.
 * 
 * (P)2000 Laurentiu Cristofor
 * 
 * Modified 2014 by Sven Hasselbach
 * 
 */
public class JarClassLoader extends ClassLoader {
    private Hashtable jarContents;

    /**
     * Creates a new JarClassLoader that will allow the loading of classes
     * stored in a jar file.
     * 
     * @param bos
     *            ByteArrayOutputStream containing a Jar
     * @exception IOException
     *                an error happened while reading the contents of the jar
     *                file
     */
    public JarClassLoader(ByteArrayOutputStream bos) throws IOException {

        // second get contents of the jar file and place each
        // entry in a hashtable for later use
        jarContents = new Hashtable();

        JarInputStream jis = new JarInputStream(new ByteArrayInputStream(bos
                .toByteArray()));

        JarEntry je;
        while ((je = jis.getNextJarEntry()) != null) {
            // make sure we have only slashes, we prefer unix, not windows
            String name = je.getName().replace('\\', '/');

            // get entry size from the entry or from our hashtable
            int size = bos.size();
            // read the entry
            byte[] ba = new byte[size];
            int bytes_read = 0;
            while (bytes_read != size) {
                int r = jis.read(ba, bytes_read, size - bytes_read);
                if (r < 0)
                    break;
                bytes_read += r;
            }

            jarContents.put(name, ba);
        }

        jis.close();
    }

    /**
     * Looks among the contents of the jar file (cached in memory) and tries to
     * find and define a class, given its name.
     * 
     * @param className
     *            the name of the class
     * @return a Class object representing our class
     * @exception ClassNotFoundException
     *                the jar file did not contain a class named
     *                <code>className</code>
     */
    public Class findClass(String className) throws ClassNotFoundException {
        // transform the name to a path
        String classPath = className.replace('.', '/') + ".class";

        byte[] classBytes = (byte[]) jarContents.get(classPath);

        if (classBytes == null)
            throw new ClassNotFoundException();

        return defineClass(className, classBytes, 0, classBytes.length);
    }
}

The trick is to ignore the size of the JarEntries and use the size of the whole ByteArrayOutputStream.

5. Now we need to load the design element, extract the Jar and load our test class:

package ch.hasselba.demo;

import ch.hasselba.demo.JarClassLoader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.EmbeddedObject;
import lotus.domino.Session;

public class JavaLoader {

    public JavaLoader(Session session) {
        try {

            Database db = session.getCurrentDatabase();

            // get the design document
            Document doc = db
                    .getDocumentByUNID("6783F550459C81B1C1257C7E007A5D44");

            // get the attachment
            EmbeddedObject embObj = doc.getAttachment("%%object%%.jar");
            FileInputStream fis = (FileInputStream) embObj.getInputStream();

            // convert the FileInputStream to a ByteArrayOutputStream
            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            int data;
            while ((data = fis.read()) != (-1)) {
                bos.write(data);
            }
            bos.flush();

            // load the Jar with the class loader
            JarClassLoader jcl = new JarClassLoader(bos);

            // get the class
            Class loadedClass = jcl.findClass("ch.hasselba.test.LoadedClass");

            // and instantiate it
            Object obj = loadedClass.newInstance();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6. Last but not least, a XPage for demonstrating how to use the code:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:button value="Label" id="button1">
        <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
            <xp:this.action>
                <![CDATA[#{javascript:
                    importPackage( ch.hasselba.demo );
                    new ch.hasselba.demo.JavaLoader(session);}]]>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>

</xp:view>

7. If you open the XPage and click the button…

… you can see the result on the server console:

If you want to access the methods and/or properties of the loaded object, you have do do this with reflection.

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter Java, XPages | Verschlagwortet mit , , , | Ein Kommentar

XPages: Modify the File Selection Button

With Dojo, you can easily customize the file upload button, f.e. to change the label, add additional style sheets or use HTML5 or Flash plugin for uploading.

Here is basic example which adds a label and a blue border around the button:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:this.data>
        <xp:dominoDocument var="document1" formName="File" />
    </xp:this.data>

    <xp:scriptBlock id="scriptBlockFileUpload">
        <xp:this.value>
            <![CDATA[
                require(['dojo/parser',
                         'dojox/form/Uploader',
                         'dojo/domReady!'], 
                         function(parser, ready){
                             parser.parse().then(function(){
                                uploader = new dojox.form.Uploader({
                                    name:'#{id:fileUpload1}', 
                                    label:'Select Some Files',
                                    multiple:false, 
                                    style: 'border:5px solid blue;'
                                }, '#{id:fileUpload1}');

                                uploader.startup();
                             });
                         });
            ]]>
        </xp:this.value>
    </xp:scriptBlock>

    <xp:fileUpload id="fileUpload1"
        value="#{document1.Body}">
    </xp:fileUpload>

    <xp:button value="Submit" id="buttonSubmit" >
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="complete" immediate="false" save="true" />
    </xp:button>

</xp:view>

Have a look at dojox.form.Uploader for more details.

Veröffentlicht unter CSS, Dojo Toolkit, HTML, HTML5, Java Script, Web, XPages | Verschlagwortet mit , , , , , , , | Schreib einen Kommentar

Quick-n-Dirty: How to use the NAPI in a Java Agent

The undocumented NAPI for direct API calls in Java can easily used in normal agents. You just need to add two jar files to your java agent:

  • lwpd.commons.jar
  • lwpd.domino.napi.jar

These files can be found in <Notes>\osgi\shared\eclipse\plugins.

After importing, you do lot of funny things, f.e. opening a Session in Full Administration Mode, etc.

NotesSession nSession = new NotesSession();
nSession.setFullAdminMode( "SERVERNAME", true );

But don’t forget: These calls are native API calls and can easily crash your system.

P.S. Keep in mind that this article has been posted in the “Quick-n-Dirty” category.

Veröffentlicht unter Agenten, Java | Verschlagwortet mit , | Ein Kommentar

Das Unitymedia-Fiasko

Es begann im Frühjahr 2013, als der bestehende DSL-Vertrag in meinem damaligen Büro auslief, und ich – in weiser Vorraussicht, demnächst in meinem Haus Kabelanschluss zu benötigen – vorübergehend in mein Büro einen Internetanschluss von Unitymedia legen ließ. Da ich bisher keine Berührungspunkte mit Unitymedia hatte und Telefon sowie HD-Fernsehen haben wollte, kam mir das 3play Angebot gerade recht: 100 Mbit Down / 2,5 Mbit Up, Telefon-Flat, und demnächst dann HD-Fernsehen zu Hause, hörte sich gut an – und es lief auch alles vorbildlich:

Der Techniker war für 9:00 Uhr angekündigt, kam pünktlich, schloss die Fritzbox an, und am Mittag hatte ich Internet. Dann kam der erste Remote-Zugriff vom Smartphone auf meine Remoteoberfläche und -schwupps- konnte ich von außen nicht auf die Box zugreifen. Denn für den Anschluss gibt es keine IPv4 – Adressen mehr, nur noch IPv6. Die IPv4-Daten werden über einen zentralen Proxy nach draußen gejagt, was zwar nett für Filesharer ist, doch die Steuerung der KNX-Haustechnik via Smartphone etwas aufwendiger gestaltet.

Nun, es gibt schlimmeres, und so wartete ich darauf, dass meine Telefonnummern zu Unitymedia portiert werden. Das war Anfang April. Nach ein paar Rückfragen zwischen dem alten Anbieter und dem Neuen war es dann Mitte Juli auch endlich soweit! Unitymedia verkündete die stolze Nachricht, dass die Nummern portiert werden konnte. Wer schuld an der Verzögerung war, der alte Anbieter oder der neue, wird wohl nie restlos aufgeklärt werden.

Ende Juni zog ich aus meinem Büro aus, und beauftragte den Umzugsservice, damit die Leitung in meinem neuen Zuhause geschaltet werden kann – das klappte wieder reibungslos. Allerdings gelang es mir daraufhin nicht mehr, mich im Unitymedia-Portal einzuloggen, denn ein dubioser Serverfehler verwerte mir den Zugriff – so blieb mir ein Einblick über Rechnungen, gebuchte Pakete, usw. erstmal verwehrt.

Es verging in Weile, und zugegebener Maßen war ich mit meinem Umzug in unser neues Büro und der Sanierung meines Hauses „etwas“ ausgelastet, um dem Thema auf den Zahn zu fühlen. Doch aufgeschoben ist nicht aufgehoben, und in einem wachen Moment fiel mir auf, dass sich meine Kundennummer auf einem der Briefköpfe stillschweigend geändert hatte. Und kaum gab ich diese Nummer im Kundenportal an, da hatte ich auch schon wieder Zugriff auf meine Daten. Nur die bisherigen Rechnungen, die es nur Online gibt, waren weg – gehörten ja auch zu einer anderen Kundennummer…

Wenigstens erhielt ich die erste Rechung auf der neuen Kundennummer, und in dieser wurde mir ein sinnfreies Sicherheitspaket für 4€  monatlich berechnet, dass ich bei der Erstbestellung definitiv ausgeschlossen hatte (wird einem dummerweise untergeschoben, da es die ersten drei Monate gratis ist, und erst danach berechnet wird). Aber in einem der folgenden Telefonate mit Unitymedia habe ich es dann geschafft, das Paket wieder abzubestellen und die bereits berechneten Kosten wurden wieder gut geschrieben.

Der aufmerksame Leser wird es bemerkt haben: Ich schrieb „folgende Telefonate“!

Aus steuerlichen Gründen benötige ich die alten Abrechnungen, und zwei Aufforderung per Fax, eine per Kontaktformular und eine per eMail hatten noch nicht ausgereicht, mir die alten Rechnungen kostenfrei zukommen zu lassen. Hinzu kommt der Umstand, dass man bei der Unitymedia-Hotline Glück haben muss, einen hilfsbereiten Mitarbeiter zu erwischen, der auch noch eine gewisse Kenntniss der Unitymedia-internen Abläufe hat. Meist sind die Mitarbeiter am anderen Ende des Telefons bemüht, aber eher im Sinne der Formulierung in Arbeitszeugnissen. Doch mit der nötigen Hartnäckigkeit gelang es mir, alle Rechnungen zu erhalten, in fünf einzelnen Briefen, so dass ich an dieses Thema einen Haken machen konnte.

Anfang Oktober wollte ich dann den im 3play-Paket enthaltene HD-Rekorder endlich in Betrieb nehmen, der bisher ungenutzt und unausgepackt zwischen den Umzugskartons stand. Zwar lief das Gerät, doch Aufnahmen waren nicht möglich, denn die Festplatte war kaputt. OK, kann ja mal passieren, die Hotlinenummer kannte ich ja bereits auswendig.

Der Mann am Telefon empfahl mir, gleich auf das neue Flagschiff, den Horizon-Rekorder, auszuweichen. Da damit ein Upgrade der Internetverbindung mit echter IPv4-Adresse verbunden war, willigte ich ein (und konnte so die Lösung mit dem VPN-Tunnel über zwei gefreezte Fritzboxen über die Leitung im Büro wieder ad acta legen)..

Es dauerte leider eine Weile, ca. zwei Wochen, dann kam das Pakte mit dem Horizon-Rekorder. Schnell ausgepackt, angeschlossen und… Fehlercode 1020. Bitte kontaktieren Sie die Service-Hotline. „Ja, hier im System steht, dass die Box erst am 22. Oktober freigeschaltet wird.“. Wäre clever gewesen, diese Information dem Kunden mitzugeben, dann hätte ich mir den Anruf sparen können. War aber auch egal, denn am 22. lief das Gerät natürlich nicht. Einige Telefonate später wurde dann ein Techniker auf den Weg geschickt, der die Box dann ein paar Tage später in Betrieb nahm.

Ganz so einfach ist es dann doch nicht, komplett in Betrieb genommen hört sich so funktional an! Aber leider konnte ich noch nicht per WLAN ins Internet, trotz inkludierter WLAN-Option. Macht ja nix, schnell die Kurzwahltaste am Telefon gedrückt (die Nummer der Hotline ist mittlerweile eingespeichert), und ein Anruf später konnte mir auch geholfen werden (Die Standardlösung, die nie funktioniert, ist übrigens, erstmal die Box zu reseten, indem die Einschalttaste vorne rechts beim Hochfahren der Box so lange gedrückt wird, bis der Willkommensbildschirm zu zweiten Mal angezeigt wird).

„Ja, hier im System haben die Kollegen da was falsch gebucht. Also Sie haben ja die Telefonkomfort-Funktion, und drei Telefonnummern gehen mit der Box ja gar nicht. Sondern nur eine. Da kann das mit dem Internet ja gar nicht gehen. Welche Nummer wollen Sie denn behalten?“. Da die Nummern durch den etwas längeren Transfer sowieso nicht mehr beruflich von mir genutzt wurden, war mir das dann auch egal.

Nun, jetzt lief die Box. Knapp einen Monat hatte ich Ruhe. Obwohl „Ruhe“ ja ein relativer Begriff ist, denn die Box ist alles andere als ruhig. Der Lüfter könnte deutlich leiser sein, insbesondere wenn man kein TV sieht. Das Nicht-Abschalten ist deshalb nötig, da die Box, sobald sie in den Stromsparmodus geht, auch das WLAN ausschaltet.

Also deaktiviert man den Stromsparmodus, und stellt dann fest, dass man vom IPad aus nicht mehr drucken kann. Denn AirPrint kann die Box nicht (ist ja auch nur so ein exotisches Protokoll ohne größere Verbreitung im Consumer-Bereich). Und wird es auch in näherer Zukunft nicht können.

Da die Box anfing, in unregelmäßigen Abständen abzustürzen (aber mindestens einmal täglich), trat dieses Problem wieder in den Hintergrund, und ich orderte eine zweite Horizon-Box Anfang Dezember. Die kam dann auch prompt am 10.01.2014, und ich musste diesmal nur zwei Mal hinterher telefonieren.

Ich hatte jedoch aus der Inbetriebnahme gelernt, und war nicht zu euphorisch, als ich die neue Box angeschlossen habe. Und – völlig überraschend – kam wieder der Fehlercode 1020. „Ja, die Box wird erst Ende Januar aktiviert.“, so war die anfängliche Aussage der Hotline. Ich redete der Dame am Telefon gut zu, so dass ich eine Gutschrift über 20€ erhielt und die Box in den nächste drei Stunden aktiviert werden sollte. Das war Samstag Mittag. Drei Stunden später war die Box immer noch tot, allerdings mit dem Fehlercode 1090. Und die alte Box deaktiviert. Kein Internet mehr.

Zwei Hotline-Anrufe später wusste ich dann, woran es lag: Das Kundenkonto war irgendwie komisch eingerichtet (das ist die zweite Lösung bei Problemen: Die Kollegen haben da irgendwas im Kundenkonto… so kann das ja gar nicht funktionieren… blabla). Leider ist Wochenende, das kann keiner ändern, erst am Montag wieder.

Seit Montag läuft die Box. Auch das Internet läuft. Und in der Nacht von Montag auf Dienstag  ist die neue Horizon-Box zum ersten Mal abgestürzt…

P.S.

Ich habe zwar die alte Hardware (Fritzbox, Smartcard, kaputter HD-Rekorder) eingeschickt, aber Unitymedia schickt mir trotzdem regelmäßig Rückgabeaufforderungen zu. Auch zu Smartcards, die ich nie hatte…

Veröffentlicht unter Allgemein | Ein Kommentar

Java: Reflection Madness

A very interesting presentation about reflection in Java: Reflection Madness from Dr. Heinz M. Kabutz. You can find the slides here.

Veröffentlicht unter Java | Verschlagwortet mit , | Schreib einen Kommentar

A productive period: cyccle.net

During the last weeks I started to think about my next steps leaving the yellow bubble. While I am still inside the bubble (and available for developement work, just contact me), I am in a very productive period: I continued to finalize the cyccle project.

I started the development in 2012, but had a lot of breaks and was unable to finish the work. But in the next weeks, I am launching a new company and the beta phase will begin.

Stay tuned…

Veröffentlicht unter Allgemein, cyccle.net | Verschlagwortet mit | Ein Kommentar