Short Version:
If I create a System.Web.HttpException as follows:
var exception = new HttpException(403, "Forbidden");
I would expect the following methods to return these values, but they don't:
var code = exception.GetHttpCode(); // is 0
var msg = exception.GetHtmlErrorMessage(); // is: null
Edit: In fact GetHttpCode() returns the correct number when being called the first time, but returns 0 when being called a second time:
var code = exception.GetHttpCode(); // is 403
code = exception.GetHttpCode(); // is 0
Long Version:
I am trying to unit-test the ASP.NET global exception handling method "Application_Error". This is an extract of the code:
var httpException = server.GetLastError() as HttpException;
if (httpException != null)
{
response.StatusCode = httpException.GetHttpCode();
response.StatusDescription = httpException.GetHtmlErrorMessage();
// ...
The unit test calls this method with a mock ServerUtilityBase object (Moq) which returns an HttpException when server.GetLastError() is called:
var exception = new HttpException(403, "Forbidden");
serverMock.Setup(server => server.GetLastError()).Returns(exception);
// ...
Unfortunately I had to find out that in the error handling code, httpException.GetHttpCode() and httpException.GetHtmlErrorMessage() methods return 0 or null, respectively.
What needs to be done to make a new HttpException(403, "Forbidden") return 403 or "Forbidden" when calling these methods?
Unfortunately it is not possible to create a Mock of the exception by subclassing it, because the said methods are sealed.
The httpException.GetHtmlErrorMessage() method is used to return the html which is sent to the browser by the ASP.NET Runtime, Patrick is correct in that you need to look at the .Message property to see the text "forbidden". If you want to see what the actual html looks like, you would need to supply an inner exception which is an exception type that the ErrorFormatter can use (for example a SecurityException).
var httpException = new HttpException(403, "Forbidden", new SecurityException());
Console.WriteLine(httpException.GetHttpCode());
Console.WriteLine(httpException.Message);
Console.WriteLine(httpException.GetHtmlErrorMessage());
will output:
403
Forbidden
<html>
<head>
<title>Security Exception</title>
<style>
body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:bl
ack;}
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5p
x}
b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red
}
H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maro
on }
pre {font-family:"Lucida Console";font-size: .9em}
.marker {font-weight: bold; color: black;text-decoration: none;}
.version {color: gray;}
.error {margin-bottom: 10px;}
.expandable { text-decoration:underline; font-weight:bold; color:navy;
cursor:hand; }
</style>
</head>
<body bgcolor="white">
<span><H1>Server Error in '' Application.<hr width=100% size=1 color
=silver></H1>
<h2> <i>Security Exception</i> </h2></span>
<font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">
<b> Description: </b>The application attempted to perform an operati
on not allowed by the security policy. To grant this application the requi
red permission please contact your system administrator or change the applicatio
n's trust level in the configuration file.
<br><br>
<b> Exception Details: </b>System.Security.SecurityException: Securi
ty error.<br><br>
<b>Source Error:</b> <br><br>
<table width=100% bgcolor="#ffffcc">
<tr>
<td>
<code>
An unhandled exception was generated during the execution of the current web req
uest. Information regarding the origin and location of the exception can be iden
tified using the exception stack trace below.</code>
</td>
</tr>
</table>
<br>
<b>Stack Trace:</b> <br><br>
<table width=100% bgcolor="#ffffcc">
<tr>
<td>
<code><pre>
[SecurityException: Security error.]
</pre></code>
</td>
</tr>
</table>
<br>
</body>
</html>
Related
"Neither BindingResult nor plain target object for bean name 'loginCommand' available as request attribute"
I keep getting this Binding result error and nothing I've tried seems to be making it stop. I've seen other posts for this question, but none of them seem to fix whatever issue I'm having.
This is the first controller of my new project and I had some issues getting the xml squared away. I think that's all fixed, but if nothing looks off I supposed the problem could be there. The weird thing is that all this code is almost straight copied from another project I have and it works just fine.
Also I'm running on glassfish if that matters at all. Thanks in advance!
edit: The webpage is /morencore/login.jsp. I tried going to login.html assuming that would bring it up, but it only seems to work when I go to login.jsp. I believe I tried changing my controller to map to the jsp instead, but that did not work.
here is my login.jsp page:
<form:form method="post" modelAttribute="loginCommand">
<form:errors cssClass="error" element="p" />
<table border="0">
<tr>
<td align="right">Username:</td>
<td><form:input path="userName" /> <form:errors path="userName" cssClass="error" /></td>
</tr>
<tr>
<td align="right">Password:</td>
<td><form:password path="password" /> <form:errors path="password" cssClass="error" /></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" id="submit" name="submit" value="Log In" disabled="disabled"></td>
</tr>
</table>
</form:form>
and here is my controller:
#Controller
#ControllerAdvice
#RequestMapping("/login.html")
public class LoginController {
protected final Logger logger = LogManager.getLogger(getClass());
#Autowired
protected LoginValidator loginValidator;
#RequestMapping(method= RequestMethod.GET)
protected String initializeForm(#ModelAttribute("loginCommand")LoginCommand loginCommand,
BindingResult result,
ModelMap model)
{
logger.info("INITIALIZING LOGIN FORM");
model.addAttribute("loginCommand", new LoginCommand());
return "login";
}
#InitBinder("loginCommand")
protected void initBinder(ServletRequestDataBinder binder) throws Exception
{
binder.addValidators(loginValidator);
}
#RequestMapping(method=RequestMethod.POST)
protected String onSubmit(#ModelAttribute("loginCommand")LoginCommand loginCommand,
BindingResult result,
HttpServletRequest request) throws Exception
{
logger.info("validating login input");
loginValidator.validate(loginCommand, result);
if (result.hasErrors())
{
result.reject("login.failure");
return "login";
}
UserDao userDao = new UserDao();
User user = userDao.by_name(loginCommand.getUserName());
if (user == null
|| !user.getName().equals(loginCommand.getUserName())
|| !user.getPassword().equals(loginCommand.getPassword()))
{
result.reject("login.failure");
return "login";
}
return "redirect:main.html";
}
}
Here is my LoginCommand class:
#XmlRootElement
public class LoginCommand
{
private String userName;
private String password;
/** blah blah blah getters and setters*/
}
Here is the full stack trace as requested:
Neither BindingResult nor plain target object for bean name 'loginCommand' available as request attribute
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'loginCommand' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:142)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:154)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:141)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:132)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:116)
at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:422)
at org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:142)
at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:84)
at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:80)
at org.apache.jsp.login_jsp._jspx_meth_form_input_0(login_jsp.java:233)
at org.apache.jsp.login_jsp._jspService(login_jsp.java:126)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:111)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:411)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:473)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:377)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1580)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:338)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.glassfish.tyrus.servlet.TyrusServletFilter.doFilter(TyrusServletFilter.java:305)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:250)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:652)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:591)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:463)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:168)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:206)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:180)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:242)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:539)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
Among other things, it seems like your mappings need to be modified. Here is what I would try. There are a lot of adjustments so no guarantees on whether it will work completely, but it should get you in the right direction.
#Controller
public class LoginController {
protected final Logger logger = LogManager.getLogger(getClass()); //look at SLF4J instead. Then you're not tied to a specific logger and you use a facade.
#Autowired //may want to use constructor wiring instead on these
private LoginValidator loginValidator;
#Autowired
private UserDao userDao; //this should be wired and not simply instantiated - Spring won't know about it otherwise
#Autowired
private LoginValidator loginValidator;
#GetMapping("/login")
public String initializeForm(Model model) {
logger.info("INITIALIZING LOGIN FORM");
model.addAttribute("loginCommand", new LoginCommand());
return "login";
}
#PostMapping("/loginPost")
public String onSubmit(#ModelAttribute("loginCommand") LoginCommand loginCommand,
BindingResult result) throws Exception {
logger.info("validating login input");
loginValidator.validate(loginCommand, result);
if (result.hasErrors()) {
result.reject("login.failure");
return "login";
}
User user = userDao.by_name(loginCommand.getUserName());
if (user == null
|| !user.getName().equals(loginCommand.getUserName())
|| !user.getPassword().equals(loginCommand.getPassword())) { //you should really refactor this and move it outside of your controller. Just keep routing code in your controller, not logic
result.reject("login.failure");
return "login";
}
return "main"; //you should return just "main" or redirect:/main depending on what you're trying to do - you want the JSP to be processed. Leaving off the extension allows you to change frameworks without changing the server-side code and allows the page to be compiled. You could switch to Thymeleaf, for example, and not touch any of this code.
}
}
Add an action to your form:
<form:errors cssClass="error" element="p" />
<table border="0">
<tr>
<td align="right">Username:</td>
<td><form:input path="userName" /> <form:errors path="userName" cssClass="error" /></td>
</tr>
<tr>
<td align="right">Password:</td>
<td><form:password path="password" /> <form:errors path="password" cssClass="error" /></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" id="submit" name="submit" value="Log In"></td>
</tr>
</table>
For the next developer reading your code, I'd rename LoginCommand to something closer to what it actually is - like UserDetailsAdapter or something along those lines. I am assuming that LoginCommand will implement UserDetails from Spring Security if you're using that.
You may also want to consider updating your UserDao to have the method called findOneByUsername instead of by_name. The naming convention can help you later when you use Spring Repositories.
Lastly, look at Project Lombok for your beans. It'll save you lots of headaches.
I am using Thymleaf href inside a loop.
<th:block th:each="study : ${studyList}">
<tr>
<td th:text="${study.patient.name}"></td>
<td th:text="${study.status}"></td>
<td th:text="${study.description}"></td>
<td><a th:href="#{/updateStudy/{studyId} (studyId=${study.id})}>"</a>Update</td>
</tr>
</th:block>
And my controller definition is
#RequestMapping(value = "/updateStudy/{studyId}")
public void updateStudy(#RequestParam("studyId") long studyId
) {
....
}
The page is not rendering as I am getting parsing exception:
Could not parse as expression: "#{/updateStudy/{studyId}
(studyId=${study.id})}>"
Also in the html page it is complianing : Undefined attribute name ((studyId).
What I am missing here ? Kindly help.
UPDATE:
Apparently the expression should have been
<td><a th:href="#{/updateStudy/{studyId} (studyId=${study.id})}" >Update</a></td>
Now at least the parsing error is resolved. But I am getting this:
There was an unexpected error (type=Bad Request, status=400).
Required long parameter 'studyId' is not present
Hopefully it is not related to the href expression.
SOLVED
Just to complete the answer, I have changed the controller definition to
#GetMapping("/updateStudy/{studyId}")
public String updateSchedulePage(#PathVariable Long studyId, Model model) {
..}
And now everything works fine.
Try with this:
<td><a th:href="#{/updateStudy/${study.id}}" >Update</a></td>
Here is another problem in controller:
#RequestParam will be `#PathVariable `
Change to it:
#RequestMapping(value = "/updateStudy/{studyId}")
public void updateStudy(#PathVariable ("studyId") long studyId
) {
....
}
I have a form ,when I am using spring form tags with the path attribute,I get the following error
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'selectedPerson' available as request attribute
However when I use the regular HTML input with the name attribute I don't see any issues.
What am I doing wrong.
Here is the controller code
#RequestMapping(value="/savePersonChanges.htm",method=RequestMethod.POST)
public ModelAndView savePersonChanges(HttpSession session,#Valid #ModelAttribute(value="selectedPerson")PersonBean editedPersonBean,BindingResult bindingResult){
ModelAndView mav=new ModelAndView();
mav.setViewName(LANDING_PAGE;
mav.addObject(TAB_SELECTOR,"EditPerson");
if(session!=null){
Rpt_date=(java.util.Date)session.getAttribute("editDate");
}
if(bindingResult.hasErrors()){
mav.addObject("errorDescription",errorDescription );
}
else{
/* Call service that updates database with table changes */
try{
PersonService.updatePerson(Rpt_date, editedPersonBean
}
catch(Exception e){
log.logError("Exception while updating Person", e.toString());
}
}
return mav;
}
The form is as follows:
<form:form modelAttribute="selectedPerson" id="editPersonForm">
<table id="selectPerson">
<tbody>
<tr>
<td>
<select id="personId" name="personId" onchange="getPersonDetails(this)">
<c:choose>
<c:when test="${empty selectedPerson}">
<option value="0" selected ="selected">Select A Person</option>
<c:forEach var="personIdItem" items="${editPersonProperties.personproperties}">
<option value="${personIdItem.personId}"><spring:escapeBody>${personIdItem.personName}</spring:escapeBody></option>
</c:forEach>
</c:when>
<c:otherwise>
<c:forEach var="personIdItem" items="${editPersonProperties.personproperties}">
<c:if test="${personIdItem.personId eq selectedPerson.personId}">
<option value="${personIdItem.personId}" selected ="selected">${personIdItem.personName}</option>
</c:if>
<c:if test="${personIdItem.personId ne selectedPerson}">
<option value="${personIdItem.personId}"><spring:escapeBody>${personIdItem.personName}</spring:escapeBody></option>
</c:if>
</c:forEach>
</c:otherwise>
</c:choose>
</select>
</td>
</tr>
</tbody>
</table>
<!-- Person Details -->
<table id="editPersonTable">
<tr>
<td>First Name</td>
<td> <form:input type ="text" path="fname" value="${selectedPerson.fname}"></form:input></td>
<td>Last Name</td>
<td> <form:input type ="text" path="lname" value="${selectedPerson.lname}"/></td>
</tr>
</table>
<div class="editcancelstyle">
<input id="savebtn" type="button" onclick="savePerson()" value="Save" />
<input id="cancelbtn" type="button" onclick="cancelPersonEdits ()"value="Cancel" />
</div>
</form:form>
I understand that the path attribute will bind the field to the form. However, I keep getting the bind error. If I replace using plain HTML, the controller sees the edited values for the fname and lname for the SelectedPerson bean.
Here is your problem, when you do :
<form:... modelAttribute="selectedPerson" ...>, the selectedPerson is the key of model object mapped from the holder of both Model & View class ( ex : ModelAndView), suppose you bind a model in your controller with new ModelAndView ("yourForm", "selectedPerson", new Person()),
#RequestMapping(value = "/form")
public ModelAndView userInput() {
ModelAndView mv = new ModelAndView("personForm", "selectedPerson", new Person());
return mv;
}
now the Person is mapped with selectedPerson so when this form returned as the response from controller, your form has already been bound to the Person model, so that you use path to refer to this Person's attributes (ex. path="name" , means it refers to Person's name attribute)
In your form, on modelAttribute="selectedPerson", the selectedPerson is not binded to any models, since "selectedPerson" is never assigned to any object because you didn't do any binding first before processing ( submitting ) the form.
this is why you got
> java.lang.IllegalStateException: Neither BindingResult nor plain
> target object for bean name 'selectedPerson' available as request
> attribute.
Note that to get this binding works, add also the following on the top of your form
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
you can then populate the fields of the form (assuming you put action="result.html"):
#RequestMapping(value = "/result")
public ModelAndView processUser( #ModelAttribute("selectedPerson") Person person, BindingResult result) {
if (result.hasErrors()) {
return new ModelAndView("errorPage","message","Something goes wrong");
}
/*System.out.println(person.getName());*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("personResult");
modelAndView.addObject("person", person);
return modelAndView;
}
Fix this Error which is thrown in console on this page: http://salestrack.meteor.com/demos/overview:
Exception in queued task: TypeError: Cannot read property 'emails' of undefined at Object.Template.demoOverviewItem.helpers.ownerRep (http://localhost:3000/client/views/overviews/demo_overview_item.js?
Here is the helper they're referring to:
ownerRep: function(){
var rep = Meteor.users.findOne({_id: this.userId});
var repEmail = rep.emails[0].address,
repArr = [],
repArr = repEmail.split('#');
return repArr[0];
}
});
The query is inserted into an inclusion block that loops over #each demos and looks up this.userId from the demos collection and matches it up to the user to return the owner of the demo's name.
Here are the templates that:
<template name="demosOverview">
<div class="container-fluid">
<div class="row-fluid col-lg-10 col-lg-offset-1">
<table class="table table-striped table-hover table-bordered dataTable" id="editable- sample">
<thead>
<tr>
<th>Rep</th>
<th>SFID</th>
<th>Date Set</th>
<th>Closed</th>
</tr>
</thead>
<tbody>
{{#each demos}}
{{> demoOverviewItem}}
{{/each}}
</tbody>
</table>
</div>
</div>
</template>
<template name="demoOverviewItem">
<tr>
<td>{{ownerRep}}</td>
<td> {{sfid}}
<a href="{{sfid}}" class="pull-right" target="blank" title="Salesforce">
<img src="/img/salesforce-logo.png" height="16" width="16">
</a>
</td>
<td>{{dateset}}</td>
<td>{{closed}}</td>
</tr>
</template>
I am pub/subbing Meteor.users.find() from the server to the client but haven't paired it down to only send over certain fields yet (for easy hacking purposes).
Weird thing is that it works correctly and returns the demo owner's email address as expected. The error gets thrown when I reload the page but not when I navigate to it from elsewhere on the site.
Seems like a wait.On error or something where when I hit reload it doesn't know that currentUser = true so it throws the error before realizing I'm logged in- that's just a theory though. I've googled, StackOv and IRCed to no avail.
Looks like on page reload you don't have the users on the client side yet.
Try
var rep = Meteor.users.findOne({_id: this.userId});
if (rep) {
var repEmail = rep.emails[0].address, (..)
}
It should rerun once findOne has the data it's looking for.
Update: ITextSharp 5.5.2 Supports this feature but previous version of ITextSharp do not.
Does ITextSharp 5.5.1 support Class Element CSS selectors? Such as
<style>
.test td {
border: 1px solid green;
}
</style>
I'm getting a result like this
When it should be something like
If not how would I go about achieving the same results?
Using the following code I do not get the desired results.
byte[] bytes;
Document document = new Document();
using (var memoryStream = new MemoryStream())
using (var pdfWriter = PdfWriter.GetInstance(document, memoryStream))
{
document.Open();
XMLWorkerHelper.GetInstance().ParseXHtml(pdfWriter, document, new StringReader(html));
document.Close();
bytes = memoryStream.ToArray();
}
return bytes;
Here is my full html for reference
<html>
<head>
<style>
.test td {
border: 1px solid green;
}
</style>
</head>
<body>
<table class='test'>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Savings</th>
</tr>
<tr>
<td>Peter</td>
<td>Griffin</td>
<td>$100</td>
</tr>
<tr>
<td>Lois</td>
<td>Griffin</td>
<td>$150</td>
</tr>
<tr>
<td>Joe</td>
<td>Swanson</td>
<td>$300</td>
</tr>
<tr>
<td>Cleveland</td>
<td>Brown</td>
<td>$250</td>
</tr>
</table>
</body>
</html>
This is a strange question since the default implementation of XML Worker does exactly what you need. We have an HTML file table_css.html that is converted to html_table_3.pdf and we use nothing more than the basic XML Worker code: ParseHtmlTable3
You only need 5 lines to achieve the result shown in the screen shot:
public void createPdf(String file) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new FileInputStream(HTML));
// step 5
document.close();
}