Skip to content


janettxt

We’ve been using janettxt this academic year to allow our academics to send text messages to students. This has been very successful. Students can choose to give our database their mobile number, and the lecturer can send an SMS message out to all students with registered numbers. The staff can’t get at the numbers. The message is also emailed (in case of SMS failure, or just the student not wanting to give us their number), and appended to the blog for that module.

There’s also an option to send emails, which also are appended to the module blog. This is all very painless, as there’s an tab on each module homepage which is only visible to staff allowing both these options. The email option rapidly gained a “cc to module leaders/teachers” option and a “cc to email xyz” option; many staff requested this option.

Any staff can send to any module, as it’s all logged and a closed group this allows maximum flexibility.

The costs are 4p per message per recipient, negotiated by JANET.

I have found working with the pageone service, however, frustrating.

  • They provide no mechanism for checking your remaining balance with them, so you could run out unexpectedly.
    • If anyone needs it, I wrote a nagios plugin that screen scrapes their web-admin console for this information.
  • The SOAP interface is needlessly complex to work with and provides no value over REST.
  • They allow multiple username/passwords per customer, but on ringing up they told me that to enable a second username/password for the SOAP interface would be £250 set up plus £50 per month. All I wanted was to be able to let a research project use our balance and to keep track of what they used separately. I’m going to assume they will use less than the 15,000 SMS messages a year the 2nd SOAP account would get me.
    • £300 of staff time would allow me to build a local REST gateway to their SOAP interface with our own auditing to account who uses it for what.
    • But I think, for now, we’ll just all use the same username/password and I’ll trust this project not to over do it.
  • There did not appear to be any indication of SOAP enabled yes/no via their web admin. interface.
  • When I rang them, they asked for my account username and password so they could look at my account. This makes me worry about their security practices as this is baffling.
  • You bulk-buy your quota of messages, and lose any unused ones after 12 months, which is a bit annoying as you have to guess in advance your likely use, and most people will either over-estimate or end up in a situation where messages are failing to go out until they notice and buy more quota.

It’s possible that there’s been a communication error, and I’m only posting the above notes to encourage pageone to pick up its game or clarify things.

I also should acknowledge that I’m a low-volume account holder, who asks difficult technical questions. The staff have always been polite and tried to help me solve my problems.

The above represents my current understanding from phone conversations with their support staff and my direct experience. I am not recommending against using their service, just documenting my frustrations. We have no plans to stop using their service at this time.

If I have made factual errors, they are unintentional and I will make corrections to the above article. cjg@ecs.soton.ac.uk

Posted in Intranet.


One Response

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. xiaoyu chen says

    I totally agree the PageOne service is not a well-support service, in particular for developers who would like to use PageOne text messaging service for application project. I’ve been struggling with interaction to PageOne service in Grails environment and using Groovy language. Finally I make it work, here is the solution to share:

    Environment: Grails 1.2
    Plugins: Spring Web service 0.5.1
    Service name: PageOneSMSService
    Configuration:
    in the Project_Dir/web-app/WEB-INF/applicationContext.xml, add following lines

    Here I am using CommonsHttpMessageSender, which is delegate to the third-party apache commons-client lib (download from http://hc.apache.org/httpclient-3.x/ and put .jar into your application classpath, the /lib directory in Grails for example).

    Code:

    ——-PageOneSMSService.groovy———-

    /**
    * authentication method invokes remote PageOne service to login and returns a session-id that is used for sending message
    */

    String authenticate(String username, String psw) {

    WebServiceTemplate template = getDefaultWebServiceTemplate ()

    String session = template.sendAndReceive(
    //source,
    new AuthenticationMessageCallback(userId:username, psw:psw),
    new AuthenticationMessageExtractor()).toString()

    print “session is obtained as : \t” + session

    return session

    }

    def String sendMessage (String content, String contact)
    {

    WebServiceTemplate template = getDefaultWebServiceTemplate ()

    print template.sendAndReceive (
    new SendMessageCallback(textMsg:content, session:authenticate(), numbers:contact),
    new SendMessageExtractor()).toString()
    }

    }

    ——————AuthenticationMessageCallback————-

    class AuthenticationMessageCallback implements WebServiceMessageCallback{

    String userId;
    String pwd;

    def void doWithMessage(WebServiceMessage message)
    {

    final String MESSAGE =
    “” +
    “” +
    “” +
    “$userId” +
    “$pwd” +
    “” +
    “”+
    “”;

    StringReader reader = new StringReader (MESSAGE)

    Source source = new StreamSource (reader)

    final TransformerFactory factory =TransformerFactory.newInstance();

    Transformer transformer = factory.newTransformer();

    transformer.transform(source, message.getPayloadResult())

    SoapMessage soap = (SoapMessage) message

    soap.setSoapAction(“login”);

    Source env = soap.getEnvelope().getSource ()

    //Source hdrSource = hdr.getSource ()

    StringWriter writer = new StringWriter ()
    StreamResult result = new StreamResult(writer);
    transformer.transform(env, result);
    print “request message -> “+writer.toString()+”\n”;
    }
    }

    ——————–AuthenticationMessageExtractor———————–

    class AuthenticationMessageExtractor implements WebServiceMessageExtractor{

    final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    final DocumentBuilder db = factory.newDocumentBuilder();

    def Object extractData(WebServiceMessage message) throws IOException, TransformerException
    {
    final TransformerFactory factory =TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer();

    StringWriter writer = new StringWriter ()
    StreamResult result = new StreamResult(writer);
    SoapMessage response = (SoapMessage)message

    SoapEnvelope env = response.getEnvelope ()
    SoapHeader hdr = env.getHeader ()
    SoapBody byd = env.getBody ()

    //Source env = env.getSource()

    transformer.transform(env.getSource(), result);
    int status = getStatus (byd.getSource())
    String session = null

    if (status == 200)
    {
    session = getSession (hdr.getSource())
    }

    //print “status -> ” + getStatus (byd.getSource())
    //print “session -> ” + getSession (hdr.getSource())

    //print “response message -> “+writer.toString()+”\n”

    return session
    }

    int getStatus (Source source)
    {
    int status = 0

    Node node = ((DOMSource)source).getNode ()

    if (node.getNodeType() == Node.ELEMENT_NODE)
    {
    Element root = (Element) ((DOMSource)source).getNode ()

    NodeList statusEles = root.getElementsByTagName(“status”)

    if (statusEles!=null && statusEles.getLength()>0)
    {
    Element statusEle = root.getElementsByTagName(“status”).item (0)

    if (statusEle != null)
    status = new Integer(statusEle.getTextContent()).intValue()
    }
    }

    return status
    }

    String getSession (Source source)
    {
    String session = null

    Node node = ((DOMSource)source).getNode ()

    if (node.getNodeType() == Node.ELEMENT_NODE)
    {
    Element root = (Element) node

    NodeList sessionEles = root.getElementsByTagName(“session-id”)

    if (sessionEles!=null && sessionEles.getLength()>0)
    {
    Element sessionEle = (Element) sessionEles.item(0)

    return sessionEle.getTextContent()
    }
    }

    return session
    }

    }

    ———————SendMessageCallback.groovy———————-
    class SendMessageCallback implements WebServiceMessageCallback{

    final TransformerFactory factory =TransformerFactory.newInstance();

    Transformer transformer = factory.newTransformer();

    String textMsg
    String numbers
    String session

    def void doWithMessage(WebServiceMessage message)
    {
    StringReader reader = new StringReader (composeRequestMessageBody())
    Source source = new StreamSource (reader)
    transformer.transform(source, message.getPayloadResult())

    SoapMessage soap = (SoapMessage) message
    soap.setSoapAction(“sendMessage”);

    reader = new StringReader (composeRequestMessageHeader())
    source = new StreamSource(reader)
    transformer.transform (source, soap.getSoapHeader().getResult())

    StringWriter writer = new StringWriter ()
    StreamResult result = new StreamResult(writer);
    transformer.transform(soap.getEnvelope().getSource (), result);
    print “send request message -> “+writer.toString()+”\n”;

    }

    def setTextMsg (String msg)
    {
    this.textMsg = msg
    }

    String composeRequestMessageBody ( )
    {
    String message = “” +
    “”+
    “”+
    “”+numbers+”” +
    “”+textMsg+””+
    “” +
    “”+
    “”
    return message
    }

    String composeRequestMessageHeader ()
    {
    return “”+session+””;
    }

    }

    —————SendMessageExtractor.groovy———-
    class SendMessageExtractor implements WebServiceMessageExtractor{

    final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    final DocumentBuilder db = factory.newDocumentBuilder();

    def Object extractData(WebServiceMessage message) throws IOException, TransformerException
    {
    final TransformerFactory factory =TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer();

    StringWriter writer = new StringWriter ()
    StreamResult result = new StreamResult(writer);
    SoapMessage response = (SoapMessage)message

    SoapEnvelope env = response.getEnvelope ()

    SoapBody byd = env.getBody ()

    //Source env = env.getSource()

    transformer.transform(env.getSource(), result);

    //print “status -> ” + getStatus (byd.getSource())
    //print “session -> ” + getSession (hdr.getSource())

    print “response message -> “+writer.toString()+”\n”

    return getStatus (byd.getSource())
    }

    int getStatus (Source source)
    {
    int status = 0

    Node node = ((DOMSource)source).getNode ()

    if (node.getNodeType() == Node.ELEMENT_NODE)
    {
    Element root = (Element) ((DOMSource)source).getNode ()

    NodeList statusEles = root.getElementsByTagName(“status”)

    if (statusEles!=null && statusEles.getLength()>0)
    {
    Element statusEle = root.getElementsByTagName(“status”).item (0)

    if (statusEle != null)
    status = new Integer(statusEle.getTextContent()).intValue()
    }
    }

    return status
    }

    }

    End.

    PS: for more information, please email me and have a discuss. I hope this helps.



Some HTML is OK

or, reply to this post via trackback.