/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program 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. * * Copyright 2008 - 2009 Pentaho Corporation. All rights reserved. */ package org.pentaho.pac.server.biplatformproxy.xmlserializer; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.pac.client.scheduler.model.Schedule; import org.pentaho.pac.common.SchedulerServiceException; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SubscriptionXmlSerializer { private static final Log logger = LogFactory.getLog(SubscriptionXmlSerializer.class); private static final ThreadLocal<SAXParserFactory> SAX_FACTORY = new ThreadLocal<SAXParserFactory>(); public SubscriptionXmlSerializer() { } public Map<String,Schedule> getSubscriptionSchedulesFromXml( String strXml ) throws XmlSerializerException, SchedulerServiceException { SubscriptionScheduleParserHandler subscriptionSchedHandler = null; try { detectSubscriptionExceptionInXml( strXml ); subscriptionSchedHandler = parseSubscriptionScheduleJobsXml( strXml ); } catch (SAXException e) { logger.error( e.getMessage() ); throw new XmlSerializerException( e.getMessage() ); } catch (IOException e) { logger.error( e.getMessage() ); throw new XmlSerializerException( e.getMessage() ); } catch (ParserConfigurationException e) { logger.error( e.getMessage() ); throw new XmlSerializerException( e.getMessage() ); } return subscriptionSchedHandler.schedules; } public void detectSubscriptionExceptionInXml( String strXml ) throws SchedulerServiceException { SubscriptionExceptionParserHandler exceptionHandler; try { exceptionHandler = parseSubscriptionExceptionXml( strXml ); } catch (SAXException e) { logger.error( e.getMessage() ); throw new SchedulerServiceException( e.getMessage() ); } catch (IOException e) { logger.error( e.getMessage() ); throw new SchedulerServiceException( e.getMessage() ); } catch (ParserConfigurationException e) { logger.error( e.getMessage() ); throw new SchedulerServiceException( e.getMessage() ); } if ( null != exceptionHandler.exceptionMessage ) { throw new SchedulerServiceException( exceptionHandler.exceptionMessage ); } } public void detectSubscriptionErrorInXml( String strXml ) throws SchedulerServiceException { SubscriptionErrorParserHandler errorHandler; try { errorHandler = parseSubscriptionErrorXml( strXml ); } catch (SAXException e) { logger.error( e.getMessage() ); throw new SchedulerServiceException( e.getMessage() ); } catch (IOException e) { logger.error( e.getMessage() ); throw new SchedulerServiceException( e.getMessage() ); } catch (ParserConfigurationException e) { logger.error( e.getMessage() ); throw new SchedulerServiceException( e.getMessage() ); } if ( null != errorHandler.errorMessage ) { throw new SchedulerServiceException( errorHandler.errorMessage ); } } /** * Get a SAX Parser Factory. This method implements a thread-relative * singleton. * * NOTE: Need sax parser factory per thread for thread safety. * See: http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/parsers/SAXParserFactory.html * @return */ private static SAXParserFactory getSAXParserFactory() { SAXParserFactory threadLocalSAXParserFactory = SAX_FACTORY.get(); if ( null == threadLocalSAXParserFactory ) { threadLocalSAXParserFactory = SAXParserFactory.newInstance(); SAX_FACTORY.set( threadLocalSAXParserFactory ); } return threadLocalSAXParserFactory; } private SubscriptionScheduleParserHandler parseSubscriptionScheduleJobsXml( String strXml ) throws SAXException, IOException, ParserConfigurationException { SAXParser parser = getSAXParserFactory().newSAXParser(); SubscriptionScheduleParserHandler h = new SubscriptionScheduleParserHandler(); // TODO sbarkdull, need to set encoding // String encoding = CleanXmlHelper.getEncoding( strXml ); // InputStream is = new ByteArrayInputStream( strXml.getBytes( encoding ) ); InputStream is = new ByteArrayInputStream( strXml.getBytes( "UTF-8" ) ); //$NON-NLS-1$ parser.parse( is, h ); return h; } /* <subscriptionAdmin> <listSchedules> <scheduledJobs> <job subscriberCount="0" triggerState="0"> <schedId>eb7ef4b4-4867-11dd-8266-ff19a6feec31</schedId> <schedRef>snb name 2</schedRef> <title>snb title 2</title> <desc>snb description 2</desc> <group>snb group 2</group> <cron>0 0 12 3 * ?</cron> <content> <action solution="samples" path="getting-started" action="HelloWorld.xaction" /> </content> <nextFireTime>Thu Jul 03 12:00:00 EDT 2008</nextFireTime> <prevFireTime>Never</prevFireTime> <jobId>snb name 2</jobId> </job> </scheduledJobs> <unScheduledJobs> <job subscriberCount="0" triggerState="-1"> <schedId>da95463f-4867-11dd-8266-ff19a6feec31</schedId> <schedRef>snb name</schedRef> <title>snb title</title> <desc>snb description</desc> <group>snb group</group> <cron>0 0 12 3 * *</cron> </job> </unScheduledJobs> <message result="WARNING">There are subscriptions that are not scheduled to run.</message> </listSchedules> <message result="INFO">There is no subscription content defined.</message> <schedulerStatus state="0" /> <returnURL>&schedulerAction=listSchedules&_TRUST_USER_=joe</returnURL> </subscriptionAdmin> */ private static class SubscriptionScheduleParserHandler extends DefaultHandler { public String currentText = null; public Map<String,Schedule> schedules = new HashMap<String,Schedule>(); private Schedule currentSchedule = null; private boolean isInSubscriptionAdmin = false; private boolean isInListSchedules = false; private boolean isInScheduledJobs = false; private boolean isInContent = false; private List<String> actionList = null; public SubscriptionScheduleParserHandler() { } public void characters( char[] ch, int startIdx, int length ) { currentText = String.valueOf( ch, startIdx, length ); } public void endElement(String uri, String localName, String qName ) throws SAXException { if ( qName.equals( "subscriptionAdmin" ) ) { //$NON-NLS-1$ isInSubscriptionAdmin = false; } else if ( qName.equals( "listSchedules" ) ) { //$NON-NLS-1$ isInListSchedules = false; } else if ( qName.equals( "scheduledJobs" ) ) { //$NON-NLS-1$ isInScheduledJobs = false; } else { if ( isInSubscriptionAdmin && isInListSchedules && isInScheduledJobs ) { if ( qName.equals( "job" ) ) { //$NON-NLS-1$ assert currentSchedule.getJobName() != null : "Error, job name cannot be null."; //$NON-NLS-1$ schedules.put( currentSchedule.getJobName(), currentSchedule ); currentSchedule = null; } else if ( qName.equals( "schedId" ) ) { //$NON-NLS-1$ currentSchedule.setSchedId( currentText ); } else if ( qName.equals( "schedRef" ) ) { //$NON-NLS-1$ currentSchedule.setJobName( currentText ); currentSchedule.setTriggerName( currentText ); } else if ( qName.equals( "title" ) ) { //$NON-NLS-1$ currentSchedule.setTitle( currentText ); } else if ( qName.equals( "desc" ) ) { //$NON-NLS-1$ currentSchedule.setDescription( currentText ); } else if ( qName.equals( "group" ) ) { //$NON-NLS-1$ currentSchedule.setJobGroup( currentText ); } else if ( qName.equals( "cron" ) ) { //$NON-NLS-1$ currentSchedule.setCronString( currentText ); } else if ( qName.equals( "repeat-count" ) ) { //$NON-NLS-1$ currentSchedule.setRepeatCount( currentText ); } else if ( qName.equals( "repeat-time-millisecs" ) ) { //$NON-NLS-1$ currentSchedule.setRepeatInterval( currentText ); } else if ( qName.equals( "nextFireTime" ) ) { //$NON-NLS-1$ currentSchedule.setNextFireTime( currentText ); } else if ( qName.equals( "prevFireTime" ) ) { //$NON-NLS-1$ currentSchedule.setPrevFireTime( currentText ); } else if ( qName.equals( "jobId" ) ) { //$NON-NLS-1$ currentSchedule.setJobName( currentText ); } else if ( qName.equals( "start-date" ) ) { //$NON-NLS-1$ currentSchedule.setStartDate( currentText ); } else if ( qName.equals( "end-date" ) ) { //$NON-NLS-1$ currentSchedule.setEndDate( currentText ); } else if ( qName.equals( "content" ) ) { //$NON-NLS-1$ isInContent = false; currentSchedule.setActionsList( actionList ); actionList = null; } } } } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ( qName.equals( "subscriptionAdmin" ) ) { //$NON-NLS-1$ isInSubscriptionAdmin = true; } else if ( qName.equals( "listSchedules" ) ) { //$NON-NLS-1$ isInListSchedules = true; } else if ( qName.equals( "scheduledJobs" ) ) { //$NON-NLS-1$ isInScheduledJobs = true; } else { if ( isInSubscriptionAdmin && isInListSchedules && isInScheduledJobs ) { if ( qName.equals( "job" ) ) { //$NON-NLS-1$ currentSchedule = new Schedule(); String val = attributes.getValue( "subscriberCount" ); //$NON-NLS-1$ currentSchedule.setSubscriberCount( val ); val = attributes.getValue( "triggerState" ); //$NON-NLS-1$ currentSchedule.setTriggerState( XmlSerializerUtil.triggerInt2Name( val ) ); } else if ( qName.equals( "content" ) ) { //$NON-NLS-1$ isInContent = true; actionList = new ArrayList<String>(); } else if ( qName.equals( "action" ) ) { //$NON-NLS-1$ if ( isInContent ) { String solution = attributes.getValue( "solution" ); //$NON-NLS-1$ String path = attributes.getValue( "path" ); //$NON-NLS-1$ String action = attributes.getValue( "action" ); //$NON-NLS-1$ actionList.add( makePath( solution, path, action )); } } } } } private static String makePath( String solution, String path, String action ) { return solution + "/" + path + "/" + action; //$NON-NLS-1$//$NON-NLS-2$ } } private SubscriptionExceptionParserHandler parseSubscriptionExceptionXml( String strXml ) throws SAXException, IOException, ParserConfigurationException { SAXParser parser = getSAXParserFactory().newSAXParser(); SubscriptionExceptionParserHandler h = new SubscriptionExceptionParserHandler(); // TODO sbarkdull, need to set encoding // String encoding = CleanXmlHelper.getEncoding( strXml ); // InputStream is = new ByteArrayInputStream( strXml.getBytes( encoding ) ); InputStream is = new ByteArrayInputStream( strXml.getBytes( "UTF-8" ) ); //$NON-NLS-1$ parser.parse( is, h ); return h; } /** * <exception> * <message result="ERROR"> * Error, these parameters are missing: actionRefs * </message> * </exception> * * @author Steven Barkdull * */ private static class SubscriptionExceptionParserHandler extends DefaultHandler { private String currentText = null; public String exceptionMessage = null; private boolean isException = false; private boolean isMessage = false; public SubscriptionExceptionParserHandler() { } public void characters( char[] ch, int startIdx, int length ) { currentText = String.valueOf( ch, startIdx, length ); } public void endElement(String uri, String localName, String qName ) throws SAXException { if ( qName.equals( "exception" ) ) { //$NON-NLS-1$ isException = false; } else if ( qName.equals( "message" ) ) { //$NON-NLS-1$ if ( isException && isMessage ) { exceptionMessage = currentText; } isMessage = false; } } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ( qName.equals( "exception" ) ) { //$NON-NLS-1$ isException = true; } else if ( qName.equals( "message" ) ) { //$NON-NLS-1$ isMessage = true; } } } private SubscriptionErrorParserHandler parseSubscriptionErrorXml( String strXml ) throws SAXException, IOException, ParserConfigurationException { SAXParser parser = getSAXParserFactory().newSAXParser(); SubscriptionErrorParserHandler h = new SubscriptionErrorParserHandler(); // TODO sbarkdull, need to set encoding // String encoding = CleanXmlHelper.getEncoding( strXml ); // InputStream is = new ByteArrayInputStream( strXml.getBytes( encoding ) ); InputStream is = new ByteArrayInputStream( strXml.getBytes( "UTF-8" ) ); //$NON-NLS-1$ parser.parse( is, h ); return h; } /** * <?xml version="1.0" encoding="UTF-8"?> * <commandResult result="ERROR"> * <message result="ERROR"> * Unable to complete request: * org.hibernate.exception.ConstraintViolationException: could not * delete: * [org.pentaho.platform.repository.subscription.SubscribeContent#34c6b532-5cd5-11dd-9b0d-53348ed62413] * </message> * </commandResult> * @author Steven Barkdull * */ private static class SubscriptionErrorParserHandler extends DefaultHandler { private String currentText = null; public String errorMessage = null; private boolean isCommandResult = false; private boolean isErrorMessage = false; private boolean isMessage = false; public SubscriptionErrorParserHandler() { } public void characters( char[] ch, int startIdx, int length ) { currentText = String.valueOf( ch, startIdx, length ); } public void endElement(String uri, String localName, String qName ) throws SAXException { if ( qName.equals( "commandResult" ) ) { //$NON-NLS-1$ isCommandResult = false; } else if ( qName.equals( "message" ) ) { //$NON-NLS-1$ if ( isCommandResult && isMessage && isErrorMessage ) { errorMessage = currentText; isErrorMessage = false; } isMessage = false; } } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ( qName.equals( "commandResult" ) ) { //$NON-NLS-1$ isCommandResult = true; } else if ( qName.equals( "message" ) ) { //$NON-NLS-1$ isMessage = true; // result="ERROR" isErrorMessage = attributes.getValue( "result" ).equals( "ERROR" ); //$NON-NLS-1$ //$NON-NLS-2$ } } } }