diff --git a/src/main/java/com/ibm/as400/access/list/AspOpenList.java b/src/main/java/com/ibm/as400/access/list/AspOpenList.java index 609fc7130..b8ddfbed4 100644 --- a/src/main/java/com/ibm/as400/access/list/AspOpenList.java +++ b/src/main/java/com/ibm/as400/access/list/AspOpenList.java @@ -425,9 +425,10 @@ protected Object[] formatOutputData(byte[] data, int recordsReturned, int record CharConverter conv = new CharConverter(system_.getCcsid(), system_); AspListItem[] aspList = new AspListItem[recordsReturned]; - int offset = 0; + int recordOffset = 0; for (int i = 0; i < recordsReturned; ++i) { + int offset = recordOffset; int aspNumber = BinaryConverter.byteArrayToInt(data, offset); offset += 4; if (format_ == 1) { @@ -622,6 +623,7 @@ protected Object[] formatOutputData(byte[] data, int recordsReturned, int record aspList[i] = new AspListItem( aspNumber, useIdentification, jobName, jobUserName, jobNumber, threadIdentifier, threadStatus); } + recordOffset += recordLength; } return aspList; diff --git a/src/main/java/com/ibm/as400/access/list/JobQueueListFilter.java b/src/main/java/com/ibm/as400/access/list/JobQueueListFilter.java new file mode 100644 index 000000000..d46f81ea6 --- /dev/null +++ b/src/main/java/com/ibm/as400/access/list/JobQueueListFilter.java @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// JTOpen (IBM Toolbox for Java - OSS version) +// +// Filename: JobQueueListFilter.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) 2018-2019 International Business Machines Corporation and +// others. All rights reserved. +// +/////////////////////////////////////////////////////////////////////////////// + +package com.ibm.as400.access.list; + +public class JobQueueListFilter { + + public static final String ALL = "*ALL"; + public static final String JOB_QUEUE_ALLOCATED = "*ALLOCATED"; + public static final String JOB_QUEUE_DEFINED = "*DEFINED"; + public static final String JOB_QUEUE_LIBRARY_ALL_USER = "*ALLUSR"; + public static final String JOB_QUEUE_LIBRARY_CURRENT_LIBRARY = "*CURLIB"; + public static final String JOB_QUEUE_LIBRARY_LIBRARY_LIST = "*LIBL"; + public static final String JOB_QUEUE_LIBRARY_USER_LIBRARY = "*USRLIBL"; + + public static final JobQueueListFilter DEFAULT = new JobQueueListFilter(ALL, ALL, null); + + private String activeSubsystemName; + private String jobQueueName; + private String jobQueueLibraryName; + + public JobQueueListFilter(String jobQueueName, String jobQueueLibraryName, String activeSubsystemName) { + this.activeSubsystemName = activeSubsystemName; + this.jobQueueLibraryName = jobQueueLibraryName; + this.jobQueueName = jobQueueName; + } + + public static JobQueueListFilter forAllAllocated() { + return new JobQueueListFilter(JOB_QUEUE_ALLOCATED, null, ALL); + } + public static JobQueueListFilter forSubsystem(String subsystemName, boolean allocatedOnly) { + return new JobQueueListFilter(allocatedOnly ? JOB_QUEUE_ALLOCATED : JOB_QUEUE_DEFINED, null, subsystemName); + } + public static JobQueueListFilter forJobQueue(String jobQueueName) { + return forJobQueue(jobQueueName, ALL); + } + public static JobQueueListFilter forJobQueue(String jobQueueName, String jobQueueLibraryName) { + return new JobQueueListFilter(jobQueueName, jobQueueLibraryName, null); + } + + public String getActiveSubsystemName() { + return activeSubsystemName; + } + public String getJobQueueName() { + return jobQueueName; + } + public String getJobQueueLibraryName() { + return jobQueueLibraryName; + } +} diff --git a/src/main/java/com/ibm/as400/access/list/JobQueueListItem.java b/src/main/java/com/ibm/as400/access/list/JobQueueListItem.java new file mode 100644 index 000000000..9c6490f05 --- /dev/null +++ b/src/main/java/com/ibm/as400/access/list/JobQueueListItem.java @@ -0,0 +1,191 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// JTOpen (IBM Toolbox for Java - OSS version) +// +// Filename: JobQueueListItem.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) 2018-2019 International Business Machines Corporation and +// others. All rights reserved. +// +/////////////////////////////////////////////////////////////////////////////// + +package com.ibm.as400.access.list; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Information about a Job Queue generated by {@link com.ibm.as400.access.list.JobQueueOpenList JobQueueOpenList}. + */ +public class JobQueueListItem { + public enum JobQueueStatus { + /** + * The job queue is currently held. No jobs can become active from this job queue. + */ + HELD("0"), + /** + * The job queue is released. Jobs can become active from this queue. + */ + RELEASED("1"), + /** + * The job queue is damaged. + */ + DAMAGED("2"), + /** + * The job queue is defined to the active subsystem, but has not been created. + * No jobs can become active from this job queue until it is created. + */ + DEFINED("3"); + + private final String systemValue; + + //java8//private static Map valToEnum = Arrays.stream(JobQueueStatus.values()).collect(Collectors.toMap(JobQueueStatus::getSystemValue, Function.identity())); + private static Map valToEnum = createMap(); + + JobQueueStatus(String sysVal) { + this.systemValue = sysVal; + } + + private static Map createMap() { + Map map = new HashMap(); + for (JobQueueStatus item : JobQueueStatus.values()) { + map.put(item.getSystemValue(), item); + } + return Collections.unmodifiableMap(map); + } + + public String getSystemValue() { + return systemValue; + } + + public static JobQueueStatus fromSystemValue(String systemValue) { + return valToEnum.get(systemValue); + } + } + + /*package*/ String jobQueueName; + /*package*/ String jobQueueLibrary; + /*package*/ String jobQueueDescription; + /*package*/ String subsystemName; + /*package*/ String subsystemLibrary; + /*package*/ int numberOfJobsWaitingToRun; + /*package*/ int numberOfJobsRunning; + /*package*/ int maximumActiveJobs; + /*package*/ JobQueueStatus jobQueueStatus; + /*package*/ int jobQueueSequence; + /*package*/ String aspName; + + /*package*/ JobQueueListItem() { + } + + /** + * The name of the job queue. + * @return The name of the job queue. + */ + public String getJobQueueName() { + return jobQueueName; + } + + /** + * The name of the library in which the job queue is located. + * @return The name of the library in which the job queue is located. + */ + public String getJobQueueLibrary() { + return jobQueueLibrary; + } + + /** + * The text description for this job queue. + * This field will be blank if the job queue is defined to an active subsystem, + * but has not been created or the job queue is damaged. + * @return The text description for this job queue. + */ + public String getJobQueueDescription() { + return jobQueueDescription; + } + + /** + * The name of the subsystem to which this job queue is allocated. + * If the job queue has been allocated by a different subsystem than was specified in the filter parameter, + * the subsystem name will identify the subsystem to which the job queue is allocated. + * This field is blank if the job queue is not allocated, is damaged, or does not exist. + * @return The name of the subsystem to which this job queue is allocated. + */ + public String getSubsystemName() { + return subsystemName; + } + + /** + * The library in which the subsystem description resides. + * This field will be blank if the job queue is not allocated, damaged or does not exist. + * @return The library in which the subsystem description resides. + */ + public String getSubsystemLibrary() { + return subsystemLibrary; + } + + /** + * The total number of jobs currently waiting to run on this job queue. + * This field is -1 if the job queue is defined to the active subsystem, but has not been created or the job queue is damaged. + * @return The total number of jobs currently waiting to run on this job queue. + */ + public int getNumberOfJobsWaitingToRun() { + return numberOfJobsWaitingToRun; + } + + /** + * The number of jobs currently running in the active subsystem from this job queue. + * This field is -1 if the job queue is not allocated, is damaged, does not exist, or the job queue has not + * been allocated by the subsystem that was specified in the active subsystem field in the filter parameter. + * @return The number of jobs currently running in the active subsystem from this job queue. + */ + public int getNumberOfJobsRunning() { + return numberOfJobsRunning; + } + + /** + * The maximum number of jobs that can be active in the subsystem from this job queue at one time. + * A -1 in this field indicates that the value is *NOMAX. This field is -2 if the job queue has not been + * defined to an active subsystem or the job queue is damaged. + * @return The maximum number of jobs that can be active in the subsystem from this job queue at one time. + */ + public int getMaximumActiveJobs() { + return maximumActiveJobs; + } + + /** + * The current status of the job queue. + * @return The current status of the job queue. + */ + public JobQueueStatus getJobQueueStatus() { + return jobQueueStatus; + } + + /** + * The job queue entry sequence number. + * The subsystem uses this number to determine the order in which the job queues are processed. + * Jobs from the job queue with the lowest sequence number in the job queue are selected first. + * This field is -1 if the job queue has not been defined to an active subsystem or the job queue is damaged. + * @return The job queue entry sequence number. + */ + public int getJobQueueSequence() { + return jobQueueSequence; + } + + /** + * The name of the auxiliary storage pool (ASP) device name where storage is allocated for the library + * that contains the object. The following special values may be returned: + *
    + *
  • *N - The name of the ASP device cannot be determined.
  • + *
  • *SYSBAS - System ASP (ASP 1) or basic user ASPs (ASPs 2-32).
  • + *
+ * @return The name of the auxiliary storage pool (ASP) device name where storage is allocated for the library + * that contains the object. + */ + public String getAspName() { + return aspName; + } +} diff --git a/src/main/java/com/ibm/as400/access/list/JobQueueOpenList.java b/src/main/java/com/ibm/as400/access/list/JobQueueOpenList.java new file mode 100644 index 000000000..234da85c2 --- /dev/null +++ b/src/main/java/com/ibm/as400/access/list/JobQueueOpenList.java @@ -0,0 +1,279 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// JTOpen (IBM Toolbox for Java - OSS version) +// +// Filename: JobQueueOpenList.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) 2018-2019 International Business Machines Corporation and +// others. All rights reserved. +// +/////////////////////////////////////////////////////////////////////////////// + +package com.ibm.as400.access.list; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.ibm.as400.access.AS400; +import com.ibm.as400.access.AS400Bin4; +import com.ibm.as400.access.AS400DataType; +import com.ibm.as400.access.AS400Exception; +import com.ibm.as400.access.AS400SecurityException; +import com.ibm.as400.access.AS400Structure; +import com.ibm.as400.access.AS400Text; +import com.ibm.as400.access.BinaryConverter; +import com.ibm.as400.access.Converter; +import com.ibm.as400.access.ErrorCodeParameter; +import com.ibm.as400.access.ErrorCompletingRequestException; +import com.ibm.as400.access.ObjectDoesNotExistException; +import com.ibm.as400.access.ProgramCall; +import com.ibm.as400.access.ProgramParameter; + +/** + * An {@link com.ibm.as400.access.list.OpenList OpenList} implementation that + * generates lists of {@link com.ibm.as400.access.list.JobQueueListItem JobQueueListItem} + * objects. + * + *
+ * AS400 system = new AS400("mySystem", "myUserID", "myPassword");
+ * // Return all queues with format FORMAT_0100 (OJBQ0100)
+ * JobQueueOpenList list = new JobQueueOpenList(system);
+ * list.open();
+ * Enumeration items = list.getItems();
+ * while (items.hasMoreElements()) {
+ * 	   JobQueueListItem item = (JobQueueListItem) items.nextElement();
+ * 	   System.out.println(item.getJobQueueName() + "/" + item.getJobQueueStatus());
+ * }
+ * list.close();
+ * 
+ **/ + +public class JobQueueOpenList extends OpenList { + + private static final long serialVersionUID = 4436839827725754124L; + + /** + * Constant indicating that this list will accept parameters for, and generate, + * JobQueueListItem objects in accordance with the OJBQ0100 format of the + * underlying JobQueue. + **/ + public static final String FORMAT_0100 = "OJBQ0100"; + + public static final AS400DataType JOB_QUEUE_NAME = new AS400Text(10); + public static final AS400DataType JOB_QUEUE_LIBRARY_NAME = new AS400Text(10); + public static final AS400DataType JOB_QUEUE_STATUS = new AS400Text(1); + public static final AS400DataType SUBSYSTEM_NAME = new AS400Text(10); + public static final AS400DataType SUBSYSTEM_LIBRARY_NAME = new AS400Text(10); + private static final AS400DataType RESERVED = new AS400Text(3); + public static final AS400DataType NUMBER_OF_JOBS_WAITING = new AS400Bin4(); + public static final AS400DataType SEQUENCE = new AS400Bin4(); + public static final AS400DataType MAXIMUM_ACTIVE = new AS400Bin4(); + public static final AS400DataType CURRENT_ACTIVE = new AS400Bin4(); + public static final AS400DataType DESCRIPTION = new AS400Text(50); + public static final AS400DataType ASP_NAME = new AS400Text(10); + + private static final List ITEM_ELEMENTS = Arrays.asList(JOB_QUEUE_NAME, JOB_QUEUE_LIBRARY_NAME, + JOB_QUEUE_STATUS, SUBSYSTEM_NAME, SUBSYSTEM_LIBRARY_NAME, RESERVED, NUMBER_OF_JOBS_WAITING, + SEQUENCE, MAXIMUM_ACTIVE, CURRENT_ACTIVE, DESCRIPTION, ASP_NAME); + + private static final AS400Structure FILTER_STRUCTURE = new AS400Structure(new AS400DataType[] { + new AS400Bin4(), // 0 length of filter + JOB_QUEUE_NAME, // 1 job queue name + JOB_QUEUE_LIBRARY_NAME, // 2 job queue library name + SUBSYSTEM_NAME }); // 3 active subsystem name + + private class SortField { + AS400DataType field; + boolean ascending; + private SortField(AS400DataType field, boolean ascending) { + this.field = field; + this.ascending = ascending; + } + } + + private JobQueueListFilter filter; + private List sortFields = new ArrayList(2); + + /////////////////////////////////////////////////////////// + + public JobQueueOpenList(AS400 system) { + super(system); + setFilter(JobQueueListFilter.DEFAULT); + } + + public JobQueueListFilter getFilter() { + return filter; + } + + public void setFilter(JobQueueListFilter filter) { + this.filter = filter; + resetHandle(); + } + + public void clearSortFields() { + this.sortFields.clear(); + } + + public void addSortField(AS400DataType sortField, boolean ascending) { + int ndx = ITEM_ELEMENTS.indexOf(sortField); + if (ndx < 0) { + throw new IllegalArgumentException("sortField"); + } + this.sortFields.add(new SortField(sortField, ascending)); + } + + /////////////////////////////////////////////////////////// + + /** + * Pad a string with spaces at the end to the desired length. + */ + protected static String padRight(String string, int length) { + return String.format("%1$-" + length + "s", string == null ? "" : string); + } + + /** + * Trim a string if not null, or return the empty string + */ + protected static String trimSafe(String string) { + return string == null ? "" : string.trim(); + } + + private int getOffset(AS400DataType field) { + int ndx = ITEM_ELEMENTS.indexOf(field); + return getOffset(ndx); + } + + private int getRecordLength() { + return getOffset(ITEM_ELEMENTS.size()); + } + + private int getOffset(int ndx) { + if (ndx < 0 || ndx > ITEM_ELEMENTS.size()) { + throw new IndexOutOfBoundsException(); + } + + int offset = 0; + for (int i = 0; i < ndx; ++i) + { + offset += ITEM_ELEMENTS.get(i).getByteLength(); + } + return offset; + } + + private short getFieldDataType(AS400DataType field) { + //TODO more types + if (field instanceof AS400Text) return 4; + return 0; //signed binary by default + } + + private byte[] createSortData() { + int sortItemSize = 12; + int numSortKeys = this.sortFields.size(); + byte[] sortData = new byte[4 + (numSortKeys * sortItemSize)]; + BinaryConverter.intToByteArray(numSortKeys, sortData, 0); + + int sortOffset = 0; + for (int i = 0; i < numSortKeys; i++) { + SortField field = sortFields.get(i); + + BinaryConverter.intToByteArray(getOffset(field.field), sortData, 4 + sortOffset); //offset of sort field in row + BinaryConverter.intToByteArray(field.field.getByteLength(), sortData, 8 + sortOffset); //length of sort field + BinaryConverter.shortToByteArray(getFieldDataType(field.field), sortData, 12 + sortOffset); //type of sort field + // '1' = ascending, '2' = descending (0xF1 = 1 and 0xF2 = 2) + sortData[14 + sortOffset] = field.ascending ? (byte)0xF1 : (byte)0xF2; + + sortOffset += sortItemSize; + } + return sortData; + } + + /////////////////////////////////////////////////////////// + + @Override + protected byte[] callOpenListAPI() throws AS400SecurityException, ErrorCompletingRequestException, InterruptedException, IOException, ObjectDoesNotExistException { + Converter conv = new Converter(system_.getCcsid(), system_); + + //TODO do this directly since structure doesn't support conv? + Object[] filterValues = new Object[] { FILTER_STRUCTURE.getByteLength(), + padRight(filter.getJobQueueName(), JOB_QUEUE_NAME.getByteLength()), + padRight(filter.getJobQueueLibraryName(), JOB_QUEUE_LIBRARY_NAME.getByteLength()), + padRight(filter.getActiveSubsystemName(), SUBSYSTEM_NAME.getByteLength()) }; + byte[] filterData = FILTER_STRUCTURE.toBytes(filterValues); + + byte[] sortData = createSortData(); + + ProgramParameter[] parms = new ProgramParameter[8]; + parms[0] = new ProgramParameter(1); // receiver variable + parms[1] = new ProgramParameter(BinaryConverter.intToByteArray(0)); // length of receiver variable //TODO set this to fit the desired number of records and read them directly + parms[2] = new ProgramParameter(conv.stringToByteArray(FORMAT_0100)); + parms[3] = new ProgramParameter(80 /*ListUtilities.LIST_INFO_LENGTH*/); // list information + parms[4] = new ProgramParameter(filterData); + parms[5] = new ProgramParameter(sortData); + parms[6] = new ProgramParameter(BinaryConverter.intToByteArray(1)); + parms[7] = new ErrorCodeParameter(); + + ProgramCall pc = new ProgramCall(system_, "/QSYS.LIB/QGY.LIB/QSPOLJBQ.PGM", parms); // threadsafe + pc.setThreadSafe(true); + if (!pc.run()) + { + throw new AS400Exception(pc.getMessageList()); + } + + return parms[3].getOutputData(); + } + + @Override + protected Object[] formatOutputData(byte[] data, int recordsReturned, int recordLength) + throws AS400SecurityException, ErrorCompletingRequestException, InterruptedException, IOException, ObjectDoesNotExistException + { + JobQueueListItem[] list = new JobQueueListItem[recordsReturned]; + Converter conv = new Converter(system_.getCcsid(), system_); + + int offset = 0; + for (int i = 0; i < recordsReturned; i++) { + int localOffset = offset; + + JobQueueListItem item = new JobQueueListItem(); + list[i] = item; + + item.jobQueueName = trimSafe(conv.byteArrayToString(data, localOffset, 10)); + localOffset += 10; + item.jobQueueLibrary = trimSafe(conv.byteArrayToString(data, localOffset, 10)); + localOffset += 10; + String statusChar = conv.byteArrayToString(data, localOffset, 1); + item.jobQueueStatus = JobQueueListItem.JobQueueStatus.fromSystemValue(statusChar); + localOffset += 1; + item.subsystemName = trimSafe(conv.byteArrayToString(data, localOffset, 10)); + localOffset += 10; + item.subsystemLibrary = trimSafe(conv.byteArrayToString(data, localOffset, 10)); + localOffset += 10; + localOffset += 3; //reserved + item.numberOfJobsWaitingToRun = BinaryConverter.byteArrayToInt(data, localOffset); //-1 is no subsystem or damaged + localOffset += 4; + item.jobQueueSequence = BinaryConverter.byteArrayToInt(data, localOffset); //-1 is no subsystem or damaged + localOffset += 4; + item.maximumActiveJobs = BinaryConverter.byteArrayToInt(data, localOffset); //-1 is *NOMAX, -2 is no subsystem or damaged + localOffset += 4; + item.numberOfJobsRunning = BinaryConverter.byteArrayToInt(data, localOffset); + localOffset += 4; + item.jobQueueDescription = trimSafe(conv.byteArrayToString(data, localOffset, 50)); + localOffset += 50; + item.aspName = trimSafe(conv.byteArrayToString(data, localOffset, 10)); + localOffset += 10; + + offset += recordLength; + } + + return list; + } + + @Override + protected int getBestGuessReceiverSize(int number) { + return getRecordLength() * number; + } + +}