GCM XMPP - 启动 Web 应用程序服务器时出错
Posted
技术标签:
【中文标题】GCM XMPP - 启动 Web 应用程序服务器时出错【英文标题】:GCM XMPP - Error on start web application server 【发布时间】:2015-10-04 22:30:02 【问题描述】:我的后端 java 的目标是使用 GCM XMPP 接收移动消息。我的网络应用程序有 spring 4.1.4 和 smack 4.1.4。
Smack 依赖项:
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-tcp</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-extensions</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-java7</artifactId>
<version>4.0.1</version>
</dependency>
XMPP 连接的 Bean:
@Component("CcsClientImpl")
public class CcsClientImpl
private static final String GCM_ELEMENT_NAME = "gcm";
private static final String GCM_NAMESPACE = "google:mobile:data";
private static XMPPTCPConnection connection;
/**
* Indicates whether the connection is in draining state, which means that it
* will not accept any new downstream messages.
*/
protected static volatile boolean connectionDraining = false;
private static final Logger logger = LoggerFactory.getLogger("CcsClientImpl");
@Value("$sender.id")
private String mSenderId;
@Value("$server.api.key")
private String mServerApiKey;
@Value("$gcm.xmpp.host")
private String mHost;
@Value("$gcm.xmpp.port")
private int mPort;
@Value("$gcm.xmpp.debuggable")
private boolean mDebuggable;
@Autowired
private ProcessorFactory processorFactory;
//@Autowired
public CcsClientImpl()
ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE, new ExtensionElementProvider<ExtensionElement>()
@Override
public DefaultExtensionElement parse(XmlPullParser parser,int initialDepth) throws org.xmlpull.v1.XmlPullParserException, IOException
String json = parser.nextText();
return new GcmPacketExtension(json);
);
try
connect(mSenderId, mServerApiKey);
catch (XMPPException ex)
logger.error("ERRO AO CONECTAR COM GCM XMPP", ex);
catch (SmackException ex)
logger.error("ERRO AO CONECTAR COM GCM XMPP", ex);
catch (IOException ex)
logger.error("ERRO AO CONECTAR COM GCM XMPP", ex);
/**
* Sends a downstream message to GCM.
*
* @return true if the message has been successfully sent.
*/
public 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.
*/
public String nextMessageId()
return "m-" + UUID.randomUUID().toString();
/**
* Sends a packet with contents provided.
*/
protected void send(String jsonRequest) throws NotConnectedException
Stanza request = new GcmPacketExtension(jsonRequest).toPacket();
connection.sendStanza(request);
/// new: customized version of the standard handleIncomingDateMessage method
/**
* Handles an upstream data message from a device application.
*/
public void handleIncomingDataMessage(CcsMessage msg)
if (msg.getPayload().get("action") != null)
PayloadProcessor processor = processorFactory.getProcessor(msg.getPayload().get("action"));
processor.handleMessage(msg);
/**
* 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.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.info("handleNackReceipt() from: " + from + ",messageId: " + messageId);
protected void handleControlMessage(Map<String, Object> jsonObject)
logger.info("handleControlMessage(): " + jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType))
connectionDraining = true;
else
logger.info("Unrecognized control type: %s. This could happen if new features are " + "added to the CCS protocol.",
controlType);
/**
* Creates a JSON encoded GCM message.
*
* @param to RegistrationId of the target device (Required).
* @param messageId Unique messageId for which CCS sends 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.
*
* @param senderId Your GCM project number
* @param apiKey API Key of your project
*/
public void connect(String senderId, String serverApiKey)
throws XMPPException, IOException, SmackException
XMPPTCPConnectionConfiguration config =
XMPPTCPConnectionConfiguration.builder()
.setServiceName(mHost)
.setHost(mHost)
.setCompressionEnabled(false)
.setPort(mPort)
.setConnectTimeout(30000)
.setSecurityMode(SecurityMode.disabled)
.setSendPresence(false)
.setDebuggerEnabled(mDebuggable)
.setSocketFactory(SSLSocketFactory.getDefault())
.build();
connection = new XMPPTCPConnection(config);
//disable Roster as I don't think this is supported by GCM
Roster roster = Roster.getInstanceFor(connection);
roster.setRosterLoadedAtLogin(false);
logger.info("Connecting...");
connection.connect();
connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
connection.addAsyncStanzaListener(new MyStanzaListener() , new MyStanzaFilter() );
// Log all outgoing packets
connection.addPacketInterceptor(new MyStanzaInterceptor(), new MyStanzaFilter() );
connection.login(senderId + "@gcm.googleapis.com" , serverApiKey);
logger.info("Logged in: " + mSenderId);
private CcsMessage getMessage(Map<String, Object> jsonObject)
String from = jsonObject.get("from").toString();
// PackageName of the application that sent this message.
String category = jsonObject.get("category").toString();
// unique id of this message
String messageId = jsonObject.get("message_id").toString();
@SuppressWarnings("unchecked")
Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
CcsMessage msg = new CcsMessage(from, category, messageId, payload);
return msg;
private class MyStanzaFilter implements StanzaFilter
@Override
public boolean accept(Stanza arg0)
// TODO Auto-generated method stub
if (arg0.getClass() == Stanza.class)
return true;
else
if (arg0.getTo() != null)
if (arg0.getTo().startsWith(mSenderId))
return true;
return false;
private class MyStanzaListener implements StanzaListener
@Override
public void processPacket(Stanza packet)
logger.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
CcsMessage msg = getMessage(jsonObject);
handleIncomingDataMessage(msg);
// 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.warn("Unrecognized message type (%s)",
messageType.toString());
catch (ParseException e)
logger.info("Error parsing JSON " + json, e);
catch (Exception e)
logger.info("Failed to process packet", e);
private class MyStanzaInterceptor implements StanzaListener
@Override
public void processPacket(Stanza packet)
logger.info("Sent: 0", packet.toXML());
// public static void main(String[] args) throws Exception
//
// SmackCcsClient ccsClient = new SmackCcsClient();
//
// ccsClient.connect(YOUR_PROJECT_ID, YOUR_API_KEY);
//
// // Send a sample hello downstream message to a device.
// String messageId = ccsClient.nextMessageId();
// Map<String, String> payload = new HashMap<String, String>();
// payload.put("Message", "Ahha, it works!");
// payload.put("CCS", "Dummy Message");
// payload.put("EmbeddedMessageId", messageId);
// String collapseKey = "sample";
// Long timeToLive = 10000L;
// String message = createJsonMessage(YOUR_PHONE_REG_ID, messageId, payload,
// collapseKey, timeToLive, true);
//
// ccsClient.sendDownstreamMessage(message);
// logger.info("Message sent.");
//
// //crude loop to keep connection open for receiving messages
// while(true)
// ;
//
/**
* XMPP Packet Extension for GCM Cloud Connection Server.
*/
private static final class GcmPacketExtension extends DefaultExtensionElement
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 Stanza 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 reconnectionSuccessful()
logger.info("Reconnecting..");
@Override
public void reconnectionFailed(Exception e)
logger.info("Reconnection failed.. ", e);
@Override
public void reconnectingIn(int seconds)
logger.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.");
@Override
public void authenticated(XMPPConnection arg0, boolean arg1)
// TODO Auto-generated method stub
@PreDestroy
public void cleanUp() throws Exception
logger.info("Bean do cliente XMPP está sendo destruído...");
if (connection.isConnected())
logger.info("Conexão GCM XMPP está aberta. Desconectando...");
connection.disconnect();
tomcat启动时出现错误:
Caused by: java.lang.NoClassDefFoundError: org/jivesoftware/smack/initializer/SmackAndOsgiInitializer
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2476)
at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:857)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1282)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1164)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.jivesoftware.smack.SmackInitialization.loadSmackClass(SmackInitialization.java:213)
at org.jivesoftware.smack.SmackInitialization.parseClassesToLoad(SmackInitialization.java:193)
at org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:163)
at org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:148)
at org.jivesoftware.smack.SmackInitialization.<clinit>(SmackInitialization.java:116)
at org.jivesoftware.smack.SmackConfiguration.getVersion(SmackConfiguration.java:96)
at org.jivesoftware.smack.provider.ProviderManager.<clinit>(ProviderManager.java:121)
at br.com.soma.service.gcm.xmpp.CcsClientImpl.<init>(CcsClientImpl.java:73)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
... 60 more
Caused by: java.lang.ClassNotFoundException: org.jivesoftware.smack.initializer.SmackAndOsgiInitializer
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1313)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1164)
... 82 more
有人可以帮助我吗?谢谢!
【问题讨论】:
您是否注意到与所有其他 smack-* 依赖项相比,您为 smack-java7 使用了不同的版本?也许这就是原因。我建议为 Smack 版本字符串定义一个变量,并从 maven 依赖项中引用该变量。 现在可以正常使用了。我只是按照您的建议为 smack 设置了一个变量。谢谢!! 【参考方案1】:现在可以正常使用了。我只是按照您的建议为 smack 设置了一个变量。谢谢!!
<properties>
<smack.version>4.1.4</smack.version>
</properties>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>$smack.version</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-tcp</artifactId>
<version>$smack.version</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-extensions</artifactId>
<version>$smack.version</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-java7</artifactId>
<version>$smack.version</version>
</dependency>
【讨论】:
以上是关于GCM XMPP - 启动 Web 应用程序服务器时出错的主要内容,如果未能解决你的问题,请参考以下文章
是否可以将 GCM 云连接服务器 (XMPP) 与 Heroku 应用程序一起使用?
使用 HTTP 和 XMPP 协议的 GCM/FCM 推送通知