Avoid lucence QueryParser Parse exception? - lucene

On the 3rd line i get exceptions such as 'IOException: read past eof' and 'LookaheadSuccess: Error in the application.'
Is there any way to avoid this? i hate the breaks and pressing continue twice everytime i execute a search
Note i only notice this when i tell visual studios to show me exceptions that are thrown even IF they are caught. I don't get the exceptions, i just see that they are being thrown thus the two (or three) breakpoints every time i search. The app runs fine.
var searcher = new IndexSearcher(directory, true);
var parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "all", analyzer);
var query = parser.Parse(text); //here

These are first-chance exceptions that occur, and are caught, within Lucene. You've configured Visual Studio to break on all exceptions, and not just those that are unhandled. Use the Exceptions dialog (ctrl-alt-e iirc) and change your settings.

Lucene.NET (at the time of version 3.0.3) used IOExceptions to manage several parts of the parser's flow. This had a bad impact on the performance (up to 90ms on my development machine).
Good news is that the version currently in their source code repository at http://lucenenet.apache.org/community.html seems to have removed the specific exceptions that were causing this. Certainly for me, this has improved performance a lot. Hope this helps.

Patch for QueryParser in Lucene 3.0.3 to avoid the LookaheadSuccess exception throwing:
--- a/src/core/QueryParser/QueryParser.cs
+++ b/src/core/QueryParser/QueryParser.cs
## -1708,16 +1708,13 ## namespace Lucene.Net.QueryParsers
}
private bool Jj_2_1(int xla)
- {
+ {
+ bool lookaheadSuccess = false;
jj_la = xla;
jj_lastpos = jj_scanpos = token;
try
{
- return !Jj_3_1();
- }
- catch (LookaheadSuccess)
- {
- return true;
+ return !Jj_3_1(out lookaheadSuccess);
}
finally
{
## -1725,29 +1722,31 ## namespace Lucene.Net.QueryParsers
}
}
- private bool Jj_3R_2()
- {
- if (jj_scan_token(TermToken)) return true;
- if (jj_scan_token(ColonToken)) return true;
+ private bool Jj_3R_2(out bool lookaheadSuccess)
+ {
+ if (jj_scan_token(TermToken, out lookaheadSuccess)) return true;
+ if (lookaheadSuccess) return false;
+ if (jj_scan_token(ColonToken, out lookaheadSuccess)) return true;
return false;
}
- private bool Jj_3_1()
+ private bool Jj_3_1(out bool lookaheadSuccess)
{
Token xsp;
xsp = jj_scanpos;
- if (Jj_3R_2())
+ if (Jj_3R_2(out lookaheadSuccess))
{
jj_scanpos = xsp;
- if (Jj_3R_3()) return true;
+ if (Jj_3R_3(out lookaheadSuccess)) return true;
}
return false;
}
- private bool Jj_3R_3()
- {
- if (jj_scan_token(StarToken)) return true;
- if (jj_scan_token(ColonToken)) return true;
+ private bool Jj_3R_3(out bool lookaheadSuccess)
+ {
+ if (jj_scan_token(StarToken, out lookaheadSuccess)) return true;
+ if (lookaheadSuccess) return false;
+ if (jj_scan_token(ColonToken, out lookaheadSuccess)) return true;
return false;
}
## -1861,14 +1860,9 ## namespace Lucene.Net.QueryParsers
throw GenerateParseException();
}
- [Serializable]
- private sealed class LookaheadSuccess : System.Exception
- {
- }
-
- private LookaheadSuccess jj_ls = new LookaheadSuccess();
- private bool jj_scan_token(int kind)
- {
+ private bool jj_scan_token(int kind, out bool lookaheadSuccess)
+ {
+ lookaheadSuccess = false;
if (jj_scanpos == jj_lastpos)
{
jj_la--;
## -1896,8 +1890,8 ## namespace Lucene.Net.QueryParsers
}
if (tok != null) Jj_add_error_token(kind, i);
}
- if (jj_scanpos.kind != kind) return true;
- if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+ if (jj_scanpos.kind != kind) return true;
+ if (jj_la == 0 && jj_scanpos == jj_lastpos) lookaheadSuccess = true;
return false;
}
## -2029,32 +2023,34 ## namespace Lucene.Net.QueryParsers
}
private void Jj_rescan_token()
- {
+ {
+ bool lookaheadSuccess = false;
jj_rescan = true;
for (int i = 0; i < 1; i++)
{
- try
+ JJCalls p = jj_2_rtns[i];
+ do
{
- JJCalls p = jj_2_rtns[i];
- do
+ if (p.gen > jj_gen)
{
- if (p.gen > jj_gen)
+ jj_la = p.arg;
+ jj_lastpos = jj_scanpos = p.first;
+ switch (i)
{
- jj_la = p.arg;
- jj_lastpos = jj_scanpos = p.first;
- switch (i)
- {
- case 0:
- Jj_3_1();
- break;
- }
+ case 0:
+ Jj_3_1(out lookaheadSuccess);
+ if (lookaheadSuccess)
+ {
+ goto Jj_rescan_token_after_while_label;
+ }
+ break;
}
- p = p.next;
- } while (p != null);
- }
- catch (LookaheadSuccess)
- {
- }
+ }
+ p = p.next;
+ } while (p != null);
+
+ Jj_rescan_token_after_while_label:
+ lookaheadSuccess = false;
}
jj_rescan = false;
}
--
Patch for QueryParser in Lucene 3.0.3 to avoid lots of System.IO.IOException exception throwing:
CharStream.cs:
--- CharStream.cs
+++ CharStream.cs
## -44,6 +44,7 ##
/// implementing this interface. Can throw any java.io.IOException.
/// </summary>
char ReadChar();
+ char ReadChar(ref bool? systemIoException);
/// <summary> Returns the column position of the character last read.</summary>
/// <deprecated>
## -93,6 +94,7 ##
/// to this method to implement backup correctly.
/// </summary>
char BeginToken();
+ char BeginToken(ref bool? systemIoException);
/// <summary> Returns a string made up of characters from the marked token beginning
/// to the current buffer position. Implementations have the choice of returning
FastCharStream.cs:
--- FastCharStream.cs
+++ FastCharStream.cs
## -48,12 +48,35 ##
public char ReadChar()
{
+ bool? systemIoException = null;
if (bufferPosition >= bufferLength)
- Refill();
+ {
+ Refill(ref systemIoException);
+ }
+ return buffer[bufferPosition++];
+ }
+
+ public char ReadChar(ref bool? systemIoException)
+ {
+ if (bufferPosition >= bufferLength)
+ {
+ Refill(ref systemIoException);
+ // If using this Nullable as System.IO.IOException signal and is signaled.
+ if (systemIoException.HasValue && systemIoException.Value == true)
+ {
+ return '\0';
+ }
+ }
return buffer[bufferPosition++];
}
- private void Refill()
+ // You may ask to be signaled of a System.IO.IOException through the systemIoException parameter.
+ // Set it to false if you are interested, it will be set to true to signal a System.IO.IOException.
+ // Set it to null if you are not interested.
+ // This is used to avoid having a lot of System.IO.IOExceptions thrown while running the code under a debugger.
+ // Having a lot of exceptions thrown under a debugger causes the code to execute a lot more slowly.
+ // So use this if you are experimenting a lot of slow parsing at runtime under a debugger.
+ private void Refill(ref bool? systemIoException)
{
int newPosition = bufferLength - tokenStart;
## -86,7 +109,18 ##
int charsRead = input.Read(buffer, newPosition, buffer.Length - newPosition);
if (charsRead <= 0)
- throw new System.IO.IOException("read past eof");
+ {
+ // If interested in using this Nullable to signal a System.IO.IOException
+ if (systemIoException.HasValue)
+ {
+ systemIoException = true;
+ return;
+ }
+ else
+ {
+ throw new System.IO.IOException("read past eof");
+ }
+ }
else
bufferLength += charsRead;
}
## -96,6 +130,12 ##
tokenStart = bufferPosition;
return ReadChar();
}
+
+ public char BeginToken(ref bool? systemIoException)
+ {
+ tokenStart = bufferPosition;
+ return ReadChar(ref systemIoException);
+ }
public void Backup(int amount)
{
## -156,4 +196,4 ##
get { return 1; }
}
}
-}
\ No newline at end of file
+}
QueryParserTokenManager.cs:
--- QueryParserTokenManager.cs
+++ QueryParserTokenManager.cs
## -1341,9 +1341,16 ##
for (; ; )
{
+ bool? systemIoException = false;
try
{
- curChar = input_stream.BeginToken();
+ curChar = input_stream.BeginToken(ref systemIoException);
+ if (systemIoException != null && systemIoException.HasValue && systemIoException.Value == true)
+ {
+ jjmatchedKind = 0;
+ matchedToken = JjFillToken();
+ return matchedToken;
+ }
}
catch (System.IO.IOException)
{
## -1459,4 +1466,4 ##
while (start++ != end);
}
}
-}
\ No newline at end of file
+}
You can also use my github version
https://github.com/franckspike/lucenenet.git

Related

Obtaining attendance logs from ZKTEco device using their SDK with C#

I'm attempting to integrate a ZKTEco U650-C with my company's systems to automatically fetch attendance logs but I'm having trouble connecting to the device using C#.
I managed to download the SDK from their website which includes the ZKHID, ZKCamera DLL files, and the SDK wrapper so I could import and use the functions from them.
In Visual Studio, I couldn't reference the ZKHID and ZKCamera DLL files because they are unmanaged. Although, I was able to reference the ZKBioModuleSDKWrapper because it uses PInvoke.
Some operations such as connecting to the device using the TcpClient class, initializing, and terminating the device were successful. However, using other operations such as opening the device and getting the device configuration couldn't be made because I don't know how to get the device handle.
Am I using the wrong set of SDK or is it something else? Any help would be appreciated.
It turns out I was using the wrong set of SDK. ZKTEco took down the SDK download link from their official website so I had to downloaded the correct SDK from a private server.
I added the zkemkeeper.dll reference as normal in Visual Studio, made some changes to the code, and I managed to receive the attendance logs.
using zkemkeeper;
namespace ZKTEco_Biometric_Device_Integration
{
internal class Program
{
public Program()
{
}
public enum CONSTANTS
{
PORT = 4370,
}
static void Main(string[] args)
{
Console.WriteLine("Connecting...");
CZKEM objCZKEM = new CZKEM();
if (objCZKEM.Connect_Net("192.168.1.11", (int)CONSTANTS.PORT))
{
objCZKEM.SetDeviceTime2(objCZKEM.MachineNumber, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
Console.WriteLine("Connection Successful!");
Console.WriteLine("Obtaining attendance data...");
}
else
{
Console.WriteLine("Connection Failed!");
}
if (objCZKEM.ReadGeneralLogData(objCZKEM.MachineNumber))
{
//ArrayList logs = new ArrayList();
string log;
string dwEnrollNumber;
int dwVerifyMode;
int dwInOutMode;
int dwYear;
int dwMonth;
int dwDay;
int dwHour;
int dwMinute;
int dwSecond;
int dwWorkCode = 1;
int AWorkCode;
objCZKEM.GetWorkCode(dwWorkCode, out AWorkCode);
//objCZKEM.SaveTheDataToFile(objCZKEM.MachineNumber, "attendance.txt", 1);
while (true)
{
if (!objCZKEM.SSR_GetGeneralLogData(
objCZKEM.MachineNumber,
out dwEnrollNumber,
out dwVerifyMode,
out dwInOutMode,
out dwYear,
out dwMonth,
out dwDay,
out dwHour,
out dwMinute,
out dwSecond,
ref AWorkCode
))
{
break;
}
log = "User ID:" + dwEnrollNumber + " " + verificationMode(dwVerifyMode) + " " + InorOut(dwInOutMode) + " " + dwDay + "/" + dwMonth + "/" + dwYear + " " + time(dwHour) + ":" + time(dwMinute) + ":" + time(dwSecond);
Console.WriteLine(log);
//logs.Add(log);
}
}
//Console.ReadLine();
}
static void getAttendanceLogs(CZKEM objCZKEM)
{
string log;
string dwEnrollNumber;
int dwVerifyMode;
int dwInOutMode;
int dwYear;
int dwMonth;
int dwDay;
int dwHour;
int dwMinute;
int dwSecond;
int dwWorkCode = 1;
int AWorkCode;
objCZKEM.GetWorkCode(dwWorkCode, out AWorkCode);
//objCZKEM.SaveTheDataToFile(objCZKEM.MachineNumber, "attendance.txt", 1);
while (true)
{
if (!objCZKEM.SSR_GetGeneralLogData(
objCZKEM.MachineNumber,
out dwEnrollNumber,
out dwVerifyMode,
out dwInOutMode,
out dwYear,
out dwMonth,
out dwDay,
out dwHour,
out dwMinute,
out dwSecond,
ref AWorkCode
))
{
break;
}
log = "User ID:" + dwEnrollNumber + " " + verificationMode(dwVerifyMode) + " " + InorOut(dwInOutMode) + " " + dwDay + "/" + dwMonth + "/" + dwYear + " " + time(dwHour) + ":" + time(dwMinute) + ":" + time(dwSecond);
Console.WriteLine(log);
}
}
static string time(int Time)
{
string stringTime = "";
if (Time < 10)
{
stringTime = "0" + Time.ToString();
}
else
{
stringTime = Time.ToString();
}
return stringTime;
}
static string verificationMode(int verifyMode)
{
String mode = "";
switch (verifyMode)
{
case 0:
mode = "Password";
break;
case 1:
mode = "Fingerprint";
break;
case 2:
mode = "Card";
break;
}
return mode;
}
static string InorOut(int InOut)
{
string InOrOut = "";
switch (InOut)
{
case 0:
InOrOut = "IN";
break;
case 1:
InOrOut = "OUT";
break;
case 2:
InOrOut = "BREAK-OUT";
break;
case 3:
InOrOut = "BREAK-IN";
break;
case 4:
InOrOut = "OVERTIME-IN";
break;
case 5:
InOrOut = "OVERTIME-OUT";
break;
}
return InOrOut;
}
}
}

Throwing an Exception In an Xss Attack

This is a Web API which Json payloads (so, no Razor).
I'm using ASP.NET Core 2.1
1st up I should mention that I am sanitizing the relevant inputs with HtmlEncoder. However, that is just in case any gets past my validator, which I want to ask about here.
I want to write a validator which will return an error code where a user tries to include an html string in an input (using a mobile app, which would be a property in the json payload).
I've seen some naive implementation suggestion here on SO - usually just checking to see of the string contains '<' or '>' (and maybe one or 2 other chars).
I guess I would like to know if that is sufficient for the task at hand. There's no reason for a user to post any kind of html/xml in this domain.
A lot of the libraries around will sanitize input. But none of them seem to have a method which tells you if a string contains potentially harmful input.
As I said, I'm already sanitizing (as a last line of defence). But ideally I would return an error code before it gets to that.
Use this class from Microsoft ASP.NET Core 1
// <copyright file="CrossSiteScriptingValidation.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
public static class CrossSiteScriptingValidation
{
private static readonly char[] StartingChars = { '<', '&' };
#region Public methods
// Only accepts http: and https: protocols, and protocolless urls.
// Used by web parts to validate import and editor input on Url properties.
// Review: is there a way to escape colon that will still be recognized by IE?
// %3a does not work with IE.
public static bool IsDangerousUrl(string s)
{
if (string.IsNullOrEmpty(s))
{
return false;
}
// Trim the string inside this method, since a Url starting with whitespace
// is not necessarily dangerous. This saves the caller from having to pre-trim
// the argument as well.
s = s.Trim();
var len = s.Length;
if ((len > 4) &&
((s[0] == 'h') || (s[0] == 'H')) &&
((s[1] == 't') || (s[1] == 'T')) &&
((s[2] == 't') || (s[2] == 'T')) &&
((s[3] == 'p') || (s[3] == 'P')))
{
if ((s[4] == ':') || ((len > 5) && ((s[4] == 's') || (s[4] == 'S')) && (s[5] == ':')))
{
return false;
}
}
var colonPosition = s.IndexOf(':');
return colonPosition != -1;
}
public static bool IsValidJavascriptId(string id)
{
return (string.IsNullOrEmpty(id) || System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(id));
}
public static bool IsDangerousString(string s, out int matchIndex)
{
//bool inComment = false;
matchIndex = 0;
for (var i = 0; ;)
{
// Look for the start of one of our patterns
var n = s.IndexOfAny(StartingChars, i);
// If not found, the string is safe
if (n < 0) return false;
// If it's the last char, it's safe
if (n == s.Length - 1) return false;
matchIndex = n;
switch (s[n])
{
case '<':
// If the < is followed by a letter or '!', it's unsafe (looks like a tag or HTML comment)
if (IsAtoZ(s[n + 1]) || s[n + 1] == '!' || s[n + 1] == '/' || s[n + 1] == '?') return true;
break;
case '&':
// If the & is followed by a #, it's unsafe (e.g. S)
if (s[n + 1] == '#') return true;
break;
}
// Continue searching
i = n + 1;
}
}
#endregion
#region Private methods
private static bool IsAtoZ(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
#endregion
}
Then use this middleware to control URL,Query Parameteres and Content:
public class XssMiddleware
{
private readonly RequestDelegate _next;
public XssMiddleware(RequestDelegate next)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
_next = next;
}
public async Task Invoke(HttpContext context)
{
// Check XSS in URL
if (!string.IsNullOrWhiteSpace(context.Request.Path.Value))
{
var url = context.Request.Path.Value;
int matchIndex;
if (CrossSiteScriptingValidation.IsDangerousString(url, out matchIndex))
{
throw new CrossSiteScriptingException("YOUR_ERROR_MESSAGE");
}
}
// Check XSS in query string
if (!string.IsNullOrWhiteSpace(context.Request.QueryString.Value))
{
var queryString = WebUtility.UrlDecode(context.Request.QueryString.Value);
int matchIndex;
if (CrossSiteScriptingValidation.IsDangerousString(queryString, out matchIndex))
{
throw new CrossSiteScriptingException("YOUR_ERROR_MESSAGE");
}
}
// Check XSS in request content
var originalBody = context.Request.Body;
try
{
var content = await ReadRequestBody(context);
int matchIndex;
if (CrossSiteScriptingValidation.IsDangerousString(content, out matchIndex))
{
throw new CrossSiteScriptingException("YOUR_ERROR_MESSAGE");
}
await _next(context);
}
finally
{
context.Request.Body = originalBody;
}
}
private static async Task<string> ReadRequestBody(HttpContext context)
{
var buffer = new MemoryStream();
await context.Request.Body.CopyToAsync(buffer);
context.Request.Body = buffer;
buffer.Position = 0;
var encoding = Encoding.UTF8;
var contentType = context.Request.GetTypedHeaders().ContentType;
if (contentType?.Charset != null) encoding = Encoding.GetEncoding(contentType.Charset);
var requestContent = await new StreamReader(buffer, encoding).ReadToEndAsync();
context.Request.Body.Position = 0;
return requestContent;
}
}

Customizing summary section of TestNG emailable report

TestNG generates an emailable report. I have seen that this report can be customized by using Listeners. But could not get what i wanted. My requirement is to include extra details in the summary section of this report. I want to be able to add may be a new table or extra columns to show the environment details of the test execution.
Trying to attach a screenshot but apparently missing something and it does not come up.
That is what I have on my framework. I'll try to explain it (sorry my English)
Copy ReporterListenerAdapter.java and rename as MyReporterListenerAdapter.java, put it on your java project (/listener folder for example)
public class MyReporterListenerAdapter implements IReporter {
public void generateReport(List<XmlSuite> xml, List<ISuite> suites, String outdir) {}
}
Next, copy ReporterListener.java and rename as MyReporterListener.java
Too much code to paste here, but on createWriter function change the report name. For example: "emailable-MyFramework-report".
MyReporterListener.java
public class MyReporterListener extends MyReporterListenerAdapter {
private static final Logger L = Logger.getLogger(MyReporterListener.class);
// ~ Instance fields ------------------------------------------------------
private PrintWriter m_out;
private int m_row;
private Integer m_testIndex;
private int m_methodIndex;
private Scanner scanner;
// ~ Methods --------------------------------------------------------------
/** Creates summary of the run */
#Override
public void generateReport(List<XmlSuite> xml, List<ISuite> suites,
String outdir) {
try {
m_out = createWriter(outdir);
} catch (IOException e) {
L.error("output file", e);
return;
}
startHtml(m_out);
generateSuiteSummaryReport(suites);
generateMethodSummaryReport(suites);
generateMethodDetailReport(suites);
endHtml(m_out);
m_out.flush();
m_out.close();
}
protected PrintWriter createWriter(String outdir) throws IOException {
java.util.Date now = new Date();
new File(outdir).mkdirs();
return new PrintWriter(new BufferedWriter(new FileWriter(new File(
outdir, "emailable-FON-report"
+ DateFunctions.dateToDayAndTimeForFileName(now)
+ ".html"))));
}
/**
* Creates a table showing the highlights of each test method with links to
* the method details
*/
protected void generateMethodSummaryReport(List<ISuite> suites) {
m_methodIndex = 0;
startResultSummaryTable("methodOverview");
int testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(), 5);
}
Map<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext = r2.getTestContext();
String testName = testContext.getName();
m_testIndex = testIndex;
resultSummary(suite, testContext.getFailedConfigurations(),
testName, "failed", " (configuration methods)");
resultSummary(suite, testContext.getFailedTests(), testName,
"failed", "");
resultSummary(suite, testContext.getSkippedConfigurations(),
testName, "skipped", " (configuration methods)");
resultSummary(suite, testContext.getSkippedTests(), testName,
"skipped", "");
resultSummary(suite, testContext.getPassedTests(), testName,
"passed", "");
testIndex++;
}
}
m_out.println("</table>");
}
/** Creates a section showing known results for each method */
protected void generateMethodDetailReport(List<ISuite> suites) {
m_methodIndex = 0;
for (ISuite suite : suites) {
Map<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext = r2.getTestContext();
if (r.values().size() > 0) {
m_out.println("<h1>" + testContext.getName() + "</h1>");
}
resultDetail(testContext.getFailedConfigurations());
resultDetail(testContext.getFailedTests());
resultDetail(testContext.getSkippedConfigurations());
resultDetail(testContext.getSkippedTests());
resultDetail(testContext.getPassedTests());
}
}
}
/**
* #param tests
*/
private void resultSummary(ISuite suite, IResultMap tests, String testname,
String style, String details) {
if (tests.getAllResults().size() > 0) {
StringBuffer buff = new StringBuffer();
String lastClassName = "";
int mq = 0;
int cq = 0;
for (ITestNGMethod method : getMethodSet(tests, suite)) {
m_row += 1;
m_methodIndex += 1;
ITestClass testClass = method.getTestClass();
String className = testClass.getName();
if (mq == 0) {
String id = (m_testIndex == null ? null : "t"
+ Integer.toString(m_testIndex));
titleRow(testname + " — " + style + details, 5, id);
m_testIndex = null;
}
if (!className.equalsIgnoreCase(lastClassName)) {
if (mq > 0) {
cq += 1;
m_out.print("<tr class=\"" + style
+ (cq % 2 == 0 ? "even" : "odd") + "\">"
+ "<td");
if (mq > 1) {
m_out.print(" rowspan=\"" + mq + "\"");
}
m_out.println(">" + lastClassName + "</td>" + buff);
}
mq = 0;
buff.setLength(0);
lastClassName = className;
}
Set<ITestResult> resultSet = tests.getResults(method);
long end = Long.MIN_VALUE;
long start = Long.MAX_VALUE;
for (ITestResult testResult : tests.getResults(method)) {
if (testResult.getEndMillis() > end) {
end = testResult.getEndMillis();
}
if (testResult.getStartMillis() < start) {
start = testResult.getStartMillis();
}
}
mq += 1;
if (mq > 1) {
buff.append("<tr class=\"" + style
+ (cq % 2 == 0 ? "odd" : "even") + "\">");
}
String description = method.getDescription();
String testInstanceName = resultSet
.toArray(new ITestResult[] {})[0].getTestName();
buff.append("<td><a href=\"#m"
+ m_methodIndex
+ "\">"
+ qualifiedName(method)
+ " "
+ (description != null && description.length() > 0 ? "(\""
+ description + "\")"
: "")
+ "</a>"
+ (null == testInstanceName ? "" : "<br>("
+ testInstanceName + ")") + "</td>"
+ "<td class=\"numi\">" + resultSet.size() + "</td>"
+ "<td>" + start + "</td>" + "<td class=\"numi\">"
+ (end - start) + "</td>" + "</tr>");
}
if (mq > 0) {
cq += 1;
m_out.print("<tr class=\"" + style
+ (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
if (mq > 1) {
m_out.print(" rowspan=\"" + mq + "\"");
}
m_out.println(">" + lastClassName + "</td>" + buff);
}
}
}
/** Starts and defines columns result summary table */
private void startResultSummaryTable(String style) {
tableStart(style, "summary");
m_out.println("<tr><th>Class</th>"
+ "<th>Method</th><th># of<br/>Scenarios</th><th>Start</th><th>Time<br/>(ms)</th></tr>");
m_row = 0;
}
private String qualifiedName(ITestNGMethod method) {
StringBuilder addon = new StringBuilder();
String[] groups = method.getGroups();
int length = groups.length;
if (length > 0 && !"basic".equalsIgnoreCase(groups[0])) {
addon.append("(");
for (int i = 0; i < length; i++) {
if (i > 0) {
addon.append(", ");
}
addon.append(groups[i]);
}
addon.append(")");
}
return "<b>" + method.getMethodName() + "</b> " + addon;
}
private void resultDetail(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
ITestNGMethod method = result.getMethod();
m_methodIndex++;
String cname = method.getTestClass().getName();
m_out.println("<h2 id=\"m" + m_methodIndex + "\">" + cname + ":"
+ method.getMethodName() + "</h2>");
Set<ITestResult> resultSet = tests.getResults(method);
generateForResult(result, method, resultSet.size());
m_out.println("<p class=\"totop\">back to summary</p>");
}
}
/**
* Write the first line of the stack trace
*
* #param tests
*/
private void getShortException(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
m_methodIndex++;
Throwable exception = result.getThrowable();
List<String> msgs = Reporter.getOutput(result);
boolean hasReporterOutput = msgs.size() > 0;
boolean hasThrowable = exception != null;
if (hasThrowable) {
boolean wantsMinimalOutput = result.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.print("<h3>"
+ (wantsMinimalOutput ? "Expected Exception"
: "Failure") + "</h3>");
}
// Getting first line of the stack trace
String str = Utils.stackTrace(exception, true)[0];
scanner = new Scanner(str);
String firstLine = scanner.nextLine();
m_out.println(firstLine);
}
}
}
/**
* Write all parameters
*
* #param tests
*/
private void getParameters(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
m_methodIndex++;
Object[] parameters = result.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
for (Object p : parameters) {
m_out.println(Utils.escapeHtml(Utils.toString(p)) + " | ");
}
}
}
}
private void generateForResult(ITestResult ans, ITestNGMethod method,
int resultSetSize) {
Object[] parameters = ans.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
tableStart("result", null);
m_out.print("<tr class=\"param\">");
for (int x = 1; x <= parameters.length; x++) {
m_out.print("<th>Param." + x + "</th>");
}
m_out.println("</tr>");
m_out.print("<tr class=\"param stripe\">");
for (Object p : parameters) {
m_out.println("<td>" + Utils.escapeHtml(Utils.toString(p))
+ "</td>");
}
m_out.println("</tr>");
}
List<String> msgs = Reporter.getOutput(ans);
boolean hasReporterOutput = msgs.size() > 0;
Throwable exception = ans.getThrowable();
boolean hasThrowable = exception != null;
if (hasReporterOutput || hasThrowable) {
if (hasParameters) {
m_out.print("<tr><td");
if (parameters.length > 1) {
m_out.print(" colspan=\"" + parameters.length + "\"");
}
m_out.println(">");
} else {
m_out.println("<div>");
}
if (hasReporterOutput) {
if (hasThrowable) {
m_out.println("<h3>Test Messages</h3>");
}
for (String line : msgs) {
m_out.println(line + "<br/>");
}
}
if (hasThrowable) {
boolean wantsMinimalOutput = ans.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.println("<h3>"
+ (wantsMinimalOutput ? "Expected Exception"
: "Failure") + "</h3>");
}
generateExceptionReport(exception, method);
}
if (hasParameters) {
m_out.println("</td></tr>");
} else {
m_out.println("</div>");
}
}
if (hasParameters) {
m_out.println("</table>");
}
}
protected void generateExceptionReport(Throwable exception,
ITestNGMethod method) {
m_out.print("<div class=\"stacktrace\">");
m_out.print(Utils.stackTrace(exception, true)[0]);
m_out.println("</div>");
}
/**
* Since the methods will be sorted chronologically, we want to return the
* ITestNGMethod from the invoked methods.
*/
private Collection<ITestNGMethod> getMethodSet(IResultMap tests,
ISuite suite) {
List<IInvokedMethod> r = Lists.newArrayList();
List<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();
for (IInvokedMethod im : invokedMethods) {
if (tests.getAllMethods().contains(im.getTestMethod())) {
r.add(im);
}
}
Arrays.sort(r.toArray(new IInvokedMethod[r.size()]), new TestSorter());
List<ITestNGMethod> result = Lists.newArrayList();
// Add all the invoked methods
for (IInvokedMethod m : r) {
result.add(m.getTestMethod());
}
// Add all the methods that weren't invoked (e.g. skipped) that we
// haven't added yet
for (ITestNGMethod m : tests.getAllMethods()) {
if (!result.contains(m)) {
result.add(m);
}
}
return result;
}
#SuppressWarnings("unused")
public void generateSuiteSummaryReport(List<ISuite> suites) {
tableStart("testOverview", null);
m_out.print("<tr>");
tableColumnStart("Test");
tableColumnStart("Methods<br/>Passed");
tableColumnStart("Scenarios<br/>Passed");
tableColumnStart("# skipped");
tableColumnStart("# failed");
tableColumnStart("Error messages");
tableColumnStart("Parameters");
tableColumnStart("Start<br/>Time");
tableColumnStart("End<br/>Time");
tableColumnStart("Total<br/>Time");
tableColumnStart("Included<br/>Groups");
tableColumnStart("Excluded<br/>Groups");
m_out.println("</tr>");
NumberFormat formatter = new DecimalFormat("#,##0.0");
int qty_tests = 0;
int qty_pass_m = 0;
int qty_pass_s = 0;
int qty_skip = 0;
int qty_fail = 0;
long time_start = Long.MAX_VALUE;
long time_end = Long.MIN_VALUE;
m_testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(), 8);
}
Map<String, ISuiteResult> tests = suite.getResults();
for (ISuiteResult r : tests.values()) {
qty_tests += 1;
ITestContext overview = r.getTestContext();
startSummaryRow(overview.getName());
int q = getMethodSet(overview.getPassedTests(), suite).size();
qty_pass_m += q;
summaryCell(q, Integer.MAX_VALUE);
q = overview.getPassedTests().size();
qty_pass_s += q;
summaryCell(q, Integer.MAX_VALUE);
q = getMethodSet(overview.getSkippedTests(), suite).size();
qty_skip += q;
summaryCell(q, 0);
q = getMethodSet(overview.getFailedTests(), suite).size();
qty_fail += q;
summaryCell(q, 0);
// NEW
// Insert error found
m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");
getShortException(overview.getFailedTests());
getShortException(overview.getSkippedTests());
m_out.println("</td>");
// NEW
// Add parameters for each test case (failed or passed)
m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");
// Write OS and Browser
// m_out.println(suite.getParameter("os").substring(0, 3) +
// " | "
// + suite.getParameter("browser").substring(0, 3) + " | ");
getParameters(overview.getFailedTests());
getParameters(overview.getPassedTests());
getParameters(overview.getSkippedTests());
m_out.println("</td>");
// NEW
summaryCell(
DateFunctions.dateToDayAndTime(overview.getStartDate()),
true);
m_out.println("</td>");
summaryCell(
DateFunctions.dateToDayAndTime(overview.getEndDate()),
true);
m_out.println("</td>");
time_start = Math.min(overview.getStartDate().getTime(),
time_start);
time_end = Math.max(overview.getEndDate().getTime(), time_end);
summaryCell(
formatter.format((overview.getEndDate().getTime() - overview
.getStartDate().getTime()) / 1000.)
+ " seconds", true);
summaryCell(overview.getIncludedGroups());
summaryCell(overview.getExcludedGroups());
m_out.println("</tr>");
m_testIndex++;
}
}
if (qty_tests > 1) {
m_out.println("<tr class=\"total\"><td>Total</td>");
summaryCell(qty_pass_m, Integer.MAX_VALUE);
summaryCell(qty_pass_s, Integer.MAX_VALUE);
summaryCell(qty_skip, 0);
summaryCell(qty_fail, 0);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(
formatter.format(((time_end - time_start) / 1000.) / 60.)
+ " minutes", true);
m_out.println("<td colspan=\"3\"> </td></tr>");
}
m_out.println("</table>");
}
private void summaryCell(String[] val) {
StringBuffer b = new StringBuffer();
for (String v : val) {
b.append(v + " ");
}
summaryCell(b.toString(), true);
}
private void summaryCell(String v, boolean isgood) {
m_out.print("<td class=\"numi" + (isgood ? "" : "_attn") + "\">" + v
+ "</td>");
}
private void startSummaryRow(String label) {
m_row += 1;
m_out.print("<tr"
+ (m_row % 2 == 0 ? " class=\"stripe\"" : "")
+ "><td style=\"text-align:left;padding-right:2em\"><a href=\"#t"
+ m_testIndex + "\">" + label + "</a>" + "</td>");
}
private void summaryCell(int v, int maxexpected) {
summaryCell(String.valueOf(v), v <= maxexpected);
}
private void tableStart(String cssclass, String id) {
m_out.println("<table cellspacing=\"0\" cellpadding=\"0\""
+ (cssclass != null ? " class=\"" + cssclass + "\""
: " style=\"padding-bottom:2em\"")
+ (id != null ? " id=\"" + id + "\"" : "") + ">");
m_row = 0;
}
private void tableColumnStart(String label) {
m_out.print("<th>" + label + "</th>");
}
private void titleRow(String label, int cq) {
titleRow(label, cq, null);
}
private void titleRow(String label, int cq, String id) {
m_out.print("<tr");
if (id != null) {
m_out.print(" id=\"" + id + "\"");
}
m_out.println("><th colspan=\"" + cq + "\">" + label + "</th></tr>");
m_row = 0;
}
/** Starts HTML stream */
protected void startHtml(PrintWriter out) {
out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
out.println("<head>");
out.println("<title>Hector Flores - TestNG Report</title>");
out.println("<style type=\"text/css\">");
out.println("table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");
out.println("td,th {border:1px solid #009;padding:.25em .5em}");
out.println(".result th {vertical-align:bottom}");
out.println(".param th {padding-left:1em;padding-right:1em}");
out.println(".param td {padding-left:.5em;padding-right:2em}");
out.println(".stripe td,.stripe th {background-color: #E6EBF9}");
out.println(".numi,.numi_attn {text-align:right}");
out.println(".total td {font-weight:bold}");
out.println(".passedodd td {background-color: #0A0}");
out.println(".passedeven td {background-color: #3F3}");
out.println(".skippedodd td {background-color: #CCC}");
out.println(".skippedodd td {background-color: #DDD}");
out.println(".failedodd td,.numi_attn {background-color: #F33}");
out.println(".failedeven td,.stripe .numi_attn {background-color: #D00}");
out.println(".stacktrace {white-space:pre;font-family:monospace}");
out.println(".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");
out.println("</style>");
out.println("</head>");
out.println("<body>");
}
/** Finishes HTML stream */
protected void endHtml(PrintWriter out) {
out.println("<center> Report customized by Hector Flores [hectorfb#gmail.com] </center>");
out.println("</body></html>");
}
// ~ Inner Classes --------------------------------------------------------
/** Arranges methods by classname and method name */
private class TestSorter implements Comparator<IInvokedMethod> {
// ~ Methods
// -------------------------------------------------------------
/** Arranges methods by classname and method name */
#Override
public int compare(IInvokedMethod o1, IInvokedMethod o2) {
// System.out.println("Comparing " + o1.getMethodName() + " " +
// o1.getDate()
// + " and " + o2.getMethodName() + " " + o2.getDate());
return (int) (o1.getDate() - o2.getDate());
// int r = ((T) o1).getTestClass().getName().compareTo(((T)
// o2).getTestClass().getName());
// if (r == 0) {
// r = ((T) o1).getMethodName().compareTo(((T) o2).getMethodName());
// }
// return r;
}
}
}
With those steps you already have your listener ready to listen.
How to call it?
If you use testng.xml add the following lines:
<listeners>
<listener class-name='[your_class_path].MyReporterListener'/>
</listeners>
If you run your tests from a java class, add the following lines:
private final static MyReporterListener frl = new MyReporterListener();
TestNG testng = new TestNG();
testng.addListener(frl);
With those steps, when you execute your tests you'll have two emailable reports, customized and original.
Now it's time to pimp your report.
In my case I had to add error messages, parameters and times (start and end), because it's very useful if you want to paste on an excel file.
My customized report:
You have to mainly modify generateSuiteSummaryReport(List suites) function.
Play with that and ask me if you have any problem.
Make a you CSS and embed it in EmailableReporter.class. This class file can be found in TestNG folder> org.testNg.reporters> EmailableReporter.class.
There you can edit the Style of the TestNG report HTML file which starts with
protected void startHtml(PrintWriter out)
Better you can use extent report html reporting library jar file. However i am using extentreport1.4.jar jar file. So you will get summary details at right corner of your report
Using the above code, I am facing the below null pointer issue, HTML report works fine when fewer tests are included in testng.xml, but when I include more tests, the HTML report does not get generated and the below exception is thrown
[TestNG] Reporter report.MyListener#60e07aed failed
java.lang.NullPointerException: Cannot invoke "String.length()" because "s" is null
at java.base/java.io.StringReader.<init>(StringReader.java:51)
at java.base/java.util.Scanner.<init>(Scanner.java:766)
at report.MyListener.getShortException(MyListener.java:294)
at report.MyListener.generateSuiteSummaryReport(MyListener.java:473)
at report.MyListener.generateReport(MyListener.java:72)
at org.testng.TestNG.generateReports(TestNG.java:1093)
at org.testng.TestNG.run(TestNG.java:1036)
at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:115)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)
To fix the above added a null check to avoid Null Pointer at line 295
if(str!=null) {
scanner = new Scanner(str);
String firstLine = scanner.nextLine()+"<br>";
m_out.println(firstLine);
}

TinyMCE ashx getting a 404 error from IIS7

I have an ASP.Net MVC 4 project. If I want TinyMCE to use gzip I need to use the following in my page (for example):
<script type="text/javascript" src="/Scripts/tiny_mce/tiny_mce_gzip.js"></script>
<script type="text/javascript">
tinyMCE_GZ.init({
plugins: 'style,layer,table,save,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras',
themes: 'simple,advanced',
languages: 'en',
disk_cache: true,
debug: false
});
</script>
I noticed this works great in testing using the development web server, but when deployed to IIS7 it does not. Further investigation shows a 404 (file not found) on the request made for:
/Scripts/tiny_mce/tiny_mce_gzip.ashx?js=true&diskcache=true&core=true&suffix=&themes=simple%2Cadvanced&plugins=style%2Clayer...
The ashx file DOES exist in the corresponding folder but IIS will not serve it for some reason. I tried adding the following route handlers but neither made any difference:
routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");
routes.IgnoreRoute("{*allashx}", new { allashx = #".*\.ashx(/.*)?" });
SOLVED
Having scoured the internet, I see that there are many that have the same problem and NOBODY seems to have found a solution! (even on the TinyMCE support pages). So I made a solution which I hope doesnt get cussed :)
The only thing you need to configure is "TinyMceScriptFolder" variable in the Global.asax - it must point to your TinyMCE scripts folder (duh) (make sure you dont begin that path with a / otherwise the route handler will reject it. It will work from the root of your site in any case)
TinyMCEGzipHandler.cs (copied from the original .ashx file, but with a couple of additions)
using System;
using System.Web;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Property;
namespace Softwarehouse.TinyMCE
{
public class GzipHandler : IHttpHandler
{
private HttpResponse Response;
private HttpRequest Request;
private HttpServerUtility Server;
public void ProcessRequest(HttpContext context)
{
this.Response = context.Response;
this.Request = context.Request;
this.Server = context.Server;
this.StreamGzipContents();
}
public bool IsReusable
{
get { return false; }
}
#region private
private void StreamGzipContents()
{
string cacheKey = "", cacheFile = "", content = "", enc, suffix, cachePath;
string[] plugins, languages, themes;
bool diskCache, supportsGzip, isJS, compress, core;
int i, x, expiresOffset;
GZipStream gzipStream;
Encoding encoding = Encoding.GetEncoding("windows-1252");
byte[] buff;
// Get input
plugins = GetParam("plugins", "").Split(',');
languages = GetParam("languages", "").Split(',');
themes = GetParam("themes", "").Split(',');
diskCache = GetParam("diskcache", "") == "true";
isJS = GetParam("js", "") == "true";
compress = GetParam("compress", "true") == "true";
core = GetParam("core", "true") == "true";
suffix = GetParam("suffix", "") == "_src" ? "_src" : "";
cachePath = Server.MapPath("/" + MvcApplication.TinyMceScriptFolder); // Cache path, this is where the .gz files will be stored
expiresOffset = 10; // Cache for 10 days in browser cache
// Custom extra javascripts to pack
string[] custom =
{
/*
"some custom .js file",
"some custom .js file"
*/
};
// Set response headers
Response.ContentType = "text/javascript";
Response.Charset = "UTF-8";
Response.Buffer = false;
// Setup cache
Response.Cache.SetExpires(DateTime.Now.AddDays(expiresOffset));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(false);
// Vary by all parameters and some headers
Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
Response.Cache.VaryByParams["theme"] = true;
Response.Cache.VaryByParams["language"] = true;
Response.Cache.VaryByParams["plugins"] = true;
Response.Cache.VaryByParams["lang"] = true;
Response.Cache.VaryByParams["index"] = true;
// Is called directly then auto init with default settings
if (!isJS)
{
Response.WriteFile(Server.MapPath("/" + MvcApplication.TinyMceScriptFolder + "/tiny_mce_gzip.js"));
Response.Write("tinyMCE_GZ.init({});");
return;
}
// Setup cache info
if (diskCache)
{
cacheKey = GetParam("plugins", "") + GetParam("languages", "") + GetParam("themes", "");
for (i = 0; i < custom.Length; i++)
cacheKey += custom[i];
cacheKey = MD5(cacheKey);
if (compress)
cacheFile = cachePath + "/tiny_mce_" + cacheKey + ".gz";
else
cacheFile = cachePath + "/tiny_mce_" + cacheKey + ".js";
}
// Check if it supports gzip
enc = Regex.Replace("" + Request.Headers["Accept-Encoding"], #"\s+", "").ToLower();
supportsGzip = enc.IndexOf("gzip") != -1 || Request.Headers["---------------"] != null;
enc = enc.IndexOf("x-gzip") != -1 ? "x-gzip" : "gzip";
// Use cached file disk cache
if (diskCache && supportsGzip && File.Exists(cacheFile))
{
Response.AppendHeader("Content-Encoding", enc);
Response.WriteFile(cacheFile);
return;
}
// Add core
if (core)
{
content += GetFileContents("tiny_mce" + suffix + ".js");
// Patch loading functions
content += "tinyMCE_GZ.start();";
}
// Add core languages
for (x = 0; x < languages.Length; x++)
content += GetFileContents("langs/" + languages[x] + ".js");
// Add themes
for (i = 0; i < themes.Length; i++)
{
content += GetFileContents("themes/" + themes[i] + "/editor_template" + suffix + ".js");
for (x = 0; x < languages.Length; x++)
content += GetFileContents("themes/" + themes[i] + "/langs/" + languages[x] + ".js");
}
// Add plugins
for (i = 0; i < plugins.Length; i++)
{
content += GetFileContents("plugins/" + plugins[i] + "/editor_plugin" + suffix + ".js");
for (x = 0; x < languages.Length; x++)
content += GetFileContents("plugins/" + plugins[i] + "/langs/" + languages[x] + ".js");
}
// Add custom files
for (i = 0; i < custom.Length; i++)
content += GetFileContents(custom[i]);
// Restore loading functions
if (core)
content += "tinyMCE_GZ.end();";
// Generate GZIP'd content
if (supportsGzip)
{
if (compress)
Response.AppendHeader("Content-Encoding", enc);
if (diskCache && cacheKey != "")
{
// Gzip compress
if (compress)
{
using (Stream fileStream = File.Create(cacheFile))
{
gzipStream = new GZipStream(fileStream, CompressionMode.Compress, true);
buff = encoding.GetBytes(content.ToCharArray());
gzipStream.Write(buff, 0, buff.Length);
gzipStream.Close();
}
}
else
{
using (StreamWriter sw = File.CreateText(cacheFile))
{
sw.Write(content);
}
}
// Write to stream
Response.WriteFile(cacheFile);
}
else
{
gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress, true);
buff = encoding.GetBytes(content.ToCharArray());
gzipStream.Write(buff, 0, buff.Length);
gzipStream.Close();
}
}
else
Response.Write(content);
}
private string GetParam(string name, string def)
{
string value = Request.QueryString[name] != null ? "" + Request.QueryString[name] : def;
return Regex.Replace(value, #"[^0-9a-zA-Z\\-_,]+", "");
}
private string GetFileContents(string path)
{
try
{
string content;
path = Server.MapPath("/" + MvcApplication.TinyMceScriptFolder + "/" + path);
if (!File.Exists(path))
return "";
StreamReader sr = new StreamReader(path);
content = sr.ReadToEnd();
sr.Close();
return content;
}
catch (Exception ex)
{
// Ignore any errors
}
return "";
}
private string MD5(string str)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(Encoding.ASCII.GetBytes(str));
str = BitConverter.ToString(result);
return str.Replace("-", "");
}
#endregion
}
}
Global.asax
public const string TinyMceScriptFolder = "Scripts/htmleditor";
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(TinyMceScriptFolder + "/tiny_mce_gzip.ashx");
}
Web.config
<system.webServer>
<httpHandlers>
<add name="TinyMCEGzip" verb="GET" path="tiny_mce_gzip.ashx" type="Softwarehouse.TinyMCE.GzipHandler"/>
</httpHandlers>
</system.webServer>

SMPP error code 61,62

I am using the jsmpp lib for sending sms. The SMSC center returns negative response like 61,62 which are Invalid scheduled delivery time and Invalid Validty Period value. After talking with SMSC support, they require to set a default timeout for the message to be delivered, after some search on jsmpp site, didn't find it. Thanks for any suggestions ?
According to SMPP standard it should be possible to leave both of these null, but if Validity Period is required, this can be either an absolute date or a relative one.
The format should be YYMMDDhhmmsstnnp, where
YY is a two digit year (00-99)
MM is month (01-12)
DD is day (01-31)
hh is hours (00-23)
mm is minutes (00-59)
ss is seconds (00-59)
t is tenths of second (00-59)
nn is the time difference in quarter hours between local time and UTC (00-48)
p can be one of the following :-
'+' local time is ahead of UTC.
'-' local time is behind UTC.
'R' This is a relative time.
So to make the validity period 1 hour using a relative time use the following: "000000010000000R"
In my project I didn't have business requirement to schedule delivery time and set validity Period, so I set them null and it's work fine :-)
I use this class to load smpp config from properties file. Code that will using it will looks more readable and simple :-)
SMPPConfigManager is an interface for this class. It's possible to read this config not only from properties file. For example from Db and you can then implement this interface in new class.
package ru.rodin.denis.smpp;
import java.util.Properties;
import org.jsmpp.bean.*;
/**
*
* #author Denis Rodin
*/
public class SMPPFileConfig implements SMPPConfigManager {
private String host;
private int port;
private String systemId;
private String password;
private String systemType;
private TypeOfNumber sourceAddrTon;
private TypeOfNumber destAddrTon;
private NumberingPlanIndicator sourceAddrNpi;
private NumberingPlanIndicator destAddrNpi;
private String addressRange;
private int connectTimeout;
private long reconnectInterval;
private String sourceAddr;
private String destinationAddr;
private SMSCDeliveryReceipt deliveryReceipt;
private RegisteredDelivery registeredDelivery;
private BindType bindType;
private ESMClass esmClass;
private byte protocolId;
private byte priorityFlag;
private String scheduleDeliveryTime;
private String validityPeriod;
private byte replaceIfPresentFlag;
private GeneralDataCoding generalDataCoding;
private boolean generalDataCoding_compressed = true;
private boolean generalDataCoding_containMessageClass = true;
private MessageClass generalDataCoding_messageClass = MessageClass.CLASS1;
private Alphabet generalDataCoding_alphabet = Alphabet.ALPHA_DEFAULT;
private byte smDefaultMsgId;
private long transactionTimer;
private int enquireLinkTimer;
public SMPPFileConfig(Properties prop) {
this.host = prop.getProperty("smpp.host");
this.port = Integer.parseInt(prop.getProperty("smpp.port"));
this.systemId = prop.getProperty("smpp.systemId");
this.password = prop.getProperty("smpp.password");
this.systemType = prop.getProperty("smpp.systemType");
this.sourceAddrTon = getTypeOfNumber(SMPPConfigManager.AddrTon.SOURCE, prop);
this.destAddrTon = getTypeOfNumber(SMPPConfigManager.AddrTon.DEST, prop);
this.sourceAddrNpi = getNumberingPlanIndicator(SMPPConfigManager.AddrNpi.SOURCE, prop);
this.destAddrNpi = getNumberingPlanIndicator(SMPPConfigManager.AddrNpi.DEST, prop);
this.addressRange = prop.getProperty("smpp.addressRange");
this.connectTimeout = Integer.parseInt(prop.getProperty("smpp.connect.timeout"));
this.reconnectInterval = Long.parseLong(prop.getProperty("smpp.reconnect.interval"));
this.sourceAddr = prop.getProperty("smpp.sourceAddr");
this.destinationAddr = null;
this.deliveryReceipt = getSMSCDeliveryReceipt(prop.getProperty("smpp.SMSC.delivery.receipt"));
this.registeredDelivery = new RegisteredDelivery(deliveryReceipt);
this.bindType = getBindTypeFromProp(prop.getProperty("smpp.bindType"));
this.esmClass = createESMClass(prop.getProperty("smpp.ESMClass.MessageMode"), prop.getProperty("smpp.ESMClass.MessageType"), prop.getProperty("smpp.ESMClass.GSMSpecificFeature"));
this.protocolId = new Byte(prop.getProperty("smpp.protocolId"));
this.priorityFlag = new Byte(prop.getProperty("smpp.priorityFlag"));
this.scheduleDeliveryTime = prop.getProperty("smpp.scheduleDeliveryTime");
this.validityPeriod = prop.getProperty("smpp.validityPeriod");
this.replaceIfPresentFlag = new Byte(prop.getProperty("smpp.replaceIfPresentFlag"));
this.generalDataCoding = new GeneralDataCoding(generalDataCoding_compressed, generalDataCoding_containMessageClass, generalDataCoding_messageClass, generalDataCoding_alphabet);
this.smDefaultMsgId = new Byte(prop.getProperty("smpp.smDefaultMsgId"));
this.transactionTimer = Long.parseLong(prop.getProperty("smpp.transactionTimer"));
this.enquireLinkTimer = Integer.parseInt(prop.getProperty("smpp.enquireLinkTimer"));
}
#Override
public String toString() {
return "SMPPFileConfig{" + "host=" + host + ", port=" + port + ", systemId=" + systemId + ", password=" + password + ", systemType=" + systemType + ", sourceAddrTon=" + sourceAddrTon + ", destAddrTon=" + destAddrTon + ", sourceAddrNpi=" + sourceAddrNpi + ", destAddrNpi=" + destAddrNpi + ", addressRange=" + addressRange + ", connectTimeout=" + connectTimeout + ", reconnectInterval=" + reconnectInterval + ", sourceAddr=" + sourceAddr + ", destinationAddr=" + destinationAddr + ", deliveryReceipt=" + deliveryReceipt + ", registeredDelivery=" + registeredDelivery + ", bindType=" + bindType + ", esmClass=" + esmClass + ", protocolId=" + protocolId + ", priorityFlag=" + priorityFlag + ", scheduleDeliveryTime=" + scheduleDeliveryTime + ", validityPeriod=" + validityPeriod + ", replaceIfPresentFlag=" + replaceIfPresentFlag + ", generalDataCoding=" + generalDataCoding + ", generalDataCoding_compressed=" + generalDataCoding_compressed + ", generalDataCoding_containMessageClass=" + generalDataCoding_containMessageClass + ", generalDataCoding_messageClass=" + generalDataCoding_messageClass + ", generalDataCoding_alphabet=" + generalDataCoding_alphabet + ", smDefaultMsgId=" + smDefaultMsgId + '}';
}
#Override
public String getAddressRange() {
return addressRange;
}
#Override
public int getConnectTimeout() {
return connectTimeout;
}
#Override
public SMSCDeliveryReceipt getDeliveryReceipt() {
return deliveryReceipt;
}
#Override
public RegisteredDelivery getRegisteredDelivery() {
return registeredDelivery;
}
#Override
public NumberingPlanIndicator getDestAddrNpi() {
return destAddrNpi;
}
#Override
public TypeOfNumber getDestAddrTon() {
return destAddrTon;
}
#Override
public void setDestinationAddr(String destinationAddr) {
this.destinationAddr = destinationAddr;
}
#Override
public String getDestinationAddr() {
return destinationAddr;
}
#Override
public String getHost() {
return host;
}
#Override
public String getPassword() {
return password;
}
#Override
public int getPort() {
return port;
}
#Override
public long getReconnectInterval() {
return reconnectInterval;
}
#Override
public String getSourceAddr() {
return sourceAddr;
}
#Override
public NumberingPlanIndicator getSourceAddrNpi() {
return sourceAddrNpi;
}
#Override
public TypeOfNumber getSourceAddrTon() {
return sourceAddrTon;
}
#Override
public String getSystemId() {
return systemId;
}
#Override
public String getSystemType() {
return systemType;
}
#Override
public BindType getBindType() {
return bindType;
}
#Override
public ESMClass getESMClass() {
return esmClass;
}
#Override
public void setESMClass(ESMClass esmClass) {
this.esmClass = esmClass;
}
#Override
public byte getProtocolId() {
return protocolId;
}
#Override
public byte getPriorityFlag() {
return priorityFlag;
}
#Override
public String getScheduleDeliveryTime() {
return scheduleDeliveryTime;
}
#Override
public String getValidityPeriod() {
return validityPeriod;
}
#Override
public byte getReplaceIfPresentFlag() {
return replaceIfPresentFlag;
}
#Override
public GeneralDataCoding getGeneralDataCoding() {
return generalDataCoding;
}
#Override
public byte getsmDefaultMsgId(){
return smDefaultMsgId;
}
#Override
public long getTransactionTimer()
{
return transactionTimer;
}
#Override
public int getEnquireLinkTimer()
{
return enquireLinkTimer;
}
private ESMClass createESMClass(String messageMode, String messageType, String GSMSpecificFeature) {
return new ESMClass(getESMClassMessageMode(messageMode), getESMMessageType(messageType), getESMGSMSpecificFeature(GSMSpecificFeature));
}
private MessageMode getESMClassMessageMode(String type) {
if (type.equals("DEFAULT")) {
return MessageMode.DEFAULT;
} else if (type.equals("DATAGRAM")) {
return MessageMode.DATAGRAM;
} else if (type.equals("STORE_AND_FORWARD")) {
return MessageMode.STORE_AND_FORWARD;
} else if (type.equals("TRANSACTION")) {
return MessageMode.TRANSACTION;
} else {
return null;
}
}
private MessageType getESMMessageType(String type) {
if (type.equals("DEFAULT")) {
return MessageType.DEFAULT;
} else if (type.equals("CONV_ABORT")) {
return MessageType.CONV_ABORT;
} else if (type.equals("ESME_DEL_ACK")) {
return MessageType.ESME_DEL_ACK;
} else if (type.equals("ESME_MAN_ACK")) {
return MessageType.ESME_MAN_ACK;
} else if (type.equals("INTER_DEL_NOTIF")) {
return MessageType.INTER_DEL_NOTIF;
} else if (type.equals("SME_DEL_ACK")) {
return MessageType.SME_DEL_ACK;
} else if (type.equals("SME_MAN_ACK")) {
return MessageType.SME_MAN_ACK;
} else if (type.equals("SMSC_DEL_RECEIPT")) {
return MessageType.SMSC_DEL_RECEIPT;
} else {
return null;
}
}
private GSMSpecificFeature getESMGSMSpecificFeature(String type) {
if (type.equals("DEFAULT")) {
return GSMSpecificFeature.DEFAULT;
} else if (type.equals("REPLYPATH")) {
return GSMSpecificFeature.REPLYPATH;
} else if (type.equals("UDHI")) {
return GSMSpecificFeature.UDHI;
} else if (type.equals("UDHI_REPLYPATH")) {
return GSMSpecificFeature.UDHI_REPLYPATH;
} else {
return null;
}
}
private BindType getBindTypeFromProp(String type) {
//String type = prop.getProperty("smpp.bindType");
if (type.equals("BIND_RX")) {
return BindType.BIND_RX;
} else if (type.equals("BIND_TX")) {
return BindType.BIND_TX;
} else if (type.equals("BIND_TRX")) {
return BindType.BIND_TRX;
} else {
return null;
}
}
private TypeOfNumber getTypeOfNumber(SMPPConfigManager.AddrTon ton, Properties prop) {
String type;
if (ton == SMPPConfigManager.AddrTon.SOURCE) {
type = prop.getProperty("smpp.sourceAddrTon");
} else {
type = prop.getProperty("smpp.destAddrTon");
}
if (type.equals("ABBREVIATED")) {
return TypeOfNumber.ABBREVIATED;
} else if (type.equals("ALPHANUMERIC")) {
return TypeOfNumber.ALPHANUMERIC;
} else if (type.equals("INTERNATIONAL")) {
return TypeOfNumber.INTERNATIONAL;
} else if (type.equals("NATIONAL")) {
return TypeOfNumber.NATIONAL;
} else if (type.equals("NETWORK_SPECIFIC")) {
return TypeOfNumber.NETWORK_SPECIFIC;
} else if (type.equals("SUBSCRIBER_NUMBER")) {
return TypeOfNumber.SUBSCRIBER_NUMBER;
} else if (type.equals("UNKNOWN")) {
return TypeOfNumber.UNKNOWN;
} else {
return null;
}
}
private SMSCDeliveryReceipt getSMSCDeliveryReceipt(String type) {
//String type = prop.getProperty("smpp.SMSC.delivery.receipt");
if (type.equals("DEFAULT")) {
return SMSCDeliveryReceipt.DEFAULT;
} else if (type.equals("SUCCESS")) {
return SMSCDeliveryReceipt.SUCCESS;
} else if (type.equals("SUCCESS_FAILURE")) {
return SMSCDeliveryReceipt.SUCCESS_FAILURE;
} else {
return null;
}
}
private NumberingPlanIndicator getNumberingPlanIndicator(SMPPConfigManager.AddrNpi npi, Properties prop) {
String type;
if (npi == SMPPConfigManager.AddrNpi.SOURCE) {
type = prop.getProperty("smpp.sourceAddrNpi");
} else {
type = prop.getProperty("smpp.destAddrNpi");
}
if (type.equals("DATA")) {
return NumberingPlanIndicator.DATA;
} else if (type.equals("ERMES")) {
return NumberingPlanIndicator.ERMES;
} else if (type.equals("INTERNET")) {
return NumberingPlanIndicator.INTERNET;
} else if (type.equals("ISDN")) {
return NumberingPlanIndicator.ISDN;
} else if (type.equals("LAND_MOBILE")) {
return NumberingPlanIndicator.LAND_MOBILE;
} else if (type.equals("NATIONAL")) {
return NumberingPlanIndicator.NATIONAL;
} else if (type.equals("PRIVATE")) {
return NumberingPlanIndicator.PRIVATE;
} else if (type.equals("TELEX")) {
return NumberingPlanIndicator.TELEX;
} else if (type.equals("WAP")) {
return NumberingPlanIndicator.WAP;
} else if (type.equals("UNKNOWN")) {
return NumberingPlanIndicator.UNKNOWN;
} else {
return null;
}
}
}
when you submit current time as scheduled delivery time this error may occur.because it takes some time to send request. so the time you mentioned might be in past .so set the scheduled delivery time to (current time + 10 seconds )
long TEN_SECONDS=10000;//millisecs
Calendar date = Calendar.getInstance();
long t= date.getTimeInMillis();
Date scheduleDeliveryTime=new Date(t + ( TEN_SECONDS));