Back? Great! Let's get started:
top
Myna Directory Layout
Myna is a Java Servlet application. This means it can be deployed like any servlet, on any servlet container. Once deployed, Myna should exist as a directory within your servlet container. Inside this directory you should see these files and directories:<context root> |-shared/ | |-docs/ | |-js/ | | |-libOO/ |-myna/ |-WEB-INF/ | |-myna/ |-examples/ |-index.html
| <context root> or / | This is the directory into which your servlet container unpacked Myna |
| /shared | This directory is for shared resources, both Myna's and your own |
| /shared/docs | This directory is for Myna's documentation, including this file |
| /shared/js/ | This directory is for shared JavaScript resources, both server- and client-side |
| /shared/js/libOO | This directory is for Myna's Object Oriented library. These files for the core of Myna's standard library and get overwritten during upgrades. |
| /myna/ | This directory contains Myna's internal applications, such as the Myna administrator. None of these are necessary for Myna's functionality. |
| /WEB-INF/ | This directory contains Myna's Java code and other resources, such as datasource definitions |
| /examples/ | This directory contains some code samples. Administrator privileges required. |
| /index.html | The default index.html just redirects to this file. Myna looks for index.ejs, index.sjs and then index.html if no file is specified in the URL |
You can place code files anywhere under the context root; however, files placed in /WEB-INF will not be served to browsers. You can use this behavior to create "internal" files that are readable from code only. All of Myna's configuration files are in /WEB-INF/myna for this reason. When upgrading Myna through the Administrator, Myna Takes care not to overwrite any files you create, and backs up every Myna file overwritten.
Myna automatically expands any file path that begins with "/" to the context root. See MynaPath for more details
top
Hello, World
Let's create the simplest possible web page:
<html> <body> Hello, World! </body> </html>Ok that's maybe a little too simple, but it does illustrate that files ending in .ejs (EmbeddedJavaScript) are assumed to be plain text with the exception of Myna specific tags. Here is the same output created with a .sjs (ServersideJavaScript) file.
$res.print("<html>");
$res.print(" <body>");
$res.print(" Hello, World!");
$res.print(" </body>");
$res.print("</html>");
As you can see it can be cumbersome to write a lot of HTML in a .sjs file. In
general, .ejs files work best for views where the majority of the code
is HTML, and sjs works best when the majority of the code is server-side
JavaScript.
Lets add a bit of dynamic content
<html> <body> <%-- Here is comment. these don't get sent to the browser at all --%> <% var today = new Date(); %> Hello, World!<br> Today is is <%=today%> </body> </html>Arbitrary JavaScript code can be placed within a <% %> tag. Javascript expressions can be directly output via a <%= %> tag. This works in a similar way to JSP.
Here is an example using a loop and an if statement:
<%
var books =[{
title:"A Brief History of Time",
author:"Stephan Hawking.",
status:"unread"
},{
title:"The Elegant Universe",
author:"Brian Greene",
status:"read"
},{
title:"The Blank Slate",
author:"Steven Pinker",
status:"reading"
},{
title:"Brother Odd",
author:"Dean Koontz",
status:"reading"
}];
%>
<table cellpadding="5" cellspacing="0" border="1" >
<caption>My library</caption>
<tr>
<th>Book Number</th>
<th>Title</th>
<th>Author</th>
<th>status</th>
</tr>
<@loop array="books" element="book" index="bookNum">
<tr >
<td><%=bookNum%></td>
<td>
<@if book.status == "reading">
<b><%=book.title%></b>
<@elseif book.status == "read">
<strike><%=book.title%></strike>
<@else>
<%=book.title%>
</@if>
</td>
<td><%=book.author%></td>
<td><%=book.status%></td>
</tr>
</@loop>
</table>
Note the new constructs <@loop>, <@if>, <@elseif>, and <@else>. These
are shortcuts to avoid writing cumbersome code such as :
<%
for (var x = 0; x < books.length; ++x) {
var book = books[x];
%>
...
<% } %>
<@loop> takes up to 3 parameters:
- array REQUIRED reference to an array element, or an array literal
- element OPTIONAL if defined, a variable of this name is created inside the loop to store the value of the current array item
- index OPTIONAL if defined, a variable of this name is created inside the loop to store the value of the current array index
top
Myna Environment
Myna, by default, includes a set of Global Variables that all start with $. Myna also provides standard functions as extensions to JavaScript's base objects ( Object.js, Array.js, String.js, Date.js Function.js ) and through the Global Myna Object ( Myna.sjs ), as well as several classes that provide more specific functionality ( Myna.Query, Myna.DataManager, Myna.WebSevice, Myna.ValidationResult, Myna.File, Myna.Profiler, Myna.Ldap, ). This tutorial is a quick tour of the standard objects and how Myna handles the normal web application workflow.Let's start with one of the most useful functions in the library for debugging: Myna.dump. Myna.dump returns nested HTML table representing the supplied object. Here is an example:
<%=Myna.dump(this,"Global Scope")%>This shows all of the objects defined in the global scope. Clicking bold-ed title on the left side of a cell will collapse or expand that object. You may notice that some cells end in "[ max recursion ]". This is a safety mechanism that prevents infinite recursion. Sometimes the default maxRecursion of 4 is not enough, in which case you can pass a third parameter to extend the recursion depth.
<%=Myna.dump(this,"Global Scope",6)%>
When in an sjs file, you can use the printDump variant:
Myna.printDump(this,"Global Scope",6); //printDump is shorthand for $res.print(Myna.dump(this,"Global Scope",6));
Another useful function is Myna.log(), used like so:
Myna.log("debug","Request Data",Myna.dump($req.data));
This creates a log entry in Myna's general log database of type "debug" with a title of
"Request Data" and a dump of the request variables as the detail. You can read these logs
in the Myna Administrator by clicking "View General Log"
Finally, there is Myna.printConsole(). This function prints to standard output which is usually captured in your servlet container's log file. Finding this file can be tricky for some servlet containers, so Myna.log is usually the better choice.
Here is a quick tour of Myna's Global Variables:
$req
$req is a global object that contains information about the web request. It contains the properties "paramNames" and "data". The paramNames property is an array of the lower case parameter names passed to this request from either the url or a form post. The data property contains the values for the request parameters, keyed by fieldName. From the API documentation:
If a parameter is passed multiple times, this property will be a comma-separated list of each value of the parameter. In addition, a property called parameter_name$array is created. This property contains an array of all the values of that parameter name, in the order they were passed. This parameter is created even if only one value is passed
If a parameter value can be interpreted as JSON, a property called parameter_name$object is created by calling String.parseJson() against the value. If there are multiple JSON valid objects, only the last will be stored in parameter_name$object, but a property called parameter_name$objectArray is populated with every object created.
Every value in data.parameter_name and data.parameter_name$array is escaped via String.escapeHtml(). This helps to protect against Cross-Site Scripting (XSS) and SQL injection. To access un-altered parameter data, see $req.data.rawData
File uploads produce an object in $req.data containing information about the upload that looks like this
$req.data.parameter_name ={
diskItem: org.apache.commons.fileupload.disk.DiskFileItem,
stats:{
fieldName:String, name of field in form,
fileName:String, name of file on client side,
contentType:String, mime type,
isInMemory:boolean, true if file contents are in memory,
sizeInBytes:int, size in bytes,
diskLocation:String, the current location of the uploaded file
}
}
multiple uploads with the same fieldname are stored in
parameter_name$array.
<form method="post">
First Name: <input name="first_name"><br>
Favorite colors:<br>
<input type="checkbox" name="favorite_colors" value="red">Red
<input type="checkbox" name="favorite_colors" value="orange">Orange
<input type="checkbox" name="favorite_colors" value="yellow">Yellow
<input type="checkbox" name="favorite_colors" value="green">Green
<input type="checkbox" name="favorite_colors" value="blue">Blue
<input type="checkbox" name="favorite_colors" value="indigo">Indigo
<input type="checkbox" name="favorite_colors" value="violet">Violet
<p>
Some Json Code:<br>
<textarea cols="60" rows="10" name="json">
{
"array":[
"thing1",
"thing2"],
"number":12,
"string":"some string",
"sub_object":{
"number":12,
"string":"some string"
}
}
</textarea><p>
More Json Code with the same name:<br>
<textarea cols="60" rows="3" name="json">
{
"phrase":"Dude, where's my car?"
}
</textarea><p>
A Cross Site Scripting hack:<br>
<textarea name="xss">';alert(String.fromCharCode(88,83,83))//\';
alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";
alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'>
<SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT></textarea>
<p>
Display: <select name="dump_data">
<option value="filtered">$req.data</option>
<option value="raw">$req.rawData</option>
</select><p>
<button type="submit">submit</button>
</form>
<@if $req.data.dump_data == "filtered">
<%=Myna.dump($req.data,"$req.data")%>
<@elseif $req.data.dump_data == "raw">
<%=Myna.dump($req.rawData,"$req.rawData")%>
</@if>
Back to Global Variables
$res
$res is the global response object. It is used to manipulate what text will be sent to the browser at the end of the request. Myna internally maintains a text buffer that represents the output for the current request. $res.print(), Myna.print() and the ejs tag <%=%> append to this buffer, $res.clear() deletes the buffer and returns it's content, and $res.getContent() returns the buffer content without deleting it. These functions can be used to change the order of text in output, or even capture output for storage in a file or database.Here is an example of wrapping generated content:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse nec nisi nec nisi scelerisque vulputate. Nullam eu elit. Aenean eleifend. Donec ipsum metus, ultricies sed, tincidunt non, elementum in, lorem. Maecenas vitae massa eget nibh tempus mattis. Pellentesque dolor nulla, scelerisque a, gravida vitae, ullamcorper quis, ipsum. Vestibulum velit ligula, rhoncus id, varius nec, porttitor auctor, nisi. Praesent magna purus, pellentesque eu, ullamcorper eu, sodales in, odio. Fusce sollicitudin. Nam adipiscing metus id lacus. <% if(!$req.data.width) $req.data.width=100 var content = $res.clear(); %> <form method="post"> set width to:<input name="width" value="<%=$req.data.width%>"> <button type="submit">resize</button> </form> <table width="100%" height="1" cellpadding="10" cellspacing="0" border="0" > <tr> <td > </td> <td bgcolor="linen" width="<%=$req.data.width%>"> <%=content%> </td> <td > </td> </tr> </table>
Back to Global Variables
$session
$session is the global session object. It represents temporary variables associated with a particular client. The get() and set() functions are used to store and retrieve values, and clear() is used to force the end of a session. Although useful for temporary values, be aware that sessions are not normally persisted and can be destroyed unexpectedly. This can be a problem if the session is used to store user information for the purposes of authentication.Back to Global Variables
$cookie
$cookie is for reading and writing browser cookies. $cookie.data contains an array of javax.servlet.http.Cookie objects. The set() and get() functions store and retrieve individual cookies. Note that you cannot send a cookie after content is sent to the browser. This is normally only an issue when using $res.flush() to send content before the end of the requestBack to Global Variables
$server
$server contains information about the server environment. Notable properties are:- globalScope a reference to the top level scope. Setting properties on this object is the same as declaring global variables
- servelet,request,response references to the Java servlet, request, and response instances for this thread.
- currentDir,currentUrl,requestDir, requestUrl the filesystem paths and url paths for the the currently executing script and the requested script.
Back to Global Variables
$profiler
$profiler is an instance of Myna.Profiler that is started at the beginning of the request. This is used in several places to record performance metrics, e.g. page includes, queries, and WebService calls. You can also use this to time your own events.Here is an example of using the global profiler:
<% $profiler.mark("before loop") %>
<@loop array="new Array(10)" element="dummy">
<% $profiler.begin("creating a hash") %>
<%="thing's and stuff".toHash()%><br>
<% $profiler.end("creating a hash") %>
</@loop>
<%
$profiler.mark("after loop")
$profiler.calcAverages();
%>
<%=$profiler.getSummaryHtml()%>
Back to Global Variables
$application
$application stores the application definition created from an application.sjs file in the current or any parent directory.Applications can define app specific properties, and manipulate the request workflow by layering functions before and after a page request.
See the
Application Overview for more details on how applications are defined.
Back to Global Variables
top