/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://IdentityConnectors.dev.java.net/legal/license.txt
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at identityconnectors/legal/license.txt.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
*/
package org.identityconnectors.rw3270;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.naming.NamingException;
import org.apache.oro.text.regex.MalformedPatternException;
import org.identityconnectors.common.script.ScriptExecutor;
import org.identityconnectors.common.script.ScriptExecutorFactory;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import expect4j.Closure;
import expect4j.Expect4j;
import expect4j.ExpectState;
import expect4j.matches.Match;
import expect4j.matches.RegExpMatch;
import expect4j.matches.TimeoutMatch;
public abstract class RW3270BaseConnection implements RW3270Connection {
protected String _lastConnError;
protected int _model;
protected Semaphore _semaphore;
protected StringBuffer _buffer;
protected RW3270Configuration _config;
protected Expect4j _expect4j;
protected RW3270IOPair _ioPair;
protected Pattern _commandPattern = Pattern.compile("(?<!\\[)\\[([^]]*)\\]");
protected Pattern _pfPattern = Pattern.compile("PF(\\d+)", Pattern.CASE_INSENSITIVE);
protected Pattern _paPattern = Pattern.compile("PA(\\d+)", Pattern.CASE_INSENSITIVE);
protected Pattern _cursorPattern = Pattern.compile("cursor\\s*\\(\\s*(\\d+)\\s*\\)", Pattern.CASE_INSENSITIVE);
protected static final String CLEAR = "CLEAR";
protected static final String ENTER = "ENTER";
protected ScriptExecutor _connectScriptExecutor;
protected ScriptExecutor _disconnectScriptExecutor;
public RW3270BaseConnection(RW3270Configuration config) throws NamingException {
_config = config;
_buffer = new StringBuffer();
_model = 2;
_semaphore = new Semaphore(0);
_expect4j = new Expect4j(_ioPair = new RW3270IOPair(this));
ScriptExecutorFactory factory = ScriptExecutorFactory.newInstance(config.getConnectScript().getScriptLanguage());
_connectScriptExecutor = factory.newScriptExecutor(getClass().getClassLoader(), config.getConnectScript().getScriptText(), true);
factory = ScriptExecutorFactory.newInstance(config.getDisconnectScript().getScriptLanguage());
_disconnectScriptExecutor = factory.newScriptExecutor(getClass().getClassLoader(), config.getDisconnectScript().getScriptText(), true);
}
public abstract void sendKeys(String keys);
public abstract void sendEnter();
public abstract void sendPAKeys(int pa);
public abstract void sendPFKeys(int pf);
public abstract void setCursorPos(short pos);
public abstract void waitForUnlock() throws InterruptedException ;
public abstract void clearAndUnlock() throws InterruptedException;
public abstract String getDisplay();
public void reset() {
dispose();
connect();
}
public RW3270Configuration getConfiguration() {
return _config;
}
public void resetStandardOutput() {
_buffer.setLength(0);
_ioPair.reset();
}
public void loginUser() {
try {
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("SHORT_WAIT", 15000);
arguments.put("USERNAME", _config.getUserName());
arguments.put("PASSWORD", _config.getPassword());
arguments.put("connection", this);
_connectScriptExecutor.execute(arguments);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
public void logoutUser() {
try {
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("SHORT_WAIT", 5000);
arguments.put("USERNAME", _config.getUserName());
arguments.put("PASSWORD", _config.getPassword());
arguments.put("connection", this);
_disconnectScriptExecutor.execute(arguments);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
public int getWidth() {
switch (_model) {
case 1 :
return 40;
case 2 :
case 3 :
case 4 :
return 80;
case 5 :
return 132;
}
return 0;
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#send(java.lang.String)
*/
public void send(GuardedString command) {
GuardedStringAccessor accessor = new GuardedStringAccessor();
command.access(accessor);
char[] string = accessor.getArray();
try {
send(string);
} finally {
accessor.clear();
Arrays.fill(string, 0, string.length, ' ');
}
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#send(java.lang.String)
*/
public void send(char[] command) {
send(new String(command));
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#send(java.lang.String)
*/
public void send(String command) {
try {
_expect4j.send(command);
} catch (IOException e) {
throw ConnectorException.wrap(e);
}
}
public void sendFromIOPair(String command) {
try {
waitForUnlock();
// Loop through the string, extracting any commands
//
Matcher matcher = _commandPattern.matcher(command);
int start = 0;
while (matcher.find(start)) {
String prefix = command.substring(start, matcher.start());
sendKeys(prefix);
String match = matcher.group(1).trim();
Matcher paMatcher = _paPattern.matcher(match);
Matcher pfMatcher = _pfPattern.matcher(match);
Matcher cursorMatcher = _cursorPattern.matcher(match);
if (CLEAR.equalsIgnoreCase(match)) {
clearAndUnlock();
} else if (ENTER.equalsIgnoreCase(match)) {
sendEnter();
} else if (paMatcher.matches()) {
int number = Integer.parseInt(paMatcher.group(1));
if (number<1 || number>3)
throw new IllegalArgumentException(_config.getConnectorMessages().format("IllegalPA", "Illegal PA key:{0}", match));
sendPAKeys(number);
waitForUnlock();
} else if (pfMatcher.matches()) {
int number = Integer.parseInt(pfMatcher.group(1));
if (number<1 || number>24)
throw new IllegalArgumentException(_config.getConnectorMessages().format("IllegalPF", "Illegal PF key:{0}", match));
sendPFKeys(number);
waitForUnlock();
} else if (cursorMatcher.matches()) {
short cursor = Short.parseShort(cursorMatcher.group(1));
setCursorPos(cursor);
} else {
throw new IllegalArgumentException(_config.getConnectorMessages().format("IllegalCommand", "Illegal Command:{0}", match));
}
start = matcher.end();
}
sendKeys(command.substring(start));
} catch (InterruptedException e) {
throw new ConnectorException(e);
} catch (NumberFormatException e) {
throw new ConnectorException(e);
}
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#waitFor(java.lang.String)
*/
private String _lastDisplay = "";
public synchronized String waitForInput() {
try {
_semaphore.acquire();
// Eliminate any trailing blank lines
//
StringBuffer buffer = new StringBuffer(getDisplay());
int last = buffer.length();
while (last>0)
if (buffer.charAt(--last)!=' ')
break;
last += getWidth()-(last%getWidth());
String value = "";
buffer.setLength(last);
String display = buffer.toString();
if (display.startsWith(_lastDisplay)) {
value = display.substring(_lastDisplay.length());
} else {
value = display;
}
_lastDisplay = display;
return value;
} catch (InterruptedException e) {
throw new ConnectorException(e);
}
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#waitFor(expect4j.matches.Match[])
*/
public void waitFor(Match[] matches) {
try {
_expect4j.expect(matches);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#waitFor(java.lang.String)
*/
public void waitFor(String expression) {
waitForLocal(null, expression, null);
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#waitFor(java.lang.String, int)
*/
public void waitFor(final String expression, int timeOut) {
waitForLocal(null, expression, new Integer(timeOut));
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#waitFor(java.lang.String, java.lang.String)
*/
public void waitFor(String expression0, String expression1) {
waitForLocal(expression0, expression1, null);
}
/* (non-Javadoc)
* @see org.identityconnectors.racf.RW3270Connection#waitFor(java.lang.String, java.lang.String, int)
*/
public void waitFor(String expression0, String expression1, int timeOut) {
waitForLocal(expression0, expression1, new Integer(timeOut));
}
private int count = 0;
private void waitForLocal(final String continue_regexp, final String complete_regexp, Integer timeout) {
try {
List<Match> matches = new LinkedList<Match>();
// Match the continue expression, so
// save the partial output
// ask for more output
//
if (continue_regexp!=null) {
matches.add(new RegExpMatch(continue_regexp, new Closure() {
public void run(ExpectState state) throws Exception {
// Need to strip off the match
//
String data = state.getBuffer();
data = data.substring(0, state.getMatchedWhere());
_buffer.append(data);
//System.out.println("+++continue("+count+++")\n:"+_buffer.toString().replaceAll("(.{80})", "$1\n"));
clearAndUnlock();
sendEnter();
state.exp_continue();
}
}));
}
// Match the command complete expression, so
// if there was an error,
// throw exception
// else
// save the final output
matches.add(new RegExpMatch(complete_regexp, new Closure() {
public void run(ExpectState state) throws Exception {
String data = state.getBuffer();
_buffer.append(data);
//System.out.println("+++complete("+count+++")\n:"+_buffer.toString().replaceAll("(.{80})", "$1\n"));
Object errorDetected = state.getVar("errorDetected");
state.addVar("timeout", Boolean.FALSE);
state.addVar("errorDetected", null);
//if (errorDetected!=null)
// throw new XXX();;
}
}));
// Match the error expression, so
// send the abort command
// continue execution, to see if we can recover
//
/*
matches.add(new RegExpMatch(expression2, new Closure() {
public void run(ExpectState state) throws Exception {
state.addVar("errorDetected", state.getBuffer());
// Need to strip off the match
//
String data = state.getBuffer();
Matcher matcher = pattern.matcher(data);
if (matcher.find()) {
data = data.substring(0, matcher.start());
}
_buffer.append(data);
clearAndUnlock();
sendPAKeys(1);
state.exp_continue();
}
}));
*/
if (timeout != null)
matches.add(new TimeoutMatch(timeout, new Closure() {
public void run(ExpectState state) throws Exception {
state.addVar("timeout", Boolean.TRUE);
}
}));
_expect4j.expect(matches);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
Boolean isTimeout = (Boolean)_expect4j.getLastState().getVar("timeout");
if (isTimeout==null || isTimeout.booleanValue())
throw new ConnectorException(_config.getConnectorMessages().format(
"IsAlive", "timed out waiting for ''{0}'':''{1}''",
complete_regexp, getStandardOutput()));
}
private static class GuardedStringAccessor implements GuardedString.Accessor {
private char[] _array;
public void access(char[] clearChars) {
_array = new char[clearChars.length];
System.arraycopy(clearChars, 0, _array, 0, _array.length);
}
public char[] getArray() {
return _array;
}
public void clear() {
Arrays.fill(_array, 0, _array.length, ' ');
}
}
protected Properties asProperties(String[] array) {
if (array==null)
return null;
Properties properties = new Properties();
for (int i=0; i<array.length; i+=2)
properties.put(array[i], array[i+1]);
return properties;
}
}