WebHost4Life
HomeDownload ALPFAQ and documentationALP SamplesALP based applicationsBuy ALP
Developers home
An area for people interested in developing applications based on ALP.
Introductory articles and overviews
ALP vs. PWS
Compatibility FAQ
Deployment examples
ALPFrame demo
How To ...
Ever growing set of articles and lessons.
Begin With ALP
Configure ALP
Writting an Application
Using The ALP Run-time library
Additional samples
Download latest documentation
newObjects forums
News, announcements and misc. articles
SQLite COM
ALP 1.5 beta
 
End-users
An area for people using ALP based/ALP compatible applications.
Get, install, license
ALP applications
Contract ALP development
Adverts
ASP Index
WebHost4Life
asbis.nl
New ASP oriented site
 

ALP Engine Core Downloads

Download ALP run-time files
Download this package if you want to run ALP based applications and you are not going to develop software based on ALP. Contains only the vital parts of ALP engine without documentation, samples and development tools. Size ~700KB self extract installation package - download and run.

Download ALP Full package
Download this package if you want not only run ALP based applications but also develop, port, adapt applications to run under ALP. Conatains documentation, samples, tools for software developers. Size 3.3MB self extract installation package - download and run. If you prefer a ZIP archive instead of self-extract, download ALP.ZIP (4.8MB).

Contact us
support@newobjects.com
sales@newobjects.com
info@newobjects.com
More about us and our products
www.newobjects.com
Exchange links/advertise

Using the ALP Run-time library

The ALP Run-time library is nothing else but a set of COM objects your ASP pages can create and use through the standard ASP object creation facilities. They are just always available with ALP and you can count on their existence without additional efforts to gather re-distribution files, add new entries to the setup configuration, deal with OS compatibility issues and so on. These components are tested on all the platforms supported by ALP and they have minimal requirements. In fact they are even developed ahead of the ALP versions in order to allow us perform more testing (for example the ALP Run-time library is already available on Windows CE while ALP for CE is under development).

The previous chapter has shown in fact a little library usage - SQLite COM database. We will take a quick look at some other interesting features in this page. You can find more illustrations in the ALP samples and on the newObjects ActiveX Pack1 page. The ALP Run-time library is available also separately without ALP under the name newObjects ActiveX Pack1 family. This allows you transfer the library features to other environments. The samples shown on the newObjects ActiveX Pack1 family pages are mostly non-ALP samples, but they use the same objects you can use in ALP and the differences with the ALP use are minimal.

Storages and Files - using the file access components. 

Most of the ASP programmers associate the file access with the Scripting.FileSystemObject. There are many other components providing similar or even extended features. The ALP Run-time library contains several objects that cover its functionality and even much more. The general file access related objects in the library are:

SFMain - plays a role much similar to the FileSystemObject.
SFStream - provides text and binary access to streams and files.
SFStorage - provides access to storages and directories.
SFFileStream and SFDirStorage - implement low level stream and respectively storage interfaces to files and directories
SFFilter - implements low level binary access to streams and files
SFRecord and SFField - implement record based access over streams through a SFFilter object.
There are also some helper objects that hold file and storage information details etc.

In the most cases the ASP pages create directly only the SFMain object and they receive the other objects as a result of member calls. Lets take a look at a small piece of code that opens a text file and reads it line by line:

<%
  Set sf = Server.CreateObject("newObjects.utilctls.SFMain")
  Set file = sf.OpenFile(Server.MapPath("test.txt"),&H40)
  lineNum = 1
  While NOT file.EOS
    line = file.ReadText(-1) ' this is equivalent to the ReadLine method in FSO
    %>
      <%= lineNum %>: <B><%= Server.HTMLEncode(line) %></B><BR>
    <%
    lineNum = lineNum + 1
  Wend
%>

The SFMain.OpenFile method used in this code opens the test.txt file specified for reading only and returns an initialized SFStream object which is saved in the file variable by this example code. Internally the OpenFile method will do more than you can see from this code. When you request a file open operation a SFFileStream object is created and bound to the file, then a SFStream object is created and bound to that SFFileStream object. The library allows you skip these details in case of regular files, but knowing about them will help you understand the concepts. The role of each of these objects is: SFFileStream implements the standard IStream interface over a regular file, the SFStream on the other hand works through this IStream interface and is not particularly interested in the physical nature of the stream.

The benefit of this abstract mechanism is that it can be applied to non-file objects that behave like files. For example we can open a stream in an OLE storage file and read it with exactly the same code:

<%
  Set sf = Server.CreateObject("newObjects.utilctls.SFMain")
  Set stg = sf.OpenStorageFile(Server.MapPath("mystorage.stg"),&H40)
  Set file = stg.OpenStream("test.txt"),&H40)
  lineNum = 1
  While NOT file.EOS
    line = file.ReadText(-1) ' this is equivalent to the ReadLine method in FSO
    %>
      <%= lineNum %>: <B><%= Server.HTMLEncode(line) %></B><BR>
    <%
    lineNum = lineNum + 1
  Wend
%>

Obviously the different nature of the OLE storages requires us to use another path to the stream we are looking for - we need to open the OLE storage (OpenStorageFile) and then open a stream from it (OpenStream) as like the storage is a directory and the stream is a file. The rest of the code will be the same.

We can go even further - open a TCP connection and read it as text file:

<%
  Set nsMain = Server.CreateObject("newObjects.net.NSMain")
  Set addr = nsMain.GetHost("myserver.com")
  addr.Port = 8121 ' See the notes below
  Set socket = nsMain.NewSocket
  socket.Connect(addr)
  Set file = Server.CreateObject("newObjects.utilctls.SFStream") 
  file.SetStream socket
 
  lineNum = 1
  While NOT file.EOS
    line = file.ReadText(-1) ' this is equivalent to the ReadLine method in FSO
    %>
      <%= lineNum %>: <B><%= Server.HTMLEncode(line) %></B><BR>
    <%
    lineNum = lineNum + 1
  Wend
%>

Of course this code will make sense if the other side of the connection writes continuously. In this kind of usage EOS will return True when the socket is disconnected. The network connections are most often used for conversations - i.e. we send something and then we read the response, then we are repeating the cycle again. Still, the code used remains very similar to the code used with files, it is even possible to simulate network connection behavior using a test file.

The only significant difference between the different kind of streams you can use through the library components is the support for random positioning. For instance a file stream can be positioned at any location - i.e. you can start reading at N-th byte then move to the M-th byte and write something there. On the other hand the network streams cannot be positioned and can be read/written only sequentially. The term seekable is most often used to characterize the streams that can be randomly accessed and non-seekable is used for the streams that cannot. If a stream is seekable or not is most often obvious from its nature - obviously the network connection does not represent a block of persistent data while the files are stored in the file system and can be addressed like memory blocks.

There are certain library features applicable only for seekable streams and they wont work over non-seekable streams. A good example can be devised using another feature of the library - the record based access to files:

' Assume dataStrm is an already opened stream - a file or something else
Set rec = Server.CreateObject("newObjects.utilctls.SFRecord")
rec.AddField "Name", vbString, 31
rec.AddField "Age", vbLong
rec.AddField "Gender", vbBoolean

rec.BindTo dataStrm 
rec.Filter.unicodeText = False
rec.MoveFirst 
Dim recCount 
recCount = rec.RecordCount

While Not dataStrm.EOS %>
  <HR>
  Name: <B><%= rec("Name").Value %></B><BR>
  Age: [<%= CLng(rec("Age").Value) %>]<BR>
  Gender: 
  <% If rec("gender").Value Then 
    Response.Write "Male"
  Else 
    Response.Write "Female" 
  %><BR><%
  rec.MoveNext
Wend

This piece of code will work correctly over a file - i.e. if the dataStrm is opened like in the samples above. Here we create a SFRecord object and add a few fields in it. The AddField method automatically creates a SFField object and initializes it, thus it is just a quick way to define a record structure. The same can be done by creating SFField objects directly, initializing them and adding them to the record, but it will require more code.

The BindTo method binds the SFRecord object to the stream. In the documentation you will notice that this method has one more optional parameter where a SFFilter object can be passed. However the BindTo method automatically creates a SFFilter object if none is passed explicitly. Most often you need to specify some general details about the data format in the stream - others than the field types and sizes. In this case we specify that the text values (i.e. the fields that contain strings - in this sample this is the Name field) are NOT UNICODE. The role of the filter objects is to stay between the SFRecord object you use and the stream itself. The SFFilter drives the low level communication with the stream and caches the records. It can be also used directly without a SFRecord by applications requiring lower level access (You can see this done in the Executable file information sample from samples installed by ALP).

When this is done we can ask the SFRecord to count the records in the stream, then we can cycle through the stream and read the records one by one. This technique allows us to define the structure of the records - in this sample we assume that the stream contains records with 3 fields each: Name, Age and Gender. The sizes of the Age and Gender fields is defined implicitly by their types, but the size of the Name field must be specified (31 characters in our sample). Further the interface looks very similar to a database recordset - we use SFRecord's MoveNext method  to move to the next record, and we can use other Move methods to position randomly if this is needed - for instance rec.Move 10, 0 will position the SFRecord over the 10-th record in the stream.

Now lets return to the seekability. Note that nor RecordCount, nor MoveNext/MoveFirst or Move can function without seekability. To count the records we need to know the current size of the stream, to move randomly we must be able to position randomly in it. The SFRecord, knowing the record structure just makes the calculations for us and repositions the stream. Apparently this can be done over a file or a stream from an OLE storage file or in a memory stream, but if we use a network connection for example there is no way to know the size of data that will be received, nor we can position over it. That is why some of the objects in the library support alternative ways to read/write records. For instance SFRecord has the Read and Write methods which will read/write the record at the current position of the stream, but will not attempt any repositioning. In case of a file this will mean that after reading a record you are positioned over the next one, in case of a network connection the data in the record is just sent. Therefore the above sample code can be changed this way to work without requiring the stream to be seekable:

' Assume dataStrm is an already opened stream - a file or something else
Set rec = Server.CreateObject("newObjects.utilctls.SFRecord")
rec.AddField "Name", vbString, 31
rec.AddField "Age", vbLong
rec.AddField "Gender", vbBoolean

rec.BindTo dataStrm 
rec.Filter.unicodeText = False
rec.MoveFirst 
Dim recCount 
recCount = 0

While rec.Read %>
  <HR>
  Name: <B><%= rec("Name").Value %></B><BR>
  Age: [<%= CLng(rec("Age").Value) %>]<BR>
  Gender: 
  <% If rec("gender").Value Then 
    Response.Write "Male"
  Else 
    Response.Write "Female" 
  %><BR><%
  recCount = recCount + 1
Wend

As the Read method returns a Boolean result indicating success we can use it instead of EOS (End Of Stream) property, but of course EOS can be used if we prefer that. We can count the records only by incrementing the variable recCount while we read them, but not before we read all the records from the stream, because the size is not known if the stream is non-seekable.

The seekability concerns some other features as well. For example the ReadText method we used in some samples above has an argument that specifies how many characters to read. The negative numbers of -1, -2 and -3 have special meanings instructing the method to determine their number over a rule. For instance -1 will read up to the end of a text line where the end of the text line is recognized as a sequence of characters as specified in the SFStream's textLineSeparator property, the -2 will read the entire stream, the -3 is much like -1 but it will recognize the end of the text line even if the line separator characters are in unexpected order. For example in Windows the text lines end with <CR><LF> characters, but if you read a text file is UNIX formatted the text lines will end with <LF> character only. Thus the -3 value instructs the ReadText method to attempt all the permutations of the characters specified in the textLineSeparator property. However, this may require the ReadText method to re-position the stream after determining the end of the line, so it would remain at the beginning of the next line after the operation. Thus the -3 option of ReadText requires seekable streams. These requirements are specified in the documentation and you must notice them when you are going to work with non-seekable streams.

We used numeric literals in the samples above, but you can include the constants definition and use friendly names instead of the numbers. While ReadText has only 3 constants which if frequently used can be remembered, the OpenFile/OpenStream flags are many and using the named constant definitions will make the code much readable.

Threads

Many ASP developers know that there are tasks that require more time to complete than an ASP page will permit. Even if the time is not too long you always risk the user to decide that the application has failed and close it or stop the navigation thus interrupting the work you want to finish. Sometimes it is even worse - you may need to establish a code that will perform service tasks scheduled by the application and there is no way to do so using a regular ASP page. Even in non-WEB development environment (like VB for instance) such a task will need to be performed in the background in order to keep the user interface responsive and not block it completely while the task is in progress. This is where the threads come handy. Lets take a look at a simple example:

1. The code in an ASP page that creates the thread:

If Not IsObject(Application("Thread1")) Then
    Set Application("Thread1") = Server.CreateObject("newObjects.utilctls.COMScriptThread.free")
End If
Set thread = Application("Thread1")

Message = ""

' Initialize the max number for user convenience
If Request("MaxNum") = "" Then 
    MaxNum = 30000
Else
    MaxNum = CLng(Request("MaxNum"))
End If


' Configure the thread's parameters
thread.Value("MaxNum") = CLng(Request("MaxNum"))
thread.Value("CurNum") = CLng(0)
thread.Value("Found") = CLng(0)
thread.Value("Results") = ""
                        
Set sf = Server.CreateObject("newObjects.utilctls.SFMain")
Set file = sf.OpenFile(Server.MapPath("threadscript.vbs"),&H40)
        
' Start the thread.

If Not thread.Start("VBScript",file.ReadText(-2)) Then
    ' An error has occured.
    Message = "Error starting the thread: " & thread.LastError
Else
    Message = "Thread has been started."
End If

2. The code running in the thread (the threadscript.vbs file we load in the code above).

Function IsPrime(n)
    Dim j
    For j = 2 To n / 2
        If n Mod j = 0 Then
            IsPrime = False
            Exit Function
        End If 
    Next
    IsPrime = True
End Function

For I = 0 To Context("MaxNum")
    Context("CurNum") = CLng(I)
    If IsPrime(I) Then
        Context("Found") = Context("Found") + 1
        If Context("Results") <> "" Then Context("Results") = Context("Results") & ", "
        Context("Results") = Context("Results") & I
    End If
Next

3. And a code in an ASP page that examines the current thread progress or result if it has finished.

Set thread = Application("Thread1") 
<% If thread.Busy Then %>
  <B>Still Running</B><BR>
  Current number <B><%= thread.Value("CurNum") %></B><BR>
<% Else %>
    <% If IsEmpty(thread.Value("MaxNum")) Then %>
        <B>Has not been started yet.</B><BR>
    <% Else %>  
        <% If thread.Success Then %>
            <B>Finished Successfuly</B><BR>
            <%= thread.Value("Results") %><BR>
        <% Else %>
            <B>Error occured</B><BR>
            <B><%= thread.LastError %></B><BR>
        <% End If %>
    <% End If %>
<% End If %>

The thread in the sample code seeks for prime numbers in quite an ineffective way in order to take more time and make the sample substantial.

The first important step done by the page that starts the thread (1) is to create the COMScriptThread object or use an existing one if it is already created. Special attention deserves the fact that the COMScriptThread object is saved in an Application variable. Without this running a thread will make no sense, because after the ASP page completes the contact with the thread will be lost. Thus in the "real world" the application should do even more if the thread object is already created - check if it is in use and if that is so delay the new task for example, or may be create another COMScriptThread object. We will skip these details here, but you can take a look at the Script thread example in the samples set installed with ALP to see a more precise implementation.

The next important step are the parameters through which the ASP pages will communicate with the thread. COMScriptThread supports a collection of variables similar to the Application and Session in ASP. This collection is accessible for the both ASP pages and the script that runs in the thread. The "outer world" - the ASP pages access it using the Value property of the COMScriptThread object. You can see the code 1 setting several named values there. On the other side - in the script that runs in the thread the same collection is visible through a global namespace Context -see the code segment 2 above. The name is different but the values are the same - so the both sides can exchange information, the ASP pages can pass parameters that will alter the thread behavior etc. This collection is most often called Thread context in the documentation and the examples. COMScriptThread uses another object from the run-time library to implement it - the VarDictionary object. Therefore the Value property and the Context namespace expose the same VarDictionary object for the both parties. This implies that you can use over that object the methods and properties listed in the VarDictionary documentation. For instance you can obtain the count of the values in the collection in the ASP page by using thread.Value.Count or on the other side (in the thread) by using Context.Count.

By default the Thread context is configured to refuse objects. Thus by default in result of an attempt to set an object in it you will receive an error message, but passing objects as parameters is not impossible. To do so you must change the Thread context behavior by setting thread.Value.extractValues = False (see VarDictionary in NDL). The HTTP server sample (installed by ALP) uses this feature because the ASP controller page in the example wants to be able to interrupt the HTTP server by closing its listening socket which is kept in the Thread context. The default behavior prevents objects to be passed as parameters in order to help avoid some human mistakes. It is not possible to pass any object as parameter to a thread. The developer must be aware of the object capabilities and life time before considering passing it as parameter. For example passing the ASP Request object as parameter will be wrong, because it will be no longer functional after the ASP page finishes its execution. This may be obvious, but there are other cases in which it is not so obvious - consider passing an ADO recordset which is closed later in the ASP page - this will be wrong again because the thread may try to access it after that moment. Thus passing objects to the thread requires you to know well how they behave, if they depend on the state of other objects, if their life-time will be enough and so on. Thus the default behavior guarantees that only basic values will be recorded in the Thread context and no issues will occur. This is especially useful in JScript where no equivalent of the VBScript's Set operator exists and one can pass an object in a mistake. In this case the collection will assume that the developer meant to pass a value and will attempt to extract it from the default property of the object (if it supports such).

Finally in the code segment 3 above we are inspecting the thread's state and display it. We can check if the thread is still busy (see the Busy property)  performing the task or is idle. If it is not doing anything and we are sure we have started it it is logical to check if an error has occurred in it and display it - see the lastError and the Success properties..

The applications can do more with the threads. Some of the typical usages are: Passing a database connection to delegate to a thread a long lasting data mining operation; Creating a set of threads (thread pool) to implement efficient server software; Performing network operations such as fetching/sending e-mail in the background while the user reads the already received messages using the other ASP pages in the application; Implementing application automation tasks such as generating, importing/exporting from/to office documents and so on. Generally any task that requires none or only occasional user attention before it is finished fits best in a thread. The ALP applications thus are able to implement user interface which will never get stuck while the application is busy performing the requested operations - which is one of the most unnerving issues for the most users.

Quick look at other run-time library features

StringUtilities object (newobjects.utilctls.StringUtilities) provides string formatting functionality which will be familiar for all the developers who know about the printf/sprintf functions in the C standard libraries. In ASP and other scripting environment it is something one often misses and the library fills this gap. However, there are additional opportunities to a such implementation in a COM environment such as ASP pages and the StringUtilties object implements many extensions to the format specification known from C preserving the backward compatibility. For example there are the %q and %Q escapes which will format a value to string escaping the quotes in it so that it can be placed directly in an SQL statement. The StingUtilities' SCprinf and SAprintf methods in contrast to the Sprintf method do not use variable arguments count. Instead they are fetching the arguments from a collection object or from an array. This allows also an extension that allows you refer to the arguments non-sequentially but by name or index in collection/array. The object also supports custom date/time formatting which can be controlled directly by the application no matter if the operating system has the locale specific files installed. The automatic formatting allows you specify %a escapes which will try to format the argument to one of the several formats you specify in a separate property, thus giving you opportunity to gain more flexibility with arguments that may hold different value types. There is also support for NULL values which is also handy for SQL statements.

VarDictionary and UtilStringList are universal collection objects. VarDictionary deserves more attention - it can be compared to the Scripting.Dictionary you may know from the your experience. Still this object is more advanced and supports features that allow effective code to be created for more complex data structures. For instance if you put VarDictionary objects as values in another VarDictionary object you can construct a tree in the memory. As it grows larger you will feel the need of a feature that will allow you search through it in depth. VarDictionary offers methods like FindByValue, FindByName and FindByInfo. They perform a serch in depth and return set of references to the "nodes" that match the criteria. Such a feature allows you use VarDictionary to create structures that offer features similar to the XML object model and often they will be simpler to use than XML. VarDictionary also offers a Clone method that allows you clone the object with its contents (even if it is a tree as described above), the behavior properties extractValues, itemsAssignmentAllowed, readOnly and so on allow you adjust the object behavior to match your particular needs or prevent wrong usage in other parts of the application. The Info, Missing and Root properties allow metadata to be attached and further behavior refining - for example the Missing property specifies what will be returned when the application attempts to read a non-existent value.

Thus the features of the VarDictionary object make it convenient for various purposes. Many other library objects use it internally, some of them return it through some of their properties or methods. This allows unification of the data obtained from different objects using the common VarDictionary features. Knowing this may save you some coding efforts in many places by giving you the opportunity to transfer or otherwise organize the data. Object that use VarDictionary: ConfigFile, SFStorage, SFMain, COMScriptThread, ScriptManager, VaryDisp, SQLiteCOM and so on.

SQLite COM. This object with little help of the VarDictionary object implements alone a fully functional SQL database engine. It is based on the SQLite free source code, but also adds some features convenient in Windows environment. The database engine and the interface to it are both in this single object, it does not use ADO, OLEDB or MS Jet (the MS Access' database engine) and is thus independent of what you have installed on your machine. It uses non-recordset based database interface which will look familiar and convenient to the ADO developers who like the usage of ADO Recordset's GetRows method. Still, SQLite COM interface is something between a recordset and array caring the features that require less code to implement database functionality. A typical database operation with SQLite COM requires up to 2 times less code than the same operation performed with ADO, the number of methods and properties you need to learn is very low (about 10 from which only 3-4 are frequently used).

The actual benefit of using SQLite COM is obvious if you consider the fact that it uses single file to store all the database objects, the database file format is device independent (it can be copied to a Pocket PC and used there without need of conversion), there is no need of external configuration, nor need to perform any preliminary steps prior to using the database - it can even run in pure autorun scenarios where the component is not installed. Therefore besides the obvious applications such as programs that aim to ultimate compatibility, autoruns that must run directly from a removable media there are other applications for SQLite COM. For example consider an application that works mainly in your organization's infrastructure, it uses access to internal database servers, office applications and other specific software. Sometimes a need may arise to create an application that must work with some data extracted from the local database servers and applications, but it must work separately on machines not connected to your network. Another application is the case in which you may need to export information extracted from such an environment to a file and use/transfer it to another office or organization. You can maintain a file or set of files, but this will require you to write additional code to support their structure - very often one dreams for a database interface to such temporary or permanent export files - SQLite COM databases are stored in single file binary compatible with all the platforms so you do not need to go for more complicated solutions if a database interface fits your needs.

 

 

 

 

 

 

 

Copyright 2001-2005 newObjects [ ]