All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.ibm.as400.access.MemberList Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////
//                                                                             
// JTOpen (IBM Toolbox for Java - OSS version)                              
//                                                                             
// Filename: MemberList.java
//                                                                             
// The source code contained herein is licensed under the IBM Public License   
// Version 1.0, which has been approved by the Open Source Initiative.         
// Copyright (C) 2009-2010 International Business Machines Corporation and     
// others. All rights reserved.                                                
//                                                                             
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.access;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Represents a list of database file members.
 * 

* Implementation note: This class internally calls the "List Database File Members" API (QUSLMBR). * Information from formats MBRL0100 and MBRL0200 is retrieved. * *

* This class is mostly based on a prototype contributed by Mihael Schmidt. * * @see MemberDescription * @see AS400File */ public class MemberList { private static final SimpleDateFormat dateTimeFormat_ = new SimpleDateFormat("yyMMddHHmmss"); private static final String QUSLMBR_FORMAT_100 = "MBRL0100"; private static final String QUSLMBR_FORMAT_200 = "MBRL0200"; private static final QSYSObjectPathName USERSPACE_PATH = new QSYSObjectPathName("QTEMP", "JT4QUSLMBR", "USRSPC"); // user space QTEMP/JT4QUSLMBR private AS400 system_; private QSYSObjectPathName path_; private String memberSelection_ = "*ALL"; // default: all members private final Map memberDescriptions_ = new HashMap(); private final List attributes_ = new ArrayList(); // Offsets for format MBRL0200, "List Data Section", of API QUSLMBR. private static final int OFFSET_MEMBER_NAME = 0; private static final int OFFSET_SOURCE_TYPE = 10; private static final int OFFSET_CREATION_DATE_TIME = 20; private static final int OFFSET_LAST_SOURCE_CHANGE_DATE = 33; private static final int OFFSET_MEMBER_TEXT_DESCRIPTION = 46; private static final int OFFSET_MEMBER_TEXT_DESCRIPTION_CCSID = 96; private final AS400Bin4 intConverter_ = new AS400Bin4(); /** * Constructs a MemberList object. * * @param file A database file. */ public MemberList(AS400File file) { if (file == null) throw new NullPointerException("file"); system_ = file.getSystem(); path_ = new QSYSObjectPathName(file.getLibraryName(), file.getFileName(), "FILE"); String memberName = file.getMemberName(); if (memberName == null || memberName.length() == 0) { // default to *ALL } else if (memberName.startsWith("*")) // *FIRST, *LAST, etc. { // The "member name" parameter of the QUSLMBR API must be either: // - a specific member name; // - a generic member name (a wildcarded name pattern); or // - special value *ALL, indicating "all members". memberSelection_ = "*ALL"; // force it to *ALL if (Trace.traceOn_) { Trace.log(Trace.DIAGNOSTIC, "Setting member selection to *ALL. Member name from AS400File object:", memberName); } } else { memberSelection_ = memberName; } } /** * Constructs a MemberList object. * * Note: Generic names are only supported for the 'member' part of the IFS path. * To retrieve all members, the IFS path should point to the database file. * * @param system AS400 system object. * @param path IFS path to the database file or member. */ public MemberList(AS400 system, QSYSObjectPathName path) { if (system == null) throw new NullPointerException("system"); if (path == null) throw new NullPointerException("path"); system_ = system; path_ = path; String memberName = path_.getMemberName(); if (memberName == null || memberName.length() == 0) { memberSelection_ = "*ALL"; if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Setting member selection to *ALL. Member name from QSYSObjectPathName object is: " + memberName); } else { memberSelection_ = memberName; } } /** * Constructs a MemberList object. * * @param system AS400 system object * @param libraryName Library where the physical file is located. * @param objectName The name of the physical file. */ public MemberList(AS400 system, String libraryName, String objectName) { this(system, new QSYSObjectPathName(libraryName, objectName, "FILE")); } /** * Determine the format to use on the API call, depending on the attribute. * * @param attribute Attribute to be retrieved * @return format string */ private String lookupFormat(int attribute) { String format; switch (attribute) { case MemberDescription.MEMBER_NAME: format = QUSLMBR_FORMAT_100; break; case MemberDescription.SOURCE_TYPE: case MemberDescription.CREATION_DATE_TIME: case MemberDescription.LAST_SOURCE_CHANGE_DATE: case MemberDescription.MEMBER_TEXT_DESCRIPTION: case MemberDescription.MEMBER_TEXT_DESCRIPTION_CCSID: format = QUSLMBR_FORMAT_200; break; default: if (Trace.traceOn_) Trace.log(Trace.WARNING, "Unrecognized attribute key:", attribute); format = QUSLMBR_FORMAT_200; } return format; } /** * Removes all member descriptions from this object. */ public void clear() { memberDescriptions_.clear(); } /** * Clears the attribute list which specifies which attributes should be retrieved. */ public void clearAttributeList() { attributes_.clear(); } /** * Adds an attribute to the attribute list which specifies which attributes of the member * are to be retrieved. Constants that specify attributes are available in class {@link MemberDescription MemberDescription}. * * @param attribute The attribute to be added. */ public void addAttribute(int attribute) { attributes_.add(new Integer(attribute)); } /** * Reloads all member descriptions that have been specified via {@link #addAttribute addAttribute()}. * * @throws ObjectDoesNotExistException If a system object necessary for the call does not exist on the system. * @throws InterruptedException If this thread is interrupted. * @throws IOException If an error occurs while communicating with the system. * @throws ErrorCompletingRequestException If an error occurs before the request is completed. * @throws AS400SecurityException If a security or authority error occurs. */ public void refresh() throws AS400SecurityException, ErrorCompletingRequestException, InterruptedException, IOException, ObjectDoesNotExistException { clear(); load(); } /** * Loads all members from the specified file(s). * * @throws ObjectDoesNotExistException If an object necessary for the call does not exist on the system. * @throws InterruptedException If this thread is interrupted. * @throws IOException If an error occurs while communicating with the system. * @throws ErrorCompletingRequestException If an error occurs before the request is completed. * @throws AS400SecurityException If a security or authority error occurs. * @throws AS400Exception If the program on the server sends an escape message. */ public void load() throws AS400Exception, AS400SecurityException, ErrorCompletingRequestException, IOException, InterruptedException, ObjectDoesNotExistException { ProgramCall program = new ProgramCall(system_, "/QSYS.LIB/QUSLMBR.PGM", buildProgramParameters(getFormat())); // this API is not threadsafe // Determine the needed scope of synchronization. Object lockObject; boolean willRunProgramsOnThread = program.isStayOnThread(); if (willRunProgramsOnThread) { // The calls will run in the job of the JVM, so lock for entire JVM. lockObject = USERSPACE_PATH; } else { // The calls will run in the job of the Remote Command Host Server, so lock on the connection. lockObject = system_; } synchronized (lockObject) { // Create a user space in QTEMP to receive output from program call. UserSpace us = new UserSpace(system_, USERSPACE_PATH.getPath()); us.setMustUseProgramCall(true); if (!willRunProgramsOnThread) { us.setMustUseSockets(true); // Force the use of sockets when running natively but not on-thread. // We have to do it this way since UserSpace will otherwise make a native ProgramCall, and will use a different QTEMP library than that used by the host server. } try { us.create(65535, true, "JT400", (byte) 0x00, "Userspace for loading members","*ALL"); // set public authority to *ALL if (!program.run()) { throw new AS400Exception(program.getMessageList()); } byte[] usBuf = new byte[65535]; // local buffer to hold bytes read from user space // Read the "generic header" from the user space, into the local buffer. // Note: For description of general layout of the "list API" headers, see: // http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/topic/apiref/listGeneral.htm int numBytesRead = us.read(usBuf, 0, 0, 0x90); // just read the needed header fields if (numBytesRead < 0x90) // verify that we at least got a header, up through "CCSID" field { Trace.log(Trace.ERROR, "Failed to read the generic header. Number of bytes read: " + numBytesRead); throw new InternalErrorException(InternalErrorException.UNKNOWN, numBytesRead); } // Parse the header, to get the offsets to the various sections. // (Generic header) Offset to header section: int offsetToHeaderSection = BinaryConverter.byteArrayToInt(usBuf, 0x74); // (Generic header) Header section size: int headerSectionSize = BinaryConverter.byteArrayToInt(usBuf, 0x78); // (Generic header) Offset to list data section: int offsetToListDataSection = BinaryConverter.byteArrayToInt(usBuf, 0x7C); // (Generic header) List data section size: int listDataSectionSize = BinaryConverter.byteArrayToInt(usBuf, 0x80); // (Generic header) Number of list entries: int numberOfListEntries = BinaryConverter.byteArrayToInt(usBuf, 0x84); // (Generic header) Size of each entry: int sizeOfEachEntry = BinaryConverter.byteArrayToInt(usBuf, 0x88); // (Generic header) CCSID of data in the user space int entryCCSID = BinaryConverter.byteArrayToInt(usBuf, 0x8C); // (Generic header) Subsetted list indicator: //String subsettedListIndicator = conv.byteArrayToString(usBuf, 0x95, 1); if (entryCCSID == 0) entryCCSID = system_.getCcsid(); // From the API spec: "The coded character set ID for data in the list entries. If 0, then the data is not associated with a specific CCSID and should be treated as hexadecimal data." final CharConverter conv = new CharConverter(entryCCSID); // Read the "header section" into the local buffer. numBytesRead = us.read(usBuf, offsetToHeaderSection, 0, headerSectionSize); if (numBytesRead < headerSectionSize) { Trace.log(Trace.ERROR, "Failed to read the header section. Number of bytes read: " + numBytesRead); throw new InternalErrorException(InternalErrorException.UNKNOWN, numBytesRead); } // (Header section) File library name used: final String libraryNameUsed = conv.byteArrayToString(usBuf, 10, 10).trim(); // Read the "list data section" into the local buffer. if (listDataSectionSize > usBuf.length) { usBuf = new byte[listDataSectionSize+1]; // allocate a larger buffer } numBytesRead = us.read(usBuf, offsetToListDataSection, 0, listDataSectionSize); if (numBytesRead < listDataSectionSize) { Trace.log(Trace.ERROR, "Failed to read the list data section. Number of bytes read: " + numBytesRead); throw new InternalErrorException(InternalErrorException.UNKNOWN, numBytesRead); } // Parse the list data returned in the user space. String format = getFormat(); for (int i = 0; i < numberOfListEntries; i++) { byte[] entryBuf = new byte[sizeOfEachEntry]; System.arraycopy(usBuf, i * sizeOfEachEntry, entryBuf, 0, sizeOfEachEntry); readMemberInfoFromUserspaceEntry(entryBuf, format, conv, libraryNameUsed); } } finally { // Delete the temporary user space, to allow other threads to re-create and use it. try { us.delete(); } catch (Exception e) { Trace.log(Trace.ERROR, "Exception while deleting temporary user space", e); } } } } /** * Builds the program parameter list for a program call to QUSLMBR (List Database File Members). * * @return Program parameter list for QUSLMBR */ private ProgramParameter[] buildProgramParameters(String format) throws UnsupportedEncodingException { ProgramParameter[] parameterList = new ProgramParameter[6]; // Qualified user space name: parameterList[0] = new ProgramParameter(CharConverter.stringToByteArray(system_, USERSPACE_PATH.toQualifiedObjectName())); // Format name: parameterList[1] = new ProgramParameter(CharConverter.stringToByteArray(system_, format)); // Qualified database file name: parameterList[2] = new ProgramParameter(CharConverter.stringToByteArray(system_, path_.toQualifiedObjectName())); // Member name: parameterList[3] = new ProgramParameter(new AS400Text(10).toBytes(memberSelection_)); // Override processing: (1 == "overrides are processed") parameterList[4] = new ProgramParameter(CharConverter.stringToByteArray(system_, "1")); // Error code: parameterList[5] = new ErrorCodeParameter(); return parameterList; } /** * Reads all available information from the userspace entry regardless of the attributes to * be retrieved. The attributes to be retrieved determines in which format the entry is in the * userspace. * * @param entryBuf Raw bytes of the entry * @param format Content and format name (f. e. MBRL0100, see {@link #getFormat() getFormat()} * @param charConverter CharConverter object used for translating bytes to String * @param usedLibraryName The resolved library name used for the API call. * */ private void readMemberInfoFromUserspaceEntry(byte[] entryBuf, String format, CharConverter charConverter, String usedLibraryName) { String memberName = charConverter.byteArrayToString(entryBuf, OFFSET_MEMBER_NAME, 10).trim(); // get existing member description or create a new one MemberDescription memberDescription = (MemberDescription) memberDescriptions_.get(memberName); if (memberDescription == null) { QSYSObjectPathName memberPath = new QSYSObjectPathName(path_.getPath()); try { memberPath.setMemberName(memberName); memberPath.setLibraryName(usedLibraryName); } catch (java.beans.PropertyVetoException pve) { // will never happen Trace.log(Trace.ERROR, "Error ignored.", pve); } memberDescription = new MemberDescription(system_, memberPath); memberDescription.setAttribute(MemberDescription.MEMBER_NAME, memberName); memberDescriptions_.put(memberName, memberDescription); } if (QUSLMBR_FORMAT_200.equals(format) ) { memberDescription.setAttribute(MemberDescription.SOURCE_TYPE, charConverter.byteArrayToString(entryBuf, OFFSET_SOURCE_TYPE, 10).trim()); memberDescription.setAttribute(MemberDescription.MEMBER_TEXT_DESCRIPTION, charConverter.byteArrayToString(entryBuf, OFFSET_MEMBER_TEXT_DESCRIPTION, 50).trim()); memberDescription.setAttribute(MemberDescription.MEMBER_TEXT_DESCRIPTION_CCSID, new Integer(intConverter_.toInt(entryBuf, OFFSET_MEMBER_TEXT_DESCRIPTION_CCSID))); memberDescription.setAttribute(MemberDescription.LAST_SOURCE_CHANGE_DATE, transformDate(charConverter.byteArrayToString(entryBuf, OFFSET_LAST_SOURCE_CHANGE_DATE, 13))); memberDescription.setAttribute(MemberDescription.CREATION_DATE_TIME, transformDate(charConverter.byteArrayToString(entryBuf, OFFSET_CREATION_DATE_TIME, 13))); } } /** * Returns an array of retrieved member descriptions. If no member descriptions could be * retrieved because there are no members or because of an error, an empty array is returned. * If no members has been retrieved yet due to no call to load(), then an empty array is * returned. * * @return Array of retrieved member descriptions */ public MemberDescription[] getMemberDescriptions() { return (MemberDescription[]) memberDescriptions_.values().toArray(new MemberDescription[memberDescriptions_.size()]); } /** * Returns the format for the call to QUSLMBR depending on the attributes to be retrieved. * * @return format name (either MBRL0100 or MBRL0200 for now) */ private String getFormat() { String format = QUSLMBR_FORMAT_100; for (Iterator iter = attributes_.iterator(); iter.hasNext(); ) { String tmpFormat = lookupFormat(((Integer) iter.next()).intValue()); if (format.compareTo(tmpFormat) < 0) { format = tmpFormat; } } return format; } /** * Parses the date string, which should be in the format CYYMMDDHHMMSS. * * @param dateString Date string * * @return Date object representing the passed date string, or null if the * date string is null, empty or not parseable. */ private Date transformDate(String dateString) { Date retVal = null; if (dateString == null || dateString.length() == 0) { // nothing => returns null } else { try { synchronized (dateTimeFormat_) // date formats are not synchronized { retVal = dateTimeFormat_.parse(dateString.substring(1)); } } catch(ParseException pe) { // return null if (Trace.traceOn_) Trace.log(Trace.ERROR, "Ignored error while parsing date string: " + dateString, pe); } } return retVal; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy