/******************************************************************************* * Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University * as Operator of the SLAC National Accelerator Laboratory. * Copyright (c) 2011 Brookhaven National Laboratory. * EPICS archiver appliance is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. *******************************************************************************/ package org.epics.archiverappliance.engine.epics; import gov.aps.jca.jni.JNITargetArch; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFilePermissions; import java.util.Arrays; import java.util.Properties; import org.apache.log4j.Logger; import org.epics.archiverappliance.config.ConfigService; import org.epics.archiverappliance.config.exception.ConfigException; /** * Read the system environment and generate a JCA config stream * As of today, this seems to be a more visible way to configure JCA for this project than hidden files or other schemes * We'll see how this stands the test of time. * * @author mshankar * */ public class JCAConfigGen { private static final String JCA_CONFIG_GEN_USE_CAJ = "org.epics.archiverappliance.engine.epics.JCAConfigGen.useCAJ"; private static final Logger configlogger = Logger.getLogger("config." + JCAConfigGen.class.getName()); /** * Use environment vars to generate a JCA config that can be handed off to a JCA DefaultConfigurationBuilder * @param configService ConfigService * @return ByteArrayInputStream   * @throws ConfigException   */ public static ByteArrayInputStream generateJCAConfig(ConfigService configService) throws ConfigException { String JCACAJContext = "gov.aps.jca.jni.SingleThreadedContext"; Properties props = configService.getInstallationProperties(); configlogger.info("JCA/CAJ prop from archappl.properties is " + props.get(JCA_CONFIG_GEN_USE_CAJ)); if(props != null && props.containsKey(JCA_CONFIG_GEN_USE_CAJ) && Boolean.parseBoolean((String) props.get(JCA_CONFIG_GEN_USE_CAJ))) { JCACAJContext = "com.cosylab.epics.caj.CAJContext"; } else { try { String targetArch= JNITargetArch.getTargetArch(); String webInfFolder = configService.getWebInfFolder(); String jniPath = webInfFolder + "/lib/native/" + targetArch; configlogger.info("Adding " + jniPath + " to the library path using the classloader's usr_paths"); final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths"); boolean previousValueOfAccessible = usrPathsField.isAccessible(); usrPathsField.setAccessible(true); final String[] paths = (String[])usrPathsField.get(null); final String[] newPaths = Arrays.copyOf(paths, paths.length + 1); newPaths[newPaths.length-1] = jniPath; usrPathsField.set(null, newPaths); configlogger.debug("Setting the " + "gov.aps.jca.jni.epics." + targetArch + ".library.path and the gov.aps.jca.jni.epics." + targetArch + ".caRepeater.path to " + jniPath); System.getProperties().put("gov.aps.jca.jni.epics." + targetArch + ".library.path", jniPath); System.getProperties().put("gov.aps.jca.jni.epics." + targetArch + ".caRepeater.path", jniPath); configlogger.debug("Trying to make caRepeater in location " + System.getProperty("gov.aps.jca.jni.epics." + targetArch + ".caRepeater.path") + "an executable"); Files.walkFileTree(Paths.get(jniPath), new FileVisitor<Path>() { @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if(file.endsWith("caRepeater")) { try { Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rwxr-x---")); } catch(Exception ex) { configlogger.warn("Cannot set permission for caRepeater " + file, ex); } } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } }); usrPathsField.setAccessible(previousValueOfAccessible); } catch(Exception ex) { throw new ConfigException("Exception adding JNI library to usr_paths", ex); } } ByteArrayOutputStream bos = new ByteArrayOutputStream(); PrintWriter out = new PrintWriter(bos); out.println("<context class=\"" + JCACAJContext + "\">"); out.println(" <preemptive_callback>true</preemptive_callback>"); String EPICS_CA_ADDR_LIST = System.getenv("EPICS_CA_ADDR_LIST"); if(EPICS_CA_ADDR_LIST == null) { configlogger.info("Setting EPICS_CA_ADDR_LIST to an empty string as it is not defined in the environment."); EPICS_CA_ADDR_LIST = ""; } out.println(" <addr_list>" + EPICS_CA_ADDR_LIST + "</addr_list>"); String EPICS_CA_AUTO_ADDR_LIST = System.getenv("EPICS_CA_AUTO_ADDR_LIST"); if(EPICS_CA_AUTO_ADDR_LIST != null) { if(EPICS_CA_AUTO_ADDR_LIST.equalsIgnoreCase("yes")) { EPICS_CA_AUTO_ADDR_LIST = "true"; } else if(EPICS_CA_AUTO_ADDR_LIST.equalsIgnoreCase("no")) { EPICS_CA_AUTO_ADDR_LIST = "false"; } else { EPICS_CA_AUTO_ADDR_LIST = "false"; } } else { // Per the Channel Access reference manual, this should default to true if the variable is unset // LNLS also relies on this. EPICS_CA_AUTO_ADDR_LIST = "true"; } out.println(" <auto_addr_list>" + EPICS_CA_AUTO_ADDR_LIST + "</auto_addr_list>"); String EPICS_CA_CONN_TMO = System.getenv("EPICS_CA_CONN_TMO"); if(EPICS_CA_CONN_TMO == null) EPICS_CA_CONN_TMO = "30.0"; out.println(" <connection_timeout>" + EPICS_CA_CONN_TMO + "</connection_timeout>"); String EPICS_CA_BEACON_PERIOD = System.getenv("EPICS_CA_BEACON_PERIOD"); if(EPICS_CA_BEACON_PERIOD == null) EPICS_CA_BEACON_PERIOD = "30.0"; out.println(" <beacon_period>" + EPICS_CA_BEACON_PERIOD + "</beacon_period>"); String EPICS_CA_REPEATER_PORT = System.getenv("EPICS_CA_REPEATER_PORT"); if(EPICS_CA_REPEATER_PORT == null) EPICS_CA_REPEATER_PORT = "5065"; out.println(" <repeater_port>" + EPICS_CA_REPEATER_PORT + "</repeater_port>"); String EPICS_CA_SERVER_PORT = System.getenv("EPICS_CA_SERVER_PORT"); if(EPICS_CA_SERVER_PORT == null) EPICS_CA_SERVER_PORT = "5064"; out.println(" <server_port>" + EPICS_CA_SERVER_PORT + "</server_port>"); String EPICS_CA_MAX_ARRAY_BYTES = System.getenv("EPICS_CA_MAX_ARRAY_BYTES"); if(EPICS_CA_MAX_ARRAY_BYTES == null) EPICS_CA_MAX_ARRAY_BYTES = "30.0"; out.println(" <max_array_bytes>" + EPICS_CA_MAX_ARRAY_BYTES + "</max_array_bytes>"); String dispatcher = props.getProperty("org.epics.archiverappliance.engine.epics.JCAConfigGen.dispatcher", "gov.aps.jca.event.QueuedEventDispatcher"); out.println(" <event_dispatcher class=\"" + dispatcher + "\"/>"); out.println("</context>"); out.close(); byte[] cfgbytes = bos.toByteArray(); try { configlogger.info("JCA Configuration:\n" + new String(cfgbytes, "UTF-8")); } catch(UnsupportedEncodingException ex) { // This is a JVM that does not support UTF-8. It is unlikely the rest of this product will work configlogger.fatal(ex); } ByteArrayInputStream bis = new ByteArrayInputStream(cfgbytes); return bis; } }