1 classdef EmailLogger<handle
2 properties (Access=
private,Hidden)
3 emailDistributionList={};
4 emailAttachmentNameList={};
5 emailAttachmentZippedNameList={};
11 isThrowExceptions=
false;
19 methods (Access=
private)
20 sendmail(self,to,subject,message,attachments)
24 logger=
mxberry.log.log4j.Log4jConfigurator.getLogger();
27 isZippedSpecified=false;
28 isFromAddressSpec=false;
31 case 'emaildistributionlist' 32 self.emailDistributionList=prop{k+1};
33 case 'emailattachmentnamelist' 34 self.emailAttachmentNameList=prop{k+1};
35 case 'emailattachmentzippednamelist' 36 self.emailAttachmentZippedNameList=prop{k+1};
37 isZippedSpecified=
true;
39 self.smtpServer=prop{k+1};
41 self.subjectSuffix=prop{k+1};
43 self.smtpPassword=prop{k+1};
45 self.smtpUserName=prop{k+1};
47 self.loggerName=prop{k+1};
49 self.isDryRun=prop{k+1};
50 case 'isthrowexceptions' 51 self.isThrowExceptions = prop{k+1};
52 case 'fromemailaddress' 53 self.fromEmailAddress=prop{k+1};
54 isFromAddressSpec=
true;
57 'unknown property %s',prop{k});
61 if numel(
self.emailAttachmentNameList)~=...
62 numel(
self.emailAttachmentZippedNameList)
64 'properties emailAttachmentNameList and '...
65 'emailAttachmentZippedNameList are not '...
66 'consistent in length']);
69 self.emailAttachmentZippedNameList=...
70 cell(size(
self.emailAttachmentNameList));
72 %% Configure email notification
73 if ~self.isDryRun&&~isFromAddressSpec
75 if ~isempty(curUserName)
76 self.userName = curUserName;
78 if ~isempty(curHostName)
79 self.hostName = curHostName;
81 self.fromEmailAddress=[
self.userName,
'@',
self.hostName];
83 [
'no dry run mode, configured for smtpServer=',...
86 logger.info(
'configured for dry run');
89 function sendMessage(
self,subjectMessage,varargin)
93 %% Create log4j logger
98 {
'emailAttachmentNameList';{};
'iscellofstring(x)'},...
99 [0 1],
'regDefList',{[]});
102 emailSubjectPrefix=[
'[',
self.loggerName,
']:'];
103 emailSubjectSuffix=[
self.subjectSuffix ,...
104 ', running on host:',
self.hostName,
'(user:',
self.userName,
'),',...
105 'matlab:',version(
'-release'),
'(arch:',computer,
')'];
106 emailSubject=[emailSubjectPrefix,subjectMessage,...
108 emailAttachNameList=
self.emailAttachmentNameList;
109 emailAttachZippedNameList=
self.emailAttachmentZippedNameList;
110 for iElem=1:numel(emailAttachNameList)
111 zippedNameStr=emailAttachZippedNameList{iElem};
112 if ~isempty(zippedNameStr)
113 zip(zippedNameStr,emailAttachNameList{iElem});
114 emailAttachNameList{iElem}=zippedNameStr;
117 attachNameList=[attachNameList,...
118 emailAttachNameList];
120 nAttachemments=length(attachNameList);
121 for iFile=1:nAttachemments
122 fileName=attachNameList{iFile};
125 'cannot find attachment %s',fileName);
129 logger.info(emailSubject);
131 self.sendmail(
self.emailDistributionList,...
137 [
'something is wrong with the following data: \n',...
138 'distributionList: %s \n',...
140 'smtpServer: %s\n',...
141 'emailAttachmentNameList: %s'],...
143 'isMatlabSyntax',
true),...
147 'isMatlabSyntax',
true));
148 meObj=addCause(meObj,causeObj);
149 logger.fatal(sprintf(
'%s\nMessage body:\n%s',...
152 if self.isThrowExceptions
158 function fromEmailAddress=getFromEmailAddress(
self)
159 fromEmailAddress=self.fromEmailAddress;
161 function smtpServer=getSMTPServer(self)
162 smtpServer=self.smtpServer;
164 function suffixStr = getSubjectSuffix(self)
165 suffixStr = self.subjectSuffix;
167 function setSubjectSuffix(self, suffixStr)
168 self.subjectSuffix = suffixStr;
170 function addSubjectSuffix(self,suffixStr,isAddedToEnd)
176 self.subjectSuffix=[self.subjectSuffix,suffixStr];
178 self.subjectSuffix=[suffixStr,self.subjectSuffix];
183 function sendmail(self,to,subject,message,attachments)
186 import javax.mail.internet.*
187 import javax.activation.*
190 throwerror('wrongState:NoJvm','no Java is running');
203 elseif ischar(attachments)
204 attachments = {attachments};
207 % Determine server and from.
208 [server,from] = getServerAndFrom(
self);
210 throwerror(
'wrongState:SMTPServerIndeterminate',...
211 'Could not determine SMTP server');
214 throwerror(
'wrongState:FromAddressIndeterminate',...
215 'Could not determine FROM address');
218 % Use the system properties, but clone them so we don
't alter them. 219 props = java.lang.System.getProperties.clone; 220 props.put('mail.smtp.host
',server); 223 username = self.smtpUserName; 224 password = self.smtpPassword; 228 pa = com.mathworks.util.PasswordAuthenticator(username,password); 230 session = Session.getInstance(props,pa); 232 % Create the message. 233 msg = MimeMessage(session); 236 msg.setFrom(getInternetAddress(from)); 240 msg.addRecipient(getRecipientTypeTo(msg), ... 241 getInternetAddress(to{i})); 244 % Try to do the right thing on Japanese machines. 245 isJapanese = ispc && strncmpi(get(0,'Language
'),'ja
',2); 248 if any(subject == char(10)) || any(subject == char(13)) 249 throwerror('wrongInput:InvalidSubject
', ... 250 'Subjects cannot contain newline characters
'); 253 msg.setSubject(subject,'SJIS
') 255 msg.setSubject(subject) 259 msg.setHeader('X-Mailer
', ['MATLAB
' version]) 260 msg.setSentDate(java.util.Date); 262 % Construct the body of the message and attachments. 263 body = formatText(message); 264 if numel(attachments) == 0 266 msg.setText(body,'SJIS
'); 272 messageBodyPart = MimeBodyPart; 274 messageBodyPart.setText(body,'SJIS
'); 276 messageBodyPart.setText(body); 278 multipart = MimeMultipart; 279 multipart.addBodyPart(messageBodyPart); 282 for iAttachments = 1:numel(attachments) 283 file = attachments{iAttachments}; 284 messageBodyPart = MimeBodyPart; 285 fullName = locateFile(file); 287 throwerror('wrongState:CannotOpenFile
', ... 288 'Cannot open file
"%s".
',file); 290 source = FileDataSource(fullName); 291 messageBodyPart.setDataHandler(DataHandler(source)); 293 % Remove the directory, if any, from the attachement name. 294 [~,fileName,fileExt] = fileparts(fullName); 295 messageBodyPart.setFileName([fileName fileExt]); 296 multipart.addBodyPart(messageBodyPart); 299 % Put parts in message 300 msg.setContent(multipart); 308 % Try to make the Java error friendlier. 309 niceError = stripJavaError(exception.message); 310 if isempty(niceError) 313 throwerror('wrongState:SmtpError
','%s
',niceError); 317 function [server,from] = getServerAndFrom(self) 320 server = self.smtpServer; 321 from = self.fromEmailAddress; 323 % Check Java properties. 325 props = java.lang.System.getProperties; 326 server = char(props.getProperty('mail.smtp.host
')); 329 % Determine defaultMailAccountRegistry. 330 if (ispc && (isempty(server) || isempty(from))) 332 defaultMailAccount = winqueryreg('HKEY_CURRENT_USER
', ... 333 'Software\Microsoft\Internet Account Manager
', ... 334 'Default Mail Account
'); 335 defaultMailAccountRegistry = ... 336 ['Software\Microsoft\Internet Account Manager\Accounts\
' ... 339 defaultMailAccountRegistry = ''; 344 if ispc && isempty(server) && ~isempty(defaultMailAccountRegistry) 346 server = winqueryreg('HKEY_CURRENT_USER
',defaultMailAccountRegistry, ... 352 server = getenv('MAILHOST
'); 356 if ispc && isempty(from) 358 from = winqueryreg('HKEY_CURRENT_USER
',defaultMailAccountRegistry, ... 359 'SMTP Email Address
'); 364 from = getenv('LOGNAME
'); 368 function internetAddress = getInternetAddress(from) 369 import mxberry.core.throwerror; 371 internetAddress = javax.mail.internet.InternetAddress(from); 373 throwerror('wrongInput:AddressError
','%s
',... 374 stripJavaError(meObj.message)); 379 function recipientTypeTo = getRecipientTypeTo(msg) 381 % Get the class loader for the Message class. 382 cl = msg.getClass.getClassLoader; 383 % Returns a Class object pointing to RecipientType using that ClassLoader. 384 rt = java.lang.Class.forName('javax.mail.Message$RecipientType
', false, cl); 385 % Returns a Field object pointint to TO. 386 field = rt.getField('TO
'); 387 % Gets the static instance of TO. 388 recipientTypeTo = field.get([]); 391 function fullPathToFile = locateFile(file) 393 % Matthew J. Simoneau, November 2003 395 % Checking that the length is exactly one in the first two checks automatically 396 % excludes directories, since directory listings always include '.
' and '..
'. 398 if (length(dir(fullfile(pwd,file))) == 1) 400 fullPathToFile = fullfile(pwd,file); 401 elseif (length(dir(file)) == 1) 403 fullPathToFile = file; 404 elseif ~isempty(which(file)) 405 % A file on the path. 406 fullPathToFile = which(file); 407 elseif ~isempty(which([file '.
'])) 408 % A file on the path without extension. 409 fullPathToFile = which([file '.
']); 415 function toSend = formatText(msgText) 419 % For a cell array, send each cell as one line. 421 toSend = strjoin(msgText,cr); 425 % For a char array, break each line at a char(10) or try to wrap to 75 429 msgText = [cr msgText cr]; 430 crList = find(msgText == cr); 432 for i = 1:length(crList)-1 433 nextLine = msgText(crList(i)+1 : crList(i+1)-1); 434 lineLength = length(nextLine); 440 if (lineLength-start+1 <= maxLineLength) 441 % The rest fits on one line. 445 % Whole line doesn't fit. Needs to be broken up.
446 spaces = find(nextLine ==
' ');
447 spaces = spaces(spaces >= start);
448 nonSpaces = find(nextLine ~=
' ');
449 nonSpaces = nonSpaces(nonSpaces >= start);
451 % % No spaces anywhere. Chop!
452 % stop = start+maxLineLength-1;
453 % No spaces anywhere. Preserve.
455 elseif isempty(nonSpaces)
456 % Nothing but spaces. Send an empty line.
458 elseif (min(spaces) > (start+maxLineLength))
459 % % The first space doesn
't show up soon enough to help. Chop! 460 % stop = start+maxLineLength-1; 461 % No spaces anywhere. Preserve. 463 elseif isempty(spaces( ... 464 spaces > min(nonSpaces) & spaces < start+maxLineLength ... 466 % % There are only leading spaces, which we respect. Chop! 467 % stop = start+maxLineLength-1; 468 % No spaces anywhere. Preserve. 471 % Break on the last space that will make the line fit. 472 stop = max(spaces(spaces <= (start+maxLineLength)))-1; 474 % After a break, start the next line on the next non-space. 475 nonSpaces = find(nextLine ~= ' '); 476 nextStart = min(nonSpaces(nonSpaces > stop)); 477 if isempty(nextStart) 481 lines{end+1,1} = nextLine(start:stop); %#ok<AGROW> 485 toSend = strjoin(lines,cr); 488 function niceError = stripJavaError(err) 490 % Two nice error messages. Pull them out and stick them together. 491 pat = 'Java exception occurred:\s*\S+: (.*?)\n\s*nested exception is:\s*\S+: (.*?)\n
'; 492 m = regexp(err,pat,'tokens
','once
'); 494 niceError = sprintf('%s\n%s
',m{:}); 498 % One nice error message. Strip it off. 499 pat = 'Java exception occurred:\s*\S+: (.*?)\n
'; 500 m = regexp(err,pat,'tokens
','once
'); 506 % Only exceptions. Special-case popular ones. 507 pat = 'Java exception occurred:\s*(\S+)\s*\n
'; 508 m = regexp(err,pat,'tokens
','once
'); 511 case 'javax.mail.AuthenticationFailedException
' 512 niceError = 'Authentication failed.
'; 517 % Can't find a nice message.
LOG4JCONFIGURATOR simplifies log4j configuration, especially when Parallel Computing Toolbox is used...
static function getLogger(in loggerName, in isSuffix)
GETLOGGER - gets logger for caller (it may be either script or function or method of some class) ...
function throwerror(in msgTag, in varargin)
THROWERROR works similarly to built-in ERROR function in case when there is no output arguments but s...
function parseparext(in args, in propNameValMat, in varargin)
PARSEPAREXT behaves in the same way as mxberry.core.parseparams but returns property values in a more...
function cell2tablestr(in titleList, in dataCell, in colSepSeq, in varargin)
CELL2TABLESTR - converts a cell array into a table-like char array (or a cell array of strings repres...
function parseparams(in args, in propNameList, in nRegExpected, in nPropExpected)
PARSEPARAMS behaves exactly as a built-in Matlab function apart from the incorrect behavior of Matlab...