/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2008-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.config; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.opennms.core.utils.LogUtils; import org.opennms.core.xml.JaxbUtils; import org.opennms.netmgt.dao.AbstractJaxbConfigDao; import org.opennms.netmgt.xml.eventconf.Event; import org.opennms.netmgt.xml.eventconf.Events; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.orm.ObjectRetrievalFailureException; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * <p>DefaultEventConfDao class.</p> * * @author ranger * @version $Id: $ */ public class DefaultEventConfDao extends AbstractJaxbConfigDao<Events, EventConfiguration> implements EventConfDao, InitializingBean { private static final String DEFAULT_PROGRAMMATIC_STORE_RELATIVE_URL = "events/programmatic.events.xml"; private final EventResourceLoader m_resourceLoader = new EventResourceLoader(); /** * Relative URL for the programmatic store configuration, relative to the * root configuration resource (which must be resolvable to a URL). */ private String m_programmaticStoreRelativeUrl = DEFAULT_PROGRAMMATIC_STORE_RELATIVE_URL; /** * The programmatic store configuration resource. */ private Resource m_programmaticStoreConfigResource; private static class EventLabelComparator implements Comparator<Event>, Serializable { private static final long serialVersionUID = 7976730920523203921L; public int compare(final Event e1, final Event e2) { return e1.getEventLabel().compareToIgnoreCase(e2.getEventLabel()); } } /** * <p>Constructor for DefaultEventConfDao.</p> */ public DefaultEventConfDao() { super(Events.class, "event"); } /** {@inheritDoc} */ @Override protected String createLoadedLogMessage(final EventConfiguration translatedConfig, final long diffTime) { return "Loaded " + getDescription() + " with " + translatedConfig.getEventCount() + " events from " + translatedConfig.getEventFiles().size() + " files in " + diffTime + "ms"; } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#reload() */ /** * <p>reload</p> * * @throws org.springframework.dao.DataAccessException if any. */ public void reload() throws DataAccessException { getContainer().reload(); } /** {@inheritDoc} */ @Override public void afterPropertiesSet() throws DataAccessException { /** * It sucks to duplicate this first test from AbstractCastorConfigDao, * but we need to do so to ensure we don't get an NPE while initializing * programmaticStoreConfigResource (if needed). */ Assert.state(getConfigResource() != null, "property configResource must be set and be non-null"); super.afterPropertiesSet(); } /** {@inheritDoc} */ @Override public EventConfiguration translateConfig(final Events events) throws DataAccessException { final EventConfiguration eventConfiguration = new EventConfiguration(); processEvents(events, getConfigResource(), eventConfiguration, "root", false); if (events.getGlobal() != null && events.getGlobal().getSecurity() != null) { eventConfiguration.getSecureTags().addAll(events.getGlobal().getSecurity().getDoNotOverrideCollection()); } for (final String eventFilePath : events.getEventFileCollection()) { loadAndProcessEvents(m_resourceLoader.getResource(eventFilePath), eventConfiguration, "included", true); } return eventConfiguration; } private Events loadAndProcessEvents(final Resource rootResource, final EventConfiguration eventConfiguration, final String resourceDescription, final boolean denyIncludes) { LogUtils.debugf(this, "DefaultEventConfDao: Loading %s event configuration from %s", resourceDescription, rootResource); final Events events = JaxbUtils.unmarshal(Events.class, rootResource); processEvents(events, rootResource, eventConfiguration, resourceDescription, denyIncludes); return events; } private void processEvents(final Events events, final Resource resource, final EventConfiguration eventConfiguration, final String resourceDescription, final boolean denyIncludes) { if (denyIncludes) { if (events.getGlobal() != null) { throw new ObjectRetrievalFailureException(Resource.class, resource, "The event resource " + resource + " included from the root event configuration file cannot have a 'global' element", null); } if (events.getEventFileCollection().size() > 0) { throw new ObjectRetrievalFailureException(Resource.class, resource, "The event resource " + resource + " included from the root event configuration file cannot include other configuration files: " + StringUtils.collectionToCommaDelimitedString(events.getEventFileCollection()), null); } } eventConfiguration.getEventFiles().put(resource, events); for (final Event event : events.getEventCollection()) { eventConfiguration.getEventConfData().put(event); } LogUtils.infof(this, "DefaultEventConfDao: Loaded %d events from %s event configuration resource: %s", events.getEventCollection().size(), resourceDescription, resource); eventConfiguration.incrementEventCount(events.getEventCount()); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#getEvents(java.lang.String) */ /** {@inheritDoc} */ public List<Event> getEvents(final String uei) { final List<Event> events = new ArrayList<Event>(); for (final Events fileEvents : getEventConfiguration().getEventFiles().values()) { for (final Event event : fileEvents.getEventCollection()) { if (event.getUei().equals(uei)) { events.add(event); } } } if (events.size() > 0) { return events; } else { return null; } } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#getEventUEIs() */ /** * <p>getEventUEIs</p> * * @return a {@link java.util.List} object. */ public List<String> getEventUEIs() { final List<String> eventUEIs = new ArrayList<String>(); for (final Events fileEvents : getEventConfiguration().getEventFiles().values()) { for (Event event : fileEvents.getEventCollection()) { eventUEIs.add(event.getUei()); } } return eventUEIs; } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#getEventLabels() */ /** * <p>getEventLabels</p> * * @return a {@link java.util.Map} object. */ public Map<String, String> getEventLabels() { final Map<String, String> eventLabels = new TreeMap<String, String>(); for (final Events fileEvents : getEventConfiguration().getEventFiles().values()) { for (final Event event : fileEvents.getEventCollection()) { eventLabels.put(event.getUei(), event.getEventLabel()); } } return Collections.unmodifiableMap(eventLabels); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#getEventLabel(java.lang.String) */ /** {@inheritDoc} */ public String getEventLabel(final String uei) { for (final Events fileEvents : getEventConfiguration().getEventFiles().values()) { for (final Event event : fileEvents.getEventCollection()) { if (event.getUei().equals(uei)) { return event.getEventLabel(); } } } return "No label found for " + uei; } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#saveCurrent() */ /** * <p>saveCurrent</p> */ public synchronized void saveCurrent() { for (final Entry<Resource, Events> entry : getEventConfiguration().getEventFiles().entrySet()) { final Resource resource = entry.getKey(); final Events fileEvents = entry.getValue(); final StringWriter stringWriter = new StringWriter(); JaxbUtils.marshal(fileEvents, stringWriter); if (stringWriter.toString() != null) { File file; try { file = resource.getFile(); } catch (final IOException e) { throw new DataAccessResourceFailureException("Event resource '" + resource + "' is not a file resource and cannot be saved. Nested exception: " + e, e); } final Writer fileWriter; try { fileWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); } catch (final IOException e) { throw new DataAccessResourceFailureException("Event file '" + file + "' could not be opened. Nested exception: " + e, e); } try { fileWriter.write(stringWriter.toString()); } catch (final IOException e) { throw new DataAccessResourceFailureException("Event file '" + file + "' could not be written to. Nested exception: " + e, e); } try { fileWriter.close(); } catch (final IOException e) { throw new DataAccessResourceFailureException("Event file '" + file + "' could not be closed. Nested exception: " + e, e); } } } final File programmaticStoreFile; try { programmaticStoreFile = getProgrammaticStoreConfigResource().getFile(); // Delete the programmatic store if it exists on disk, but isn't in the main store. This is for cleanliness if (programmaticStoreFile.exists() && (!getEventConfiguration().getEventFiles().containsKey(getProgrammaticStoreConfigResource()))) { LogUtils.infof(this, "Deleting programmatic store configuration file because it is no longer referenced in the root config file %s", getConfigResource()); if (!programmaticStoreFile.delete()) { LogUtils.warnf(this, "Attempted to delete %s, but failed.", programmaticStoreFile); } } } catch (final IOException e) { LogUtils.infof(this, "Programmatic store resource '%s'; not attempting to delete an unused programmatic store file if it exists (since we can't test for it).", getProgrammaticStoreConfigResource()); } /* * XXX Should we call reload so that the EventConfData object is updated * without the caller having to call reload() themselves? */ //reload(); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#getEventsByLabel() */ /** * <p>getEventsByLabel</p> * * @return a {@link java.util.List} object. */ public List<Event> getEventsByLabel() { final List<Event> list = new ArrayList<Event>(); for (final Events fileEvents : getEventConfiguration().getEventFiles().values()) { list.addAll(fileEvents.getEventCollection()); } Collections.sort(list, new EventLabelComparator()); return list; } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#addEvent(org.opennms.netmgt.xml.eventconf.Event) */ /** {@inheritDoc} */ public void addEvent(final Event event) { getRootEvents().addEvent(event); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#addEventToProgrammaticStore(org.opennms.netmgt.xml.eventconf.Event) */ /** {@inheritDoc} */ public void addEventToProgrammaticStore(final Event event) { // Check for, and possibly add the programmatic store to the in-memory structure if (!getEventConfiguration().getEventFiles().containsKey(getProgrammaticStoreConfigResource())) { // Programmatic store did not already exist. Add an empty Events object for that file getEventConfiguration().getEventFiles().put(getProgrammaticStoreConfigResource(), new Events()); } // Check for, and possibly add, the programmatic store event-file entry to the in-memory structure of the root config file final Events root = getRootEvents(); if (!root.getEventFileCollection().contains(getProgrammaticStoreRelativeUrl())) { root.addEventFile(getProgrammaticStoreRelativeUrl()); } // Finally, do what we came here to do getProgrammaticStoreEvents().addEvent(event); } private Events getRootEvents() { return getEventConfiguration().getEventFiles().get(getConfigResource()); } private Events getProgrammaticStoreEvents() { return getEventConfiguration().getEventFiles().get(getProgrammaticStoreConfigResource()); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#removeEventFromProgrammaticStore(org.opennms.netmgt.xml.eventconf.Event) */ /** {@inheritDoc} */ public boolean removeEventFromProgrammaticStore(final Event event) { if (!getEventConfiguration().getEventFiles().containsKey(getProgrammaticStoreConfigResource())) { return false; // Oops, doesn't exist } final Events events = getProgrammaticStoreEvents(); final boolean result = events.removeEvent(event); if (events.getEventCount() == 0) { getEventConfiguration().getEventFiles().remove(getProgrammaticStoreConfigResource()); getRootEvents().removeEventFile(getProgrammaticStoreRelativeUrl()); } return result; } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#isSecureTag(java.lang.String) */ /** {@inheritDoc} */ public boolean isSecureTag(final String tag) { return getEventConfiguration().getSecureTags().contains(tag); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#findByUei(java.lang.String) */ /** {@inheritDoc} */ public Event findByUei(final String uei) { return getEventConfiguration().getEventConfData().getEventByUEI(uei); } /* (non-Javadoc) * @see org.opennms.netmgt.config.EventConfDao#findByEvent(org.opennms.netmgt.xml.event.Event) */ /** {@inheritDoc} */ public Event findByEvent(final org.opennms.netmgt.xml.event.Event matchingEvent) { return getEventConfiguration().getEventConfData().getEvent(matchingEvent); } private Resource getProgrammaticStoreConfigResource() { if (m_programmaticStoreConfigResource == null) { try { m_programmaticStoreConfigResource = getConfigResource().createRelative(getProgrammaticStoreRelativeUrl()); } catch (final IOException e) { log().warn("Could not get a relative resource for the programmatic store configuration file using relative URL '" + getProgrammaticStoreRelativeUrl() + "': " + e, e); throw new DataAccessResourceFailureException("Could not get a relative resource for the programmatic store configuration file using relative URL '" + getProgrammaticStoreRelativeUrl() + "': " + e, e); } } return m_programmaticStoreConfigResource; } /** * <p>getProgrammaticStoreRelativeUrl</p> * * @return a {@link java.lang.String} object. */ public String getProgrammaticStoreRelativeUrl() { return m_programmaticStoreRelativeUrl; } /** * <p>setProgrammaticStoreRelativeUrl</p> * * @param programmaticStoreRelativeUrl a {@link java.lang.String} object. */ public void setProgrammaticStoreRelativeUrl(final String programmaticStoreRelativeUrl) { m_programmaticStoreRelativeUrl = programmaticStoreRelativeUrl; } private EventConfiguration getEventConfiguration() { return getContainer().getObject(); } private class EventResourceLoader extends DefaultResourceLoader { @Override public Resource getResource(final String location) { final String cleanLocation = StringUtils.cleanPath(location); // Check if this is a spring classpath:foo style resource // but first make sure if we're on windows that it's not // just a C:\foo path. boolean uriResource = false; if (org.opennms.core.utils.StringUtils.isLocalWindowsPath(cleanLocation)) { uriResource = false; } else if (cleanLocation.contains(":")) { // otherwise, something with a : is probably a spring URI resource uriResource = true; } if (uriResource) { return super.getResource(cleanLocation); } else { final File file = new File(cleanLocation); if (file.isAbsolute()) { return new FileSystemResource(file); } else { try { return getConfigResource().createRelative(cleanLocation); } catch (final IOException e) { throw new ObjectRetrievalFailureException(Resource.class, cleanLocation, "Resource location has a relative path, however the configResource does not reference a file, so the relative path cannot be resolved. The location is: " + cleanLocation, null); } } } } } }