<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/wordpress-mu-1.2.3-2.2.1" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>Wayne Niddery</title>
	<link>http://blogs.teamb.com/wayneniddery</link>
	<description></description>
	<pubDate>Sun, 10 Sep 2006 13:34:08 +0000</pubDate>
	<generator>http://wordpress.org/?v=wordpress-mu-1.2.3-2.2.1</generator>
	<language>en-US</language>
			<item>
		<title>ECO Example: Backup Utility</title>
		<link>http://blogs.teamb.com/wayneniddery/2006/09/10/27447</link>
		<comments>http://blogs.teamb.com/wayneniddery/2006/09/10/27447#comments</comments>
		<pubDate>Sun, 10 Sep 2006 13:34:08 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2006/09/10/27409/</guid>
		<description><![CDATA[ECO Example: Backup Utility&#160;- by Wayne Niddery  Abstract: Created as an ECO learning vehicle for myself, it acts as a practical example demonstrating various abilities of ECO and that, even for such a modest project, ECO is an advantage and should never be considered &#8220;overkill&#8221;.   As part of learning to use ECO, [...]]]></description>
			<content:encoded><![CDATA[<p><SPAN class=title3 id=_ctl2_lblTitle><STRONG>ECO Example: Backup Utility</STRONG></SPAN>&nbsp;<A id=_ctl2_lblAuthor href="http://bdn.borland.com/sameauthor/33669"><FONT color=#191970>- by Wayne Niddery</FONT></A> <BR> <BLOCKQUOTE class=abstract><B>Abstract:</B> <SPAN id=_ctl2_lblAbstract>Created as an ECO learning vehicle for myself, it acts as a practical example demonstrating various abilities of ECO and that, even for such a modest project, ECO is an advantage and should never be considered &#8220;overkill&#8221;.</SPAN> </BLOCKQUOTE> <P><SPAN id=_ctl2_lblContent> <P class=toc1><A name=tocentry1></A>As part of learning to use ECO, I decided to create something I need anyway. As an independent developer I work on different projects over time and often at the same time. To protect myself and my clients, I like to back up my work to an offsite location.</P> <P>Now there are certainly lots of existing backup products of various capabilities, and it&#8217;s even possible to use WinZip and batch files. But I decided I wanted something easy to use and specific for my needs, and it would be a good exercise.</P> <P>In developing it, I also answered for myself a frequent question I hear: Isn&#8217;t ECO overkill for small projects? My answer is no. Despite this being a very modest project, I saved a great deal of time by not having to define relationships in code, manage lists of objects, and design and manage some kind of storage mechanism.</P> <P>So first let&#8217;s define the requirements. </P> <UL> <LI>We want to be able to define any number of backup &#8220;projects&#8221;, typically these would be one on one for each development project, but can be anything. <LI>Under each project, I want to define any number of folder specifications, and for each of those, indicate whether to recurse into its sub-folders. <LI>Also at the project level, I want to define any number of file masks. For development projects, this is of course going to typically include *.pas, *.dfm (or *.nfm), etc. but of course a backup project can include anything at all. <LI>The project will also need some information about where to store the backups it generates and what to name them. And of course we&#8217;re going to want to have some visible feedback as it does its work.</LI></UL> <P>Note: for the ability to zip up the selected files I used open source library called SharpZipLib available from <A href="http://www.icsharpcode.net/OpenSource/SharpZipLib/"><FONT color=#1f20a6>http://www.icsharpcode.net/OpenSource/SharpZipLib/</FONT></A>. This library is distributed under a modified GPL license that allows the library to be used by executables without requiring the executable to be GPL.</P> <P>Let&#8217;s start. Create a new ECO Winform Application project. Save the project with the name ECOBackup.</P> <P>We will need persistence in order to store the project definitions we create with our utility, so we will define that first. Due to the nature of this project, simple XML storage will be ideal.</P> <P>In the Project Manager, find the EcoBackupEcoSpace and open it. In the tool palette, under the Enterprise Core Objects node, there is a PersistenceMapperXML component that can be dropped on our ECO Space.</P> <P>Keyboard shortcut: Focus the tool palette with Ctrl-P, then just type &#8220;pe&#8221; and the palette will display the ECO persistence components. Scroll down to the PersistenceMapperXML and hit Enter.</P> <P>In the Object Inspector for the PersistenceMapperXML, we need to set a file name. This can just be ECOBackup.xml. You can give it a complete path if desired. At this point take a moment to Save All and compile the project. Then open Winform1 from the Project Manager. In the set of components at the bottom will be one called rhRoot. This is a reference handle used to provide access to the model from the form. In the Object Inspector for rhRoot you will see an EcoSpaceType property and you should now be able to click on the dropdown arrow and select the EcoSpace.</P><A name=1DefiningtheModel></A> <H1>Defining the Model</H1> <DIV class=section id=docsection1 header="Defining the Model"> <P>As mentioned above, this is a tiny model. There are only four classes, and only three of those will be persisted. We&#8217;ll start by defining the necessary attributes and associations to get us started, and we&#8217;ll add some extra bits to it as needed.</P> <P>Click on the Model View tab of the project manager and expand the ECOBackup node. There you should see a &#8220;Package_1&#8221;, open this to get to the model surface.</P> <P>From the tool palette, drag an ECO class on to the diagram and name it Project. This class will represent a single backup project, so we&#8217;ll define attributes needed at that level. The attributes that initially need to be added are a Name for the project, and what name to give the backup file being made. Since the goal is to be able to backup somewhere remotely, we will also need information about the destination computer: ServerName, ServerPath, ServerUserID, and ServerPassword. The Project class also needs two public methods: Preview and Backup. </P> <P>Keyboard shortcut: Use Ctrl-W to add attributes and Ctrl-M to add operations to a class.</P> <P><IMG id=docimage1 src="http://bdn.borland.com/article/33669/15338/images/33669/1.png" border=0></P> <P>According to our requirements, for a Project, we need to be able to define folders on the computer that we want backed up, and whether the backup should process a folder&#8217;s sub-folders. So we&#8217;ll add a second class to the project called Folder. Folder only needs two attributes: Path (String) and Recurse (Boolean).</P> <P>In addition to Folder, we need to be able to define file masks to control which files are backed up, so a 3rd class needs to be created which we&#8217;ll call FileMask and it will contain just one string attribute named Mask.</P> <P>Before our project can work, we need to define associations between them. For both the Folder and FileMask classes, there can be one to many of them for each Project, therefore we will add an association to each of these. If you create the associations by first clicking on Project and dragging to the other class, then for both, End 1 will be the Project. For the End 2 property then, we want to change the Multiplicity of each to 0..*. Strictly speaking this is enough, but to make the model better, we should recognize that both Folder and FileMask cannot logically exist apart from a Project, therefore they should be marked <I>Composite</I> in the End 2 Aggregation property. This will give the correct visual representation in the model as well.</P> <P>With this much defined in the model, we could go ahead and start defining the user interface. However, thinking ahead a bit, in order for Project to do its work, either Backup or Preview, it will need to process its Folders and FileMasks in order to generate a list of files to be backed up &#8211; which means we need something to store that information in - another class.</P> <P>Drop a new class on the diagram and name it FileEntry. Add two attributes, FilePath (string) and FileSize (double). Just like Folder and FileMask, we need to add a one-to-many composite association between Project and FileEntry. An important difference between FileEntry and the other classes in our model is that there is no need to store a FileEntry permanently, it is only needed while Project performs its tasks, therefore we need to mark the Persistence property of FileEntry as <I>transient</I> so that ECO will not bother storing them.</P> <P>Here&#8217;s our diagram so far:</P> <P><IMG id=docimage2 src="http://bdn.borland.com/article/33669/15338/images/33669/2.png" border=0></P> <P>At this point we are ready to start developing our user interface. We will revisit the model later in order to add some additional details.</P></DIV><A name=2DefiningtheUserInterface></A> <H1>Defining the User Interface</H1> <DIV class=section id=docsection2 header="Defining the User Interface"> <P>Go back to Winform1. We&#8217;ll start by setting up display for Projects and the ability to create or delete them. Drop a DataGrid and a couple of buttons on the form. For the grid, I found a size of roughly 225 wide by 275 high, and placed at the upper left to be about right. Set the CaptionText of the DataGrid to Projects. Position the two buttons under it. Name the DataGrid to ProjectList and the buttons to AddProject and DeleteProject respectively.</P> <P>Here we&#8217;ll add our first ECO ExpressionHandle component. Name it to ProjectsHandle and set its RootHandle to rhRoot in order to connect it to the EcoSpace. The Expression property needs to be set to Project.allInstances, this can just be typed in or you can click on the ellipsis and use the expert to help you &#8211; selecting Project from the class node then allInstances from the Ocl Operations node.</P> <P>Now we can connect the DataGrid and buttons. For the DataGrid, set its DataSource property to our new ProjectsHandle. For the buttons you will see a property called &#8220;RootHandle on EcoListActions&#8221;, set this also to the ProjectsHandle. You can now set the actions for these buttons. Since they will be operating on the list of Projects, find the &#8220;EcoListAction on EcoListActions&#8221; property and set this to Add and Delete respectively. Eco even changes the button captions for you. Finally, for the Delete button, we want to disable it if there are no projects to delete. This can be done using the property &#8220;EnabledOcl on EcoListActions&#8221;, set it to self-&gt;notEmpty. Because this button is tied to the ProjectsHandle expression handle, that&#8217;s what &#8220;self&#8221; refers to in this OCL statement.</P> <P>Drop one more button for now and place it somewhere below the Add and Delete buttons, name this SaveButton. Find the &#8220;EcoAction on EcoGlobalActions&#8221; property and set it to UpdateDatabase.</P> <P>While not very pretty yet, you can now compile and run this project and you will be able to add and delete projects and edit them in the grid. You can click the Update DB button to save the project to file, and as you can see, the Delete button will correctly be disabled whenever there are no projects in the list.</P> <P>It&#8217;s never too early to start making the UI nicer. I prefer to edit records in individual controls instead of grids. Since we will also need to manage other classes as well, we&#8217;ll use a Tab Control to divide up the functionality. Drop one down to the right and set its Dock property to Right, then size it to take up all the space right of the datagrid. Add three tabs and label them &#8220;Project&#8221;, &#8220;Folders&#8221;, and &#8220;File Masks&#8221;, respectively.</P> <P>On the Projects tab, we need an edit control for each of the six current Project attributes and corresponding labels. Now we could hook these edit controls directly to the ProjectsHandle handle and everything would work, however we will soon need to access the <I>current</I> project in code anyway and to do that we will need a CurrencyManager, therefore we&#8217;ll do that now. </P> <P>.Net binding is designed to allow any list type component (DataGrid, Listbox) link to many kinds of data containers whether a DataSet, DataView, ArrayList, or an ECO ExpressionHandle. However, the interface that allows this has no concept of a current position in the list. This is done separately via a BindingContext. The ECO CurrencyManager wraps this functionality.</P> <P>Drop a CurrencyManager on the form and name it to &#8220;ProjectsCM&#8221;. Set its RootHandle to ProjectsHandle and its BindingContext to the ProjectList datagrid. We&#8217;ll now be able to use this CurrencyManager to hook up to the currently selected Project.</P> <P>For each of the edit controls, in the DataBindings.Text property, you&#8217;ll now be able to select the desired attribute from the ProjectsCM handle. Don&#8217;t forget to set the PasswordChar property of the edit control hooked to the UserPassword attribute!</P> <P>Now that we have separate edit controls hooked up, we don&#8217;t need the datagrid to be editable, or to show all attributes. The first thing to do is set its ReadOnly flag to True. Now click the ellipsis of the TableStyles property and in the dialog add a TableStyle. Click its GridColumStyles ellipsis. Here you can add a Member, set its HeaderText to Name and select the Name attribute in the MappingName property. You can also set the column Width here. </P> <P>Here&#8217;s our form so far:</P> <P><IMG id=docimage3 src="http://bdn.borland.com/article/33669/15338/images/33669/3.png" border=0></P> <P>Now let&#8217;s add functionality for Folders and File Masks.</P> <P>On the Folders tab we need another datagrid and a couple of buttons to add and delete folders. These can be laid out any way desired, each line of the grid will display a folder path. For the DataGrid, you can turn off the CaptionVisible property since we already know we&#8217;re on the Folders tab.</P> <P>To power these controls, we need a new ExpressionHandle. Drop one down and name it &#8220;FoldersHandle&#8221;. Since we want this handle to present the set of folders linked to the currently selected Project, we need to set this expression&#8217;s RootHandle to the CurrencyManager, ProjectsCM. This is very much like creating a master/detail link between two datasets in Win32 Delphi applications. The expression property needs to access the list of folders. Since the FoldersHandle is tied to the current Project, this means that &#8220;self&#8221;, specified in the Expression property, will refer to that Project object. Because of the association between Project and Folder in the model, there will be a Role available in the Project object called Folders. Thus the Expression property needs to be set to self.Folders.</P> <P>Now the grid can be hooked to the FoldersHandle via its DataSource property. On doing this, the grid will display all three members &#8211; the 3rd being the Project this object belongs to. We don&#8217;t need the Project to display here. This is solved simply by adding a TableStyle to the grid, adding a GridColumnStyle to that and adding two columns, setting these to the Path and Recurse fields respectively. Set the column widths while there.</P> <P>For the Delete button, set the RootHandle on EcoListActions to the FolderHandle, the EcoListAction on EcoListActions to Delete, and the Enabled on EcoListActions to self-&gt;not Empty.</P> <P>For the Add button, we can&#8217;t use the EcoAction because we need to prompt the user to enter a folder path, so we need to write our first code here!</P> <P>Label the Add button appropriately, and then double click the Add button&#8217;s Click event. The following code will do what we need. After typing in this code, you will notice that LastBrowsePath has not been declared. We can use the Declare Field refactoring feature to do this (from the menu or Shift-Ctrl-D), set the type to string. This variable is not necessary, but simply adds a nice touch &#8211; each time the user selects a folder, it will start the browse dialog in the location that was last selected from.</P><PRE><FONT color=#000080>var f: Folder;<br />
    p: Project;<br />
    dlg: FolderBrowserDialog;<br />
begin<br />
  dlg := FolderBrowserDialog.Create;<br />
  try<br />
    dlg.ShowNewFolderButton := False;<br />
    dlg.SelectedPath := LastBrowsePath;<br />
    if dlg.ShowDialog(Self) = System.Windows.Forms.DialogResult.OK  then<br />
    begin<br />
      f := Folder.Create(EcoSpace);<br />
      f.Path := dlg.SelectedPath;<br />
      p := ProjectsCM.CurrentElement(ProjectList).AsObject as Project;<br />
      p.Folders.Add(f);<br />
      LastBrowsePath := dlg.SelectedPath;<br />
    end;<br />
  finally<br />
    dlg.Free;<br />
  end;</FONT></PRE><br />
<P>The above code presents a folder browser to the user to allow a selection and, if selected, adds a new Folder object to the Project. The try/finally block isn&#8217;t really needed here since it will be garbage-collected anyway, but good habits die hard. </P><br />
<P>As you can see, creating a new object in code and adding it as a member of another object&#8217;s list is very simple in ECO. The only line that looks weird is the one getting access to the <I>current</I> Project object. We use the CurrencyManager object to get the current element from the ProjectList datagrid. We need to use the AsObject property of that element to get an actual Project object, and finally it needs to be cast as a Project object in order to assign it to our variable.</P><br />
<P>The Folders tab is now functional, the project can be run and tested. The grid is left editable so the Recurse field can be edited to True or False (this could be enhanced to be done with a button or checkbox, to do so would also require another CurrencyManager for the Folders list). Different folders can be assigned to different projects and all is saved properly.</P><br />
<P><A href="http://bdn.borland.com/article/33669/15338/images/33669/4.png"><IMG id=docimage4 title="Click to see full-sized image" alt="Click to see full-sized image" src="http://bdn.borland.com/article/33669/15338/images/33669/thumb4.png" border=0></A></P><br />
<P>The File Masks tab will be similar, but here we&#8217;ll just use a Listbox instead of a DataGrid. Again we need two buttons to add and delete File Masks, and we need a TextBox to allow entry of masks. Lay these out as desired, the ListBox does not need to be too wide.</P><br />
<P>Once again we need an ExpressionHandle, drop one down and name this one &#8220;MasksHandle&#8221;. Again its RootHandle will be ProjectsCM and the expression will be self.FileMasks.</P><br />
<P>For the Listbox, name it &#8220;MaskList&#8221; and set its Datasource to the MasksHandle. In addition, set its DisplayMember property to the Mask attribute. You can optionally set Sorted to True. Name the textbox to &#8220;MaskText&#8221;.</P><br />
<P>Using the same pattern as described for the Delete button on the Folder tab, the delete button for File Masks can be hooked up also using the MasksHandle. </P><br />
<P>Again like the Add button on the folders tab, set its caption and then create a Click event for it. The code needed for adding a File Mask is as follows:</P><PRE><FONT color=#000080>var fm: FileMask;<br />
    p: Project;<br />
begin<br />
  if MaskText.Text.Trim = &#8221; then<br />
  begin<br />
    MessageBox.Show(&#8217;Please enter file mask specification&#8217;);<br />
    Exit;<br />
  end;<br />
  if MaskList.FindStringExact(MaskText.Text) &gt;= 0 then<br />
  begin<br />
    MessageBox.Show(&#8217;File mask already added&#8217;);<br />
    Exit;<br />
  end;<br />
  fm := FileMask.Create(EcoSpace);<br />
  fm.Mask := MaskText.Text;<br />
  p := ProjectsCM.CurrentElement(ProjectList).AsObject as Project;<br />
  p.FileMasks.Add(fm);<br />
end;</FONT></PRE><br />
<P>Most of the above code is merely checking that there is something entered and that it isn&#8217;t duplicating an already added mask. Then it simply creates a new FileMask object, sets its Mask attribute, and adds it to the current Project object.</P><br />
<P><A href="http://bdn.borland.com/article/33669/15338/images/33669/5.png"><IMG id=docimage5 title="Click to see full-sized image" alt="Click to see full-sized image" src="http://bdn.borland.com/article/33669/15338/images/33669/thumb5.png" border=0></A></P><br />
<P>At this point we can completely create and configure backup projects.</P><A name=3AddingthePreviewFeature></A><br />
<H2>Adding the Preview Feature</H2><br />
<DIV class=section id=docsection5 header="Adding the Preview Feature"><br />
<P>One of our main requirements was to be able to preview a backup as well as execute it and we have endowed our Project class with a Preview method. Now it&#8217;s time to implement it.</P><br />
<P>We need a place to display the preview &#8211; the list of actual files that would be backed up by the Backup method. We can add another tab to the Tab control and label it Preview. To this, add a DataGrid named PreviewGrid, and a button labeled Preview.</P><br />
<P>Again we need an ExpressionHandle to power this feature, add one and name it FileEntriesHandle. The RootHandle will once again be ProjectsCM, and the Expression will be self.FileEntries. Link the datagrid up by setting the DataSource to FileEntriesHandle. Once again it will show the unneeded Project column in the grid. Get rid of this as before by adding a TableStyle and defining just two columns for the FilePath and FileSize attributes.</P><br />
<P>Create a Click event for the Preview button. The code needed here is:</P><PRE><FONT color=#000080>var p: Project;<br />
begin<br />
  p := ProjectsCM.CurrentElement(ProjectList).AsObject as Project;<br />
  p.Preview;<br />
end;</FONT></PRE><br />
<P>As elsewhere, we need to get access to the current Project. Then we call the Preview method of the Project.</P><br />
<P>Of course we don&#8217;t yet have any functionality in the Preview method. We&#8217;ll fix this now. Go to the model and right click on the Preview method in the Project class. Here you can click on &#8220;Go to Definition&#8221;. This takes you to the interface declaration of Preview (actually the attribute line above it). Move down to the Preview method and you can use Ctrl-Shift-DownArrow to move to the implementation section. In this method, you only need type one line:</P><PRE><FONT color=#000080>  BuildEntryList;</FONT></PRE><br />
<P>Now of course we need to define the BuildEntryList method. We&#8217;re placing this in a separate method because our Backup method is also going to need to call this. Following is the BuildEntryList method. Note that you need to add the System.IO namespace to the unit&#8217;s Uses clause. </P><PRE><FONT color=#000080>procedure Project.BuildEntryList;<br />
var fld: Folder;<br />
    di: DirectoryInfo;</p>
<p>  // find requested files for passed directory<br />
  procedure ProcessMask(d: DirectoryInfo);<br />
  var mask: FileMask;<br />
      fi: FileInfo;<br />
      files: array of FileInfo;<br />
      fe: FileEntry;<br />
  begin<br />
    // apply each mask in turn<br />
    for mask in FileMasks do<br />
    begin<br />
      files := d.GetFiles(mask.Mask);<br />
      for fi in files do<br />
      begin<br />
        fe := FileEntry.Create(self.AsIObject.ServiceProvider);<br />
        fi.Refresh;<br />
        fe.FileSize := fi.Length;<br />
        fe.FilePath := fi.FullName;<br />
        self.FileEntrys.Add(fe);<br />
      end;<br />
    end;<br />
  end;</p>
<p>  // recurse folders from passed starting point<br />
  procedure RecurseFolders(di: DirectoryInfo);<br />
  var dirs: array of DirectoryInfo;<br />
      di2: DirectoryInfo;<br />
  begin<br />
    dirs := di.GetDirectories;<br />
    for di2 in dirs do<br />
    begin<br />
      ProcessMask(di2);<br />
      RecurseFolders(di2);<br />
    end;<br />
  end;</p>
<p>begin<br />
  FileEntrys.Clear;<br />
  for fld in Folders do<br />
  begin<br />
    di := DirectoryInfo.Create(fld.Path);<br />
    ProcessMask(di);<br />
    if fld.Recurse then<br />
    begin<br />
      RecurseFolders(di);<br />
    end;<br />
  end;<br />
end;</FONT></PRE><br />
<P>This method makes use of Delphi nested procedures and should make the code easier to understand. The mainline of the method starts by clearing any existing FileEntry objects from the Project. It then loops through the list of Folder classes that have been added by the user for this Project. For each, it calls ProcessMask to find and add any matching files in that folder. It then checks to see if it should recurse sub-folders, and if so, calls RecurseFolders.</P><br />
<P>RecurseFolders is a recursive procedure. It asks the passed DirectoryInfo object for a list of sub-folders. If any are returned, it loops through them, calling ProcessMask and then calling itself in order to continue the recursion. This will continue for any depth of sub-folders.</P><br />
<P>ProcessMask loops through each of the FileMask objects the user has added and, for each, asks the passed DirectoryInfo object for a list of matching files (much easier then the equivalent FindFirst/FindNext/FindClose technique needed in Win32!). For each file found, a FileEntry object is created and added to the Project&#8217;s FileEntrys list. Note the FileInfo object returned by the system must be refreshed in order for it to correctly report the size of the file.</P><br />
<P>With this complete, we have some actual functionality, run the application and, with folders and masks defined, go to the Preview tab and click the button!</P><br />
<H5><B><I><A href="http://bdn.borland.com/article/33669/15338/images/33669/6.png"><IMG id=docimage6 title="Click to see full-sized image" alt="Click to see full-sized image" src="http://bdn.borland.com/article/33669/15338/images/33669/thumb6.png" border=0></A></I></B></H5></DIV><A name=4AddingtheBackupFeature></A><br />
<H2>Adding the Backup Feature</H2><br />
<DIV class=section id=docsection6 header="Adding the Backup Feature"><br />
<P>Before we can add the functionality, first we need to make a reference to the needed zip dll. As noted at the beginning of this article, you need to download and install SharpZipLib. We also need to reference the Indy components in order to use its FTP component. Right click on the References node in the Project Manager and click Add Reference.</P><br />
<P>Here in the list you should be able to find the needed Indy assemblies, these are IndyCore, IndyProtocols, and IndySystem. Highlight these 3 and click Add Reference.</P><br />
<P>You can then click the browse button to find the ICSharpCode.SharpZipLib.dll assembly, wherever you installed it.</P><br />
<P>Clicking OK on this dialog will add the four selected references to the project. You will then also be able to add these to the Uses clause.</P><br />
<P>Add a new button on the main form under the ProjectList datagrid and label it Backup. Name it as BackupButton and create a Click event for it. The Click event will contain the following code:</P><PRE><FONT color=#000080>var p: Project;<br />
begin<br />
  BackupButton.Enabled := False;<br />
  try<br />
    p := ProjectsCM.CurrentElement(ProjectList).AsObject as Project;<br />
    // clear the file list display<br />
    p.FileEntrys.Clear;<br />
    if PreviewGrid.Visible then<br />
      PreviewGrid.Update;</p>
<p>    p.Backup;</p>
<p>  finally<br />
    BackupButton.Enabled := True;<br />
  end;</FONT></PRE><br />
<P>Here we use a try/finally block to disable the Backup button and re-enable it at completion, just to ensure it cannot be clicked again while a backup is in progress. As usual, we get the current Project object and clear out any FileEntry objects it may be holding. To make the display a little cleaner, we also update the PreviewGrid to show it empty prior to starting the backup. It will be regenerated by the backup process. Finally, we call the Backup method.</P><br />
<P>Find your way to the Project class&#8217; Backup method. In that package unit, add the following references to the Uses clause: ICSharpCode.SharpZipLib.Zip, ICSharpCode.SharpZipLib.Core, IDFTP, and IDComponent.</P><br />
<P>Now we can add the code for the Backup method.</P><PRE><FONT color=#000080>var fe: FileEntry;<br />
    zip: ZipOutputStream;<br />
    zentry: ZipEntry;<br />
    infile: FileStream;<br />
    memfile: MemoryStream;<br />
    buffer: array of byte;<br />
    ftp: TIdFtp;<br />
begin<br />
  BuildEntryList;</p>
<p>  if BackupName = &#8221; then<br />
    BackupName := BackupName.Format(&#8217;{0}{1}.zip&#8217;, self.Name,<br />
      DateTime.Now.ToString(&#8217;yyyyMMddhhmmss&#8217;));</p>
<p>  memfile := MemoryStream.Create;<br />
  zip := ZipOutputStream.Create(memfile);</p>
<p>  // now process the entries<br />
  for fe in FileEntrys do<br />
  begin<br />
    zentry := ZipEntry.Create(fe.FilePath);<br />
    infile := &amp;File.OpenRead(fe.FilePath);<br />
    SetLength(buffer, infile.Length);<br />
    infile.Read(buffer, 0, infile.Length);<br />
    zip.PutNextEntry(zentry);<br />
    zip.Write(buffer, 0, infile.Length);<br />
  end;<br />
  zip.Finish;<br />
  zip.Flush;<br />
  memfile.Position := 0;</p>
<p>  // upload to a server?<br />
  if ServerName &lt;&gt; &#8221; then<br />
  begin // yes<br />
    ftp := TIdFtp.Create;<br />
    try<br />
      ftp.Host := ServerName;<br />
      ftp.Username := ServerUserID;<br />
      ftp.Password := ServerPassword;<br />
      ftp.Connect();<br />
      ftp.ChangeDir(ServerPath);<br />
      ftp.Put(memfile, BackupName);<br />
    finally<br />
      ftp.Free;<br />
    end;<br />
  end else<br />
  begin // save locally<br />
    infile := &amp;File.OpenWrite(ServerPath + &#8216;\&#8217; + BackupName);<br />
    memfile.WriteTo(infile);<br />
    infile.Close;<br />
  end;</p>
<p>  memfile.Free;<br />
  zip.Close;<br />
end;  </FONT></PRE><br />
<P>The first thing Backup does is call the BuildEntryList method we created earlier. This gives it an up-to-date list of files to be backed up. Then we make sure there&#8217;s a name for the backup file, if one was not specified then we generate one combining the Project name with the current timestamp. We then create a zip file using a memory stream and add each of the target files to it. Finally, if server information has been specified, we attempt to FTP the file to the specified location on the server, otherwise we assume the ServerPath attribute is a local file path and attempt to save the zip file to there.</P><br />
<P>We now have a fully functional backup utility. However, it has one major flaw: there is no feedback of any kind while it&#8217;s performing a backup, no way to see its progress. Typically, a backup utility is going to have a progress meter, and usually displays the file names as they are processed, so we&#8217;ll do that.</P></DIV><A name=5Feedback></A><br />
<H2>Feedback</H2><br />
<DIV class=section id=docsection7 header="Feedback"><br />
<P>We&#8217;d like to get feedback from our Project object as it&#8217;s performing its work. However, under no circumstances do we want the model to know anything about our user interface, thus it cannot directly access our form. In order to get feedback, we need to <I>observe</I> our Project object. ECO implements the Observer (also know as Publish and Subscribe) pattern in all ECO classes you define and provides the necessary methods to let you set up such subscriptions. You can observe an object or a specific attribute (property) of an object.</P><br />
<P>We will add some new attributes to our Project class specifically to provide information about its progress. The attributes we will add are as follows:</P><br />
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"><br />
<P>BackupSize: Int64</P><br />
<P>ProgressPoint: Integer</P><br />
<P>ProgressState: string</P></BLOCKQUOTE><br />
<P>Because these are only intended for use while the Project object is performing a backup, we do not want these attributes to be stored when saved to file. Therefore be sure to set the Persistence property of all three of these to Transient.</P><br />
<P>Now we need to add some code to make use of these attributes. Navigate to the BuildEntryList method and as the first line of its (main) code, add:</P><PRE><FONT color=#000080>  ProgressState := &#8216;Preparing File List&#8217;;</FONT></PRE><br />
<P>We will want to report the status of the FTP process, so we need to provide a couple of event handlers to hook to. These will look as follows, use code-completion after entering the code in order to declare them in the class:</P><PRE><FONT color=#000080>procedure Project.FTPStatus(ASender: TObject; const AStatus: TIDStatus;<br />
const AStatusText: string);<br />
begin<br />
  ProgressState := AStatusText;<br />
end;</p>
<p>procedure Project.FTPWork(ASender: TObject; AWorkMode: TWorkMode;<br />
AWorkCount: integer);<br />
begin<br />
  ProgressState := &#8216;Writing &#8216; + AWorkCount.ToString(&#8217;N0&#8242;) + &#8216; of &#8216;<br />
    + BackupSize.ToString(&#8217;N0&#8242;);<br />
end;</FONT></PRE><br />
<P>In the Backup method, there are a number of additional lines to be added, rather than listing each one, here is the complete backup method again with the new lines in bold.</P><PRE><FONT color=#000080>procedure Project.Backup;<br />
var fe: FileEntry;<br />
    zip: ZipOutputStream;<br />
    zentry: ZipEntry;<br />
    infile: FileStream;<br />
    memfile: MemoryStream;<br />
    buffer: array of byte;<br />
    ftp: TIdFtp;<br />
begin<br />
  BuildEntryList;</p>
<p>  if BackupName = &#8221; then<br />
    BackupName := BackupName.Format(&#8217;{0}{1}.zip&#8217;, self.Name,<br />
      DateTime.Now.ToString(&#8217;yyyyMMddhhmmss&#8217;));<br />
  memfile := MemoryStream.Create;<br />
  zip := ZipOutputStream.Create(memfile);</p>
<p><B>  ProgressState := &#8216;Compressing Files&#8217;;</B><br />
<B>  ProgressPoint := -1; // tell subscribers to initialize</B><br />
<B>  ProgressPoint := 0; // start</B></p>
<p>  // now process the entries<br />
  for fe in FileEntrys do<br />
  begin<br />
<B>    ProgressState := fe.FilePath;</B><br />
    zentry := ZipEntry.Create(fe.FilePath);<br />
    infile := &amp;File.OpenRead(fe.FilePath);<br />
    SetLength(buffer, infile.Length);<br />
    infile.Read(buffer, 0, infile.Length);<br />
    zip.PutNextEntry(zentry);<br />
    zip.Write(buffer, 0, infile.Length);<br />
<B>    ProgressPoint := ProgressPoint + 1;</B><br />
  end;<br />
  zip.Finish;<br />
  zip.Flush;</p>
<p><B>  BackupSize := zip.Length;</B><br />
<B>  ProgressState := &#8216;Writing Backup File&#8217;;</B></p>
<p>  memfile.Position := 0;<br />
  // upload to a server?<br />
  if ServerName &lt;&gt; &#8221; then<br />
  begin // yes<br />
    ftp := TIdFtp.Create;<br />
    try<br />
      ftp.Host := ServerName;<br />
      ftp.Username := ServerUserID;<br />
      ftp.Password := ServerPassword;<br />
<B>      ftp.OnStatus := FTPStatus;</B><br />
<B>      ftp.OnWork := FTPWork;</B><br />
      ftp.Connect();<br />
      ftp.ChangeDir(ServerPath);<br />
      ftp.Put(memfile, BackupName);<br />
    finally<br />
      ftp.Free;<br />
    end;<br />
  end else<br />
  begin // save locally<br />
    infile := &amp;File.OpenWrite(ServerPath + &#8216;\&#8217; + BackupName);<br />
    memfile.WriteTo(infile);<br />
    infile.Close;<br />
  end;</p>
<p>  memfile.Free;<br />
  zip.Close;<br />
<B>  ProgressState := &#8216;Backup complete&#8217;;</B><br />
end;</FONT></PRE><br />
<P>To take advantage of subscriptions, the observer must implement the ISubscriber interface. ISubscriber is defined in the Borland.Eco.Subscription namespace, so add this to the Uses clause of the form. We can then alter our form to implement the interface by changing the first line of the form definition to be:</P><PRE><FONT color=#000080>TWinForm1 = class(System.Windows.Forms.Form, ISubscriber)</FONT></PRE><br />
<P>The ISubscriber interface requires two methods to be implemented, these are IsAlive and Receive. The IDE can help here, just scroll down to the bottom of the TWinform1 class and, in the public section, hit Ctrl-Space, Delphi will show you a list of overrideable methods with IsAlive and Receive at the top and in red. Highlight both of these (the list allows multi-select) and hit return, then Shift-Ctrl-C to create the implementation stubs.</P><br />
<P>The IsAlive method needs only one line &#8211; set the Result to True to indicate you want the subscription to continue. </P><br />
<P>Before implementing the Receive method, let&#8217;s set up the subscriptions. These will go in the click event of the Backup button, just before the call to Backup:</P><PRE><FONT color=#000080>    p.AsIObject.Properties[&#8217;ProgressState&#8217;].SubscribeToValue(self);<br />
    p.AsIObject.Properties[&#8217;ProgressPoint&#8217;].SubscribeToValue(self);</FONT></PRE><br />
<P>Underlying our Project (and other) objects, is an ECO object that implements the plumbing ECO needs to do its work for us. The AsIObject property gives us access to that underlying object and the Properties property gives access to the plumbing for our individual properties. The ability to subscribe exists at that level. The above lines set up a subscription for our form (self) on two of the attributes we added. When passing self, we are actually passing the ISubscriber interface we&#8217;ve implemented, and so ECO will now be able to call our Receive method whenever either of these Project attributes changes.</P><br />
<P>We now need to add a couple of items to our user interface to display progress, a label and a progress gauge. Name them ProgressLabel and ProgressBar respectively.</P><br />
<P>Below is the code for the Receive method. The first line gets the actual attribute that has changed. When the type of the object is a string, it must be ProgressState that has changed, if an integer, then it must be ProgressPoint. If an integer, we check if it is -1, the signal from Project that it is about to start the counting, this gives us an opportunity to initialize our progress gauge.</P><PRE><FONT color=#000080>var o: TObject;<br />
    p: Project;<br />
begin<br />
  o := (ElementChangedEventArgs(e).Element.AsObject);<br />
  if o is string then<br />
  begin<br />
    ProgressLabel.Text := o.ToString;<br />
    ProgressLabel.Refresh;<br />
  end<br />
  else if o is Integer then<br />
  begin<br />
    if Integer(o) = -1 then<br />
    begin<br />
      p := ProjectsCM.CurrentElement(ProjectList).AsObject as Project;<br />
      ProgressBar.Value := 0;<br />
      ProgressBar.Maximum := p.FileEntrys.Count;<br />
    end else<br />
    if Integer(o) &lt;= ProgressBar.Maximum then<br />
    begin<br />
      ProgressBar.Value := Integer(o);<br />
    end;<br />
    ProgressBar.Refresh;<br />
  end;</p>
<p>  Result := True;<br />
end;</FONT></PRE><br />
<P>You should now be able to compile and run the application and, pressing the backup button on a project, you should see visible feedback.</P><A name=6Doingitbetter></A><br />
<H3>Doing it better&#8230;</H3><br />
<DIV class=section id=docsection12 header="Doing it better&#8230;"><br />
<P>This way of doing things is acceptable for our purposes; however, if your application needs to set up subscriptions to many different classes or attributes, then trying to sort it all out in a single Receive method is going to get ugly very quickly and becomes almost impossible as soon as you need to receive two attributes of the same type.</P><br />
<P>Fortunately, because subscriptions are done using interfaces, we can easily create a separate class to handle individual subscriptions, create as many of these as we need, and use event handlers to get the subscription events into our main form for processing. Because this class will be handy for any project needing such subscriptions, we&#8217;ll put it in its own unit. Create a new code-only unit (File | New | Other | Delphi for .Net Projects | New Files | Unit). Save this file as ECOSubscribe.pas. The unit should look like this:</P><PRE><FONT color=#000080>unit ECOSubscribe;</p>
<p>interface</p>
<p>uses<br />
  Borland.Eco.Subscription;</p>
<p>type<br />
  TECOSubscriber = class(TObject, ISubscriber)<br />
  private<br />
    FOnReceive: EventHandler;<br />
  published<br />
  public<br />
    function IsAlive: Boolean;<br />
    function Receive(sender: TObject; e: EventArgs): Boolean;<br />
    procedure set_OnReceive(const Value: EventHandler);<br />
    property OnReceive: EventHandler add FOnReceive remove set_OnReceive;<br />
  end;</p>
<p>implementation</p>
<p>{ TECOSubscriber }</p>
<p>function TECOSubscriber.IsAlive: Boolean;<br />
begin<br />
  Result := True;<br />
end;</p>
<p>function TECOSubscriber.Receive(sender: TObject; e: EventArgs): Boolean;<br />
begin<br />
  if Assigned(FOnReceive) then<br />
    FOnReceive(Self, e);<br />
end;</p>
<p>procedure TECOSubscriber.set_OnReceive(const Value: EventHandler);<br />
begin<br />
  FOnReceive := Value;<br />
end;</p>
<p>end.</FONT></PRE><br />
<P>This class acts as a relay, it is able to receive calls from a subscribed object and pass that call onto whoever attaches to its OnReceive event. We can create an instance of this class for each subscription we wish to make, and attach a different event handler to each one.</P><br />
<P>We can now improve our implementation in the main form. Add ECOSubscribe to the main form&#8217;s Uses clause. In the main form&#8217;s private section, add the following declarations:</P><PRE><FONT color=#000080>    FProgressState: TECOSubscriber;<br />
    FProgressPoint: TECOSubscriber;<br />
    procedure ProgressStateChanged(sender: TObject; e: EventArgs);<br />
    procedure ProgressPointChanged(sender: TObject; e: EventArgs);</FONT></PRE><br />
<P>Complete the procedures with Shift-Ctrl-C and add code to the implementations so they look as follows:</P><PRE><FONT color=#000080>procedure TWinForm1.ProgressStateChanged(sender: TObject; e: EventArgs);<br />
begin<br />
  ProgressLabel.Text :=<br />
    (ElementChangedEventArgs(e).Element.AsObject).ToString;<br />
end;</p>
<p>procedure TWinForm1.ProgressPointChanged(sender: TObject; e: EventArgs);<br />
var p: Project;<br />
    point: integer;<br />
begin<br />
  point := Integer(ElementChangedEventArgs(e).Element.AsObject);<br />
  if point = -1 then<br />
  begin<br />
    p := ProjectsCM.CurrentElement(ProjectList).AsObject as Project;<br />
    ProgressBar.Value := 0;<br />
    ProgressBar.Maximum := p.FileEntrys.C</FONT></PRE></DIV></DIV></DIV></SPAN></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=27447&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_27447" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=ECO%20Example%3A%20Backup%20Utility&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2006%2F09%2F10%2F27447" id="akst_email_27447" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2006/09/10/27447/feed</wfw:commentRss>
		</item>
		<item>
		<title>Eco Sample</title>
		<link>http://blogs.teamb.com/wayneniddery/2006/09/07/27408</link>
		<comments>http://blogs.teamb.com/wayneniddery/2006/09/07/27408#comments</comments>
		<pubDate>Thu, 07 Sep 2006 21:57:25 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2006/09/07/23307/</guid>
		<description><![CDATA[My&#160;ECO example article is now posted - click on Articles in the column at left or (better) see it on BDN here: http://bdn.borland.com/article/33669
]]></description>
			<content:encoded><![CDATA[<p><P>My&nbsp;ECO example article is now posted - click on Articles in the column at left or (better) see it on BDN here: <A href="http://bdn.borland.com/article/33669">http://bdn.borland.com/article/33669</A></P></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=27408&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_27408" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Eco%20Sample&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2006%2F09%2F07%2F27408" id="akst_email_27408" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2006/09/07/27408/feed</wfw:commentRss>
		</item>
		<item>
		<title>Borland Selling IDE Products</title>
		<link>http://blogs.teamb.com/wayneniddery/2006/02/13/23306</link>
		<comments>http://blogs.teamb.com/wayneniddery/2006/02/13/23306#comments</comments>
		<pubDate>Mon, 13 Feb 2006 21:13:30 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2006/02/13/4146/</guid>
		<description><![CDATA[Obviously this is big news and a big concern to customers of Delphi, JBuilder and Interbase. Many have posted their views in the newsgroups (borland.public.delphi.non-technical) and as one would be expect, there are many ranging from complete doom to high optimism.

Since I have the privilege of having a soap box here, I will offer my opinions. I've posted bits of this in various newsgroup posts, but I gather them together along with additional thoughts.
]]></description>
			<content:encoded><![CDATA[<p><P>Obviously this is big news and a big concern to customers of Delphi, JBuilder and Interbase. Many have posted their views in the newsgroups (<A href="news://newsgroups.borland.com/borland.public.delphi.non-technical">borland.public.delphi.non-technical</A>) and as one would be expect, there are many ranging from complete doom to high optimism.</P> <P>Until an actual buyer is confirmed and the details of the transaction are known, not much can be guaranteed, but Borland has made it clear that their intention is to find a good home for these products, not solely to get the highest possible bid. Obviously they need to get some reasonable price, but they are putting other critieria as high or higher. There is already some action according to <A href="http://www.computing.co.uk/itweek/news/2150184/borland-plots-ide-sale">one source</A>, but only details of the successful suitor will likely become public knowledge.</P> <P>Since I have the privilege of having a soap box here, I will offer my opinions. I&#8217;ve posted bits of this in various newsgroup posts, but I gather them together along with additional thoughts.</P> <P><FONT size="4">Why sell these products?</FONT></P> <P>It has become more and more evident over the last few years that Borland&#8217;s focus, and where they see themselves being most successful, has changed. Clearly resources have not been sufficiently allocated to these products with the result of little marketing, no commission incentives for sales personnel, and worst of all, less than stellar quality releases with C#Builder, Delphi 8, and Delphi 2005. I won&#8217;t even attempt to describe the torment Borland&#8217;s C++ customers have endured for several years. It isn&#8217;t that Borland didn&#8217;t hear all our cries during these years, it is that they wanted to put resources where they believed their future lay, and that left little for these products.  As <A href="http://blogs.codegear.com/abauer/">Allen Bauer</A> has said, Borland is too big to act small and too small to act big - and there are too little resources to effectively pursue two different divisions that require completely different infrastructure - different kinds of sales and marketing efforts, different kinds of support. As a result the bulk of the profits have been invested in furthering their other goals.</P> <P>To their credit, they did not want these products to <EM>continue </EM>dying due to lack of investment and due to the resulting loss of confidence over the quality issues with the last few versions. So for BDS2006, sufficient investment was made to allow the development team to release a quality product. That, in turn, has made a successful divestment much more likely, since it has clearly helped keep, and even get back, many customers and that helps the bottom line. That reinvestment however, from Borland&#8217;s POV, is money they would&#8217;ve preferred to spend on their other products, and thus it cannot be expected to continue indefinitely under Borland&#8217;s care.</P> <P>I have also voiced my opinion previously in newsgroup postings (in response to the attempt of a particular shareholder to force sale of Delphi and middleware last year) that I would <EM>prefer</EM> to see these tools stay with Borland because the continuity of the Borland name, and the fact of their larger size, was a factor in selling customers on allowing me to develop their projects in Delphi. My own fear is that, with a much smaller company, it will be a harder sell than it already is. I still have those concerns, however I have realized that if the current state of affairs were to continue (insufficient funding), it will do more to damage the perception of Delphi in the eyes of my customers then the size of the company behind it. </P> <P><FONT size="4"><STRONG>The Borland Name</STRONG></FONT></P> <P>Many have suggested that it&#8217;s wrong for &#8220;Borland&#8220; to go with the newer ALM tools instead of the developer&#8217;s tools that are Borland&#8217;s legacy. I fully understand their sentiment since I&#8217;ve been a Borland customer for about 18 years (starting with Turbo Pascal 5.5). It will certainly seem strange to see these products under a different name and will seem like something has been lost. </P> <P>It is a simple fact that Borland has established their name in the industry for their other products (ALM, SDO) to the tune of about 70% of their revenue. For them to unstablize this part of their business by adopting a new name would not make good business sense at all. They already learned that lesson several years ago when a certain CEO decided to rename the company to Inprise instead of captializing on the still good Borland name, fortunately that mistake was eventually rectified.</P> <P>I&#8217;m also now convinced that a new company name for the development tools might be exactly what the doctor ordered. &#8220;Borland&#8220; is of sentimental value to those of us that have been using these products for a long time, but is of proportionately lesser value to people that have come to these products more recently, and of no value to the new customers that hopefully will be attracted in future. &#8220;Borland&#8220; in fact has a lot of baggage, some of it negative - the poor quality releases of the last few years, their &#8220;dissapearance&#8220; during the Inprise years (&#8220;Borland? Are they still around?&#8220;). A new corporate name provides a fresh slate to build on. Combined with the market recognition that does exist for the products themselves, the new company stands a chance of establishing a new and good reputation going forward.</P> <P><FONT size="4"><STRONG>It&#8217;s all Microsoft&#8217;s fault</STRONG></FONT></P> <P>I would&#8217;ve been very surprised if this hadn&#8217;t come up, and I wasn&#8217;t dissappointed. The arguments with regard to developer&#8217;s tools is identical with the general public arguments about web browsers and office suites.</P> <P>The argument is that Microsoft makes and sells Windows and therefore should not be <EM>allowed</EM> to sell developer tools as this gives them an &#8220;unfair&#8221; advantage in two ways:</P> <UL> <LI>They can always release developer support for their new Windows platforms (e.g. most recently, .Net 2.0 and CF) before Borland and others are able. <LI>They don&#8217;t need to make a profit on Visual Tools and so can sell them cheaply or even give them away</LI></UL> <P>These advantages are certainly real, but are they unfair? These advantages are cited endlessly as reasons Delphi has never caught on big time against C++, and more recently, C#/VB.Net. </P> <P>It is true that Microsoft would not be seriously dented if it made no money whatever from direct sales of Visual Studio, and in fact they already give away &#8220;express&#8220; versions of it; while Borland on the other hand <EM>must</EM> make profit from these to stay in that business. It will be even more crucial for new owners as this will be their <EM>only </EM>source of revenue, at least initially. The bottom line is that competition cannot easily compete with Microsoft on price, it must compete on other criteria, mainly features, reliability, and support.</P> <P>But this is unfair only if one accepts the notion that each company has some inherent right to sufficient market share to stay in business (theory of &#8220;perfect competition&#8221;), and thus a company like Microsoft must be forced to either charge a price that allows others to compete on that criteria, or be broken up. </P> <P>What gets forgotten in this &#8220;fairness&#8221; argument is the customer - the one that must pay for the product. How is it &#8220;fair&#8221; to force a customer to pay a higher than necessary price for any product? The answer is: it isn&#8217;t. Cheap and free products are good for customers and good for the economy as it frees up money to be spent on other things; it increases the standard of living. If cheap or free products were truly harmful to either customers or the economy in general, then freeware and open-source products are far more deserving of criticism than is Microsoft. It is a fact that the free availability of Eclipse has damaged Borland&#8217;s revenues far more than Microsoft ever has with its pricing or other advantages, yet very few (any?) suggest that the efforts behind Eclipse are &#8220;unfair&#8220; and should be stopped or controlled. So there is the double standard - products like Linux, Open Office, or Eclipse are perfectly ok (apparently because they are not profit-oriented) but it is &#8220;anti-competitive&#8221; for Microsoft to offer cheap or free versions of such software and deserving of government force.</P> <P>It is almost a given that if Borland and Microsoft both sold identical quality lumps of coal at the same price, more people would buy from Microsoft <EM>simply because it is Microsoft</EM>. That may seem irrational, but it is not unfair, it is the rightful free choice of customers. If Microsoft decides to bundle a coal lump with every Christmas stocking, it does not become unfair just because Borland does not produce and sell Christmas stockings. If Borland finds it is no longer profitable to produce and sell lumps of coal because not enough customers are willing to buy it from Borland (even if Borland&#8217;s coal was demonstrably better), thus giving Microsoft a coal monopoly, it does not harm the customer; they have rightfully chosen what they want.</P> <P><FONT size="4">So what should we do?</FONT></P> <P>All that said, I very much hope the products find a good home with all the key personnel intact as a team, and I will be doing whatever I can to support the effort - i.e. when the time comes I will trade in my TeamB status for Team &#8220;DevCo&#8221; and continue helping in the newsgroups, I will continue to use Delphi/BDS and Interbase wherever I can, I will attend &#8220;DevCo&#8221; conferences, I will support my local User Group, and I ask all that want Delphi to continue to do similar. With our support, &#8220;DevCo&#8221; stands a good chance of not only surviving, but becoming a bigger commercial success than it has under Borland&#8217;s stewardship. </P> <P>Delphi does not need to match Visual Studio in profits to be successful, it simply needs to be profitable enough to make it worthwhile for a dedicated company to continue pursuing it. So far, despite the problems in the past <EM>and cheaper alternatives from Microsoft and others</EM>, Delphi has been able to do that. There is no reason to think it cannot do even better with the right people behind it.</P> <P> </P></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=23306&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_23306" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Borland%20Selling%20IDE%20Products&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2006%2F02%2F13%2F23306" id="akst_email_23306" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2006/02/13/23306/feed</wfw:commentRss>
		</item>
		<item>
		<title>Using popups to indicate input errors</title>
		<link>http://blogs.teamb.com/wayneniddery/2005/05/03/4145</link>
		<comments>http://blogs.teamb.com/wayneniddery/2005/05/03/4145#comments</comments>
		<pubDate>Tue, 03 May 2005 11:26:13 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2005/05/03/3642/</guid>
		<description><![CDATA[ There&#8217;s currently a discussion in the aspdotnet.general newsgroup regarding popups. Specifically some are requesting that popup message dialogs (i.e. VCL-like MessageDlg) be made easy to do in ASP.Net apps.   That got me thinking about the whole issue of using popups - even in traditional GUI applications. Below is a slightly edited version of my [...]]]></description>
			<content:encoded><![CDATA[<p><DIV><SPAN class="q1"><FONT color="#0000bb"> <DIV><SPAN class="q0"><SPAN class="q1"><FONT color="#000000">There&#8217;s currently a discussion in the aspdotnet.general newsgroup regarding popups. Specifically some are requesting that popup message dialogs (i.e. VCL-like MessageDlg) be made easy to do in ASP.Net apps.</FONT></SPAN></SPAN></DIV> <DIV><SPAN class="q0"><SPAN class="q1"><FONT color="#000000"></FONT></SPAN></SPAN> </DIV> <DIV><SPAN class="q0"><SPAN class="q1"><FONT color="#000000">That got me thinking about the whole issue of using popups - even in traditional GUI applications. Below is a slightly edited version of my newsgroup reply.</FONT></SPAN></SPAN></DIV> <DIV><SPAN class="q0"><SPAN class="q1"><FONT color="#000000"></FONT></SPAN></SPAN> </DIV></FONT></SPAN></DIV> <DIV><SPAN class="q1"><FONT color="#000000">On the one hand, popups are <EM>easy</EM> (in GUI apps at least) and so have become ubiquitous - there is likely not a single GUI app that <EM>doesn&#8217;t </EM>use them. But does that mean they are the best way to do things? I personally think that most programs use them way too much, they are often annoying and, I think, often unnecessary. I&#8217;m not saying they should be eliminated completely - I don&#8217;t know about that (maybe), but I do think they are way overused.</FONT></SPAN></DIV> <DIV><SPAN class="q1"><FONT color="#000000"></FONT></SPAN> </DIV> <DIV><SPAN class="q1"><FONT color="#000000">A simple example: I fill out several fields in a form and there are problems with 3 of them.</FONT></SPAN></DIV> <DIV><SPAN class="q1"><FONT color="#000000"></FONT></SPAN> </DIV> <DIV><SPAN class="q1"><FONT color="#000000">Popup method 1: I hit Submit and get informed that field X needs to be filled in. I fill it in and hit submit and another popup informs me that field Y is out of range. I fix and submit just to have yet another popup tell me field Z is invalid.</FONT></SPAN></DIV> <DIV><SPAN class="q1"></SPAN><SPAN class="q1"></SPAN> </DIV> <DIV><SPAN class="q0"><SPAN class="q1"><FONT color="#000000">Popup method 2: I hit Submit and get informed with a single popup that lists the three fields and whats wrong. Definitely an improvement over 3 separate popups (but also very rare to see). However, once I hit ok and the popup goes away, I have to <EM>remember</EM> which 3 fields and what was wrong with each. So I could end up getting that popup more than once anyway, but simply with different details.</FONT></SPAN></SPAN></DIV> <DIV><SPAN class="q0"><SPAN class="q1"><FONT color="#000000"></FONT></SPAN></SPAN> </DIV> <DIV><SPAN class="q0"><FONT color="#000000">Bottom line: I think using popups to indicate problems in input is a <EM>horrible</EM> way to "help" the user enter data! So where should a popup be used? In the case where all data has been entered <EM>correctly</EM> but then something happens on trying to submit it - database error, server down, etc - i.e. something completely unexpected and abnormal - a popup is perfectly reasonable - but then so is another web-page indicating such a condition, or a large in-line message on the current page.</FONT></SPAN></DIV> <DIV><FONT color="#000000"></FONT><SPAN class="q0"> </DIV> <DIV><FONT color="#000000">I much prefer seeing indications <EM>directly on the form or page</EM> telling me what&#8217;s wrong and where. Indications that interactively appear as I enter incorrect information and dissappear as I correct that information. Indications that provide a means of getting more detail <EM>if I request it</EM>.</FONT></DIV> <DIV><FONT color="#000000"></FONT> </DIV> <DIV>I think this is the direction things will go - and should go - in future. <FONT color="#000000">The only issue is how to make such indications clear enough to get the user&#8217;s attention. The complaint is that users apparently don&#8217;t seem to see &#8220;subtle hints&#8220; that they&#8217;ve made a mistake an so need to have an alarm that they cannot possibly miss.</FONT></DIV> <DIV><FONT color="#000000"></FONT> </DIV> <DIV><FONT color="#000000">There will always be the odd person that won&#8217;t get the message if you were to hit them over the head with it, but for most people, I honestly believe well-designed visual indicators right on the page are more than sufficient <EM>and more interactive and usable. </EM>I believe it is only a matter of presentation.</FONT></DIV> <DIV> </DIV> <DIV>Like so many others, I&#8217;ve liberally used message dialogs in my applications over the years. From now on though, I&#8217;m going to be experimenting with alternate ways to communicate with the user that does not require popup dialogs.</DIV> <DIV> </DIV></SPAN></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=4145&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_4145" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Using%20popups%20to%20indicate%20input%20errors&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2005%2F05%2F03%2F4145" id="akst_email_4145" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2005/05/03/4145/feed</wfw:commentRss>
		</item>
		<item>
		<title>Caching in ASP.Net</title>
		<link>http://blogs.teamb.com/wayneniddery/2005/04/01/3641</link>
		<comments>http://blogs.teamb.com/wayneniddery/2005/04/01/3641#comments</comments>
		<pubDate>Fri, 01 Apr 2005 13:31:17 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2005/04/01/3207/</guid>
		<description><![CDATA[Someone in the newsgroups recently asked:   If ASP.NET can and does cache data, why not cache the page as  an object with all it&#8217;s glory?  And when the page is refreshed,  refresh the native underlying HTTP object, blending it into the  cached instance. The answer (copied from my reply there):  Scalability. The whole point of being [...]]]></description>
			<content:encoded><![CDATA[<p><P>Someone in the newsgroups recently asked:</P> <P>  If ASP.NET can and does cache data, why not cache the page as<BR>  an object with all it&#8217;s glory?  And when the page is refreshed,<BR>  refresh the native underlying HTTP object, blending it into the<BR>  cached instance.</P> <P>The answer (copied from my reply there): </P> <P>Scalability.</P> <P>The whole point of being stateless is to allow a small number of resources <BR>to serve a large number of requests. If the entire page was cached as you <BR>suggest (making the page stateful) then, if 1000 clients connected, there <BR>would need to be 1000 instances of each of your pages in the cache. If the <BR>average user visits 5 of your pages, that&#8217;s 5000 cached pages. Then, how <BR>long should they be cached for? 5 minutes, an hour? - all while more clients <BR>are creating more instances.</P> <P>With a stateless architecture, those same 1000 users can be effectively <BR>served with just a few page instances. You can decide, for your specific <BR>needs, just the data that actually needs to be cached.</P> <P> </P></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=3641&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_3641" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Caching%20in%20ASP.Net&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2005%2F04%2F01%2F3641" id="akst_email_3641" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2005/04/01/3641/feed</wfw:commentRss>
		</item>
		<item>
		<title>Borland Developer&#8217;s Conference 2005</title>
		<link>http://blogs.teamb.com/wayneniddery/2005/03/18/3206</link>
		<comments>http://blogs.teamb.com/wayneniddery/2005/03/18/3206#comments</comments>
		<pubDate>Fri, 18 Mar 2005 17:47:54 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2005/03/18/2660/</guid>
		<description><![CDATA[I have amended this post somewhat to reflect my changing view and due to clarifications made by Borland on their site, so it is not your imagination if you thought I said something a bit different before&#8230;. Borland has finally announced information for the 2005 conference. Call for papers is here. As it stands currently, it seems [...]]]></description>
			<content:encoded><![CDATA[<p><P>I have amended this post somewhat to reflect my changing view and due to clarifications made by Borland on <a href="http://info.borland.com/conf2005/c4p/tracks.html">their site</A>, so it is not your imagination if you thought I said something a bit different before&#8230;.</P> <P>Borland has finally <a href="http://info.borland.com/devcon/">announced information </A>for the 2005 conference. Call for papers is <a href="http://info.borland.com/conf2005/c4p/">here</A>.</P> <P>As it stands currently, it seems this year&#8217;s conference has been scaled down over previous years. There are no specific tracks for database (Interbase, JDataStore, NDataStore), ALM (StarTeam, Caliber RM, Together, TerraQuest), or management (i.e. their SDO initiative along with other management-related topics), however it has been made clear that there will be sessions on these as part of the listed tracks. </P> <P>The days of the conference have also been shifted, they are Sunday to Thursday. In North America at least, it is a standard in the airline industry to charge just about double for flights that do not span a Saturday. It has been normal for many attendees  to fly into the conference Friday night, thus taking advantage of cheaper fares (at least if they are attending the preconference tutorials). But to do that this year, it will cost them an extra day away from home and work and an extra night of hotel charges.</P> <P>It is clear that Borland is focusing more on developers at this conference with apparent plans to offer a separate conference that focuses more on management levels (where they will highlight their SDO initiative more than development tools).</P> <P>I have conflicting thoughts on this presently. There are many who think this is a positive move - more purely a developer&#8217;s conference concentrating on the tools we love along with some attention on the ALM tools. </P> <P>However, I also have interest in learning about SDO and how it might help me or my customers, but I will not be able to afford to go to a separate conference just for that. </P> <P>I also think that there is a lot of value in different levels intermixing at a conference - one gets to meet people besides their direct peers, possibly &#8220;networking&#8221; with others that may pay future benefits, as well as promoting some degree of learning across those boundaries. I think separating customers (and thus emphasizing such boundaries) might not be the best way to go.</P> <P>But we&#8217;ll see. </P> <P> </P></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=3206&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_3206" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Borland%20Developer%27s%20Conference%202005&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2005%2F03%2F18%2F3206" id="akst_email_3206" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2005/03/18/3206/feed</wfw:commentRss>
		</item>
		<item>
		<title>We don&#8217;t need no stinkin&#8217; refactoring!</title>
		<link>http://blogs.teamb.com/wayneniddery/2005/02/14/2659</link>
		<comments>http://blogs.teamb.com/wayneniddery/2005/02/14/2659#comments</comments>
		<pubDate>Mon, 14 Feb 2005 12:52:22 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2005/02/14/1994/</guid>
		<description><![CDATA[There has been a discussion taking place in the Delphi non-tech newsgroup on the merits of refactoring with a few participants suggesting that refactoring is only needed if one designs or writes poor code in the first place - i.e. that refactoring is only useful for fixing bad code, not for writing new code. In [...]]]></description>
			<content:encoded><![CDATA[<p><P>There has been a discussion taking place in the Delphi non-tech newsgroup on the merits of refactoring with a few participants suggesting that refactoring is only needed if one designs or writes poor code in the first place - i.e. that refactoring is only useful for fixing bad code, not for writing new code. In an effort to prevent this developing myth from spreading too far I responded there with the following examples showing this is not true (a couple of typos have been corrected):</P> <P>1. Enhancing existing code, even when originally well designed and written, <BR>in order to allow it to be more easily reused for a new feature being added. <BR>This can take the form of extracting methods or abstracting out a baser <BR>class, or delegating some functionality, originally built into a class, to <BR>an interface (e.g. a plug-in).</P> <P>Unless you&#8217;re omniscient, you cannnot blame anyone for being shortsighted <BR>for not foreseeing every possible future enhancement that could be asked <BR>for, and even if one could, it still does not justify coding for every <BR>possible enhancement in advance of actually needing it.</P> <P>2. During original development, realizing that something you are doing now could be reused by another part of the application. Such realizations do not always happen during design no matter how thorough one tries to be or how well modelled it was.</P> <P>3. During development, flaws in the design may come to light - as above, this can happen no matter how much care was taken in design. In this case, one should, of course, go back to the design with that new knowledge, but then the code does indeed need to be refactored at that point.</P> <P>4. Finally, we all (hopefully) improve our practices with time and experience (and if we stop doing so then, IMO, it is time to quit and do something else). So it is not at all unusual that one writes code a certain way and then, looking at it again later, realizes a better way to do it - <BR>even in the course of a single project.</P> <P>I&#8217;ve often written code where I was not really satisfied with my solution but did not yet know a better way to do it, or where time constraints forced me to allow a solution I knew was not the best I could do, had I more time. When a future opportunity allows, it&#8217;s nice to be able to go back to that code and refactor it. The existing code is not necessarily wrong - i.e. no bug exists, it could just be better!</P> <P>Bottom line is that refactoring is as useful during original development as it is for fixing/improving/extending older code, and makes writing correct code the first time a whole lot more likely!<BR></P></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=2659&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_2659" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=We%20don%27t%20need%20no%20stinkin%27%20refactoring%21&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2005%2F02%2F14%2F2659" id="akst_email_2659" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2005/02/14/2659/feed</wfw:commentRss>
		</item>
		<item>
		<title>Toronto Falafel Conference a success</title>
		<link>http://blogs.teamb.com/wayneniddery/2004/11/30/1993</link>
		<comments>http://blogs.teamb.com/wayneniddery/2004/11/30/1993#comments</comments>
		<pubDate>Tue, 30 Nov 2004 21:52:20 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2004/11/30/1548/</guid>
		<description><![CDATA[Falafel&#8217;s conference in Toronto wrapped up today. It was excellent - of course it couldn&#8217;t be anything but, with Lino, Danny, and Allen presenting (Steve had to bow out this time). They of course covered Delphi 2005 features in the compiler and IDE for both .Net and Win32, ASP.Net, Security, and lots of other things. Lino [...]]]></description>
			<content:encoded><![CDATA[<p><P>Falafel&#8217;s conference in Toronto wrapped up today. It was excellent - of course it couldn&#8217;t be anything but, with Lino, Danny, and Allen presenting (Steve had to bow out this time). They of course covered Delphi 2005 features in the compiler and IDE for both .Net and Win32, ASP.Net, Security, and lots of other things. Lino finished the day with some coverage of what to expect in ASP.Net 2.0 once Microsoft releases it next year.</P> <P>If you were anywhere within driving distance of Toronto and didn&#8217;t come, well&#8230;  One lucky attendee left with a copy of Delphi 2005 Architect, and Lino also gave away other products from Falafel and swag supplied by Borland. </P> <P>We would very much like to make this an annual event, so please consider attending - the more the merrier. </P> <P> </P></p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=1993&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_1993" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Toronto%20Falafel%20Conference%20a%20success&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2004%2F11%2F30%2F1993" id="akst_email_1993" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2004/11/30/1993/feed</wfw:commentRss>
		</item>
		<item>
		<title>Color modeling</title>
		<link>http://blogs.teamb.com/wayneniddery/2004/10/06/1547</link>
		<comments>http://blogs.teamb.com/wayneniddery/2004/10/06/1547#comments</comments>
		<pubDate>Wed, 06 Oct 2004 14:33:37 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[Borland Conference 2004]]></category>

		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2004/10/06/699/</guid>
		<description><![CDATA[Well, I fell down on the job a little here with regard to the Conference, I was going to post more.
 One of the sessions I attended was on color modeling. I first read about this only a few months ago and have gradually been warming up to it as a result of trying it [...]]]></description>
			<content:encoded><![CDATA[<p>Well, I fell down on the job a little here with regard to the Conference, I was going to post more.
<p> One of the sessions I attended was on color modeling. I first read about this only a few months ago and have gradually been warming up to it as a result of trying it on various example problems derived from projects I&#8217;ve done in the past. The session was good and cleared up a couple of gray areas for me and now I like it even more and intend to actively use it. </p>
<p> There are a number of articles available on BDN, here are a couple to get you started: </p>
<ul>
<li> <a href="http://bdn.borland.com/article/0,1410,29697,00.html"> The Coad Letter: Modeling and Design Edition, Issue 68, a New Beginning </a> </li>
<li> <a href="http://bdn.borland.com/article/0,1410,32542,00.html"> Whole-Part Relationships in Color Modeling </a> </li>
</ul>
<p> The results of this technique is it makes diagrams a lot more readable and easy to follow, but most importantly, at least for me, is I found it really makes a difference in how I think about the classes and the responsibilities and relationships each class should have. My class diagrams most definitely change from what I would&#8217;ve done previously and I can clearly see it as a change for the better.</p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=1547&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_1547" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Color%20modeling&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2004%2F10%2F06%2F1547" id="akst_email_1547" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2004/10/06/1547/feed</wfw:commentRss>
		</item>
		<item>
		<title>Court rules against reality</title>
		<link>http://blogs.teamb.com/wayneniddery/2004/06/30/698</link>
		<comments>http://blogs.teamb.com/wayneniddery/2004/06/30/698#comments</comments>
		<pubDate>Wed, 30 Jun 2004 12:17:29 +0000</pubDate>
		<dc:creator>Wayne Niddery</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://blogs.teamb.com/wayneniddery/2004/06/30/540/</guid>
		<description><![CDATA["According to a post on the North American Network Operators Group mailing list, the state of New Jersey has issued a temporary restraining order allowing a former customer of Web hosting and Internet service provider Network Access Corporation (nac.net) to take non-portable IP space with it as it leaves for another provider. If the ruling [...]]]></description>
			<content:encoded><![CDATA[<p>"According to a post on the North American Network Operators Group mailing list, the state of New Jersey has issued a temporary restraining order allowing a former customer of Web hosting and Internet service provider Network Access Corporation (nac.net) to take non-portable IP space with it as it leaves for another provider. If the ruling stands, any customer would conceivably take their IP space with them when they leave their service provider."<BR><BR>Such a ruling is an ignorant contradiction of reality. The court might as well rule that when one moves to a new house, one should be able to take their old street address with them! This silly ruling needs to be overturned!</p>
<p class="akst_link"><a href="http://blogs.teamb.com/wayneniddery/?p=698&amp;akst_action=share-this"  title="Post to del.icio.us, etc." id="akst_link_698" class="akst_share_link" rel="nofollow">Share This</a> | <a href="mailto:?subject=Court%20rules%20against%20reality&body=Have you seen this? http%3A%2F%2Fblogs.teamb.com%2Fwayneniddery%2F2004%2F06%2F30%2F698" id="akst_email_698" class="akst_share_email" rel="nofollow">Email this page to a friend</a></p>]]></content:encoded>
			<wfw:commentRss>http://blogs.teamb.com/wayneniddery/2004/06/30/698/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
