Please be patient, more source files and demos coming soon!
Introduction
What is FreeDOM? Well, FreeDOM is a new concept I created that changes a webpage into a fully asynchronous event driven application by using currently available technologies. Below, I will compare FreeDOM with currently working Web 2.0 concepts, and show you the benefits of switching to this architecture.
Web 2.0 Techniques
There are many ways of creating web pages that go and get content from servers and update themselves. Below is a list of the most common ways:
- AJAX - An ActiveX object built into browsers that sends and receives text or XML HTTP requests.
- IFrame - A built-in browser element control that can send and request webpage content similar to AJAX.
- Comet - A concept of leaving a webpage in a state of constant downloading that simulates an HTTP push technology, to receive data events.
- HTTP Streaming - Using third party technologies such as Java Applets or ActiveX to stream data from a server to a client.
Most of these concepts come with plenty of downfalls to true application state. Both AJAX and IFrames are very resource happy. Every time you want to get data from the server, you must create an object or element and send an HTTP Request. This causes the HTTP server to have huge amounts of requests eating up memory and processor time. There is also a delay in the response time due to the header information being passed back and forth. Also, AJAX and IFrames do not know if there is data available; they have to send a request just to find out if it is available, and once again causing a lot of unnecessary talking between the server and the client. Another issue with HTTP Requests is that it fills up your server logs. This can be a headache for servers with large amounts of traffic. Extremely large logs take up processor time, hard-drive space, and backup space. Also, AJAX/IFrames can only receive text or XML. This can be very irritating because in order to use it on a page, you must convert it into a usable format. A lot of websites use XML objects backend and JSON objects front end; in doing this, they must have a conversion process. This is at another memory and processor expense.
A lot of developers have noticed these flaws in AJAX and IFrames, and have started to invent new technologies. One being Comet, another cleansing name. Comet is very hard to implement, and comes with a lot of unknown bugs. Basically, in order to use it, you usually have to create a custom web server, because most servers today were not made to leave requests in an open state. Most servers have timeouts and caching built in to make them faster for a get in get out way of doing business. Comet is a step up from AJAX, but it's not the cure. Comet can only receive asynchronous events, it cannot talk back to the server. You need to implement Comet to receive and AJAX to send, making Coment dependant on AJAX. Another downside of using Comet is that browsers also have an issue with a never ending webpage. Have you ever been on a page and it locks up causing all your other browsers windows to close? This is caused by the EXE sharing memory and thread space. So, Comet is a great idea, but it is not feasible due to drawbacks in the current technologies.
Another technology is HTTP Streaming. There are many different versions of HTTP streaming. One way is setting up a server to talk back and forth to an ActiveX or Java Applet. Very hard to implement and maintain, with many security and licensing issues for both the client and the server. Most users will not install your code because of fears of corrupt code that they will not be able to remove, plus cause damage to their Operating System or files. This can be very scary to the end user.
In the next section, I will describe FreeDOM and how it will fix these issues.
FreeDOM!
So, how would one go about fixing these old technologies to make websites and applications one? Should we wait for large companies to create better working browsers, or should we invent it now with the currently working technologies? As a developer, I feel I should wait... but as an inventor, I have decided to make it work. What I want is the freedom to do what I want and build what I want on a webpage. I came up with the name FreeDOM for this reason, plus it is really what we want to do. We want to be able to work with the browser DOM freely and update it whenever we choose. So, I asked myself what current technologies could I use to create a true event state application. One that came to mind was Flash, with so much new hype around AIR and Flex applications. It's hard not to notice a big boom in Flash usage everywhere since Adobe took over it from Macromedia. Some of the latest statistics say Flash is installed on 90% or more of machines world-wide across Operating Systems and browsers. This got me thinking! Could I use Dlash in some way with my currently working webpage technology? Flash seems to work well for creating banners and animations, but could it handle passing data back and forth? After some testing with ActionScript 3.0, I figured out a way to replace AJAX, IFrames, Comet, and HTTP Streaming. I am sure though you could probably use Silverlight the same way, which I am currently looking into, maybe some MS guys could help figure that out for me.
How Does FreeDOM Work?
FreeDOM works by using Flash Binary Sockets to communicate with your custom socket server. To pass binary data from Flash sockets to your JavaScript webpage, you need a custom class called ExternalInterface
. This class will allow you to make calls to Flash methods and add callbacks for your JavaScript methods. So, let's see some code so you will understand.
Flash SWF Code
Add the following code to a document class .AS file. It is a wrapper for the custom JSocket
class and the main movie clip. Please keep in mind to keep the size of this and your jsockets
small. By keeping them small, you can load everything in the first preloader frame and bypass the activate SWF message.
package {
import flash.external.ExternalInterface;
import JSockets.JSocket;
import flash.display.MovieClip;
import flash.system.Security;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class Main extends MovieClip {
private var SocketArray:Array;
public function Main() {
ExternalInterface.addCallback("CreateSocket", this.CreateSocket);
ExternalInterface.addCallback("Connect", this.Connect);
ExternalInterface.addCallback("Close", this.Close);
ExternalInterface.addCallback("Flush", this.Flush);
ExternalInterface.addCallback("Send", this.Send);
}
public function CreateSocket(_ID:String, _Cls:String, _Con:String,
_Ioe:String, _Se:String, _Dat:String):void {
var NS:JSocket = new JSocket();
NS.ID = _ID;
NS.CloseEventCallback = _Cls;
NS.ConnectEventCallback = _Con;
NS.IOErrorEventCallback = _Ioe;
NS.SecurityErrorEventCallback = _Se;
NS.SocketDataEventCallback = _Dat;
this.SocketArray = new Array();
this.SocketArray.push(NS);
}
private function GetSocket(_ID:String):JSocket {
for each (var S:JSocket in this.SocketArray) {
if (S.ID == _ID) {
return S;
}
}
return null;
}
public function Connect(_ID:String, _Host:String, _Port:uint):void {
var FS:JSocket = this.GetSocket(_ID);
if (FS) {
FS.Connect(_Host, _Port);
}
}
public function Close(_ID:String):void {
var FS:JSocket = this.GetSocket(_ID);
if (FS) {
FS.close();
}
}
public function Flush(_ID:String):void {
var FS:JSocket = this.GetSocket(_ID);
if (FS) {
FS.flush();
}
}
public function Send(_ID:String, _Data:String):void {
var FS:JSocket = this.GetSocket(_ID);
if (FS) {
FS.writeUTFBytes(_Data);
}
}
}
}
Next is the JSocket
class which wraps the Flash sockets class. Below is the code for that:
package JSockets{
import flash.external.ExternalInterface;
import flash.net.Socket;
import flash.errors.*;
import flash.events.*;
public class JSocket extends Socket {
public var ID:String;
public var Host:String;
public var Port:uint;
public var CloseEventCallback:String;
public var ConnectEventCallback:String;
public var IOErrorEventCallback:String;
public var SecurityErrorEventCallback:String;
public var SocketDataEventCallback:String;
public function JSocket() {
AttachEvents();
}
private function AttachEvents():void {
addEventListener(Event.CLOSE, Handler_Close);
addEventListener(Event.CONNECT, Handler_Connect);
addEventListener(IOErrorEvent.IO_ERROR, Handler_IOError);
addEventListener(SecurityErrorEvent.SECURITY_ERROR, Handler_SecurityError);
addEventListener(ProgressEvent.SOCKET_DATA, Handler_SocketData);
}
public function Connect(_Host:String, _Port:uint):void {
Host = _Host;
Port = _Port;
connect(Host,Port);
}
private function Handler_Close(event:Event):void {
ExternalInterface.call(this.CloseEventCallback, event.type);
}
private function Handler_Connect(event:Event):void {
ExternalInterface.call(this.ConnectEventCallback, event.type);
}
private function Handler_IOError(event:IOErrorEvent):void {
ExternalInterface.call(this.IOErrorEventCallback, event.type, event.text);
}
private function Handler_SecurityError(event:SecurityErrorEvent):void {
ExternalInterface.call(this.SecurityErrorEventCallback,
event.type, event.text);
}
private function Handler_SocketData(event:ProgressEvent):void {
ExternalInterface.call(this.SocketDataEventCallback, event.type,
this.readUTFBytes(this.bytesAvailable));
}
}
}
There is a lot of tweaks you can do to these classes to customize them to your needs. These are just simple examples that I got my test app to work with. Keeping the Flash as a simple byte router is the best solution.
JavaScript Implementation
Let's see how to use these classes in JavaScript. The code below is for the FreeDOM chat program example. It simulates a custom personal IRC. If the page is not up, its because I'm updating it. Here is the code for that example. Please keep in mind, this is a simple test, it doesn't support validation or bugs, use at your own risk.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http:
http:
<head>
<title>Test</title>
</head>
<script type="text/javascript">
function Connect()
{
var f = thisMovie("flashObject");
f.CreateSocket("id1", "CloseEventCallback",
"ConnectEventCallback", "IOErrorEventCallback",
"SecurityErrorEventCallback", "SocketDataEventCallback");
f.Connect("id1", "http://www.codevendor.com/">www.codevendor.com}
function DisConnect()
{
var f = thisMovie("flashObject");
f.Send("id1", "Logout: \n");
f.Flush("id1");
}
function thisMovie(movieName)
{
return (navigator.appName.indexOf("Microsoft") != -1)?
window[movieName]:document[movieName];
}
function SendMessage()
{
var M = document.getElementById("Message").value;
document.getElementById("Message").value = "";
var f = thisMovie("flashObject");
f.Send("id1", "MESSAGE: " + M + "\n");
f.Flush("id1");
}
function SendCommand()
{
var M = document.getElementById("CommandBox").value;
document.getElementById("CommandBox").value = "";
var f = thisMovie("flashObject");
f.Send("id1", M + "\n");
f.Flush("id1");
}
function CloseEventCallback(T)
{
document.getElementById("Status").value+=" -> " + T + "\n";
ScrollToBottom();
}
function ConnectEventCallback(T)
{
document.getElementById("Status").value+=" -> " + T + "\n";
ScrollToBottom();
GetUsers();
}
function GetUsers()
{
var f = thisMovie("flashObject");
f.Send("id1", "USERS: ");
f.Flush("id1");
}
function IOErrorEventCallback(T, E)
{
document.getElementById("Status").value+=" -> " + T + "\n";
document.getElementById("Errors").innerHTML+=" -> " + E + "\n";
ScrollToBottom();
}
function SecurityErrorEventCallback(T, E)
{
document.getElementById("Status").value+=" -> " + T + "\n";
document.getElementById("Errors").innerHTML+=" -> " + E + "\n";
ScrollToBottom();
}
function SocketDataEventCallback(T, D)
{
eval("D2 = " + D);
if(D2.CMD == "USERLIST" || D2.CMD == "LOGOUT")
{
document.getElementById("Users").value = D2.DATA.split(",").join("\n");
document.getElementById("ChatRoom").value+= D2.STAT + "\n";
}
if(D2.CMD == "MESSAGE")
{
document.getElementById("ChatRoom").value+= D2.STAT + "\n";
}
document.getElementById("Status").value+=" -> " + T + "\n";
document.getElementById("Data").value+=D + "\n";
ScrollToBottom();
}
function ScrollToBottom()
{
document.getElementById("ChatRoom").scrollTop =
document.getElementById("ChatRoom").scrollHeight;
document.getElementById("Status").scrollTop =
document.getElementById("Status").scrollHeight;
document.getElementById("Data").scrollTop =
document.getElementById("Data").scrollHeight;
document.getElementById("Errors").scrollTop =
document.getElementById("Errors").scrollHeight;
}
</script>
<body>
<table cellpadding="0px" cellspacing="10px">
<tr>
<td valign="top"><strong>Client Chat</strong><br /><br />
<input type="button" onclick="Connect();"
name="Connect" value="Connect" /> <input type="button" onclick="DisConnect();"
name="DisConnect" value="DisConnect" />
<br /><br /><table><tr><td>Chat Room</td>
<td>Users</td></tr>
<tr><td><textarea id="ChatRoom" rows="20" cols="40">
</textarea></td><td><textarea
id="Users" rows="20" cols="20"></textarea></td></tr></table> Message:
<input type="text" id="Message" style="width:230px;" />
<input type="button" onclick="SendMessage();" name="Send"
value="Send" /> CMD:
<input type="text" id="CommandBox" style="width:80px;" />
<input type="button" onclick="SendCommand();" name="Issue"
value="Issue" /><br />
</td>
<td valign="bottom"><strong>Socket Events</strong><br /><br />
Status<br />
<textarea id="Status" rows="5" cols="30"></textarea>
<br />
Data<br />
<textarea id="Data" rows="5" cols="30"></textarea>
<br />
Errors<br />
<textarea id="Errors" rows="5" cols="30"></textarea></td>
</tr>
</table>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="flashObject" width="0" height="0"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"
style="display: hidden">
<param name="movie" value="javascriptsockets.swf" />
<param name="quality" value="low" />
<param name="allowScriptAccess" value="always" />
<embed src="javascriptsockets.swf" quality="low"
width="0" height="0" name="flashObject"
id="flashObject" align="middle" play="true"
loop="false" allowscriptaccess="always"
type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer%22%3Cbr%3E">
http:
style="display: hidden">
</embed>
</object>
</body>
</html>"
", 81);
">
">
Socket Server Implementation
I won't go into the full details of the code, but it's a simple async socket server created in C#. It handles the incoming connection requests and sends out responses. The important things to look at is the Sandbox Cross Domain Policy it sends out. Basically, Flash will request this from your server to make sure it's allowed to use your server. The code for this is given below:
class Listener
{
#region Variables
private ManualResetEvent vManualReset = null;
private Dictionary<Guid, StateObject> vStateObjects = null;
private List<Guid> vStateIDS = null;
private Socket vListenerSocket = null;
#endregion Variables
#region Properties
public List<Guid> StateIDS
{
get { return vStateIDS; }
set { vStateIDS = value; }
}
internal Dictionary<Guid, StateObject> StateObjects
{
get { return vStateObjects; }
}
public Socket ListenerSocket
{
get { return vListenerSocket; }
}
#endregion Properties
#region Constructor
public Listener()
{
}
#endregion Constructor
#region Server Methods
public void Listen()
{
IPHostEntry Host = null;
IPEndPoint EP = null;
IPAddress IP = null;
IP = IPAddress.Parse("127.0.0.1");
if (IPAddress.TryParse(
ConfigurationManager.AppSettings["ListeningAddress"], out IP))
{
EP = new IPEndPoint(IP,
int.Parse(ConfigurationManager.AppSettings["ListeningPort"]));
}
else
{
Host =
(ConfigurationManager.AppSettings["ListeningAddress"] ==
string.Empty) ?
Dns.GetHostEntry(ConfigurationManager.AppSettings["ListeningAddress"]) :
Dns.GetHostEntry(Dns.GetHostName());
EP = new IPEndPoint(Host.AddressList[0],
int.Parse(ConfigurationManager.AppSettings["ListeningPort"]));
}
vManualReset = new ManualResetEvent(false);
vListenerSocket = new Socket(EP.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
try
{
vListenerSocket.Bind(EP);
vListenerSocket.Listen(int.Parse(
ConfigurationManager.AppSettings["TotalAllowedConnections"]));
while (true)
{
vManualReset.Reset();
vListenerSocket.BeginAccept(
new AsyncCallback(ConnectionAccept), vListenerSocket);
vManualReset.WaitOne();
}
}
catch { }
finally
{
}
}
private void ConnectionAccept(IAsyncResult _AR)
{
Socket ListSocket = null;
Socket CurrentSocket = null;
StateObject CurrentStateObject = null;
ListSocket = (Socket)_AR.AsyncState;
CurrentSocket = ListSocket.EndAccept(_AR);
vManualReset.Set();
if (vStateObjects == null) { vStateObjects =
new Dictionary<Guid, StateObject>(); }
if (vStateIDS == null) { vStateIDS = new List<Guid>(); }
CurrentStateObject = new StateObject(CurrentSocket,
int.Parse(ConfigurationManager.AppSettings["BufferSize"]));
vStateObjects.Add(CurrentStateObject.UniqueID, CurrentStateObject);
vStateIDS.Add(CurrentStateObject.UniqueID);
try
{
CurrentSocket.BeginReceive(CurrentStateObject.Buffer, 0,
CurrentStateObject.BufferSize, 0, new AsyncCallback(ReadCallback),
CurrentStateObject);
}
catch { CloseSocket(CurrentStateObject); return; }
}
private void ReadCallback(IAsyncResult _AR)
{
StateObject CurrentState = null;
int BytesRead = 0;
string Command = string.Empty;
string r = string.Empty;
bool first = false;
Guid[] CurrentIDS = new Guid[vStateIDS.Count];
vStateIDS.CopyTo(CurrentIDS);
CurrentState = (StateObject)_AR.AsyncState;
try
{
BytesRead = CurrentState.WorkSocket.EndReceive(_AR);
}
catch { CloseSocket(CurrentState); return; }
if (BytesRead > 0)
{
Command = Encoding.Default.GetString(CurrentState.Buffer, 0, BytesRead);
switch (Command.ToUpper())
{
case "<POLICY-FILE-REQUEST/>\0":
XmlDocument XD = new XmlDocument();
Configuration Config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
ConfigurationSection CS = (ConfigurationSection)
Config.GetSection("CrossDomainPolicy/Policy");
XD.LoadXml(CS.SectionInformation.GetRawXml());
XmlNode XN = XD.SelectSingleNode("Policy");
Send(XN.InnerText.Replace("\r\n", "") + "\0", CurrentState, true);
break;
default:
string[] cmd = Command.Split(':');
switch (cmd[0].ToUpper())
{
case "MESSAGE":
foreach (Guid ID in CurrentIDS)
{
if (ID != CurrentState.UniqueID)
{
if (vStateObjects.ContainsKey(ID))
{
Send("{CMD:'MESSAGE', STAT:'<" +
CurrentState.WorkSocket.RemoteEndPoint.
ToString().Split(':')[0] + "> " +
cmd[1].Trim() + "'}", vStateObjects[ID], false);
}
else
{
vStateIDS.Remove(ID);
}
}
}
Send("{CMD:'MESSAGE', STAT:'<" +
CurrentState.WorkSocket.RemoteEndPoint.
ToString().Split(':')[0] + "> " + cmd[1].Trim() +
"'}", CurrentState, false);
break;
case "NICKNAME": Send("Your nickname has been changed to" +
cmd[1], CurrentState, false); break;
case "LOGOUT":
foreach (Guid ID in CurrentIDS)
{
if (ID != CurrentState.UniqueID)
{
if (vStateObjects.ContainsKey(ID))
{
if (first) { r += "\n"; } else { first = true; }
r += vStateObjects[ID].WorkSocket.
RemoteEndPoint.ToString().Split(':')[0];
}
else
{
vStateIDS.Remove(ID);
}
}
}
foreach (Guid ID in CurrentIDS)
{
if (ID != CurrentState.UniqueID)
{
if (vStateObjects.ContainsKey(ID))
{
Send("{CMD:'LOGOUT', DATA:'" + r +
"',STAT:'User Disconnected - " +
CurrentState.WorkSocket.RemoteEndPoint.
ToString().Split(':')[0] + "'}",
vStateObjects[ID], false);
}
else
{
vStateIDS.Remove(ID);
}
}
}
Send("{CMD:'LOGOUT', DATA:'" + r +
"',STAT:'User Disconnected - " +
CurrentState.WorkSocket.RemoteEndPoint.
ToString().Split(':')[0] + "'}",
CurrentState, true);
break;
case "USERS" :
foreach (Guid ID in CurrentIDS)
{
if (vStateObjects.ContainsKey(ID))
{
if (first) { r += ","; } else { first = true; }
r += vStateObjects[ID].WorkSocket.
RemoteEndPoint.ToString().Split(':')[0];
}
else
{
vStateIDS.Remove(ID);
}
}
foreach (Guid ID in CurrentIDS)
{
if (ID != CurrentState.UniqueID)
{
if (vStateObjects.ContainsKey(ID))
{
Send("{CMD:'USERLIST', DATA:'" + r +
"', STAT:'Welcome User - " +
CurrentState.WorkSocket.RemoteEndPoint.
ToString().Split(':')[0] + "'}",
vStateObjects[ID], false);
}
else
{
vStateIDS.Remove(ID);
}
}
}
Send("{CMD:'USERLIST', DATA:'" + r +
"',STAT:'Welcome User - " +
CurrentState.WorkSocket.RemoteEndPoint.
ToString().Split(':')[0] + "'}",
CurrentState, false);
break;
}
break;
}
try
{
CurrentState.WorkSocket.BeginReceive(CurrentState.Buffer, 0,
CurrentState.BufferSize, 0,
new AsyncCallback(ReadCallback), CurrentState);
}
catch { CloseSocket(CurrentState); return; }
}
else
{
CloseSocket(CurrentState);
}
}
private void CloseSocket(StateObject _StateObject)
{
Guid UniqueID = _StateObject.UniqueID;
if (_StateObject.WorkSocket != null)
{
try
{
_StateObject.WorkSocket.Shutdown(SocketShutdown.Both);
_StateObject.WorkSocket.Close();
}
catch { }
finally
{
_StateObject.WorkSocket = null;
_StateObject = null;
}
}
vStateIDS.Remove(UniqueID);
vStateObjects.Remove(UniqueID);
}
public void Send(string _StringData,
StateObject _StateObject, bool _CloseConnection)
{
Send(Encoding.Default.GetBytes(_StringData),
_StateObject, _CloseConnection);
}
public void Send(byte[] _ByteData,
StateObject _StateObject, bool _CloseConnection)
{
_StateObject.Terminate = _CloseConnection;
try
{
_StateObject.WorkSocket.BeginSend(_ByteData, 0, _ByteData.Length, 0,
new AsyncCallback(SendCallback), _StateObject);
}
catch { CloseSocket(_StateObject); return; }
}
private void SendCallback(IAsyncResult _AR)
{
StateObject CurrentState = null;
int BytesSent = 0;
CurrentState = (StateObject)_AR.AsyncState;
try
{
BytesSent = CurrentState.WorkSocket.EndSend(_AR);
}
catch { CloseSocket(CurrentState); return; }
if (CurrentState.Terminate) { CloseSocket(CurrentState); }
}
#endregion Server Methods
}
The full code can be found in the source files. You can create a socket server however you desire with whatever language, as long as it handles requests. You can even choose to use an HTTP Web Server if you want. It all depends on what you want to code for.
Conclusion
Well, that concludes my FreeDOM tutorial. Please check back for updates as I create more apps using this technique. Have fun! Enjoy!
History
- December 15, 2007 - FWS - FreeDOM Web Server Version 1.0b created.
- December 13, 2007 - Creating a custom open source Unix web server in C to handle FreeDOM projects.
- November 15, 2007 - Bought a domain name for FreeDOM: WWW.FDOM.US.
- November 07, 2007 - Created ActionScript tutorial.
- November 05, 2007 - Created SourceForge project for FreeDOM.
- November 02, 2007 - Added JavaScript Sockets source code and added a Wikipedia entry for FreeDOM (programming).
- November 01, 2007 - Added FreeDOM Ping example and source code.
- October 30, 2007 - Version 1.0A.
External Links
Terms and Conditions For Use, Copy, Distribution, and Modification
THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.