package com.sap.trex.client;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.lang.System;
public class ConnectionImpl {
private String itsUrl;
private boolean itsSecure;
private String itsProfile = "";
private int itsRootIndex = 0;
private List<String> itsRootLocations = new ArrayList<String>();
private String itsSession = "";
private int itsSequence = 0;
private List<Location> itsLocations = new ArrayList<Location>();
private long itsLocationsRefreshTime;
private long itsChannelsIdleTime;
private int itsChannelsIdleInterval;
private int itsLocationsRefreshInterval;
private final int itsLocationsRefreshDefault = 300;
private final int itsChannelsIdleDefault = 600;
public ConnectionImpl(String theUrl) {
// parse the string for several url's into the locations arraylist
// don't throw exception from constructor. instead return error when using an empty location
itsUrl = theUrl;
itsSecure = false;
if(theUrl.startsWith("trex-is:")) {
itsRootLocations.add(theUrl.substring(8));
} else {
int pbgn = 0;
if (theUrl.startsWith("trex://")) {
pbgn = 7;
} else if (theUrl.startsWith("trexs://")) {
pbgn = 8;
itsSecure = true;
}
if(pbgn>0) {
int pnxt;
int pend = theUrl.indexOf('/',pbgn);
if (pend == -1)
pend = theUrl.length();
for(; pbgn<pend; pbgn=pnxt) {
pnxt = theUrl.indexOf(',',pbgn);
if(pnxt == -1 || pnxt > pend)
pnxt = pend;
itsRootLocations.add(theUrl.substring(pbgn,pnxt));
if(pnxt!=pend) pnxt++;
}
pbgn = pend+1;
if (pbgn < theUrl.length()) {
pend = theUrl.indexOf('?',pbgn);
if(pend == -1)
pend = theUrl.length();
itsProfile = theUrl.substring(pbgn,pend);
}
}
}
itsLocationsRefreshTime = 0L;
itsChannelsIdleTime = 0L;
itsLocationsRefreshInterval = itsLocationsRefreshDefault;
itsChannelsIdleInterval = itsChannelsIdleDefault;
}
private void getLocations() throws TrexException, Exception {
if (itsRootLocations.size() == 0)
throw new TrexException (Error.TDBC_INVALID_CONNECTION_URL, itsUrl);
for(int num=0; num<itsRootLocations.size(); num++) {
Channel channel = null;
try {
int idx=itsRootIndex % itsRootLocations.size();
// server side of this call is in TrexService/TrexClient.cpp
channel = new Channel();
channel.open(itsRootLocations.get(idx), itsSecure);
channel.writeHeader(itsSession, ++itsSequence, "_getServer");
channel.output().writeInt(4); // special protocol version for this core call
channel.output().writeString(itsRootLocations.get(idx)); // for detection of location format (name,ip,fqdn)
channel.output().writeString(itsProfile);
channel.output().writeString(""); // should be changelist
channel.writeEOF();
Error error = new Error();
// don't lock itsLocations while waiting for server
error.read(channel.input());
if(channel.readEOF())
throw new TrexException(error.getCode(), error.getErrorMessage());
int t;
t = channel.input().readInt();
itsLocationsRefreshInterval = t;
t = channel.input().readInt();
itsChannelsIdleInterval = t;
itsSession = channel.input().readString();
int i,j;
j = channel.input().readVectorSize();
if(j > 0) {
itsLocations = new ArrayList<Location>(j);
for(i=0; i<j; i++) {
String tmp = channel.input().readString();
itsLocations.add(new Location(tmp, true));
}
} else { // continue with the initial locations
j=itsRootLocations.size();
itsLocations = new ArrayList<Location>(j);
for(i=0; i<j; i++) {
itsLocations.add(new Location(itsRootLocations.get(i), true));
}
}
channel.releaseRef();
break;
} catch(Exception ex) {
if(channel != null)
channel.releaseRef();
itsRootIndex++;
if(num==itsRootLocations.size()-1) {
itsLocationsRefreshTime = 0L;
throw ex;
}
}
}
}
public synchronized Channel getChannel(String theMethod) throws Exception
{
Channel channel = null;
long now = System.currentTimeMillis()/1000;
for(int loop=0; channel==null && loop<2; loop++) {
if(loop > 0 || now > itsLocationsRefreshTime + itsLocationsRefreshInterval) {
itsLocationsRefreshTime=now;
getLocations();
}
for(int num=0; channel==null && num<itsLocations.size(); num++) {
// increment itsIndex without write lock -> round robin could be slightly imbalanced
int idx=itsSequence++ % itsLocations.size();
try {
if(itsLocations.get(idx).getFlag()) {
channel = new Channel();
channel.open(itsLocations.get(idx).getLocationName(), itsSecure);
channel.writeHeader(itsSession, itsSequence, theMethod);
}
} catch(IOException ex) {
itsLocations.get(idx).setFlag(false);
if(loop==1 && num == itsLocations.size()-1)
throw new Exception(ex);
}
}
}
if(channel != null)
return channel;
throw new TrexException(Error.TREX_SAPRC_SERVER_NOT_FOUND,itsUrl);
}
public String getUrl() {
return this.itsUrl;
}
}
class Location {
private String itsLocation;
private boolean itsFlag;
public Location(String location, boolean flag) {
this.itsLocation = location;
this.itsFlag = flag;
}
public void setLocation(String theLocation ) {
this.itsLocation = theLocation;
}
public void setFlag (boolean theFlag) {
this.itsFlag = theFlag;
}
public String getLocationName() {
return this.itsLocation;
}
public boolean getFlag() {
return this.itsFlag;
}
}