Community Tip - Learn all about the Community Ranking System, a fun gamification element of the PTC Community. X
I have created one extension and i m trying to test the service and getting below error.
Please suggest possible ways for it.
[context: com.thingworx.webservices.context.HttpExecutionContext@44302b5c][message: Unable to Invoke Service SendMessageWithImage on Test : No service handler defined for service SendMessageWithImage on thing [Test]]
Unable to create service handler for SendMessageWithImage in MailThingPackage:ThingPackage : Unable to bind service [SendMessageWithImage] to method on class [com.test.tnt.ext.mail.MailThing]
Vaibhav, have you verified if your Mail Thing is working as expected i.e. are you able to send/receive emails with that? Have you tried testing the SendMessageWithImage?
Sushant, I am facing this error at the time of testing SendMessageWithImage.
Could you share screenshot how you are testing it and the values you are putting in to test the service?
Is this extension a custom extension or are you using the mail extension downloaded from the ThingWorx marketplace?
Please share the definition/code for SendMessageWithImage service.
package com.test.tnt.ext.mail;
import com.thingworx.common.RESTAPIConstants;
import com.thingworx.common.exceptions.InvalidRequestException;
import com.thingworx.entities.utils.ThingUtilities;
import com.thingworx.logging.LogUtilities;
import com.thingworx.metadata.annotations.ThingworxConfigurationTableDefinitions;
import com.thingworx.metadata.annotations.ThingworxServiceDefinition;
import com.thingworx.metadata.annotations.ThingworxServiceParameter;
import com.thingworx.things.Thing;
import com.thingworx.things.repository.FileRepositoryThing;
import java.io.PrintStream;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@ThingworxConfigurationTableDefinitions(tables={@com.thingworx.metadata.annotations.ThingworxConfigurationTableDefinition(name="ConnectionInfo", description="Mail Server Connection Parameters", isMultiRow=false, ordinal=0, dataShape=@com.thingworx.metadata.annotations.ThingworxDataShapeDefinition(fields={@com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=0, name="smtpServer", description="SMTP server name", baseType="STRING", aspects={"defaultValue:smtp.domain.com", "friendlyName:SMTP Server"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=1, name="smtpPort", description="SMTP server port", baseType="NUMBER", aspects={"defaultValue:80", "friendlyName:SMTP Server Port"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=2, name="pop3Server", description="POP3 server name", baseType="STRING", aspects={"defaultValue:pop3.domain.com", "friendlyName:POP3 Server"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=3, name="pop3Port", description="POP3 server port", baseType="NUMBER", aspects={"defaultValue:110", "friendlyName:POP3 Server Port"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=4, name="useTLS", description="Use transport layer security", baseType="BOOLEAN", aspects={"defaultValue:false", "friendlyName:Use TLS"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=5, name="useSSL", description="Use an SSL connection", baseType="BOOLEAN", aspects={"defaultValue:false", "friendlyName:Use SSL"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=6, name="accountId", description="Mail account user id", baseType="STRING", aspects={"defaultValue:account@domain.com", "friendlyName:Mail Account User"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=7, name="accountPassword", description="Mail account password", baseType="PASSWORD", aspects={"friendlyName:Mail Account Password"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=8, name="defaultFrom", description="Default From field", baseType="STRING", aspects={"friendlyName:Default From Field"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=9, name="connectionTimeout", description="Timeout (milliseconds) to establish a connection", baseType="NUMBER", aspects={"defaultValue:30000", "friendlyName:Connection Timeout"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=10, name="operationTimeout", description="Timeout (milliseconds) to perform an operation", baseType="NUMBER", aspects={"defaultValue:30000", "friendlyName:Operation Timeout"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=11, name="socksHost", description="Specifies the host name of a SOCKS5 proxy server that will be used for connections to the mail server", baseType="STRING", aspects={"defaultValue:", "friendlyName:Socks Host"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=12, name="socksPort", description="Specifies the port number for the SOCKS5 proxy server", baseType="NUMBER", aspects={"defaultValue:1080", "friendlyName:Socks Port"}), @com.thingworx.metadata.annotations.ThingworxFieldDefinition(ordinal=13, name="fileRepository", description="File Repository for recieved attachments", baseType="THINGNAME", aspects={"defaultValue:SystemRepository", "friendlyName:Attachment File Repository"})}))})
public class MailThing extends Thing{
private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(MailThing.class);
private String _smtpServer = "";
private Number _smtpPort = Integer.valueOf(80);
private String _accountId = "";
private String _accountPassword = "";
private String _defaultFrom = "";
private Number _connectionTimeout = Integer.valueOf(30000);
private Number _operationTimeout = Integer.valueOf(30000);
private String _socksServer = "";
private Number _socksPort = Integer.valueOf(1080);
private Boolean _useSSL = Boolean.valueOf(false);
private Boolean _useTLS = Boolean.valueOf(false);
private Boolean _debug = Boolean.valueOf(false);
protected void initializeThing(){
this._smtpServer = ((String)getConfigurationSetting("ConnectionInfo", "smtpServer"));
this._smtpPort = ((Number)getConfigurationSetting("ConnectionInfo", "smtpPort"));
this._accountId = ((String)getConfigurationSetting("ConnectionInfo", "accountId"));
this._accountPassword = ((String)getConfigurationSetting("ConnectionInfo", "accountPassword"));
this._defaultFrom = ((String)getConfigurationSetting("ConnectionInfo", "defaultFrom"));
this._connectionTimeout = ((Number)getConfigurationSetting("ConnectionInfo", "connectionTimeout"));
this._operationTimeout = ((Number)getConfigurationSetting("ConnectionInfo", "operationTimeout"));
this._useSSL = ((Boolean)getConfigurationSetting("ConnectionInfo", "useSSL"));
this._useTLS = ((Boolean)getConfigurationSetting("ConnectionInfo", "useTLS"));
this._socksServer = ((String)getConfigurationSetting("ConnectionInfo", "socksHost"));
this._socksPort = ((Number)getConfigurationSetting("ConnectionInfo", "socksPort"));
}
/*
@ThingworxServiceDefinition(name="IsDebug", description="State of Email Debugger")
@ThingworxServiceResult(name="result", description="State of Email Debugger", baseType="BOOLEAN")
public boolean IsDebug(){
return this._debug.booleanValue();
}
@ThingworxServiceDefinition(name="SetDebug", description="Set debug mode on or off")
public void SetDebug(@ThingworxServiceParameter(name="debug", description="Set debug mode on or off", baseType="BOOLEAN", aspects={"defaultValue:false"}) Boolean debug){
this._debug = debug;
}
*/
@ThingworxServiceDefinition(name="SendMessageWithAttachment", description="Send a simple textual e-mail message with a file attachment")
public void SendMessageWithAttachment(@ThingworxServiceParameter(name="from", description="", baseType="STRING") String from, @ThingworxServiceParameter(name="to", description="", baseType="STRING") String to, @ThingworxServiceParameter(name="cc", description="", baseType="STRING") String cc, @ThingworxServiceParameter(name="bcc", description="", baseType="STRING") String bcc, @ThingworxServiceParameter(name="subject", description="", baseType="STRING") String subject, @ThingworxServiceParameter(name="body", description="", baseType="HTML") String body, @ThingworxServiceParameter(name="fileRepository", description="File repository", baseType="THINGNAME") String fileRepository, @ThingworxServiceParameter(name="path", description="Path to file", baseType="STRING") String path, @ThingworxServiceParameter(name="attachmentName", description="File name for attachment", baseType="STRING") String attachmentName, @ThingworxServiceParameter(name="mimeType", description="Mime type for attachment", baseType="STRING") String mimeType) throws Exception{
_logger.debug("Enter into SendMessageWithAttachment() Method");
_logger.info("Enter into SendMessageWithAttachment() Method");
if(StringUtils.isBlank(fileRepository)){
throw new InvalidRequestException("File Repository Name Must Be Provided", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
if(StringUtils.isBlank(path)){
throw new InvalidRequestException("File Path Must Be Provided", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
Thing thing = ThingUtilities.findThing(fileRepository);
_logger.debug("Creating Thing for fileRepository");
_logger.info("Creating Thing for fileRepository");
if(StringUtils.isBlank(""+thing)){
throw new InvalidRequestException("File Repository [" + fileRepository + "] Does Not Exist", RESTAPIConstants.StatusCode.STATUS_NOT_FOUND);
}
if(!(thing instanceof FileRepositoryThing)){
throw new InvalidRequestException("Thing [" + fileRepository + "] Is Not A File Repository", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
FileRepositoryThing fileRepoThing = (FileRepositoryThing)thing;
byte[] data = fileRepoThing.LoadBinary(path);
_logger.debug("After converting path into byte stream");
_logger.info("After converting path into byte stream");
SendMessage(from, to, cc, bcc, subject, body, data, attachmentName, mimeType);
_logger.debug("Exit From SendMessageWithAttachment() Method");
_logger.info("Exit From SendMessageWithAttachment() Method");
}
@ThingworxServiceDefinition(name="SendMessageWithImage", description="Send a simple textual e-mail message with an image attachment")
public void SendMessage(@ThingworxServiceParameter(name="from", description="", baseType="STRING") String from, @ThingworxServiceParameter(name="to", description="", baseType="STRING") String to, @ThingworxServiceParameter(name="cc", description="", baseType="STRING") String cc, @ThingworxServiceParameter(name="bcc", description="", baseType="STRING") String bcc, @ThingworxServiceParameter(name="subject", description="", baseType="STRING") String subject, @ThingworxServiceParameter(name="body", description="", baseType="HTML") String body, @ThingworxServiceParameter(name="image", description="Image content", baseType="IMAGE") byte[] image, @ThingworxServiceParameter(name="attachmentName", description="File name for attachment", baseType="STRING") String attachmentName, @ThingworxServiceParameter(name="mimeType", description="Mime type for attachment", baseType="STRING") String mimeType) throws Exception{
_logger.debug("Enter into sendMessage() Method");
_logger.info("Enter into sendMessage() Method");
if((StringUtils.isBlank(mimeType)) && (StringUtils.isNotBlank(""+image))){
throw new InvalidRequestException("Mime type Must Be Provided", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
if((StringUtils.isBlank(attachmentName)) && (StringUtils.isNotBlank(""+image))){
throw new InvalidRequestException("Attachment Name Must Be Provided", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
try{
_logger.debug("Before setting properties of server");
_logger.info("Before setting properties of server");
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.host", this._smtpServer);
if(this._useTLS.booleanValue()){
_logger.debug("Enabling TLS Value");
_logger.info("Enabling TLS Value");
props.put("mail.smtp.starttls.enable", "true");
}
if(this._useSSL.booleanValue()){
_logger.info("Enabling SSL Value");
_logger.debug("Enabling SSL Value");
props.put("mail.smtp.socketFactory.port", new Integer(this._smtpPort.intValue()).toString());
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.quitwait", "false");
}
props.setProperty("mail.smtp.port", new Integer(this._smtpPort.intValue()).toString());
props.put("mail.debug", this._debug);
Integer connTimeout = Integer.valueOf(this._connectionTimeout.intValue());
props.setProperty("mail.smtp.connectiontimeout", connTimeout.toString());
Integer opTimeout = Integer.valueOf(this._operationTimeout.intValue());
props.setProperty("mail.smtp.timeout", opTimeout.toString());
_logger.debug("Setting Timeout Values");
_logger.info("Setting Timeout Values");
this._socksServer = this._socksServer.trim();
if (StringUtils.isNotBlank(this._socksServer)){
props.setProperty("mail.smtp.socks.host", this._socksServer);
Integer thesocksport = Integer.valueOf(this._socksPort.intValue());
props.setProperty("mail.smtp.socks.port", thesocksport.toString());
_logger.debug("Setting Socket Server IP or Port");
_logger.info("Setting Socket Server IP or Port");
}
this._accountId = this._accountId.trim();
Session mailSession;
if (StringUtils.isNotBlank(this._accountId)){
props.setProperty("mail.smtp.auth", Boolean.toString(true));
final String uid = this._accountId;
final String pwd = this._accountPassword;
_logger.debug("Setting AccountId and Password");
_logger.info("Setting AccountId and Password");
Authenticator auth = new Authenticator(){
protected PasswordAuthentication getPasswordAuthentication(){
_logger.debug("Inside Password Authentication");
_logger.info("Inside Password Authentication");
return new PasswordAuthentication(uid, pwd);
}
};
mailSession = Session.getInstance(props, auth);
}else{
props.put("mail.smtp.auth", Boolean.toString(false));
mailSession = Session.getInstance(props);
}
mailSession.setDebug(this._debug.booleanValue());
if (mailSession.getDebug()){
_logger.debug("SendMail debug mode is ON");
_logger.info("SendMail debug mode is ON");
LogOutputStream sendMailDebugOut = new LogOutputStream(){
protected void processLine(String line, int level){
MailThing._logger.debug(line);
}
};
mailSession.setDebugOut(new PrintStream(sendMailDebugOut));
}
MimeMessage message = new MimeMessage(mailSession);
message.setSubject(subject, "UTF-8");
String fromAddress = from;
if (StringUtils.isBlank(fromAddress)){
fromAddress = this._defaultFrom;
if(StringUtils.isBlank(fromAddress)){
throw new InvalidRequestException("No sender address specified.", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
}
message.setFrom(new InternetAddress(fromAddress));
message.setSender(new InternetAddress(fromAddress));
if(body == null){
body = "";
}
if(StringUtils.isNotBlank(""+image)){
Multipart multipart = new MimeMultipart();
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(body, "text/html; charset=utf-8");
multipart.addBodyPart(messageBodyPart);
MimeBodyPart attachmentBodyPart = new MimeBodyPart();
ByteArrayDataSource source = new ByteArrayDataSource(image, mimeType);
attachmentBodyPart.setDataHandler(new DataHandler(source));
attachmentBodyPart.setFileName(attachmentName);
multipart.addBodyPart(attachmentBodyPart);
message.setContent(multipart);
}else{
message.setContent(body, "text/html; charset=utf-8");
}
if((StringUtils.isBlank(to)) && (StringUtils.isBlank(cc)) && (StringUtils.isBlank(bcc))) {
throw new InvalidRequestException("No recipient address specified.", RESTAPIConstants.StatusCode.STATUS_BAD_REQUEST);
}
if (StringUtils.isNotBlank(to)){
String[] addresses = to.split(";");
InternetAddress[] recipients = new InternetAddress[addresses.length];
for(int i = 0; i < addresses.length; i++){
recipients = new InternetAddress(addresses.trim());
}
message.setRecipients(Message.RecipientType.TO, recipients);
}
if(StringUtils.isNotBlank(cc)){
String[] addresses = cc.split(";");
InternetAddress[] recipients = new InternetAddress[addresses.length];
for(int i = 0; i < addresses.length; i++){
recipients = new InternetAddress(addresses.trim());
}
message.setRecipients(Message.RecipientType.CC, recipients);
}
if(StringUtils.isNotBlank(bcc)){
String[] addresses = bcc.split(";");
InternetAddress[] recipients = new InternetAddress[addresses.length];
for(int i = 0; i < addresses.length; i++){
recipients = new InternetAddress(addresses.trim());
}
message.setRecipients(Message.RecipientType.BCC, recipients);
}
Transport.send(message, message.getAllRecipients());
}catch (Exception e){
_logger.error("Unable to Send Message: {}", e.getMessage(), e);
throw e;
}
}
protected static class ConfigConstants{
public static final String ConnectionInfo = "ConnectionInfo";
public static final String SmtpServer = "smtpServer";
public static final String SmtpPort = "smtpPort";
public static final String UseSSL = "useSSL";
public static final String UseTLS = "useTLS";
public static final String AccountId = "accountId";
public static final String AccountPassword = "accountPassword";
public static final String DefaultFrom = "defaultFrom";
public static final String ConnectionTimeout = "connectionTimeout";
public static final String OperationTimeout = "operationTimeout";
public static final String SocksHost = "socksHost";
public static final String SocksPort = "socksPort";
public static final String FileRepository = "fileRepository";
}
}
I thing I found your problem.
In your service definition, the name "SendMessageWithImage" is not the java method name (SendMessage)
Fix the name, re-import the extension and clean the cache or restart the server, it should work
Regards,
Cyril