Tuesday, December 22, 2009

Jaxer in Aptana

I downloaded version 2.02 of Aptana Studio; it is supposed to have Jaxer in it. I cannot find it.

I have looked at many sites for using Jaxer, I am unable to do step 1 in any of these sites. They all refer to some button or menu in Aptana that is grayed out or that I do not have.

I found a good site at IBM that details how to install Jaxer as a stand-alone server on Windows.

It used a simple example demonstrating how to use a callback; of course, it failed. It turns out that the file that it is trying to access, http://update.aptana.com/update/jaxer/win32/version.txt, cannot be accessed.

I created a file on my own server that will report some number in the correct format: .0.9. Now the example works.

I have found the framework for Jaxer. The query string is in Jaxer.parsedUrl.queryparts.

Functions can be run on the server, client or both. A property of the Function object can be used to allow a server function to be called from the client: functionName.proxy = true. The runat property of the script can also be set to server-proxy, which sets the proxy property to true for all functions in the script block.

I have found an excellent introduction to using Jaxer.

Use Jaxer.request.currentFolder to get the absolute path to the current folder.

Use Jaxer.Dir.resolve to get an absolute path to a relative reference.
Jaxer.Dir.resolve(Jaxer.request.currentFolder + "/posts")

Unobtrusive JavaScript does not set event handlers in the HTML tags. Use JavaScript to set the handlers. If the tag is used, then it removes all other handlers.

form.submit is called when a form is submitted programatically, not by clicking a submit button.
form.addEventListener("submit", handler, bool) is called when a submit button in the form is called. The same effect as using onLoad, but onLoad can only register one event.

When overriding submit, it is good to store the old submit method. The new method will not have the default behavior of submitting the form; call the old method to submit the form. This also has an advantage of allowing the current method to be canceled without canceling the entire event. Once an event is canceled using event.preventDefault, it is not possible to enable it again.


<html>
<head>
<title>Submit Events, Handlers and Prototypes</title>
<script type="text/javascript">

function submitFromScript() {
//There is no event object, but 'this' points to the form.
alert("Submitted by calling submit from a script: this = " + this);
//call the original, if it has been set.
if (this.submitPrototype) this.submitPrototype();
}

function submitFromSubmitButton(event) {
alert("Submitted by clicking a submit button in a form");
}

function submitFromWindowSubmitButton(event) {
alert("Submitted by clicking a submit button in a form, caught by window event");
}

function submitFromHandler(event) {
alert("Submitted by clicking submit button and overriding the submit handler for the form");
if (this.submitHandler) this.submitHandler();
}

// Function to change the content of t2
function modifyText(event) {
var t2 = document.getElementById("t2");
alert("modify t2: " + t2);
t2.firstChild.nodeValue = "three";
}

// Function to add event listener to t
function load() {
alert("load")

HTMLFormElement.prototype.submitPrototype = HTMLFormElement.prototype.submit;
HTMLFormElement.prototype.submit = submitFromScript;

var el = document.getElementById("frmPost");
el.submitHandler = el.submit;
el.submit = submitFromHandler;

el.addEventListener("submit", submitFromSubmitButton, false);
el.addEventListener("submit", modifyText, false);

}

window.addEventListener("load", load, false);

</script>
</head>
<body>
<form id="frmPost">
<input type="button" onClick="document.getElementById('frmPost').submit();" value="Submit by Script">
<input type="submit" value="Submit Button">
</form>
<table id="t">
<tr><td id="t1">one</td></tr>
<tr><td id="t2">two</td></tr>
</table>
</body>
</html>


The event handler code should always be run in the client. If it needs to run server code, then it should call a proxy on the server.

When using jQuery and Prototype, they have a conflict with $(). Use jQuery.noConflict() to allow the Prototype definition to prevail.

$(id) only works on the server. Add a function on the client that does the same thing:
function $(id) {
return window.document.getElementById(id);
}


The blog example from this site has errors when accessing files. Use resolve and combine to translate to a path that can be read by the native file system.
Jaxer.Dir.resolve(Jaxer.Dir.combine(Jaxer.request.documentRoot, "blog/posts"));


Here is the modified file that works on a PC.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Write Blog</title>
<script src="lib/prototype/prototype.js" runat="server" type="text/javascript">
</script>
<script src="lib/showdown/showdown.js" runat="server" type="text/javascript">
</script>

<script runat="client" type="text/javascript">

function $(id) {
return window.document.getElementById(id);
}

function onSubmit(evt) {
var newPost = $("txaPostText").value;

if (newPost.length == 0) {
alert("You haven't entered anything.");
}
else {
if (save(newPost)) {
//add the new post
formatPost(newPost);
$("txaPostText").clear();
}
else {
alert("An error occurred. Couldn't save your post.");
}
}
//stop the form from submitting
evt.preventDefault();

$("txaPostText").focus();

}

function onLoad(){
$('frmPost').addEventListener('submit', onSubmit, false);
}

if (window.addEventListener) {
window.addEventListener("load", onLoad, false);
}
</script>
<script runat="server" type="text/javascript">

function serverLoad(){
//create the main posts <DIV>

var posts_div = document.createElement('div');
posts_div.id = 'posts';
document.body.appendChild(posts_div);

var path = Jaxer.Dir.resolve(Jaxer.Dir.combine(Jaxer.request.documentRoot, "blog/posts"));
if (!Jaxer.Dir.exists(path)) {
formatPost("Path does not exist: " + path);
return;
}
var dir = new Jaxer.Dir(path);
var files = dir.readDir().map(function(file){
var post = Jaxer.File.read(Jaxer.Dir.combine(dir.path, file.leaf));
formatPost(post);
});
}

window.addEventListener("serverload", serverLoad, false);

var formatPost = (function(showdown){
return function(content){
var post_div = document.createElement('div');
Element.extend(post_div);
post_div.innerHTML = showdown.makeHtml(content);

// insert new post in the main div
$('posts').appendChild(post_div);
}
})(new Showdown.converter());

function save(text){
try {
var path = Jaxer.Dir.resolve(Jaxer.Dir.combine(Jaxer.request.documentRoot, "blog/posts"));
Jaxer.Log.info(path);
var dir = new Jaxer.Dir(path);
//get the last post number
var lastFileNo = (dir.readDir()).length;
path = Jaxer.Dir.resolve(Jaxer.Dir.combine(dir.path, (lastFileNo + 1) + ".txt"));
Jaxer.Log.info(path);
Jaxer.File.write(path, text);

return true;
}
catch (e) {
Jaxer.Log.error(e);

return false;
}
}
save.proxy = true;
</script>
</head>
<body>
<h1>The Blog Page that Writes Files</h1>
<form id="frmPost" action="">
<textarea rows="10" cols="40" id="txaPostText"></textarea>
<input id="btnPost" type="submit" value="Submit Post"/>
</form>
</body>
</html>

While exploring the Aptana resources, I found that there is a new forum site. The old site has a lot of references to Jaxer, but the new site does not. The jaxer.org site references the old forum, but there is link in the old to the new.

The new forum site has a page for forum for App Studio that has an article on manual plugins. On the link for the Jaxer plugin, I followed these instructions and was able to add Jaxer to Aptana Studio 2.0.2.

Installing this Plugin via Aptana or Eclipse

1. From the Help menu, select Install New Software... to open an Install pop-up window.
2. In the Work with: text box of the Install window, type the URL http://update15.aptana.org/jaxer/25739/ for the update site, and hit the Enter key.
3. In the populated table below, check the box next to the name of the plug-in, and click the Next button.
4. Click the Next button to go to the license page.
5. Choose the option to accept the terms of the license agreement, and click the Finish button.

More things are working now. I am writing test code to see if everything works. I now have access to the samples.

The API shows there is a data object in Jaxer, but it is called clientData.
Jaxer.Cache is Jaxer.CacheManager.
Jaxer.SendOptions does not exist.

Friday, October 30, 2009

Outer Join in MS Query

SELECT UffMacBen.`FIRST NAME`, UffMacBen.`LAST NAME`, UffMacBen.MAC_EMPLID, UffMacBen_Original.`FIRST NAME`, UffMacBen_Original.`LAST NAME`, UffMacBen_Original.MAC_EMPLID, UffMacBen.`FIRST NAME` FROM {oj `U:\uweb\members\2009\UFFmacBEN_09032009_copy`.UffMacBen UffMacBen LEFT OUTER JOIN `U:\uweb\members\2009\UFFmacBEN_09032009_copy`.UffMacBen_Original UffMacBen_Original ON UffMacBen.MAC_EMPLID = UffMacBen_Original.MAC_EMPLID} WHERE (UffMacBen_Original.MAC_EMPLID Is Null) UNION SELECT UffMacBen.`FIRST NAME`, UffMacBen.`LAST NAME`, UffMacBen.MAC_EMPLID, UffMacBen_Original.`FIRST NAME`, UffMacBen_Original.`LAST NAME`, UffMacBen_Original.MAC_EMPLID, UffMacBen.`FIRST NAME` FROM {oj `U:\uweb\members\2009\UFFmacBEN_09032009_copy`.UffMacBen_Original UffMacBen_Original LEFT OUTER JOIN `U:\uweb\members\2009\UFFmacBEN_09032009_copy`.UffMacBen UffMacBen ON UffMacBen_Original.MAC_EMPLID = UffMacBen.MAC_EMPLID} WHERE (UffMacBen.MAC_EMPLID Is Null)

Wednesday, October 14, 2009

Reading Excel Files from JDBC

Reading Excel Files from JDBC


I followed this example from JavaWorld and it worked. The instructions were good, except for the explanation of why qas was used as the name of the worksheet. The file was named qa.xls, the connection was qa-list; it took me a few minutes to realize that the name on the worksheet tab was qas.

Next step is to read my own file and do a query. It worked.

Next step is to perform an outer join between two sheets in the same file.

I have created the outer join with the following syntax (from ibm):

query = "SELECT * FROM {oj [09032009$] LEFT OUTER JOIN [09172009$] ON ([09032009$].MAC_EMPLID=[09172009$].MAC_EMPLID)}";

The names of my sheets in the file are 09032009 and 09172009.

I have determined the number of columns in the result set (from devdaily):

    //------------------------------------------------------//
   //  Here's the code to determine the number of columns  //
   //  in the ResultSet.                                   //
   //------------------------------------------------------//
   Statement st = conn.createStatement();
   ResultSet rs = st.executeQuery("SELECT * from Customer");
   ResultSetMetaData rsmd = rs.getMetaData();

   int numCols = rsmd.getColumnCount();
This can also be used to retrieve a column name.

Saturday, September 19, 2009

Replacing the Hard Drive on a Vista Machine

I dropped my HP tx2-1950dx Touchsmart laptop the other day. Since then, the hard drive has been making clicking sounds. After doing a disk check, it seems that the area from 11% to 14% on the hard disk is in bad shape.

I ordered a replacement disk from HP and it arrived the next day.

The first thing that needs to be done is to create a recovery disk that can be used to boot the system from the CD. In Vista, type Recovery in the start box and select Create Recovery Disks. This uses the recovery partition on the hard drive to create disks that will restore the system to its original factory state. Please note, this should have been done BEFORE the drive was damaged!!!

I have also created a current backup of the drive using the Windows Recovery option. I hope that I can use these disks to restore the disk instead of using the original state disks.

I also used DriveImageXML to create a compressed backup of the disk, but I will need to figure out a way to run the software before the main partition is installed.

I will follow the instructions on http://www.bleepingcomputer.com/tutorials/tutorial144.html
, once I have my recovery disks created.

After running the recovery disk, it seems that I will need to reinstall the factory state and then restore the later recovery that was created in Vista.

There is no way to load an image of the disk, except for the original factory state. The HP recovery disk does not allow access to the Repair Computer utility that is normally accessed after pressing F8 during boot. The only way to load another image is to buy a piece of hardware that will add the new drive to the computer through USB. I have ordered such a device.

I borrowed the hardware from a friend, but it didn't work on Vista. I bought the hardware for Vista. It still didn't work. Some of the partitions could be restored but not the boot partition. After several attempts, I surmised that the boot partition was corrupt, since I made it after the crash.

I ran the "restore to factory state" disks and reinstalled additional software and updates. All is well now.

Wednesday, August 26, 2009

Modifying the boot loader in Vista

I miss boot.ini. Why isn't there a GUI version of bcdedit?

I could run bcdedit, but I could not read the boot loader store without admin permission. I found a useful link that had the secret to running cmd with admin permission: type cmd in the search window in the start menu and hit CTRL-SHIFT-ENTER. So obvious.

bcdedit /? will show all the options.

bcdedit /v will show all the boot choices, including the annoying ID.

bcdedit /delete ID /cleanup will delete a choice from the list, as long as you get the ID correct.

If the partition has already been deleted, then this will not work!!!

bcdedit /deletevalue ID type will remove the entry, if you know the type.

bcdedit /? /TYPES will tell you more about the types.

bcdedit /deletevalue ID DESCRIPTION desc should remove the entry, but it doesn't, of course.

The problem that I have is that I have already deleted the Windows 7 partition, so none of the commands work. If I use the current OS, then they work. For instance,

bcdedit /set description "Vista Sucks"

works beautifully. I can even use the ID as

bcdedit /set ID description "Vista Really Sucks"

According to several posts, the /delete command should work but it doesn't.

The solution was to download easybcd

Wednesday, June 24, 2009

Fedora 11 Installing Samba

yum install samba
version 3.3.2-0.33.fc11

cd /etc/samba
mv smb.conf smb.conf.backup
vi smb.conf
[global]
workgroup = wrkgrp
netbios name = smbserver
security = SHARE
load printers = No
default service = global
path = /home
available = No
encrypt passwords = yes
[share]
writeable = yes
admin users = smbuser
path = /home/share
force user = root
valid users = smbuser
public = yes
available = yes

save and exit

add user smbuser to local and smb
useradd smbuser
passwd smbuser
smbpasswd -a smbuser

/etc/init.d/smb restart

new use z: \\ip\share passwd username

I could not connect from Windows.

I changed security to user: it still didn't work.

I am now reading an online Samba book.
http://us1.samba.org/samba/docs/man/Samba-HOWTO-Collection/


testparm is a command that will test the configuration of the samba conf file.
testparm /etc/samba/smb.conf

The file passes the test.

Optimize the file and strip all comments with
testparm -s source dest

SWAT is a web-based config tool. I will look into that after I get the conf file working.

Display all shares that are defined on a host:
smbclient -L hostname

This asks for a password for the current user. Even if I am logged in as the smb user, I am unable to connect.

Do an anonymous login by addeng -N to the command. I am able to see the share that I created.

Connect to home dir:
smbclient //host/user
This also requires password and fails.

/etc/sysconfig/network contains nameservers and hostname.
Where is the alias for eth0 and wlan0 defined? There is no /etc/modprobe.conf

Name resolution of DHCP addresses needs to be hardcoded, which defeats DHCP. I found a link,
http://www.cameratim.com/computing/linux/using-bind-as-a-local-dns-server, that indicates how to get around this. I will investigate later; for now I am hardcoding the DHCP address.

I failed to start nmb. Both the nmb and smb services must be running.
service smb start
service nmb start

Test if all the appropriate services are running (from http://troy.jdmz.net/samba/fw/):
# service smb stop
# netstat -ln > netstat-ln-smb.before
# service smb start
# netstat -ln > netstat-ln-smb.after
# diff netstat-ln-smb.*

Open firewall for udp and tcp in the /etc/sysconfig/iptables file:
-A INPUT -p udp -m udp --dport 137 -j ACCEPT
-A INPUT -p udp -m udp --dport 138 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 139 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 445 -j ACCEPT

Then restart the firewall
service iptables restart

The smbserver is now visible from the Window machine. I am still unable to connect to a share.

Tried a simple config from http://us6.samba.org/samba/docs/man/Samba-Guide/simple.html#draft-smbconf
[global]
workgroup = MIDEARTH
security = SHARE

[Plans]
path = /plans
read only = Yes
guest ok = Yes

When I tried smbclient -L server -Uroot%passwd, I got an error about LANMAN authorization. I fixed it by adding the following to the global section of the conf file and restarted smb:
client lanman auth = Yes
lanman auth = Yes

I can now see the server and the share (though I changed the name of the workgroup, but windows still sees the old one); however, when I try to access the share, my Linux security warns me that someone is trying to access my home directory. I placed the share in my home directory, so the error is correct. I will move the share. I just noticed that on Linux, the new workgroup name is not recognized, either.

I seem to being editing the wrong smb.conf file. The changes that I am making are not being implemented. I am editing /etc/samba/smb.conf ... No, I am editing the correct file, but the workgroup command in the global section is changing the domain, but not the workgroup.

I tried following the advice of the AVC warning: setsebool -P samba_enable_home_dirs=1, it did not resolve the problem.

I am still getting the home dir error; eventhough I moved the directory to the root. I will try moving it to /tmp ... Did not work.

I have added the following share
[homes]
browseable = ok
guest ok = yes


I can now access homes, but it opens my root directory.

I am able to mount a windows share with
mount.cifs //ip/path /mnt
It asks for a password.

I found a page on printing from linux to a windows printer: http://www.math.colostate.edu/~reinholz/freebsd/smb_print_client.html

In the original smb.conf, there are comments about SELinux for home directories and domains.
setsebool -P samba-domain-controller on
setsebool -P samba-enable-home-dirs on

There is also a comment about setting samba-share-t for new directories, for allowing SELinux to write to a dir.

Use the command ls -ldZ to see the context for a directory.

I used the original smb.conf file and I can access home directories.

Installing Fedora 11 on Eee PC

Fedora is using anaconda 11.5.0.59 as its installer.

Graphical screen with fedora logo: click Next.
Select Language: Next
Select Keyboard: Next
Finding storage devices ...
Name this computer - Hostname: eeepc: Next
Map of globe; select city in time zone: America/NY: next (check box for System clock uses UTC: checked)
root password: Next
Partition drives: sda and sdb; sda contains /boot: Next
Write changes to disk
Failed! Could not mount /boot

Restarted
...
Write changes to disk
Installation packages: Office and Productivity; Installation repo; Customize later:Next
Checking Dependencies
Starting Install
Installing 1091 packages

Congratulations. Reboot and install updates.

Additional configuration: Forward
License info: Forward
Create user (can set up network login here): Forward
Set date: Forward
SMOLT is hardware profiler; send profile: Finish

Thursday, May 28, 2009

JSP include tag versus include directive

The first thing to note is that if either of these directives fails, then the page stops loading.

The directive will accept a relative path from the current location; the tag must have a path that starts with /, which is the root of the web application, not the root of the server.

The difference between the two is how they handle dynamic content. The tag includes the output of the dynamic reference in the page at runtime; the directive includes the contents of the dynamic reference when the including JSP is compiled into a servlet.

I am hard pressed to devise an example using JSPS that has different output for the tag and the directive. The directive can only include actual files, so the directive is useless if CGI or PHP files are to be included.

If the tag includes a reference to a servlet, be sure that the servlet does not close the output stream. This is a problem for a servlet that forwards to a JSP, since the output stream is closed after the forward is complete.

Both by-pass the security constraints in the web.xml file. Both can access WEB-INF, directly.

Both can only include resources that are in the web application.



I just read on another site that the difference is when they are loaded.

The tag loads at runtime.

<jsp:include page="includedPage" /> 

The directive loads at compile time.

<%@ include file="banner.jsp" %> 

From http://stackoverflow.com/questions/9188478/how-to-implement-a-include-jsp-tag

Friday, May 22, 2009

Connecting with JDBC to SQL Server

My tale of woe that has a happy ending.

I wanted to connect to SQL Server 2005 using JDBC. I verified the connection string and used standard code to test the connection.

try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String connectionUrl = "jdbc:sqlserver://localhost:1433;" +
"databaseName=Northwind;user=x;password=x;";
Connection con = DriverManager.getConnection(connectionUrl);
} catch (SQLException e) {
System.out.println("SQL Exception: " + e.toString());
} catch (ClassNotFoundException cE) {
System.out.println("Class Not Found Exception: " + cE.toString());
}
When I ran the code, I received this error
com.microsoft.sqlserver.jdbc.SQLServerConnection Prelogin
WARNING: ConnectionID:1 Prelogin error: host localhost port 1433 Unexpected response status:0
Actually, I received that error multiple times and eventually received a stack trace of

SQL Exception: com.microsoft.sqlserver.jdbc.SQLServerException: The TCP/IP connection to the host localhost, port 1433 has failed. Error: "The driver received an unexpected pre-login response. Verify the connection properties and check that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. This driver can be used only with SQL Server 2000 or later.

I then checked many sites for this message, but none had the exact message.

I tried logging into SQL Server directly, using the SQL Server Management Express program. I was able to log in with my user account, but could not access the Northwind or pubs databases. I conferred with some colleagues and needed to set the server with mixed access: Windows and SQL. There was an old account that I had created, named sa. I reactivated it and changed its password. I logged on with it and then updated my Windows login account with sysadmin rights. After this, I was able to connect to the Northwind and pubs databases; however, I still received the original error when I tried to connect to the database using JDBC.

After more searching, I found the Code Ranch site which talked about removing dynamic ports and setting a static port in the SQL Server Configuration Manager. I tried to follow the instructions, but my server would not restart with a static port of 1433. I then tried to access the server from JDBC using the dynamic port and was able to connect.

Next, I tried to connect to the database using Hibernate. I used the JDBC driver from microsoft, the package for the driver is com.microsoft.sqlserver.jdbc.SQLServerDriver. The Hibernate dialect for SQL Server is org.hibernate.dialect.SQLServerDialect. I was unable to connect. I used the same URL as I used when connecting with Java, jdbc:sqlserver://localhost:3346/Northwind. The dynamic port is 3346, the name of the database is Northwind.

There was a helpful post about clients and servers that guided me to the SQL Server Configuration Manager again. This time, I started to understand what I was seeing. There is the SQL server and the SQL client. The client was using port 1433, the server was using port 3346. I changed the client to 3346, but I still had an error.

The error was an SSL error. I found a helpful link about certificates, but I was unclear about exporting my certificates and lost them. Since this link referred to installing SQL Server after deleting the certificates, I am downloading Server 2008. I am hoping that a new installation will miraculously have no problems!

The referring page also had some information about not installing the old certificates, but creating a new one. I am investigating. It refers to a program named SelfSSL.exe.

I did not pursue the creation of a certificate, since the reference was for IIS. I did complete the new installation of SQL Server 2008. I am able to access sqlcmd from the command line and am able to connect to the server from Java. I did not have to tweak any parameters, both the client and the server were using 1433 and TCP/IP was enabled.

I did have one confusion, that may have been the basis of a lot of my problems: I didn't know what a schema was in SQL Server. According to MicroSoft, a schema is like a namespace for tables that can be accessed. If you don't have the correct schema, then you can't see all the tables. The default schema is dbo. Once I changed all my tables to that schema, I was able to connect and see them in NetBeans database services.

I still could not connect using Hibernate. I decided to reinstall MySQL, since I was missing the GUI admin app. After installing, I followed an excellent tutorial on using MySQL in NetBeans. I also found an excellent tutorial for using NetBeans to reverse engineer tables to create POJOs. Once I had these tutorials running, I tackled SQL Server 2008.

I modified the process from the tutorial to create the POJO for two tables from the Northwind database. I found an excellent tutorial for installing Northwind and pubs databases, which for some reason are not included in the install of SQL Server. Once I had the table, I changed the database to the SQL Server instead of MySQL. I have included the important properties below.
   <property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.url">jdbc:sqlserver://localhost:1433;databaseName=mydb</property>
<property name="hibernate.connection.username">name</property>
<property name="hibernate.connection.password">pass</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
The only difference that I could see from what I had used before was the current session context class; however, in my last iteration of testing before the reinstall, I omitted the dialect. That was probably the big error.

It finally works. In review, I had a problem with SQL Server 2005 that I never resolved; the new installation works without a hitch; I probably had a problem with schemas in the 2005 tests.
 

Sunday, May 3, 2009

Enabling SSI in Apache Tomcat 6.0

I used the filter option for SSI in Tomcat 5.5. When I upgraded to Tomcat 6.0, I had to use the servlet option. When I used the filter option I received the error

SEVERE: Error filterStart

The web.xml file that I used had a comment about renaming a Jar file; however, it seems that the comment is left over from 5.5. There is no such Jar file in 6.0. The comment was also missing the instruction to uncomment the servlet mapping for the ssi servlet.

In summary, follow these steps to enable SSI in Tomcat 6.0

  1. Use the servlet, not the filter.
  2. Uncomment the servlet definition and servlet mapping for the ssi servlet in web.xml.
  3. Mark the context as privileged.

Tuesday, March 3, 2009

Serialization

There are several ways to implement serialization in .NET. The simplest way is to mark the class as Serializable and then to create a formatter to do the serialization. Fields that are not serializable can be marked as such and will not be serialized.
[Serializable]
public class SomeClass {
//mark members that are not serializable
[NonSerialized] SomeNoneSerializedType t;
}
The formatter can be created in any class that needs to serialize the above class.
public void SomeSaveHandler(Object sender, EventArgs e) {

using (SaveFileDialog dlg = new SaveFileDialog())
{
if (dlg.ShowDialog() != DialogResult.OK) return;
using (Stream stream =
new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write))
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, instanceSomeClass);
}
}
}
Deserialization is similar.
public void SomeOpenHandler(Object sender, EventArgs e) {

using (OpenFileDialog dlg = new OpenFileDialog())
{
if (dlg.ShowDialog() != DialogResult.OK) return;
using (Stream stream =
new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read))
{
IFormatter formatter = new BinaryFormatter();
SomeClass someClass = (SomeClass) formatter.Deserialize(stream);
}
}
}
More than one object can be serialized by having additional calls to serialize; one for each object. During deserialization, make a corresponding call to deserialize for each object that was serialized; use the same order when deserializing as was used when serializing.

For elements that are calculated fields, they can be recalculated after the object has been deserialized, by implementing the IDeserializationCallback interface. Define the callback method OnDeserialization to instantiate any calculated fields. The parameter to this method is not implemented at this time, so it is always null.
[Serializable]
public class SomeClass: IDerializationCallback {
//mark members that are not serializable
[NonSerialized] SomeNoneSerializedType t;

public void OnDeserialization(object sender) {
t = SomeCalculation();
}
}


The other method to serialize takes a different approach. Instead of assuming that everything is serializable, it assumes that nothing is serializable. Instead of indicating what should not be serialized, the class must specifically serialize each element. This technique is implemented in the interface ISerializable. Define the method GetObjectData and define a constructor that will be used during deserialization.
[Serializable]
public class SomeClass : ISerializable {
SomeType t;
SomeNoneSerializedType non;

public SomeClass(
SerializationInfo info, StreamingContext context)
{
t = (SomeType) info.GetValue("myType", typeof(SomeType));
non = SomeCalculation();
}

public void GetObjectData(
SerializationInfo info, StreamingContext context)
{
info.AddValue("myType", t);
}
}
Call info.AddValue for every member that needs to be serialized. Call info.GetValue for every member that needs to be deserialized.

There is no need for the deserialization callback interface, since calculated fields can be instantiated in the deserialization constructor.

This technique is useful for classes that extend a non-serializable class. The first technique will fail, if the base class cannot be serialized. By using the second technique and specifically serializing each element, the base class does not need to be serializable. The trick in this case is to make sure that any other constructors also get called when the deserialization constructor is called.

For the class that has to serialize this class, there is no difference when creating a formatter for either of these techniques. The calling class just calls serialize and deserialize on the formatter, it is up to the serializable class to determine how it will serialize.



It is possible to add methods to a class that will serialize/deserialize the class by creating a formatter. This encapsulates the entire process of serializing the class so that the class that is doing the serialization will not have to implement the details for creating the formatter.

The second technique is the preferred technique for implementing such methods. While the first technique could be used, its simplicity is lost, since it is not possible to deserialize into this. In order to make the first technique work from a method in the class, it would be necessary to deserialize into another class and then copy member by member into the current class. Another possible implementation would be to serialize/deserialize member by member.

Sunday, March 1, 2009

Handling Arrows

The arrow keys for a container control in .NET are used to switch the focus amongst multiple controls in the container. If a container has several controls, then the arrow keys will move the focus from one control to the next. This is a special behavior for the arrow keys in a container control. In the default case, the arrow keys never reach the controls in the container. There are several ways to get the arrow keys to the controls themselves.

The first way is to allow the arrow keys to maintain the special function that they have. Override the ProcessCmdKey method and add the additional handling for the arrow keys. To allow the keys to have the special function, be sure to call the base class method, in order to have the special function executed.

The second technique is to remove the special function altogether. There is another method called IsInputKey. If it returns true, then the key is passed directly to the control, without the preprocessing of the key. If it returns false, then preprocessing is executed; the key will only make it to the control if the preprocessing routes it there. If the key makes it to the control, then it can be caught in the normal KeyDown handler.

There is a third way, which I will call technique one-and-a-half. If in technique one, the ProcessCmdKey method returns true instead of returning the base class method, then the preprocessing will not be done. This has the same effect as method two. Instead of handling the key in KeyDown, handle the key in ProcessCmdKey.

There is another method for solving this problem: override OnPreviewKeyDown. This method can behave like ProcessCmdKey, but it works for all the keys: extra coding can be done for any key. There is even an event args property for IsInputKey which allows this method to perform like IsInputKey. To remove the command key functionality, set IsInputKey to true.

KeyCode is the code for the key pressed, without any modifiers like Shift or Control.

KeyData is the code for the key OR-ed with any modifier keys, like Shift or Control.

KeyValue is the integer equivalent of KeyData for passing to unmanaged methods.

KeyChar is generated from KeyPress event and has the ASCII code for a key.

Thursday, February 26, 2009

Testing for a Toggled Key

There are several keys on the keyboard that can be in a toggled state: num lock, scroll lock, cap lock and insert. There is a convenient method in the Control class that will get the state of the button: Control.IsKeyLocked(key). The key parameter can be one of Keys.CapLock, Keys.NumLock, Keys.Scroll and Keys.Insert. The method returns true if the key is in the toggled state, otherwise it returns false.

According to the MSDN documentation, the method will throw a NotSupportedException if the key is not CapLock, NumLock or Scroll; however, Insert also works and does not throw an exception.

This method has been available since .NET 2.0. For earlier versions, it was necessary to do some interop with the user32.dll. There is a method that returns a bit field for the state of a key. For keys that can be toggled, the lowest bit in the field will be 1. Use a bitwise & operation to isolate the bit.

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern ushort GetKeyState(int keyCode);

//From winuser.h
private const int VK_INSERT = 0x2D;
private const int VK_NUMLOCK = 0x90;
private const int VK_SCROLL = 0x91;
private const int VK_CAPITAL = 0x14;

public static bool IsKeyToggled(int nKey)
{
return (GetKeyState(nKey) & 0x01) == 1;
}

The hex codes for the keys can be obtained from winuser.h. In VS08, I found this file under the SDK folder for smart phones and pocket PC.

For Vista, there is also a Keyboad class that is part of the System.Windows.Input namespace. References to the presentation core and to the windows base dll's must be made. The presentation core contains the Keyboard and Keystate classes. The windows base contains the Key class. Be careful with the Key class, since there is also a Key class in System.Windows.Forms.

Keyboard.GetKeyStates(Key.CapsLock) == KeyStates.Toggled

 

Thursday, February 19, 2009

Running WPF in a Browser

Aaarrrggghhhh!!!!! Microsoft Security Exceptions!

I have been trying to get my first example of an XBAP file running in a browser. I am using the book Essential Windows Presentation Foundation, by Chris Anderson. I did not have any problems with Chapter 1 and was able to download the source code for the more complicated examples at the end of the chapter.

Chapter 2 does not have any source code available. I was trying to run a very simple "Hello World" application. I was still using the command line to run my examples, just to get a feel for the syntax of the project and application files. I ran the application as an exe without a problem. Next, I added the property in the project file to allow the app to run in the browser.

<PropertyGroup>
<HostInBrowser>true</HostInBrowser>
</PropertyGroup>

When I ran it, I received an error about signing the manifest.

Error 3 The ClickOnce manifest for XAML Browser Applications must always be signed. You must specify properties: SignManifests (value set to True), and either ManifestKeyFile (with the name of your key file) or ManifestCertificateThumbprint (hexadecimal thumbprint value in SHA-1 format, of key file). Alternatively, you may use your IDE's Publish Wizard or Signing options.


I referred back to the book and saw a footnote about singing manifests using Visual Studio. I opened Visual Studio and created a new WPF Project. A lot of files were created for me. I signed the manifest from the Properties -> Signing tab. I created a temporary certificate. I ran the simple application as an exe and it worked. I edited the csproj in a text editor and added the code for running in a browser. I tried to run the app from VS, but could not, since it is meant to be run in the browser. I found the XBAP file in the file system and double-clicked it. The browser opened and I received this error.

System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.

Looking further in the error list, I noticed that the assembly did have UIPermission, but only for save top level windows. I then remembered that the example from the book used Page as the base class, but that the default application in VS used Window. I edited the project so that it used Page instead of Window. I still had the same error.

When I had developed this from a different computer, I received a different error about the assembly not allowing partially trusted callers to use it. I had resolved this on the other computer, so I thought it might be a problem on this one. There is a very detailed explanation of the problem on the web. The trick is to add the AllowPartiallyTrustedCallers attribute to the code-behind file, at the assembly level.

using System.Security;
[assembly: AllowPartiallyTrustedCallers]

I still had the error. I had seen some posts on the web about similar errors that referred to publishing the application, so I published it, using the Build menu. The URL that was generated had an extra " at the end, so it failed. After removing that, I was given a directory listing. I double-clicked the XBAP file and it worked.

Since I had made so many changes before the application worked, I backtracked to see which one(s) had done the trick. The only one that I did not need was for the PartiallyTrustedCallers. Apparently, the computer I am using now has different access rights than the one I was using earlier, so I will keep the assembly attribute in the final solution.

Looking back at the first error I received, I see that it suggests using the IDE's signing or publishing wizards to sign the manifest; apparently, both signing and publishing are needed.

PS. There was another error that I saw referenced on the web about the application cache. At some point I also tried clearing it. The process was to close the browser, open the VS commmand prompt and execute the command mage -cc. The command took a minute to complete. I do not think this had anything to do with getting my example to run.

PPS. I have reconfigured the application for the original computer: I changed the app from a base class of Window to Page and published the application. I did not need to add the partially trusted attribute. To be more specific, I ran it with the attribute, then I removed the attribute, rebuilt, republished and it still ran.
 

Tuesday, February 17, 2009

ASP.NET

I have been developing web applications for many years. I started with Perl many years ago; for the past few years, I have been using Servlets and JSPs. I decided to port an application to ASP.NET, just to see how it went.

I prefer the MVC architecture and have found that in order to implement it, I need to bypass the rich control set that ASP.NET offers. The controller in MVC needs to get all of the requests; since anything with runat="server" is consumed by the associated code file, they cannot be used with a controller.

I use a separate class for the model; accessing this from an ASP is complicated, since a cast is required and the class must be registered in the page. I prefer Expression Language in JSPs.

I talked to a friend who uses ASP.NET all the time and he did help me out with Server.Transfer, which can be used in place of Response.Redirect. However, there is a bug in the Server.Transfer when the form data is to be forwarded. I found a comprehensive discussion of Server.Tranfser.

I will continue to explore. The friend I mentioned suggested that WPF can be used to implement MVC. I will look into that.

Sunday, February 15, 2009

Using LINQ in ASP.NET

I have a new book: C# 2008 by Mei-Weng Lee, WROX. I am playing around with LINQ in ASP.NET. The first step was to remember how to access SQL Server and SQL Server Express; I had already installed both of these some years ago. When I opened the Server Explorer in VS08, the Data Connections was empty.

I followed the instructions in the book to download pubs and Northwind databases, although I was sure I already had Northwind installed. The key step was to run the installation scripts from a VS08 command prompt.

sqlcmd -S .\SQLEXPRESS -i installpubs.sql
sqlcmd -S .\SQLEXPRESS -i installnwnd.sql

After returning to the Server Explorer, I still did not have any data connections, so I added one. I used SQL Server as the connection type; I chose SQLEXPRESS as the server; I chose the pubs database. Now I have a connection.

I used the visual designer for the .dbml file and dragged two tables onto it. After saving, the designer file had created a class with declared properties for accessing the tables.

I had to start again: at first, I selected new project, instead of new web site. Much better as a web site, I am now seeing all the prompts that I am supposed to see. After I understand this better, I will need to analyze the difference between a project and a web site.

I was able to access the table from ASP. I see that LINQ can create classes based on tables, but can it create tables based on classes? I have not seen any information on this. The other problem I have is that a lot of code is written in the ASP and not in a separate controller for the business logic.

I will look into using Hibernate with .NET. I will also look into moving all the business logic into one code-behind file and put all the validation in the data class.

Thursday, February 12, 2009

Stylesheet for Printing

I never questioned how some HTML pages print everything on the page and others only print the relevant information. I just learned that there is a media attribute in a style sheet tag that controls to which media the style will apply.

The options for the media attribute are
all
Suitable for all devices.
aural
Intended for speech synthesizers. See the section on aural style sheets for details.
braille
Intended for braille tactile feedback devices.
embossed
Intended for paged braille printers.
handheld
Intended for handheld devices (typically small screen, monochrome, limited bandwidth).
print
Intended for paged, opaque material and for documents viewed on screen in print preview mode. Please consult the section on paged media for information about formatting issues that are specific to paged media.
projection
Intended for projected presentations, for example projectors or print to transparencies. Please consult the section on paged media for information about formatting issues that are specific to paged media.
screen
Intended primarily for color computer screens.
tty
Intended for media using a fixed-pitch character grid, such as teletypes, terminals, or portable devices with limited display capabilities. Authors should not use pixel units with the "tty" media type.
tv
Intended for television-type devices (low resolution, color, limited-scrollability screens, sound available).
The media type can be included in the stylesheet with
@media print{
style1 {
...
}
}
Alternatively, separate style sheets can be created for each media type.
<link href="styles/screen.css" type="text/css" rel="stylesheet" 
media="all" id="screenCSS" />
<link href="styles/print.css" type="text/css" rel="stylesheet"
media="print" id="printCSS" />


A javascript trick can be used to make the current page look like print preview; kudos to the creator. The idea is to change the media type of the 'print' sytlesheet to 'all' using javascript.

I would make one addition to the technique: use three style sheets. The third stylesheet would be for print preview. This is necessary in the event that you don't want to print the link or button that toggles the style sheet.
<link rel="stylesheet" href="styles/all.css" type="text/css" 
id="screenCSS" media="all">
<link rel="stylesheet" href="styles/print.css" type="text/css"
id="printCSS" media="print">
<link rel="stylesheet" href="styles/preview.css" type="text/css"
id="previewCSS" media="preview">
<script language='javascript'>
function togglePrintPreview()
{
var currCSS = document.getElementById('previewCSS');
if(currCSS.media == 'all')
currCSS.media = 'preview';
else currCSS.media = 'all';
}
</script>

Wednesday, February 11, 2009

Multithreading in C#

I am reading the book C# 2008, Programmer's Reference, by Wei-Meng Lee. I have just finished the chapter on threading. The book covers the material very well. It does not have a discussion of the process thread pool, but it does cover the different ways to implement multi-threading. The most convenient method is to use the BackgroundWorker component; however, for more advanced threads, the Monitor class is most useful.

The Monitor class has the methods Enter, Exit, Wait and Pulse. They all use an object for locking; use a static object that both threads can access. Use Enter and Exit around the critical section. Use Wait in the event that one thread needs to wait for the other thread to do something before it can continue. The other thread must call pulse when it has completed the task that waiting thread needs to be done.

The lock method is a simple Monitor that only has Enter and Exit.

For some functions, like increment and decrement, there is the Interlock class, which has static methods that are thread safe.

Sunday, February 8, 2009

Control Library is more Useful than ClassLibrary

A ClassLibrary project and a UserControl project are not much different. Each can contain user controls and forms. There was a reference to a problem with using user controls in a class library.

There is also a reference to a problem with using a control library and a class library. I also had a problem with this, since each one referred to the other; after doing a clean, I was unable to rebuild.

The only differences I have observed are some different references. The class library has a reference to System.Linq.

I will stick to a user control library and add general classes to it, too.

As for visual inheritance of a form in the library, the controls that have protected access will also be set in the InitalizeComponent of the child, so the child can change them; private controls will not be referenced in the child.

The components container in the child class will not be instantiated until a control or component is added to the child form.

I have been modifying a base form and adding child forms based on it. After I have added a child by creating an inherited class, then made changes to the base and then added another child by creating an inherited class, there can be a reference error when adding the second child; resolve this by cleaning the solution, rebuilding the control library and rebuilding the solution.

Visual Inheritance of Statusbars and Toolstrips

I wanted to create a base form that contained a default menu and a default status bar. When I inherited from this form, I wanted to be able to customize the menu and the status bar. After many attempts to get this to work, I realized that it cannot be done in the designer.

I watched an interesting video on Visual Inheritance that confirmed that I was doing things correctly. After I implemented the tutorial for an inherited button, I realized that there was something wrong with menus and status bars.

After searching google for a while, I stumbled on a discussion on the topic. The reference from there was the goldmine: it can't be done!

Back to the drawing board. I will look into adding a second menu to the child form and then adding the menu items to the main form in the base class. The child can still access the menu, it just can't access it at design time in the designer. Sigh.

I have devised a workable solution for the menu strip.
  1. Create a context menu in the child class in the designer; this will support visual editing.
  2. Create a menu item at run time in the child for a new top level menu item.
  3. Set the DropDown event as the context menu.
  4. Insert the top level menu item into the main menu.
//Place in constructor or load event handler
ToolStripMenuItem menuItem = new ToolStripMenuItem("Tools");
menuItem.DropDown = contextMenuStripTools;
this.menuStripBase.Items.Insert(0, menuItem);
Here is another idea:
  1. Add a menu strip to the child. Add all the menu items to it.
  2. Add all the items from the main menu to the base menu.
  3. Access the items in the main menu in reverse order, since each item is moved to the base menu when it is inserted. A foreach cannot be used, since the item collection changes. AddRange won't work for the same reason.
 int count = 0;

 //Use this for loop, or a foreach, to see it crash
 //for (count = 0; count < max; count++)
 //A reverse pass through the items will do the trick
 for (count = menuStripMain.Items.Count - 1; count > -1; count--)
 {
   this.menuStripBase.Items.Insert(0, menuStripMain.Items[count]);
 }

Update: 

The ToolStripManager class has a method that does menu merging like MDI applications.

public static bool Merge(
    ToolStrip sourceToolStrip,
    ToolStrip targetToolStrip
)


ToolStripManager.Merge documentation. Notice that it is a static method.

Saturday, February 7, 2009

UserControl Container at Design Time

Today I learned how to create a UserControl in C#.NET that can act as a container control at design time. In other words, the control will act like a group control; otherwise, when a control is dropped onto it, it will pass to the container control that is behind the control.

Create a UserControl.
Add a reference to System.Design; this will add the System.Windows.Forms.Design.ParentControlDesigner designer to the application. Use this to adorn the class definition of the user control.

[Designer(typeof(System.Windows.Forms.Design.ParentControlDesigner))]
public partial class ControlMovable : UserControl

Now, the user control will act as a container at design time: when controls are dropped onto the user control, they will be added to the ControlCollection class in the user control.

In order to handle events in the user control class for the controls that are added to it at runtime, handle the ControlAdded event and ControlRemoved event. Register and remove control events in these handlers.

For example, the mouse down events are sent to the controls directly. In order for the user control to handle the MouseDown event, register a MouseDown handler with the e.Control member from the event args sent to the event.

private void ControlMovable_ControlAdded(object sender, ControlEventArgs e)
{
  e.Control.MouseDown += this.ControlMovable_MouseDown;
}

private void ControlMovable_ControlRemoved(object sender, ControlEventArgs e)
{
   e.Control.MouseDown -= this.ControlMovable_MouseDown;
}


Followers

Blog Archive