package org.epics.archiverappliance.engine.bpl;
import gov.aps.jca.Channel;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.common.BPLAction;
import org.epics.archiverappliance.config.ConfigService;
import org.epics.archiverappliance.config.PVTypeInfo;
import org.epics.archiverappliance.engine.pv.EngineContext.CommandThreadChannel;
import org.epics.archiverappliance.utils.ui.MimeTypeConstants;
import org.json.simple.JSONValue;
import com.cosylab.epics.caj.CAJChannel;
/**
* This is a reasonably dangerous call.
* It appears that there is a bug somewhere in CAJ (or in the engine code that uses it) where we leave channels hanging around in an inconsistent state.
* Specifically, we seem to have an entry in the ChannelSearchManager but the channel itself has not proceeded to the tcp/ip circuit.
* This code forcibly closes these CAJ channels.
* Now; I'm unclear what that does to the engine layer above; it may leave the engine in a state where we need to restart it.
* However; I'm going to attempt this on one of the stuck PVs soon.
* I'd rather fix the bug in CAJ but until I can get Matej a reproducible usecase....
* @author mshankar
*
*/
public class CleanUpAnyImmortalChannels implements BPLAction {
private static Logger logger = Logger.getLogger(CleanUpAnyImmortalChannels.class.getName());
@Override
public void execute(HttpServletRequest req, HttpServletResponse resp, ConfigService configService) throws IOException {
String pvName = req.getParameter("pv");
if(pvName == null || pvName.equals("")) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
PVTypeInfo typeInfo = configService.getTypeInfoForPV(pvName);
if(typeInfo == null) {
logger.error("Cannot get typeinfo for PV " + pvName);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if(!typeInfo.isPaused()) {
logger.error("PV " + pvName + " is not paused. At the very least, please pause the PV first.");
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
List<CommandThreadChannel> immortalChannelsForPV = configService.getEngineContext().getAllChannelsForPV(pvName);
for(final CommandThreadChannel immortalCommandThreadChannel : immortalChannelsForPV) {
immortalCommandThreadChannel.getCommandThread().addCommand(new Runnable() {
@Override
public void run() {
try {
Channel immortalChannel = immortalCommandThreadChannel.getChannel();
logger.error("Forcibly closing channel for " + immortalChannel.getName());
if(immortalChannel instanceof CAJChannel) {
((CAJChannel)immortalChannel).destroy(true);
} else {
immortalChannel.destroy();
}
} catch(Throwable t) {
logger.error("Exception forcibly closing channel", t);
}
}
});
}
HashMap<String, Object> infoValues = new HashMap<String, Object>();
resp.setContentType(MimeTypeConstants.APPLICATION_JSON);
try(PrintWriter out = resp.getWriter()) {
infoValues.put("status", "ok");
infoValues.put("Channels destroyed", immortalChannelsForPV.size());
out.println(JSONValue.toJSONString(infoValues));
}
}
}