/* * Created on May 10, 2006 Copyright (C) 2001-5, Anthony Harrison anh23@pitt.edu * (jactr.org) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. This library is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. You should have * received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.io.environment; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.parser.RealityParser; import org.jactr.core.model.IModel; import org.jactr.core.reality.connector.IConnector; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.runtime.controller.DefaultController; import org.jactr.core.runtime.controller.IController; import org.jactr.core.utils.IInstallable; import org.jactr.core.utils.parameter.IParameterized; import org.jactr.instrument.IInstrument; import org.jactr.io.IOUtilities; import org.jactr.io.antlr3.builder.JACTRBuilder; import org.jactr.io.antlr3.misc.ASTSupport; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * parses the environment.xml file to configure the runtime <code> * <environment> * * <!-- iff you want to control reality from here --> * <commonreality> * .. * </commonreality> * * * <controller class="org.jactr.core.runtime.DefaultController"/> * <onStart class="com.some.class.onStartRunnable"/> * <onStop class="com.some.class.onStopRunnable"/> * * <connector class="org.jactr.core.reality.connector.CommonRealityConnector"> <credentials> <credential value="visualTest:pass" alias="visualTest"/> </credentials> </connector> * * <models> * <model url=".../bleck.jactr" alias="bob"/> * </models> * * <attachments> * <attachment class="com.some.class.implements.IInstallable" attach="bob"> * <parameters> * </parameters> * </attachment> * </attachments> * </environment> * </code> * * @author developer */ public class EnvironmentParser { /** * logger definition */ static public final Log LOGGER = LogFactory.getLog(EnvironmentParser.class); public void parse(URL input) throws IOException, SAXException, ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = factory.newDocumentBuilder(); Document doc = parser.parse(input.openStream()); process(doc, getModelDescriptors(doc, input)); } /** * process the environment descriptor. * * @param document * @param modelDescriptors */ public void process(Document document, Collection<CommonTree> modelDescriptors) { ACTRRuntime runtime = ACTRRuntime.getRuntime(); /* * first things first.. if there are reality tags, proccess them */ configureReality(document); instantiateConnector(document, runtime); instantiateController(document, runtime); instantiateOnStartStop(document, runtime); Collection<IModel> models = buildModels(modelDescriptors); // now let's actually add all the models for (IModel model : models) runtime.addModel(model); instantiateAttachments(document, models); } /** * build all the models * * @param modelDescriptors * @return */ protected Collection<IModel> buildModels( Collection<CommonTree> modelDescriptors) { Collection<IModel> models = new ArrayList<IModel>(); for (CommonTree modelDescriptor : modelDescriptors) { Collection<Exception> warnings = new HashSet<Exception>(); Collection<Exception> errors = new HashSet<Exception>(); if (LOGGER.isDebugEnabled()) LOGGER.debug("Building " + ASTSupport.getName(modelDescriptor)); IModel model = IOUtilities.constructModel(modelDescriptor, warnings, errors); if (errors.size() != 0) throw new RuntimeException("Failed to build " + ASTSupport.getName(modelDescriptor), errors.iterator().next()); models.add(model); } return models; } /** * load the model descriptors based on the document contents and the root url * * @param document * @return */ public Collection<CommonTree> getModelDescriptors(Document document, URL root) { Collection<CommonTree> models = new ArrayList<CommonTree>(); NodeList nl = document.getElementsByTagName("model"); for (int i = 0; i < nl.getLength(); i++) { Element modelElement = (Element) nl.item(i); String modelName = modelElement.getAttribute("alias"); String location = modelElement.getAttribute("url"); URL modelLocation = resolveURLLocation(root, location); if (modelLocation != null) { Collection<Exception> warnings = new HashSet<Exception>(); Collection<Exception> errors = new HashSet<Exception>(); CommonTree modelDescriptor = null; try { if (LOGGER.isDebugEnabled()) LOGGER.debug("Loading " + modelLocation); modelDescriptor = IOUtilities.loadModelFile(modelLocation, warnings, errors); } catch (Exception e) { throw new RuntimeException("Failed to load " + modelLocation, e); } if (errors.size() != 0) throw new RuntimeException("Parsing error on " + modelLocation, errors.iterator().next()); IOUtilities.compileModelDescriptor(modelDescriptor, warnings, errors); if (errors.size() != 0) throw new RuntimeException("Compilation error on " + modelLocation, errors.iterator().next()); /* * set the model name */ ASTSupport.getFirstDescendantWithType(modelDescriptor, JACTRBuilder.NAME).getToken().setText(modelName); models.add(modelDescriptor); } else throw new RuntimeException( String .format( "Could not load model from %s. If this was working a second ago, check out %s.", location, "http://jact-r.org/node/148")); } return models; } protected void configureReality(Document env) { NodeList nl = env.getElementsByTagName("commonreality"); if (nl.getLength() == 1) { if (LOGGER.isDebugEnabled()) LOGGER.debug("configuring reality interface"); // pass it on to the RealityParser RealityParser rp = new RealityParser(); rp.parse((Element) nl.item(0)); } } static public Object instantiate(Element element, String objectType) { String className = element.getAttribute("class"); try { Object rtn = EnvironmentParser.class.getClassLoader() .loadClass(className).newInstance(); if (rtn instanceof IParameterized) { NodeList nl = element.getElementsByTagName("parameters"); if (nl.getLength() != 0) applyParameters((Element) nl.item(0), (IParameterized) rtn); } return rtn; } catch (Exception e) { String message = new String("Could not instantiate " + objectType + " from " + className); LOGGER.error(message, e); throw new RuntimeException(message, e); } } protected Collection<IInstallable> instantiateAttachments(Document env, Collection<IModel> models) { Collection<IInstallable> attachments = new ArrayList<IInstallable>(); NodeList nl = env.getElementsByTagName("attachment"); for (int i = 0; i < nl.getLength(); i++) { Element attachment = (Element) nl.item(i); String[] modelNames = attachment.getAttribute("attach").split(","); IInstallable installable = (IInstallable) instantiate(attachment, "installable"); for (String modelName : modelNames) { modelName = modelName.trim(); // attach to everyone if (modelName.equalsIgnoreCase("all")) { for (IModel model : models) if (installable instanceof IInstrument) model.install((IInstrument) installable); else installable.install(model); } else for (IModel model : models) if (model.getName().equals(modelName)) if (installable instanceof IInstrument) model.install((IInstrument) installable); else installable.install(model); } } return attachments; } protected IConnector instantiateConnector(Document env, ACTRRuntime runtime) { IConnector connector = null; NodeList nl = env.getElementsByTagName("connector"); if (nl.getLength() == 1) { Element contEl = (Element) nl.item(0); connector = (IConnector) instantiate(contEl, "connector"); runtime.setConnector(connector); } return connector; } /** * handle the controller * * @param env * @param runtime * @return */ protected IController instantiateController(Document env, ACTRRuntime runtime) { IController controller = null; NodeList nl = env.getElementsByTagName("controller"); if (nl.getLength() == 1) { Element contEl = (Element) nl.item(0); controller = (IController) instantiate(contEl, "controller"); runtime.setController(controller); } if (controller == null) { controller = new DefaultController(); runtime.setController(controller); } return controller; } static protected void applyParameters(Element parameters, IParameterized parameterized) { NodeList nl = parameters.getElementsByTagName("parameter"); for (int i = 0; i < nl.getLength(); i++) { Element param = (Element) nl.item(i); String parameterName = param.getAttribute("name"); String parameterValue = param.getAttribute("value"); parameterized.setParameter(parameterName, parameterValue); } } /** * snag the onStart onStop classes and instantiate them * * @param env * @param runtime */ protected void instantiateOnStartStop(Document env, ACTRRuntime runtime) { NodeList nl = env.getElementsByTagName("onstart"); if (nl.getLength() == 1) runtime .setOnStart((Runnable) instantiate((Element) nl.item(0), "onstart")); nl = env.getElementsByTagName("onstop"); if (nl.getLength() == 1) runtime.setOnStop((Runnable) instantiate((Element) nl.item(0), "onstop")); } protected URL resolveURLLocation(URL enviromentLocation, String location) { URL url = null; try { url = new URL(location); if (LOGGER.isDebugEnabled()) LOGGER.debug("Loading from full url " + url); } catch (MalformedURLException e) { if (LOGGER.isDebugEnabled()) LOGGER.debug(location + " is not a valid url, trying to resolve it"); } if (url == null) try { url = enviromentLocation.toURI().resolve(location).toURL(); File fp = new File(url.toURI()); if (!fp.exists()) { url = null; if (LOGGER.isDebugEnabled()) LOGGER.debug("No file found at " + url); } else if (LOGGER.isDebugEnabled()) LOGGER.debug("Loading from relative URL " + url); } catch (Exception e) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Could not resolve url from " + location, e); } if (url == null) url = getClass().getClassLoader().getResource(location); if (LOGGER.isDebugEnabled()) LOGGER.debug("Loading " + url); return url; } }