连接到GCM CCS时出现java.lang.ExceptionInInitializerError

时间:2014-10-13 17:28:23

标签: google-app-engine xmpp google-cloud-messaging google-cloud-endpoints smack

我是GCM世界的新手。 我已按照链接[https://developer.android.com/google/gcm/ccs.html#smack][1]

中的说明创建了XMPP GCM服务器

我已经修改了一些代码,为方法 prepareClient创建一个端点作为connectXmpServer 。此方法建立与CCS的连接。我在移动设备上从我的客户端调用此端点。

这是我的服务器代码 -

public class SmackCcsClientEndpoint {

private static final Logger logger = Logger.getLogger("SmackCcsClientEndpoint");

private static final String GCM_SERVER = "gcm.googleapis.com";
private static final int GCM_PORT = 5236;

private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";

private static SmackCcsClientEndpoint sInstance = null;
private String mApiKey = null;
private String mProjectId = null;
private boolean mDebuggable = false;

public static SmackCcsClientEndpoint getInstance() {
    if (sInstance == null) {
        throw new IllegalStateException("You have to prepare the client first");
    }
    return sInstance;
}

@ApiMethod(name = "connectXmpServer")
public void prepareClient(@Named("senderId") String projectId, @Named("apiKey") String apiKey,
                          @Named("debuggable") boolean debuggable)
        throws XMPPException, IOException, SmackException {
    synchronized(SmackCcsClientEndpoint.class) {
        if (sInstance == null) {
            sInstance = new SmackCcsClientEndpoint(projectId, apiKey, debuggable);
        }
    }
    sInstance.connect();
}

private SmackCcsClientEndpoint(String projectId, String apiKey, boolean debuggable) {
    this();
    mApiKey = apiKey;
    mProjectId = projectId;
    mDebuggable = debuggable;
}

public SmackCcsClientEndpoint() {

    ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
            new PacketExtensionProvider() {
                @Override
                public PacketExtension parseExtension(XmlPullParser parser) throws
                        Exception {
                    // TODO check nextText method
                    logger.severe("Before parse");
                    String json = parser.getText();
                    logger.severe("After parse");
                    return new GcmPacketExtension(json);
                }
            });
}

private XMPPConnection connection;

/**
 * Indicates whether the connection is in draining state, which means that it
 * will not accept any new downstream messages.
 */
protected volatile boolean connectionDraining = false;

/**
 * Sends a downstream message to GCM.
 *
 * @return true if the message has been successfully sent.
 */
private boolean sendDownstreamMessage(String jsonRequest) throws
        NotConnectedException {
    if (!connectionDraining) {
        send(jsonRequest);
        return true;
    }
    logger.info("Dropping downstream message since the connection is draining");
    return false;
}

/**
 * Returns a random message id to uniquely identify a message.
 *
 * <p>Note: This is generated by a pseudo random number generator for
 * illustration purpose, and is not guaranteed to be unique.
 */
private String nextMessageId() {
    //TODO get unique ID
    return "m-" + UUID.randomUUID().toString();
}

/**
 * Sends a packet with contents provided.
 */
protected void send(String jsonRequest) throws NotConnectedException {
    Packet request = new GcmPacketExtension(jsonRequest).toPacket();
    connection.sendPacket(request);
}

/**
 * Handles an upstream data message from a device application.
 *
 * <p>This sample echo server sends an echo message back to the device.
 * Subclasses should override this method to properly process upstream messages.
 */
protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
    // PackageName of the application that sent this message.
    String category = (String) jsonObject.get("category");
    String from = (String) jsonObject.get("from");
    @SuppressWarnings("unchecked")
    Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
    payload.put("ECHO", "Application: " + category);

    // Send an ECHO response back
    String echo = createJsonMessage(from, nextMessageId(), payload,
            "echo:CollapseKey", null, false);

    try {
        sendDownstreamMessage(echo);
    } catch (NotConnectedException e) {
        logger.log(Level.WARNING, "Not connected anymore, echo message is not sent", e);
    }
}

/**
 * Handles an ACK.
 *
 * <p>Logs a INFO message, but subclasses could override it to
 * properly handle ACKs.
 */
protected void handleAckReceipt(Map<String, Object> jsonObject) {
    String messageId = (String) jsonObject.get("message_id");
    String from = (String) jsonObject.get("from");
    logger.log(Level.INFO, "handleAckReceipt() from: " + from + ", messageId: " + messageId);
}

/**
 * Handles a NACK.
 *
 * <p>Logs a INFO message, but subclasses could override it to
 * properly handle NACKs.
 */
protected void handleNackReceipt(Map<String, Object> jsonObject) {
    String messageId = (String) jsonObject.get("message_id");
    String from = (String) jsonObject.get("from");
    logger.log(Level.INFO, "handleNackReceipt() from: " + from + ", messageId: " + messageId);
}

protected void handleControlMessage(Map<String, Object> jsonObject) {
    logger.log(Level.INFO, "handleControlMessage(): " + jsonObject);
    String controlType = (String) jsonObject.get("control_type");
    if ("CONNECTION_DRAINING".equals(controlType)) {
        connectionDraining = true;
    } else {
        logger.log(Level.INFO, "Unrecognized control type: %s. ",
        controlType);
    }
}

/**
 * Creates a JSON encoded GCM message.
 *
 * @param to RegistrationId of the target device (Required).
 * @param messageId Unique messageId for which CCS will send an
 *         "ack/nack" (Required).
 * @param payload Message content intended for the application. (Optional).
 * @param collapseKey GCM collapse_key parameter (Optional).
 * @param timeToLive GCM time_to_live parameter (Optional).
 * @param delayWhileIdle GCM delay_while_idle parameter (Optional).
 * @return JSON encoded GCM message.
 */
public static String createJsonMessage(String to, String messageId,
                                       Map<String, String> payload, String collapseKey, Long timeToLive,
                                       Boolean delayWhileIdle) {
    Map<String, Object> message = new HashMap<String, Object>();
    message.put("to", to);
    if (collapseKey != null) {
        message.put("collapse_key", collapseKey);
    }
    if (timeToLive != null) {
        message.put("time_to_live", timeToLive);
    }
    if (delayWhileIdle != null && delayWhileIdle) {
        message.put("delay_while_idle", true);
    }
    message.put("message_id", messageId);
    message.put("data", payload);
    return JSONValue.toJSONString(message);
}

/**
 * Creates a JSON encoded ACK message for an upstream message received
 * from an application.
 *
 * @param to RegistrationId of the device who sent the upstream message.
 * @param messageId messageId of the upstream message to be acknowledged to CCS.
 * @return JSON encoded ack.
 */
protected static String createJsonAck(String to, String messageId) {
    Map<String, Object> message = new HashMap<String, Object>();
    message.put("message_type", "ack");
    message.put("to", to);
    message.put("message_id", messageId);
    return JSONValue.toJSONString(message);
}

/**
 * Connects to GCM Cloud Connection Server using the supplied credentials.
 *
 */
public void connect()throws XMPPException, IOException, SmackException {
    ConnectionConfiguration config =
            new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
    config.setSecurityMode(SecurityMode.enabled);
    config.setReconnectionAllowed(true);
    config.setRosterLoadedAtLogin(false);
    config.setSendPresence(false);
    config.setSocketFactory(SSLSocketFactory.getDefault());

    connection = new XMPPTCPConnection(config);
    connection.connect();

    connection.addConnectionListener(new LoggingConnectionListener());

    // Handle incoming packets
    connection.addPacketListener(new PacketListener() {

        @Override
        public void processPacket(Packet packet) {
            logger.log(Level.INFO, "Received: " + packet.toXML());
            Message incomingMessage = (Message) packet;
            GcmPacketExtension gcmPacket =
                    (GcmPacketExtension) incomingMessage.
                            getExtension(GCM_NAMESPACE);
            String json = gcmPacket.getJson();
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> jsonObject =
                        (Map<String, Object>) JSONValue.
                                parseWithException(json);

                // present for "ack"/"nack", null otherwise
                Object messageType = jsonObject.get("message_type");

                if (messageType == null) {
                    // Normal upstream data message
                    handleUpstreamMessage(jsonObject);

                    // Send ACK to CCS
                    String messageId = (String) jsonObject.get("message_id");
                    String from = (String) jsonObject.get("from");
                    String ack = createJsonAck(from, messageId);
                    send(ack);
                } else if ("ack".equals(messageType.toString())) {
                    // Process Ack
                    handleAckReceipt(jsonObject);
                } else if ("nack".equals(messageType.toString())) {
                    // Process Nack
                    handleNackReceipt(jsonObject);
                } else if ("control".equals(messageType.toString())) {
                    // Process control message
                    handleControlMessage(jsonObject);
                } else {
                    logger.log(Level.WARNING,
                            "Unrecognized message type (%s)",
                            messageType.toString());
                }
            } catch (ParseException e) {
                logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Failed to process packet", e);
            }
        }
    }, new PacketTypeFilter(Message.class));

    // Log all outgoing packets
    connection.addPacketInterceptor(new PacketInterceptor() {
        @Override
        public void interceptPacket(Packet packet) {
            logger.log(Level.INFO, "Sent: {0}", packet.toXML());
        }
    }, new PacketTypeFilter(Message.class));

    connection.login(mProjectId + "@gcm.googleapis.com", mApiKey);
}


/**
 * XMPP Packet Extension for GCM Cloud Connection Server.
 */
private static final class GcmPacketExtension extends DefaultPacketExtension {

    private final String json;

    public GcmPacketExtension(String json) {
        super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
        this.json = json;
    }

    public String getJson() {
        return json;
    }

    @Override
    public String toXML() {
        return String.format("<%s xmlns=\"%s\">%s</%s>",
                GCM_ELEMENT_NAME, GCM_NAMESPACE,
                StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
    }

    public Packet toPacket() {
        Message message = new Message();
        message.addExtension(this);
        return message;
    }
}

private static final class LoggingConnectionListener
        implements ConnectionListener {

    @Override
    public void connected(XMPPConnection xmppConnection) {
        logger.info("Connected.");
    }

    @Override
    public void authenticated(XMPPConnection xmppConnection) {
        logger.info("Authenticated.");
    }

    @Override
    public void reconnectionSuccessful() {
        logger.info("Reconnecting..");
    }

    @Override
    public void reconnectionFailed(Exception e) {
        logger.log(Level.INFO, "Reconnection failed.. ", e);
    }

    @Override
    public void reconnectingIn(int seconds) {
        logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
    }

    @Override
    public void connectionClosedOnError(Exception e) {
        logger.info("Connection closed on error.");
    }

    @Override
    public void connectionClosed() {
        logger.info("Connection closed.");
    }
}
}

在客户端,我在Asynctask中调用端点 -

protected String doInBackground(Object... params) {
    if(messageService == null){
        SmackAppServer.Builder builder = new SmackAppServer.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null);
        messageService = builder.build();
        try {
            messageService.connectXmpServer(gcmSenderId, password, true).execute();
        } catch (IOException e) {
            e.printStackTrace();
            String logMsg;
            logMsg = "Error while connecting : " + e.getMessage();
            return logMsg;
        }
    }

    context = (Context)params[0];
    message = (String)params[1];

    if (gcm == null) {
        gcm = GoogleCloudMessaging.getInstance(context);
    }

    String logMsg;
/*    if(!connected) {
        try {
            messageService.connect(senderId, password).execute();
            connected = true;
            logMsg = "Connection established!";
        } catch (IOException e) {
            e.printStackTrace();
            logMsg = "Error while connecting! " + e.getMessage();
            return logMsg;
        }
    }*/

    String id = Integer.toString(msgId.incrementAndGet());
    Bundle data = new Bundle();
    // Bundle data consists of a key-value pair
    data.putString("message", message);
    // "time to live" parameter
    // This is optional. It specifies a value in seconds up to 24 hours.

    try {
        gcm.send(gcmSenderId + "@gcm.googleapis.com", id, data);
        logMsg = "Message sent!";
    } catch (IOException e) {
        e.printStackTrace();
        logMsg = "Error while sending message! " + e.getMessage();
    }
    return logMsg;
}

然而,此调用会导致异常。我不知道这里出了什么问题。

com.google.api.server.spi.SystemService invokeServiceMethod: null
java.lang.ExceptionInInitializerError
at org.jivesoftware.smack.ConnectionConfiguration.<init>(ConnectionConfiguration.java:67)
at   com.example.mymodule.XMPPServer.SmackCcsClientEndpoint.connect(SmackCcsClientEndpoint.java:255)
at com.example.mymodule.XMPPServer.SmackCcsClientEndpoint.prepareClient(SmackCcsClientEndpoint.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:45)
at com.google.api.server.spi.SystemService.invokeServiceMethod(SystemService.java:359)
at com.google.api.server.spi.SystemServiceServlet.execute(SystemServiceServlet.java:160)
at com.google.api.server.spi.SystemServiceServlet.doPost(SystemServiceServlet.java:118)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:438)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:445)
at com.google.tracing.CurrentContext.runInContext(CurrentContext.java:220)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:309)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:301)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:442)
at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.IllegalStateException: org.xmlpull.v1.XmlPullParserException: could not load any factory class (even small or full default implementation); nested exception is: 
org.kxml2.io.XmlReader
at org.jivesoftware.smack.SmackConfiguration.<clinit>(SmackConfiguration.java:158)
... 35 more
Caused by: org.xmlpull.v1.XmlPullParserException: could not load any factory class (even small or full default implementation); nested exception is: 
org.kxml2.io.XmlReader
at org.xmlpull.v1.XmlPullParserFactory.newInstance(XmlPullParserFactory.java:225)
at org.xmlpull.v1.XmlPullParserFactory.newInstance(XmlPullParserFactory.java:76)
at org.jivesoftware.smack.SmackConfiguration.processConfigFile(SmackConfiguration.java:352)
at org.jivesoftware.smack.SmackConfiguration.processConfigFile(SmackConfiguration.java:347)
at org.jivesoftware.smack.SmackConfiguration.<clinit>(SmackConfiguration.java:155)
... 35 more

在服务器端,原始代码段位于静态块内。但是 parser.nextText 给出了编译错误。我下载了最新的XMLPullParser jar并用 parser.getText 替换了这个调用。这可能是可能的原因吗?

原始服务器代码 -

static {

    ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
        new PacketExtensionProvider() {
            @Override
            public PacketExtension parseExtension(XmlPullParser parser) throws
                    Exception {
                String json = parser.nextText();
                return new GcmPacketExtension(json);
            }
        });
}

请帮帮我。我无法继续,因为我陷入了非常基本的水平。

此致 SHAILESH

2 个答案:

答案 0 :(得分:3)

我从XPP3下载了jar并将其添加到我的后端模块的Android studio中的依赖项中。这不再与Smack API捆绑在一起了。

答案 1 :(得分:0)

下载www.extreme.indiana.edu/dist/java-repository/xpp3/jars/xpp3-1.1.4c.jar并保留确切的行:String json = parser.nextText();