May 31

Bob Cancilla’s off the mark!

I thought Bob Cancilla was actually changing his position on the need to pull away from the IBM i, but it looks like he has had yet another episode! You can find a copy of his latest rant here
http://planet-i.org/2013/05/29/continued-decline-of-the-ibm-i/

Here are my views on his comments.

1. Yes the IBM i install base is dwindling, but that is not because of the platform not being supported by IBM. Companies Merge so the server technology changes and generally decreases through consolidation. Companies go bust and close their doors meaning the servers are no longer needed, if you haven’t noticed the last 5 – 10 years have not been growth years.

2. The fact that COMMON Europe cancelled its conference is not a sign that there are no IBM i installs out there, the economy in Europe is bad and budgets have been cut for everyone! He does not mention what other conferences for his platforms of choice have seen in terms of attendance etc. Having a conference in an exclusive French resort which is very expensive is not the best idea COMMON Europe made. IBM pulled out because sending people to Europe is expensive and the location chosen is obviously a major factor in their decision, especially when no one else was going!

3.The Nordic numbers are not backed up by the graphic in the link, so I assume the reduction in numbers is something he has from some other source? If there were 10,000 customers running IBM i was that systems or was that an actual customer count? Why concentrate on the Nordics as an indicator for the rest of the world? As I have said the numbers must be dwindling, but some of that has to be to do with the power of the newer systems. I personally had 3 systems running for our business until we purchased a new Power 6 system, all of them were in the P05 tier group! I now have a single system running 3 Partitions each of which are probably 3 – 4 times faster than the previous Power 5+ i515 system alone so I need a lot less systems to deliver better user experiences. If I went to a Power 7 this would be increased exponentially again!!! Others have obviously done the same as I did and reduced the number of servers.

4. IBM is getting out of hardware and has been since I worked at IBM Havant in 1975 – 1993, nothing has changed there. The fact that they are selling the x86 business is good for Power, if Power was the problem they would be getting rid of it! Yes IBM invested in Linux, but obviously not for x86 hardware (they are desperately trying to get out of that) so again it was probably for the Power hardware, so why are they doing that if it is being dropped. There are many other reasons such as services revenue and software licensing (Linux is not free at the Enterprise level) so it is a mix of everything above.

5. RPG locks you into the platform so it is bad, hmmmm then why not use one of the other languages available on the platform? You have a choice of many languages on the IBM i and my very personal opinion is that anyone who is just using RPG is cutting their own throat! RPG is just a tool in the toolbox, so pick the best tool for the job. If I am going to have to rebuild my entire application just to change the language why would I ever add a new platform and all of the complexities of the OS into the mix? I could train a ‘C’ developer on Linux to develop in ‘C’ on the IBM i a lot faster than I could train an RPG developer to develop in C on Linux, that goes for any language and the IBM i supports them all (especially Java). Even though RPG is a key tool on the IBM i we need to reduce the emphasis placed on it and start to push the other languages just as hard.

We are being told CLOUD is the next leap in faith for the IT community. If you are to believe the hype it means you are not interested in how the result is delivered and what produced it just that it is available all the time and at a lower cost. As usual there are lots of ideas on what this means in terms of application delivery and many of them are a new set of acronyms for the same technologies that refused to fly years ago. I have doubts if the Cloud is the answer and I am sure that before too long we will have a new word for it! Having said that, if the Cloud is the next evolution of IT delivery why does this do anything but create the need for stable, dependable, highly available, flexible systems (oh did I just explain what the IBM i is???). So while I appreciate Bobs right to keep trying to build his business using scare tactics and bluff, I for one will keep an open mind about dumping IBM i in favor of moving to something new.

Just to set the record straight, I run Windows, MAC, Linux, AIX and IBM i. I have spent a lot of time developing on Windows, Linux and IBM i (IBM i the most) and all in a single language ‘C’ (or the related object version). In my view IBM i is the simplest for many reasons, not least the integration of everything you need to build a total solution. I use PHP for interface building (80 column screens just don’t hack it for me) and prefer to run the Web Services from Linux or Windows, but the IBM i can perform as a web server if needed.

So if you do as Bob says and take a deep and meaningful look at your IT infrastructure, consider changing the development language before jumping to a new development language, platform, OS and development tool set! Remember with ILE you can build the solution out of many languages and they will all work in harmony so you can steadily replace older programs with new ones.

Chris…

Nov 23

A Bit more fun with the File display and update web pages.

OK so we thought we would have a bit more fun with the previous programs which allowed you to move around and update file records. This is very basic code so don’t expect too much, but as we always say it’s free and you can do what you want with it (or not!). We were thinking about what we could add to make it a bit more of a complete project, so we decided to add a few more pages and functions which would allow you to drill down to the data you were interested in updating. REMEMBER this code took us about 1 hour to create so it is not anything like production ready, but it does show some of the power you can use to add new interfaces over your existing programs and products using PHP and the i5_toolkit().

The first thing we did was moved the process back a few steps so we could let the user sign on and then pick the library they were interested in and then displaying the files it contained.

This is the index.php file which is the start of the process. Very simple and all it does is force you to pass in some credentials (sign on info) which it uses to get a list of the libraries on the target system. This is the code which generates the first page.


<?php
/*
Copyright © 2010, Shield Advanced Solutions Ltd
All rights reserved.

http://www.shieldadvanced.ca/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the Shield Advanced Solutions, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/
// start the session to allow session variables to be stored and addressed
session_start();
require_once("scripts/functions.php");
// get the previous return URL
$ret_url = $_SESSION['ret_url'];
// store this page as return URL
$_SESSION['ret_url'] = $_SERVER['PHP_SELF'];
if(!isset($_SESSION['server'])) {
load_config();
}
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Display File Content</title>
<!-- add the main CSS -->

</head>

<body>
<?php
// if valid user is set connect using Private connection
if(isset($_SESSION['valid_usr'])) {
$conn = 0;
if(!connect($conn)) {
if(isset($_SESSION['Err_Msg'])) {
echo($_SESSION['Err_Msg']);
$_SESSION['Err_Msg'] = "";
}
echo("Failed to connect");
}
else {
Dsp_Lib_Obj($conn);
}
}
else {?>
<form name=login method="post" action="scripts/login.php">
<table width="20%" align="center" border="1" cellpadding="1">
<tr><td><label>User ID :</label></td><td><input type="text" name="usr" /></td></tr>
<tr><td>Password:</td><td><input type="password" name="pwd" /></td></tr>
<tr><td colspan="2"><?php if(isset($_SESSION['Err_Msg'])) { echo($_SESSION['Err_Msg']); $_SESSION['Err_Msg'] = ''; } ?></td></tr>
<tr><td><label>Connection type</label></td><td><select id="conn_type" name="conn_type"><option value="decrypt" selected="selected">Decrypted</option><option value="encrypt">Encrypted</option></select></td></tr>
<tr><td colspan="2" align=center><input type="submit" value="Log in" /></td></tr><?php
if(isset($_SESSION['Pwd_Err'])) {
if($_SESSION['Pwd_Err'] == 1) { ?>
<tr><td colspan="2" align=center>Sorry the credentials were rejected by the <?php echo($_SESSION['sys']); ?> System</td></tr><?php
$_SESSION['Pwd_Err'] = 0;
}
} ?>
</table>
</form><?php
} ?>
</body>
</html>

The config file is loaded at the beginning of the process which contains the target server to use. The function which does that is contained in the functions.php file which is shown next. this also contains all of the functions used to connect to the server and display the content for other pages. (I hope I cut and pasted everything we used). I did not copy the config file but you should be able to work out the layout and content based on the load_config() function.


<?php
/*
Copyright © 2010, Shield Advanced Solutions Ltd
All rights reserved.

http://www.shieldadvanced.ca/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the Shield Advanced Solutions, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

function connect(&$conn) {
// reset the the ErrMsg variable
unset($_SESSION['ErrMsg']);
// connect to the i5
$conId = 0;
if (isset($_SESSION['ConnectionID'])) {
$conId = $_SESSION['ConnectionID'];
}
$server = $_SESSION['server'];
$addlibl = array($_SESSION['install_lib']);
// options array for the private connection
$options = array(
I5_OPTIONS_PRIVATE_CONNECTION => $conId,
I5_OPTIONS_IDLE_TIMEOUT => $_SESSION['timeout'],
I5_OPTIONS_JOBNAME => 'PHPTSTSVR');
// connect to the system
if($_SESSION['conn_type'] == 'encrypt')
$conn = i5_pconnect($server,$_SESSION['usr'],d_pwd($_SESSION['pwd']),$options);
else
$conn = i5_pconnect($server,$_SESSION['usr'],$_SESSION['pwd'],$options);
// if connect failed
if(is_bool($conn) && $conn == FALSE) {
$errorTab = i5_error();
if ($errorTab['cat'] == 9 && $errorTab['num'] == 285){
$_SESSION['ConnectionID'] = 0;
$_SESSION['Err_Msg'] = "Failed to connect";
return -1;
}
else {
//set the error message
$_SESSION['Err_Msg'] = "Connection Failed " .i5_errormsg();
// send back to the sign on screen
$_SESSION['ConnectionID'] = 0;
return -1;
}
}
else {
// add the library list
add_to_librarylist($addlibl,$conn);
$ret = i5_get_property(I5_PRIVATE_CONNECTION, $conn);
if(is_bool($ret) && $ret == FALSE) {
$_SESSION['Err_Msg'] = "Failed to retrieve connection ID " .i5_errormsg();
$_SESSION['ConnectionID'] = 0;
return -1;
}
else {
$_SESSION['ConnectionID'] = $ret;
}
}
return 1;
}

/*
* Function to add the install library to the library list
* @parms
* libraries to add to the library list
* connection to use
*/

function add_to_librarylist($libraries,&$conn) {
$curlibl = "";
// get current librarylist
if (!i5_command("rtvjoba",array(),array("usrlibl"=>"curlibl"),$conn))
die("Could not retrieve current librarylist:" . i5_errormsg($conn));
// add our libraries to the librarylist
$curlibl .= " " .implode(" ",$libraries);
// if library list already set a CPF2184 message will be generated, treat as warning and by pass.
if(!i5_command("chglibl",array("libl"=>$curlibl),array(),$conn)) {
if(i5_errormsg($conn) != 'CPF2184')
echo("Could not change current librarylist:" . i5_errormsg($conn));
}
return 1;
}

/*
* function Dsp_Pfm()
* display the contents of a file
* @parms
* File
* File Library
* returns the number of errors.
*/

function Dsp_Recs($conn,$file,$flib) {

// query to get the data frm the file
$query = "SELECT rrn(a) as RRN, a.* FROM " .rtrim($flib) ."/" .rtrim($file) ." a";
$result = i5_query($query,$conn);
if(!$result){
echo("Failed to get the data $_SESSION['ErrMsg'] = "Error code: " .i5_errno($result) ." Error message: " .i5_errormsg($result);
}
$rec = i5_fetch_assoc($result);
echo("
// the assoc array contains the field names so we can use those as the headers
foreach($rec as $key => $value) {
echo(" }
echo(" // now just write out the data again and get the next record to the end
do {
echo(" foreach($rec as $key => $value) {
echo(" }
echo(" } while($rec = i5_fetch_assoc($result));
echo(" i5_free_query($result);
return;
}

/*
* function Dsp_Pfm()
* display the contents of a file
* @parms
* File
* File Library
* returns the number of errors.
*/

function Dsp_Pfm_2($conn,$file,$flib,$id) {

// query to get the data frm the file
$query = "SELECT rrn(a) as RRN, a.* FROM " .rtrim($flib) ."/" .rtrim($file) ." a WHERE RRN(a) = '" .$id ."' FETCH FIRST ROW ONLY";
$result = i5_query($query,$conn);
if(!$result){
echo("Failed to get the data<br>" .$query);
$_SESSION['ErrMsg'] = "Error code: " .i5_errno($result) ." Error message: " .i5_errormsg($result);
}
$rec = i5_fetch_assoc($result);
echo("<table><form name=updent method=post action=scripts/update_pfm.php?file=" .$file ."&flib=" .$flib ."&id=" .$rec['RRN'] ." >");
// the assoc array contains the field names so we can use those as the headers
$i = 0;
foreach($rec as $key => $value) {
echo("<tr><td>" .$key ."</td><td><input type=text name=" .$key ." id=" .$key ." value='" .$value ."'");
if($key == "RRN")
echo(" readonly=readonly");
echo(" /></td></tr>");
$i++;
}
echo("<tr><td colspan=2><input type=submit value=Update /></td</tr></form></table>");
// button to get the next record
echo("<table><tr><td>");
if($id > 0) {
$id--;
echo("<input type=button value=PREV OnClick=location='dsp_pfm.php?file=" .$file ."&flib=" .$flib ."&id=" .$id ."' /></td><td>");
$id++;
}
$id++;
echo("<input type=button value=NEXT OnClick=location='dsp_pfm.php?file=" .$file ."&flib=" .$flib ."&id=" .$id ."' /></td></tr></table>");
echo("<input type=button value='Log Out' OnClick=location='scripts/logout.php' ?>");
// break the reference to the last element as per the manual
unset($value);
i5_free_query($result);
return;
}

/*
* function load_config()
* @parms
* *NONE
*/

function load_config() {
$config_file = "scripts/config.conf";
$comment = "#";
// open the config file
$fp = fopen($config_file, "r");
if(!$fp) {
echo("Failed to open file");
return 0;
}
// loop through the file lines and pull out variables
while(!feof($fp)) {
$line = trim(fgets($fp));
if ($line && $line[0] != $comment) {
$pieces = explode("=", $line);
$option = trim($pieces[0]);
$value = trim($pieces[1]);
$config_values[$option] = $value;
}
}
fclose($fp);
$_SESSION['server'] = $config_values['server'];
$_SESSION['install_lib'] = $config_values['install_lib'];
$_SESSION['timeout'] = $config_values['timeout'];
$_SESSION['refresh'] = $config_values['refresh'];
$_SESSION['logo'] = $config_values['company_logo'];
$_SESSION['dft_logo'] = $config_values['default_logo'];
$_SESSION['co_name'] = $config_values['company_name'];
if(isset($config_value['valid_usr'])) {
if($config_values['valid_usr'] != '') {
if(($config_values['usr'] != '') && ($config_values['pwd'] != '')) {
$_SESSION['valid_usr'] = $config_values['valid_usr'];
$_SESSION['usr'] = $config_values['usr'];
$_SESSION['pwd'] = $config_values['pwd'];
$_SESSION['auto_signon'] = 1;
}
}
}
return 1;
}

/*
* function to retrieve a list of libraries
*/

function Dsp_Lib_Obj(&$conn) {
$HdlList = i5_objects_list('QSYS', '*ALL', '*LIB',$conn);
if (is_bool($HdlList)) trigger_error('i5_objects_list error : '.i5_errormsg(), E_USER_ERROR);
echo("

    while($list = i5_objects_list_read($HdlList)){
    echo(" if($list['DESCRIP'])
    echo(str_pad($list['DESCRIP'],50," ",STR_PAD_RIGHT));
    else
    echo("-");
    echo(" echo(" }
    echo(" $ret = i5_objects_list_close($HdlList);
    if (!$ret) trigger_error('i5_object_list_close error : '.i5_errormsg(), E_USER_ERROR);
    return;
    }

    /*
    * function to retrieve a list of Files in a Library
    */

    function Dsp_Pf_Obj(&$conn,$lib) {
    $HdlList = i5_objects_list($lib, '*ALL', '*FILE',$conn);
    if (is_bool($HdlList)) trigger_error('i5_objects_list error : '.i5_errormsg(), E_USER_ERROR);
    echo("

      while($list = i5_objects_list_read($HdlList)){
      // check if a PF-DTA
      if($list['EXT_ATTR'] == "PF") {
      echo(" if($list['DESCRIP'])
      echo(str_pad($list['DESCRIP'],50," ",STR_PAD_RIGHT));
      else
      echo("-");
      echo(" echo(" }
      }
      $ret = i5_objects_list_close($HdlList);
      if (!$ret) trigger_error('i5_object_list_close error : '.i5_errormsg(), E_USER_ERROR);
      return;
      }
      ?>

      The next page called provides a list of the *FILE objects in the library which have a EXT_ATTR of "PF", this allow you to select a file and display all of the records from that file. Again all we do is use the i5_toolkit function i5_list_objects() to provide the list of objects in the library and then filter based on the extended attribute.


      <?php
      /*
      Copyright © 2010, Shield Advanced Solutions Ltd
      All rights reserved.

      http://www.shieldadvanced.ca/

      Redistribution and use in source and binary forms, with or without
      modification, are permitted provided that the following conditions
      are met:

      - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

      - Neither the name of the Shield Advanced Solutions, nor the names of its
      contributors may be used to endorse or promote products
      derived from this software without specific prior written
      permission.

      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
      INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
      BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
      CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
      ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.

      */
      // start the session to allow session variables to be stored and addressed
      session_start();
      require_once("scripts/functions.php");
      // if not signed in just force to sign in page
      if(!isset($_SESSION['valid_usr'])) {
      header("Location: /index.php");
      exit(0);
      }
      // get the previous return URL
      $ret_url = $_SESSION['ret_url'];
      // store this page as return URL
      $_SESSION['ret_url'] = $_SERVER['PHP_SELF'];
      ?>

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title>List Files in Library</title>
      <!-- add the main CSS -->
      <link rel="stylesheet" href="css/tst.css" type="text/css">
      </head>

      <body>
      <?php
      $conn = 0;
      if(!connect($conn)) {
      if(isset($_SESSION['Err_Msg'])) {
      echo($_SESSION['Err_Msg']); $_SESSION['Err_Msg'] = "";
      }
      }
      Dsp_Pf_Obj($conn,$_REQUEST['lib']); ?>
      </body>
      </html>

      From this list you will display all of the records in the file, we only used a small file to test so the amount of data coming back was not important, if this was production and you had files with millions of records in them it could blow up the page as it would be too big! If you want to add something consider paging the file in sets of records or passing in a range of records at the start so only a limited number of records are retrieved and displayed. We have written plenty of posts and forums entries about how to achieve this, Google should bring up enough information to allow you to do it. This is the code for the page to display all of the records, it simply displays them and has a button which allows you to edit specific records. We just left the display for this as it was so once you start to edit you will have the ability to move around the records as previously.


      <?php
      /*
      Copyright © 2010, Shield Advanced Solutions Ltd
      All rights reserved.

      http://www.shieldadvanced.ca/

      Redistribution and use in source and binary forms, with or without
      modification, are permitted provided that the following conditions
      are met:

      - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

      - Neither the name of the Shield Advanced Solutions, nor the names of its
      contributors may be used to endorse or promote products
      derived from this software without specific prior written
      permission.

      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
      INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
      BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
      CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
      ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.

      */
      // start the session to allow session variables to be stored and addressed
      session_start();
      require_once("scripts/functions.php");
      // if not signed in just force to sign in page
      if(!isset($_SESSION['valid_usr'])) {
      header("Location: /index.php");
      exit(0);
      }
      // get the previous return URL
      $ret_url = $_SESSION['ret_url'];
      // store this page as return URL
      $_SESSION['ret_url'] = $_SERVER['PHP_SELF'];
      ?>

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title>Edit File Content</title>
      <!-- add the main CSS -->
      <link rel="stylesheet" href="css/tst.css" type="text/css">
      </head>

      <body>
      <?php
      $conn = 0;
      if(!connect($conn)) {
      if(isset($_SESSION['Err_Msg'])) {
      echo($_SESSION['Err_Msg']); $_SESSION['Err_Msg'] = "";
      }
      }
      Dsp_Pfm_2($conn,$_REQUEST['file'],$_REQUEST['flib'],$_REQUEST['id']); ?>
      </body>
      </html>

      An that is it, took us about 1 hour to complete the set of functions and pages (lots of cut and paste). I thought I would add a few pictures to show you just what it produces. Remember this uses the Aura tollkit functions so they must be available for it to work. Again we did not add any fancy page sizing CSS etc so the images are very basic.

      This is the inital page which requires the user to pass in valid credentials to connect to the IBMi.

      Uses the sign in information to access the IBMi

      Initial signon screen

      Once you have signed in you should see a list of the libraries available on the system. You could provide filters or use the IBMi security to only allow the user to display the available libraries.

      List of Libraries

      The list of libraries from the system

      When you select the library you will be provided with a list of Physical Files.

      List of files in library

      A list of files in the library for selection

      Select the option to display the records and the following page is displayed.

      Records contained in the file

      A list of the records contained in a file select to edit

      Select a record to start editing from and the following page is displayed we moved the record up one to show the movement buttons.

      Edit a file

      Editing records in a file

      That is it, plenty of improvements I can think of just as I write this post but you get the idea. IBMi is the perfect hub for delivering data via web pages, we prefer to keep the web page generation off the IBMi but you have the option to use the iAMP server or Zend Server etc. to provide the same functionality. Stop thinking about how to move off the platform and start thinking about how you can harvest all of the good business logic and data you have built into the system.

      Hope you find this useful, for us its just a bit of fun and provides us with the ability to show what we can do with PHP. You should take up the challenge and start your investment in PHP now. Call us if you need more information or help.

      Happy PHP'ing..

      Chris...

Jun 15

Adding CGI capabilities to iAMP

We have been working with a client who wanted to be able to install a new website on their IBMi. The new website was developed on another OS using cake PHP by a development company that had no idea about how to set up a web server on an IBMi so we were asked to help them implement the new site.

The companies old site did in fact run on the IBMi but was not written in PHP and was mainly composed of static pages with some parts that needed CGI access to run. The initial request stated that these CGI generated pages were no longer needed so we did not have to worry about setting them up. They had looked at Zend Server in the past and rejected using it due to its complexity so we had the opportunity to start fresh and just install the iAMP server.

At first we set up test sites just to make sure the server ran and we had no problems setting up VirtualHosting. We did this on a new IP address so it did not impact the current site which was being run from the same saver. We could have used the same IP address and a different port but that is just plain ugly! We also needed a MySQL instance because the content management used the MySQL database to store the page data, an admin feature allowed the content to be changed via an editor which updated in the MySQL database when it was finished.

Everything went OK and we managed to import the MySQL database which contained all of the web page content so far with a few issues which we soon worked around. On starting the web server everything looked OK but we found the links in the navigation objects were all screwed up, a quick fix from the developers soon fixed that and the site appeared to be running just fine.

At this point the client said OK lets go live! They wanted the new site up and running in the wild straight away. Although this was quite a leap of faith all we had to do at this point was change the new configurations to the run on the original servers IP address, stop the existing server and bring the new iAMP server online. This went through without any hitches and the new site was up and running in the wild.

We thought everything was OK but when we tried to use the Admin functions (which are important to the client because it allowed them to control the content of their webpages through a user friendly interface) we hit our first problem. In the code they had a function which uses sockets to talk back to the HTTP server, this request hung every time it was called. The messages in the logs did not help much in finding out exactly what the problem was so it was Google to the rescue. We found a post to a forum about a different problem but the description of the resolution gave us the idea of where to look. The problem was the socket tried to connect to the external interface of the HTTP server, the firewall (like all good firewalls) stopped the connection because it determined the request was coming from the LAN to the WAN and back to the same LAN. We did not want to change the firewall rules to allow this connectivity so we had to come up with another option. The solution was pretty simple, we added a host table entry to the IBMi that pointed all requests for the website address to the internal IP address, this meant any request from the HTTP server to the company website would go to the internal IP address of the IBMi not the external one defined in the DNS. After this the admin function worked and we move on.

The next challenge came when we found out that despite being told otherwise, one of the functions in the website required a call to a CGI script. We thought we could just configure the iAMP server to run the request by configuring the CGI module etc. That did not work. The output said it was a permissions error which lead us down a long and fruitless path of looking at the permission on each of the objects etc. but as we discovered the problem was because PASE programs cannot call a Native IBMi program.

This meant we needed a copy of the IBM HTTP server running which we could use to serve the CGI generated content. Setting up the IBM HTTP server was pretty simple as we had just removed the old site from the configs and another internal site was still running on it. We just added a new configuration and ran it against a separate port. The version of the iAMP Server we had installed did not have proxy support so we had to come up with an alternative while Aura could create and test the additional modules required for proxy support. The solution we eventually came up with had one draw back in that we had to open the port the new website was using in the firewall, this allowed the request to be routed from the iAMP server back to the correct port on the IBM HTTP server but we did not like having to open more ports in the firewall. Once we had everything configured the CGI processes all worked and the client was again happy with their solution.

When we received the new code from Aura for iAMP with Proxy support we upgraded the server and set up the link to the IBMi server using the ProxyPass and ProxyReverse statements in just the same way you had to configure ZendCore. We also added the new modules into the base config and after some false starts we had the site back up and running with the proxy support and we could remove the additional open port from the firewall.

If you need to have an AMP stack on the IBMi you now have the option of using the iAMP Server or Zend Server, iAMP is a free download from the Aura Equipments website. Support for iAMP is provided free of charge via the Aura forums but you can also request full support for the product from Aura for a very low fee. As an incentive Aura are also offering to inlcude the support costs for iAMP with Easycom so you will have one support structure or all your AMP stack plus the Easycom i5_toolkit for one very low fee. The same setup for Zend requires you to enter into a support contract with Zend for their Server, a support contract with another company for ZendDBi (MySQL) plus a non supported open source solution for the i5_toolkit server. I know which one I think is the better option.

If you have any questions about running iAMP or would like to know more about Easycom for PHP i5_Toolkit gives us a call, you may surprised just how cost effective our solution for running AMP on the IBMi is. Even if you are running an AMP stack on Linux or Windows, Easycom will allow the same i5_toolkit functions to run on those stacks in exactly the same way they do on the IBMi, don’t just integrate, innovate…

Chris…

Feb 22

Installing RedHat into a IBMi Guest LPAR

We had a lot of issues recently with the hosting LPAR which caused us to un-install all of the Guest partitions except the iOS partition to see if they were having any adverse effects on the Hosting partition. A Reclaim Storage found a number of damaged objects but none of them related to the installs we had done. It also gives us a chance to go back over the installs and document what we did to get them up and running.
So this is a list of the actions we took with a number of pretty pictures to show some of the important steps. The HMC is used to setup the Partitions, we were going to use VPM but the restrictions on the number of Guest partitions meant we had to step up to the HMC/VIOS standard edition to meet the requirements.
This is the current partition information it shows we still have a Hosting partition (SHIELD3) and a Guest partition (IOS2) running. We also have a couple of other servers which are powered off at the moment which are not important for this exercise.

Initial-HMC

First thing to do is create the configuration for the LPAR, we created one called RedHat with the following requirements.
1. The Partition would use .2 of a processor uncapped.
2. Maximum Virtual processors will be 2.
3. It will have 1GB of memory.
4. It will use the LHEA resource for the LAN.
5. We need a Serial Server Virtual adapter for the console.
6. It will not have Power control
This is the summary which is produced for the LPAR profile.

New-RedHat-partition-information

You will notice we have no Physical IO, all of the storage is provided by the Virtual storage device. The next change we have to make is to add the storage definition to the Hosting partition. We need to add the change twice as the LPAR is currently running because updating the profile will only take effect once the partition is restarted, we need the active partition to be updated to recognize the new storage requirement.

Here is a picture of the updated Host Partitions Virtual adapter settings. We also added it to the profile so when a restart of the LPAR happens the new profile will have the same information. Strange IBM doesn’t automatically update the profile.

SCSI-adapter-update-Host-LPAR

That is all we had to do for the HMC configuration we did a quick start of the LPAR with a console screen which showed it connected correctly and we did manage to see the initial startup of the console connection.
Next we created the objects required in the hosting partition to allow the LPAR to be started and installed.
First step is to create the Console user for the remote console access. This allows the console to be started and have sufficient authority to carry out the required tasks. To configure you need to go through SST and create a new Console user which is accessed from the Work with service tools user IDs and Devices menu option then the Service tools user IDs menu option. Most of the default options are acceptable but you need to add at least the following options when you create the new ID.
– System partitions – operations
– System partitions – administration
– Partition remote panel key
Once you have updated the profile you can exit back to the command line and the profile should be enabled for use. Here is a view of the profile authorities after creation.

Console-User

To install RedHat we need to create a Network Server Description, this is connected to a Network Storage Space which is how the installation can be carried out. To install RedHat we need to create a storage space of about 6GB according to the manuals we looked through. The recent install of Linux SuSe (which was the first install we did) worked well with 10GB of disk space so we thought we would use a bit more for this install just so we can install more utilities so we settled on 20GB.
Creating the NWSD is the first object to be created, there are a couple of items we tripped over so we have added a couple of images of our configs to show them.
First of all we found the use of *AUTO required the Partition entry had to match exactly the HMC definition we had created. We also wanted to control when the partition was started so we changed the Online at IPL to *NO.

Network Storage definition 1

The next changes are related to the installation media, we used the WRKLNK command to find the correct path to the install image on the CD, the manuals we had read through previously had totally different paths defined so it is important you find the correct path before you attempt to do the install

Network Storage definition 2

We are going to install the product from a *STMF (we were baffled by the AIX install which was installed from CD but had *NWSSTG as the IPL source?). Another gotcha is the Power Control setting, make sure that this is set to *NO for the installation otherwise the NWSD will refuse to vary on. We added the VNC=1 to say we want to have a VNC server started at IPL. This allows us to get to the graphical interface when using RedHat.
Now the definition is created we need to create the network storage space to attach to the definition. As with all IBMi functions you can access them via the menu or list options plus use the command directly. Here is our storage space setup.

Network Storage Space 1

After pressing enter the system will go out and allocate the entire space and format it for use. The disk utilization will increase based on the amount allocated not

Network Storage definition 2

The format and allocation of the space takes a bit of time so time to make a coffee while you wait. Once the storage space has been created and formatted you now need to link the storage space to the definition.

Network Storage Link defined

Now we are ready to activate the partition. You can achieve this by taking option 8 from the WRKNWSD list which will show you the partition and take option 1 against it. If the NWSD does not become ACTIVE you have a configuration problem somewhere which has to be resolved before you can go any further.

Active Network Storage Definition

We are now ready to install the Linux OS. The redbook we looked at mentioned the ability to use the VNC graphical interface to do the install but as we have no network connection to the install device we could not figure out what they were on about? So we took the console window option as the only way we could install the RHE OS. To get to the console just start the partition from the HMC and select the Open Terminal Window from the Console dropdown. You should see something like the following.

Console-window

The option 1 which is out of sight in this picture is the first option to take it sets the language for the install. Next we need to set up the boot options. Your install may be different so you may need to change a few things. We were installing from the CD so we had to select the device information using the options presented after pressing option 5. Once the boot device had been set the console will re-connect and see the installation image available on the CD.

Booting-from-CD-into-RedHat

We had to press enter a couple of time to get through to the actual installer (anaconda) and we also took the option not to check the media as we decided as it has only just been created and it has not been scratched or anything else it should be OK. The install procedure is pretty straight forward although the selection process for some entries was a bit confusing but after letting the Autopartition do its business and allowing it to remove all existing data the install went off pretty smoothly. We only used the default installation for expediency as we are only going to use this demo development with PHP and Easycom in mind although that did amount to 692 packages..
Once the installation is finished you can remove the installation CD and press enter to reboot the partition. At this point it will not be able to start RedHat because the NWSD is set to start from the CD so it will simple go into the console management software, just close the terminal window at that point.
We now need to tell the NWSD where the real boot device is so we need to go in and set the correct boot point which is the *NWSSTG option. This requires the NWSD is turned off and started again after it is updated. The following screen shows the new settings.

Active Network Storage Definition

Now we should be able to start the partition again and it will boot into RedHat. For us the partition in the HMC was showing it was loading the Firmware so we took the option to end the partition and started it up again. This did bring up the same state because it was trying to boot from the network, we simply change the boot device back to the Hard Drive we had configured and it started up.

RedHat partition re-booted

Now we need to configure the network etc so we can access the OS from the VNC client and Putty, as this is specific to our install we have not provided screen shots and this is a normal activity for configuring Linux. Perhaps once we get the Apache Server up and running with Easycom installed we will go over that part?

So that is it we now have a fully functioning RedHat Enterprise partition running as a Guest under an IBMi hosting partition.

If you have any questions about the install or what we have done since let us know.

Chris…

Feb 15

IBMi running a Guest AIX Partition accessing IBMi data.

We have been playing around with the setup of our IBMi system to run various guest partitions that are running difference supported OS’s. The first install was another IBMi guest partition which would allow us to run HA testing between partitions and not require the use of a second system. The next partition we installed was a Linux partition which is running SuSe Linux, this is going to allow us to test the running of Easycom clients to the IBMi Easycom servers to pull back IBMi data and objects. Finally the last partition is going to be running AIX which will be used as a test platform to run PHP/Easycom in the same manner we do for Linux. As an additional future option we will install a RedHat partiton to allow us to run the PHP Easycom processes mainly to allow us to get comfortable with running RedHat and the PHP/Easycom setup.

The IBMi install was a breeze when we look back, setting up the initial Partition information using the HMC and building the NWSD etc was a challenge but IBM was very good at helping us get things running. Due to the lack of supply of the AIX software (that’s a whole story on its own) we moved to the Linux install next, with all of the information we had gathered while setting up the IBMi partition that went very well and took less than a day to complete including all the configuration.

AIX is a totally different story, to say I hate AIX is pretty strong but I am not far off. Trying to get a reason interface (KDE was our preference) and a VNC server setup and running has been a nightmare! I though with the limited knowledge we have with Linux it should be pretty achievable but that was not to be! BUT we have finally managed to get things up and running (I am not sure I want to try to restart the box though in case it does not come back up in the same manner) and have a working PHP/Easycom install. I should warn you the PHP/Easycom install is a botch up that we did with the help of our friends at Aura, we have taken the iAMP server and chnaged a few things to allow it to run on AIX, putting it all together in an RPM is a distant dream but it should be possible. IBM does have its own Apache and PHP bundle but we took the iAMP server route to see just what issue it would present and how effective it will be when running the i5_toolkit function to the IBMi and we could not be sure the AIX modules generated would run under the IBM bundle.

So long story short we now have a working POWER Systems configuration that allow AIX to pull IBMi data and objects for the HTTP server to present.

Here is a picture of the VNC client running on a Windows 7 Laptop that is connected to a AIX Guest running under a IBMi hosting partition running iAMP server and pulling data from another IBMi parition! WoW that a lot of complexity!

AIX running under IBMi through a VNC connection

Now to grow some hair back.

Chris..

Dec 27

Simple PHP scripts to display a List of spool files and their content

As part of the development of the JobQGenie PHP interface we needed to be able to display a list of the spool files which are generated for the job queue reload process. The i5_toolkit functions have the ability to provide all of the information we needed, you could achieve the same with the new XMLSERVICE function as well but that will require you build all of the underlying functions yourself which requires a lot more effort and skill.

We just wanted to display a list of the spool files for the current user and then have a link to another page which would display the content of the spool file. Once you understand the calls we have used you can change the code to refine the list and display just a few spool files you are interested in, but for the purposes of this exercise *CURRENT was all we needed to use. The following is the very simple code we developed, the connection and the sign-on processes are not shown but use the same code we have posted previously. You will also notice that we did not show all of the returned information from the i5_spool_list_read() function as we did not need to display or use it. If you want to know all of the data which is returned a simple var_dump() will show you.


function get_spl_list(&$conn) {

// get a list of the spool files for the current user
$HdlSpl = i5_spool_list(array(I5_USERNAME=>"*CURRENT"));
if(is_bool($HdlSpl)){
$ret = i5_errno();
print_r($ret);
}
echo("<table border=1><tr><td><label>Job Name</label></td><td><label>User Name</label></td><td><label>Job Number</label></td><td><label>Splf Number</label></td>
<td><label>OutQ Name</label></td><td><label>Pages</label></td><td><label>Spool File Size</label></td><td><label>Action</label></td></tr> ");
// read the list and display to the user
while ($ret = i5_spool_list_read($HdlSpl)){
// build the request string
$url = "dspsplf.php?name=" .urlencode($ret['SPLFNAME']) ."&jobname=" .urlencode($ret['JOBNAME']) ."&user=" .urlencode($ret['USERNAME']) ."&jobnum="
.urlencode($ret['JOBNBR']) ."&splnbr=" .urlencode($ret['SPLFNBR']);
// encode the string
$newurl = urlencode($url);
echo("<tr><td>" .$ret['JOBNAME'] ."</td><td>" .$ret['USERNAME'] ."</td><td>" .$ret['JOBNBR'] ."</td><td>" .$ret['SPLFNBR'] ."</td><td>" .$ret['OUTQLIB'] ."/" .$ret['OUTQNAME']
."</td><td>" .$ret['PAGES'] ."</td><td>" .round((($ret['SPLFSIZE']*$ret['SPLFMULT'])/1024),2) ."KB</td>
<td><a href=\"" .$url ."\" target=_blank>Display</a></td></tr>");
}
echo("</table>");
$ret = i5_spool_list_close($HdlSpl);
return 1;
}

In the above code we built a table of the spool files plus added a link to call another page which will display the content of the spool file. The output is pretty simple and has no real formatting applied, I am sure you can make this look a lot better with a bit of color and formatting, but for our purposes this will suffice for now.

One thing we had to do is encode the url to the new page, we found that some of our IBMi jobnames had a character which the browsers did not like (‘#’) I am sure there are others but his one caused us a few scratch the head moments. Initially the browser would drop everything after the ‘#’ from the $_REQUEST variable stack so we tried various endcoding of the url. If we built and encoded the entire url in a single request the browser would fail to display the content because of a repeating error, yet encoding each parameter separately allowed the request to be correctly interpreted. We are not sure why this occurs but the above solution does work so we will leave that investigation for a later time.

Here is an image of the output from our test.

Spool File List

Spool File List

Once we have the list all we wanted to do is display the data in the spool file, you could decide to move the spool file or delete it etc. but this exercise simply being able to view the data was sufficient. The i5_spool_get_data() function can be used to write the data to a file in the IFS or provide the data back to the caller as a string. We decided to take the data as a string and format the string onto the page so we did not look at the format of the output generated in the IFS file. We also converted the line return characters so the output displayed correctly in the browser, but I am sure there are other character strings which would need converting in some of the application driven spool files out there such as link transations etc. Here is the code which retrieves the spool file data and displays it to the screen.


function dsp_splf_dta(&$conn,$name,$jobname,$jobnum,$user,$splnbr,$br) {

$order = array("\r\n", "\n", "\r");
$replace = '<br />';

// Processes \r\n's first so they aren't converted twice.

$str = i5_spool_get_data($name,$jobname,$user,$jobnum,$splnbr);
$newstr = str_replace($order, $replace, $str);
echo($newstr);
return 1;
}

We have cut down the size of the data shown just to keep the size of the images to a minimum, but the entire content of the spool file is displayed to the user. Here is a sample of the output using the above code.

Spoll File data

Spool File Data

And that is all there is to it, less than 30 lines of code and you have a solution which will display all of the spool files and allow their content to be displayed.
The documentation needs an update as the order of the parameters to pass for the i5_spool_get_data() is wrong, Aura will update the documentation soon so use the following as a guide for calling the function:

string i5_spool_get_data (string spool_name,
string jobname,
string username,
int job_number,
int spool_id[, string filename] )
note: the [, string filename] denotes that the parameter is optional, if you pass in a file name the file will be used for the output from the call.

if you would like to look closer at the PHP interface capabilities we are developing or have an idea on what it can do for your own applications let us know, we are still surprised at how quickly we can turn old 5250 interfaces into browser views with a minimal amount of code and no changes to the underlying business logic.

Chris…

Dec 15

Preparing for new System setup

We are busy installing new disks and a Raid control on our 8203-E4A in readiness for partitioning up the system. Out aim is to develop an environment we can use to demonstrate the products we sell and how the IBMi can be used in a virtualized setting.

The system will have 1TB of disk which is a big jump from our existing 280GB which is going to help in allowing us to carve out a number of virtual partitions. Unfortunately we did not get a new Power7 this year so we are going to be sticking with our Power6 for the immediate future, if IBM decides to play ball on the pricing that could change but not today.. Still that’s another discussion, we have also installed a new HMC (that is the first one we have ever had and I must say its turning out to be a good investment) and added raid to the disks. We felt that having all of our environments running on a single system needed a bit more resilience than the famous availability of the Power Systems, so Raid was an important investment.

Our plan is to have 3 partitions on the IBMi, the first partition will be our source HA system with another partition being allocated as the target. The last partition will be a Linux partition that will be running Suse Enterprise. The Suse partition is important to allow us to set up a Linux server to server all of the websites that we develop plus provide the web interfaces for our products running in the other partitions. Today we have a Linux standalone server and Windows server running the development websites which allows us to demonstrate our in-house products and tests, once the Suse partition is set up we will migrate the setups to it and tear down the other setups.

The system is currently striping the disks in readiness to add to the ASP before we carve it up. I am told it will take about 4 hours to complete so that gave me time to blog about the plans and share our experience so far.

Installing the new 5769 feature and drives was pretty easy, but once we installed everything and tried to start it up nothing worked! Having never used a HMC before I was struggling to work out what was happening and why things would not start up. The problem was the system could not find anything, the fact that we had installed a new Raid card meant that the Load-source was no longer where it should be! I had no idea until I called a very good friend (who is a real expert on the HMC and hardware setup) who kindly pushed me in the right direction. I am sure I will be going back to him when I need to carve up the DASD and CPU ect for the partitions!
Once he built a new profile and loaded it to the HMC everything started up OK and we could add the Raid card and start Raid 5 over the disks, he did say I should let the raid be built over the unallocated disks first (they are assigned to an ASP yet) so I was glad of his expertise. His other note of caution was to make sure I did not disconnect the console once the striping had started, point taken and being observed.

Once the stripping is finished I need to go in and set up the drives to the ASP and change a couple settings for the IPL so it starts up correctly, I had no idea I could power down the partition without the hardware so after installing the HMC I was surprised to find the hardware still running after issuing a PWRDWNSYS *IMMED command!

So that is where we are at, once I get to setting up the partitions and installing the OS’s I will add more posts in the hope it helps others who walk this path..

Chris…

Sep 16

5733SC1 Downloads, we are still getting lots of requests for downloads


If you are looking for the 5733SC1 downloads please contact IBM. We have removed the downloads for a number of reasons but mainly because IBM legal has an issue with anyone but IBM providing their software for download even if it is a free option.

My suggestion is you contact your IBM representative to get hold of the download links from the IBM website, the software is non chargeable but you need a maintenance agreement to have access to the IBM download area.

If you are stuck without maintenance and still want to run PHP with access to the IBMi I would suggest one of the following options.

Firstly you could install the XMLSERVICE which is available from the Yips Wiki. This should allow you to connect to the IBMi and use the functions available to access your data. We have not tested the DB2 access only the program calling (see the Blog entry) which was not as fast as we had been expecting considering all of the statements we had seen. There are a number of articles around that discuss the capabilities of the XMLSERVICE so we will not discuss them here.

If you would like to have a much more comprehensive solution you can opt for the EasyCom Server which is supplied by Aura Equipments of France. This is our preferred option because it allows us to run the PHP code on Windows and Linux access the IBMi via the EasyCom Server port. This is the very same EasyCom server provided by Zend that allows the i5_toolkit to function but it can be accessed from remote servers (except the Zend Version is usually way behind the Aura Equipments version). Not sure how long the i5_toolkit will be included in the Zend Server product but the word on the street is Zend are looking to replace it with the XMLSERVICE toolkit? I think this is a bad move on a number of fronts but for those who are looking for an open source option with access to the source code to allow updating by yourself this is a valid option. The i5_toolkit has a lot more capabilities than the XMLSERVICE toolkit to-date (its still only in BETA) and I am sure there are lots of sites out there now which are going to need to continue to use the EasyCom Server so not sure how Zend will overcome that. If you are interested in the EasyCom Server option gives us a call and we will be happy to supply you with the information you need.

If IBM does put up any additional links for 5733SC1 we will post them..

Chris…

Sep 07

New XMLService is not what I expected.

As posted in the previous post I was setting up the Zend Server on our V6R1 system to give the new XMLSERVICE a try to see if the program calls would be faster using it rather than the old i5_toolkit API’s. Unfortunately we had to abandon the V6R1 install due to a number of problems with the install of the XMLSERVICE and the NewToolkit install. After 5 hours of installing and configuring trying to get things running we were ready to give up.

However we decided to give it a go on the V7R1 system as it did seem to compile the RPG XMLSERVICE programs etc OK. We installed the latest download of the ZendServer but did not install the update Cum package. This seems to have resolved our initial problem with the XMLSERVICE requests which ended in error when calling the Db2supp.php script? But that might be a total co-incidence.

Once we had everything installed we connected all of our old web configurations back up and started the main webserver (we have 5 virtualhosts running below the main webserver) to begin testing the setup. As far as the main PHP services went everything seemed to be running OK so we decide to set up the XMLSERVICE.

First program we compiled CRTXML worked perfectly and the XMLSERVICE programs all created correctly. However as we wanted to use the ZENDSVR install we then tried to run the CRTXML2 program, this ended with an error creating the XMLSERVICE program due to some missing definitions. A quick compare of the 2 programs showed the CRTXML2 program did not create or embed the PLUDB2 or PLUGSQL modules in the service programs. We amended the program and re-created it and then everything worked as it should.

Next we had to link the NewToolkit directory to the web server we were going to use and add the relevant configs to make sure we could use the symbolic links we created to the directory.

The XMLSERVICE is started the first time it is called according to the documentation but to be honest its a bit sparse and not easily understood when this is the first time you go to use it. I am not an expert in the product but I did get it working so it can’t be that bad. So all we had to do was create a couple of scripts to test things out.

A program is shipped in the NewToolkit library which can be compiled and used for the tests. It is called ZZCALL and is an RPG program that takes a few parameters and just adds some values to them. I am not an RPG expert as I have said all along, but the program is pretty simple which is all we needed for the test.

The next thing we needed is the scripts which will call the program and capture time information as they run.

First program calls the RPG program sending in new parameters each time.


<?php
/*
RPG program parameters definition
INCHARA S 1a
INCHARB S 1a
INDEC1 S 7p 4
INDEC2 S 12p 2
INDS1 DS
DSCHARA 1a
DSCHARB 1a
DSDEC1 7p 4
DSDEC2 12p 2
*/
include_once 'authorization.php';
include_once '../API/ToolkitService.php';
include_once 'helpshow.php';

$loop = 5000;

$start_time = microtime();
echo "New Zend Toolkit. Input value is changing on each call.<br><br>";

try {
$ToolkitServiceObj = ToolkitService::getInstance($db, $user, $pass);
}
catch (Exception $e) {
echo $e->getMessage(), "\n";
exit();
}

$ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>"/tmp/$user"));

$IOParam['var1'] = array("in"=>"Y", "out"=>"" );
$param[] = $ToolkitServiceObj->AddParameterChar('both', 1,'INCHARA', 'var1', $IOParam['var1']['in']);

$IOParam['var2'] = array( "in"=>"Z", "out"=>"" );
$param[] = $ToolkitServiceObj->AddParameterChar('both', 1,'INCHARB', 'var2', $IOParam['var2']['in']);

$IOParam['var3'] = array( "in"=>"001.0001" ,"out"=>"");
$param[] = $ToolkitServiceObj->AddParameterPackDec('both', 7, 4, 'INDEC1', 'var3', '001.0001');

$IOParam['var4'] = array( "in"=>"0000000003.04","out"=>"" );
$param[] = $ToolkitServiceObj->AddParameterPackDec('both',12,2,'INDEC2', 'var4', '0000000003.04');

$IOParam['ds1'] = array( "in"=>"A" ,"out"=>"");
$ds[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'DSCHARA', 'ds1','A');

$IOParam['ds2'] = array( "in"=>"B" ,"out"=>"");
$ds[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'DSCHARB', 'ds2','B');

$IOParam['ds3'] = array( "in"=>"005.0007","out"=>"" );
$ds[] = $ToolkitServiceObj->AddParameterPackDec('both',7, 4, 'DSDEC1', 'ds3', '005.0007' );

$IOParam['ds4'] = array("in"=>"0000000006.08" ,"out"=>"");
$ds[] = $ToolkitServiceObj->AddParameterPackDec('both',12, 2, 'DSDEC1', 'ds4', '0000000006.08');

//$param[] = array('ds'=>$ds);
$param[] = $ToolkitServiceObj->AddDataStruct($ds);
$param[2]->setParamValue(0);
for ($i=0;$i<$loop;$i++) {
$result = $ToolkitServiceObj->PgmCall('ZZCALL', "ZENDSVR", $param, null, null);
$param[2]->setParamValue($result['io_param']['ds3']);
} // end loop

$end_time = microtime();
$wire_time= control_microtime_used($start_time,$end_time)*1000000;
echo
sprintf("<br><strong>Time (loop=$loop) total=%1.2f sec (%1.2f ms per call)</strong><br>",
round($wire_time/1000000,2),
round(($wire_time/$loop)/1000,2));

if($result){

/*update parameters array by return values */
foreach($IOParam as $key=> &$element){
$element['out'] = $result['io_param'][$key];
}

echo "<br>";
showTableWithHeader(array("Parameter name","Input value", "Output value"), $IOParam);
}
else
echo "Execution failed.";
/* Do not use the disconnect() function for "state full" connection */
$ToolkitServiceObj->disconnect();

function control_microtime_used($before,$after) {
return (substr($after,11)-substr($before,11))+(substr($after,0,9)-substr($before,0,9));
}

?>

Running this script resulted in the following results.

New Zend Toolkit. Input value is changing on each call.

Time (loop=5000) total=26.11 sec (5.22 ms per call)

Parameter name Input value Output value
var1 Y C
var2 Z D
var3 001.0001 321.1234
var4 0000000003.04 1234567890.12
ds1 A E
ds2 B F
ds3 005.0007 333.3330
ds4 0000000006.08 4444444444.44

The next test we ran would just call the program without changing the parameters to see what effect it had.


<?php
/*
RPG program parameters definition
INCHARA S 1a
INCHARB S 1a
INDEC1 S 7p 4
INDEC2 S 12p 2
INDS1 DS
DSCHARA 1a
DSCHARB 1a
DSDEC1 7p 4
DSDEC2 12p 2
*/
include_once 'authorization.php';
include_once '../API/ToolkitService.php';
include_once 'helpshow.php';

$loop = 5000;

$start_time = microtime();
echo "New Zend Toolkit. Input values never change on each call.<br><br>";

try {
$ToolkitServiceObj = ToolkitService::getInstance($db, $user, $pass);
}
catch (Exception $e) {
echo $e->getMessage(), "\n";
exit();
}

$ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>"/tmp/$user"));

$IOParam['var1'] = array("in"=>"Y", "out"=>"" );
$param[] = $ToolkitServiceObj->AddParameterChar('both', 1,'INCHARA', 'var1', $IOParam['var1']['in']);

$IOParam['var2'] = array( "in"=>"Z", "out"=>"" );
$param[] = $ToolkitServiceObj->AddParameterChar('both', 1,'INCHARB', 'var2', $IOParam['var2']['in']);

$IOParam['var3'] = array( "in"=>"001.0001" ,"out"=>"");
$param[] = $ToolkitServiceObj->AddParameterPackDec('both', 7, 4, 'INDEC1', 'var3', '001.0001');

$IOParam['var4'] = array( "in"=>"0000000003.04","out"=>"" );
$param[] = $ToolkitServiceObj->AddParameterPackDec('both',12,2,'INDEC2', 'var4', '0000000003.04');

$IOParam['ds1'] = array( "in"=>"A" ,"out"=>"");
$ds[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'DSCHARA', 'ds1','A');

$IOParam['ds2'] = array( "in"=>"B" ,"out"=>"");
$ds[] = $ToolkitServiceObj->AddParameterChar('both', 1, 'DSCHARB', 'ds2','B');

$IOParam['ds3'] = array( "in"=>"005.0007","out"=>"" );
$ds[] = $ToolkitServiceObj->AddParameterPackDec('both',7, 4, 'DSDEC1', 'ds3', '005.0007' );

$IOParam['ds4'] = array("in"=>"0000000006.08" ,"out"=>"");
$ds[] = $ToolkitServiceObj->AddParameterPackDec('both',12, 2, 'DSDEC1', 'ds4', '0000000006.08');

//$param[] = array('ds'=>$ds);
$param[] = $ToolkitServiceObj->AddDataStruct($ds);
$param[2]->setParamValue(0);
for ($i=0;$i<$loop;$i++) {
$result = $ToolkitServiceObj->PgmCall('ZZCALL', "ZENDSVR", $param, null, null);
//$param[2]->setParamValue($result['io_param']['ds3']);
} // end loop

$end_time = microtime();
$wire_time= control_microtime_used($start_time,$end_time)*1000000;
echo
sprintf("<br><strong>Time (loop=$loop) total=%1.2f sec (%1.2f ms per call)</strong><br>",
round($wire_time/1000000,2),
round(($wire_time/$loop)/1000,2));

if($result){

/*update parameters array by return values */
foreach($IOParam as $key=> &$element){
$element['out'] = $result['io_param'][$key];
}

echo "<br>";
showTableWithHeader(array("Parameter name","Input value", "Output value"), $IOParam);
}
else
echo "Execution failed.";
/* Do not use the disconnect() function for "state full" connection */
$ToolkitServiceObj->disconnect();

function control_microtime_used($before,$after) {
return (substr($after,11)-substr($before,11))+(substr($after,0,9)-substr($before,0,9));
}

?>

This resulted in the following output.

New Zend Toolkit. Input values never change on each call.

Time (loop=5000) total=26.95 sec (5.39 ms per call)

Parameter name Input value Output value
var1 Y C
var2 Z D
var3 001.0001 321.1234
var4 0000000003.04 1234567890.12
ds1 A E
ds2 B F
ds3 005.0007 333.3330
ds4 0000000006.08 4444444444.44

After this we ran the same request using the old i5_toolkit option using the following code.


<?php
//require_once('connection.inc');

$loop = 5000;

$start_time = microtime();
echo "Original Easycom Toolkit.<br><br>";

$conn = i5_connect( "", "", "");
if (!$conn)
{ $tab = i5_error();
die("Connect: ".$tab[2]." "."$tab[3], $tab[0]");
}

/* prepare */
$description =
array
(
// single parms
array
( "Name"=>"INCHARA","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
array
( "Name"=>"INCHARB","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
array
( "Name"=>"INDEC1","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"7.4"),
array
( "Name"=>"INDEC2","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"12.2"),
// structure parm
array
( "DSName"=>"INDS1",
"Count"=>1,
"DSParm"=>
array
(
array
( "Name"=>"DSCHARA","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
array
( "Name"=>"DSCHARB","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
array
( "Name"=>"DSDEC1","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"7.4"),
array
( "Name"=>"DSDEC2","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"12.2"),
)
)
);
$pgm = i5_program_prepare("ZENDSVR/ZZCALL", $description);
if (!$pgm)
{ $tab = i5_error();
die("Prepare: ".$tab[2]." "."$tab[3], $tab[0]");
}

// *** parameter list allocation
$list=
array
(
"DSCHARA"=>"x",
"DSCHARB"=>"y",
"DSDEC1"=>66.6666,
"DSDEC2"=>77777.77,
);
// *** parameter values passed to procedure
$in =
array
(
"INCHARA"=>"a",
"INCHARB"=>"b",
"INDEC1"=>0,
"INDEC2"=>222.22,
"INDS1"=>$list,
);
// *** name of variables created for out parameters
$out =
array
(
"INCHARA"=>"INCHARA",
"INCHARB"=>"INCHARB",
"INDEC1"=>"INDEC1",
"INDEC2"=>"INDEC2",
"INDS1"=>"OUTDS1",
);

for ($i=0;$i<$loop;$i++) {
$rc=i5_program_call($pgm, $in, $out);
if ($rc != false)
{
}
else
{ $tab = i5_error();
die("Call: ".$tab[2]." "."$tab[3], $tab[0]");
}
$in['INDEC1']=$OUTDS1['DSDEC1'];
} // end loop

$end_time = microtime();
$wire_time= control_microtime_used($start_time,$end_time)*1000000;
echo
sprintf("<br><strong>Time (loop=$loop) total=%1.2f sec (%1.2f ms per call)</strong><br>",
round($wire_time/1000000,2),
round(($wire_time/$loop)/1000,2));

echo "<br>";
echo $INCHARA."<br>";
echo $INCHARB."<br>";
echo $INDEC1."<br>";
echo $INDEC2."<br>";
echo $OUTDS1['DSDEC1'].'<br>';
//var_dump($INDS1);

/* close */
/*flush();
set_time_limit(60);
for(;;);
*/

$rc = i5_close($conn);

function control_microtime_used($before,$after) {
return (substr($after,11)-substr($before,11))+(substr($after,0,9)-substr($before,0,9));
}

This resulted in the following output.

Original Easycom Toolkit.

Time (loop=5000) total=0.66 sec (0.13 ms per call)

C
D
321.1234
1234567890.12
333.333

Next we looked at the new XML request using the new i5_toolkit, this is available for download from the Aura website and will install the required objects over an existing ZendCore or ZendServer install. This is good for those developers who wanted an XML type interface for program calls especially as Zend regularly said it was something users wanted as they found the old i5_toolkit method difficult to understand.

This is the code it ran.


<?php
//require_once('connection.inc');

$loop = 5000; //50000;

$start_time = microtime();
echo "Xml Easycom, using Associative Arrays Input/Output.<br><br>";

$conn = i5_connect( "", "", "");
if (!$conn)
{ $tab = i5_error();
die("Connect: ".$tab[2]." "."$tab[3], $tab[0]");
}

/* prepare */

$SRPG="DS1 DS;
DSCHARA 1a;
DSCHARB 1a;
DSDEC1 7p4;
DSDEC2 12p2;

ZZCALL PR extpgm(ZENDSVR/ZZCALL);
INCHARA 1a;
INCHARB 1a;
INDEC1 7p4;
INDEC2 12p2;
INDS1 likeds(DS1);
";
i5_XmlDefine ("s-rpg", $SRPG);

// *** parameter list allocation
$list=array(
"DSCHARA"=>"x",
"DSCHARB"=>"y",
"DSDEC1"=>66.6666,
"DSDEC2"=>77777.77,
);
$ArrayIn["INCHARA"] = "a";
$ArrayIn["INCHARB"] = "b";
$ArrayIn["INDEC1"] = 0;
$ArrayIn["INDEC2"] = 222.22;
$ArrayIn["INDS1"] = $list;

for ($i=0;$i<$loop;$i++) {
$ArrayIn["INDEC1"] = $i/1000;
$ArrayOut = i5_XmlCallProgram ("ZZCALL", $ArrayIn);
$ArrayIn["INDEC1"] = $ArrayOut['INDS1']['DSDEC1'];
} // end loop

$end_time = microtime();
$wire_time= control_microtime_used($start_time,$end_time)*1000000;
echo
sprintf("<br><strong>Time (loop=$loop) total=%1.2f sec (%1.2f ms per call)</strong><br>",
round($wire_time/1000000,2),
round(($wire_time/$loop)/1000,2));

echo '<UL><LI><PRE>';
print_r($ArrayOut);
echo '</PRE></LI></UL>';

/* close */
/*flush();
set_time_limit(60);
for(;;);
*/

$rc = i5_close($conn);

function control_microtime_used($before,$after) {
return (substr($after,11)-substr($before,11))+(substr($after,0,9)-substr($before,0,9));
}

This is the output.

Xml Easycom, using Associative Arrays Input/Output.

Time (loop=5000) total=4.27 sec (0.85 ms per call)

Array
(
[INCHARA] => C
[INCHARB] => D
[INDEC1] => 321.1234
[INDEC2] => 1234567890.12
[INDS1] => Array
(
[DSCHARA] => E
[DSCHARB] => F
[DSDEC1] => 333.333
[DSDEC2] => 4444444444.44
)

)

Finally we decide to look at the performance hit using our preferred installation which is Easycom Server running on the IBMi with Apache and PHP running on a PC or Linux server. The script run on the Linux/PC box with the program being run on the IBMi. The ode we ran is eactly the same code which ran for the test on the IBMi.

here is the output.

Original Easycom Toolkit.

Time (loop=5000) total=1.23 sec (0.25 ms per call)

C
D
321.1234
1234567890.12
333.333

So it looks like the claims against the Easycom i5_toolkit and how the new toolkit is much faster do not stand up in this situation? Is this representative? Maybe Maybe not.. As you can see from the above the old i5_toolkit is around 5 times faster (actually over 40 see the comment) than the new XMLSERVICE using the test programs, the new XML request provided in the new i5_toolkit is also slower than it predecessor but no way near as slow as the new XMLSERVICE, even running the old i5_toolkit requests on a PC and adding the delay for communication is still faster than the new IBM/Zend toolkit.

So we have done what we set out to do and looked at a simple test to see if the new XMLSERVICE stands up to its marketing, our opinion is it is not as fast based on our very simple tests. If you have different results share them with us.

If you would like to understand more about our PHP experiences let us know.

Chris…

Feb 22

HA4i PHP interfaces gains more functionality


We have just completed writing the code to bring the spool file replication process to the HA4i PHP interface. As with our other screens we have also added the ability to retry failed requests from the screens plus start and stop the process.

Here is the new main monitoring screen which shows the overall status of all of the replication processes.

Main Monitor with Spool status

Spool status in Main Monitor screen

The main screen shows that we have 2 errors in the replication process, clicking in the field will bring up the following screen which displays all of the details about the replication error.

Spool File error listing

List of spool file replication errors

We can also see from the main monitoring screen that there are 4 entries still waiting to be processed (we held the replication queue to allow us to capture this), clicking in the field will bring up the following display which provides the details about the spool files which are still to be processed.

spoolfile_waiting

Spool Files waiting replication to target

We continue to improve the interface and use different techniques to retrieve the data from the IBM i which in turn improves our understanding and the power of the i5_toolkit functions provided by the EasyCom server. Some of the data is pulled back using C programs on the IBM i which interface with the i5_toolkit functions using Service Program connections while others retrieve the data directly using SQL and Native File Access within the i5_toolkit. The main purpose of the interface is still about systems management and monitoring the replication process, we are considering how we can implement a configuration tool based around the PHP i5_toolkit functions with a number of our own C Service Programs. High Availability does not need to be hard, we think this interface helps ease the burden on system administrators while providing accurate and up to date information.

if you would like to see a demo of the product or discuss the implementation we have used for providing the data to the browser let us know, we will be more than willing to share..

Chris…