/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-impl/src/main/java/org/sakaiproject/content/impl/serialize/impl/conversion/SAXSerializableResourceAccess.java $ * $Id: SAXSerializableResourceAccess.java 106560 2012-04-05 10:48:00Z matthew.buckett@oucs.ox.ac.uk $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.content.impl.serialize.impl.conversion; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.content.api.ResourceType; import org.sakaiproject.content.api.ResourceTypeRegistry; import org.sakaiproject.content.api.GroupAwareEntity.AccessMode; import org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess; import org.sakaiproject.content.impl.serialize.impl.Type1BaseContentResourceSerializer; import org.sakaiproject.entity.api.serialize.EntityParseException; import org.sakaiproject.entity.api.serialize.SerializableEntity; import org.sakaiproject.time.api.Time; import org.sakaiproject.util.StringUtil; import org.sakaiproject.util.Xml; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * @author ieb */ public class SAXSerializableResourceAccess implements SerializableResourceAccess, SerializableEntity { protected static final long END_OF_TIME = 8000L * 365L * 24L * 60L * 60L * 1000L; protected static final long START_OF_TIME = 365L * 24L * 60L * 60L * 1000L; protected static final Log log = LogFactory .getLog(SAXSerializableResourceAccess.class); private Type1BaseContentResourceSerializer type1ResourceSerializer; private SAXParserFactory parserFactory; private AccessMode accessMode = AccessMode.INHERITED; private long contentLength; private String contentType; private String filePath; private Collection<String> group = new ArrayList<String>(); private boolean hidden; private String id; private SAXSerializablePropertiesAccess saxSerializableProperties = new SAXSerializablePropertiesAccess(); private Time releaseDate; private String resourceType; private Time retractDate; private byte[] body; private ConversionTimeService conversionTimeService; public SAXSerializableResourceAccess() { type1ResourceSerializer = new Type1BaseContentResourceSerializer(); conversionTimeService = new ConversionTimeService(); type1ResourceSerializer.setTimeService(conversionTimeService); } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getResourceTypeRegistry() */ public ResourceTypeRegistry getResourceTypeRegistry() { return null; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableAccess() */ public AccessMode getSerializableAccess() { return accessMode; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableBody() */ public byte[] getSerializableBody() { return body; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableContentLength() */ public long getSerializableContentLength() { return contentLength; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableContentType() */ public String getSerializableContentType() { return contentType; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableFilePath() */ public String getSerializableFilePath() { return filePath; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableGroup() */ public Collection<String> getSerializableGroup() { return group; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableHidden() */ public boolean getSerializableHidden() { return hidden; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableId() */ public String getSerializableId() { return id; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableProperties() */ public SerializableEntity getSerializableProperties() { return saxSerializableProperties; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableReleaseDate() */ public Time getSerializableReleaseDate() { return releaseDate; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableResourceType() */ public String getSerializableResourceType() { return resourceType; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#getSerializableRetractDate() */ public Time getSerializableRetractDate() { return retractDate; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableAccess(org.sakaiproject.content.api.GroupAwareEntity.AccessMode) */ public void setSerializableAccess(AccessMode access) { accessMode = access; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableBody(byte[]) */ public void setSerializableBody(byte[] body) { } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableContentLength(long) */ public void setSerializableContentLength(long contentLength) { this.contentLength = contentLength; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableContentType(java.lang.String) */ public void setSerializableContentType(String contentType) { this.contentType = contentType; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableFilePath(java.lang.String) */ public void setSerializableFilePath(String filePath) { this.filePath = filePath; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableGroups(java.util.Collection) */ public void setSerializableGroups(Collection<String> groups) { this.group = groups; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableHidden(boolean) */ public void setSerializableHidden(boolean hidden) { this.hidden = hidden; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableId(java.lang.String) */ public void setSerializableId(String id) { this.id = id; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableReleaseDate(org.sakaiproject.time.api.Time) */ public void setSerializableReleaseDate(Time releaseDate) { this.releaseDate = releaseDate; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableResourceType(java.lang.String) */ public void setSerializableResourceType(String resourceType) { this.resourceType = resourceType; } /* * (non-Javadoc) * * @see org.sakaiproject.content.impl.serialize.api.SerializableResourceAccess#setSerializableRetractDate(org.sakaiproject.time.api.Time) */ public void setSerializableRetractDate(Time retractDate) { this.retractDate = retractDate; } /** * @param xml * @throws EntityParseException */ public void parse(String xml) throws Exception { Reader r = new StringReader(xml); InputSource ss = new InputSource(r); SAXParser p = null; if (parserFactory == null) { parserFactory = SAXParserFactory.newInstance(); parserFactory.setNamespaceAware(false); parserFactory.setValidating(false); } try { p = parserFactory.newSAXParser(); } catch (ParserConfigurationException e) { throw new SAXException("Failed to get a parser ", e); } final Map<String, Object> props = new HashMap<String, Object>(); saxSerializableProperties.setSerializableProperties(props); p.parse(ss, new DefaultHandler() { /* * (non-Javadoc) * * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if(qName == null) { // will be ignored } else { qName = qName.trim(); } if ("property".equalsIgnoreCase(qName)) { String name = attributes.getValue("name"); String enc = StringUtils.trimToNull(attributes.getValue("enc")); String value = null; if ("BASE64".equalsIgnoreCase(enc)) { String charset = StringUtils.trimToNull(attributes .getValue("charset")); if (charset == null) charset = "UTF-8"; value = Xml.decode(charset, attributes.getValue("value")); } else { value = attributes.getValue("value"); } // deal with multiple valued lists if ("list".equals(attributes.getValue("list"))) { // accumulate multiple values in a list Object current = props.get(name); // if we don't have a value yet, make a list to // hold // this one if (current == null) { List values = new Vector(); props.put(name, values); values.add(value); } // if we do and it's a list, add this one else if (current instanceof List) { ((List) current).add(value); } // if it's not a list, it's wrong! else { log.warn("construct(el): value set not a list: " + name); } } else { props.put(name, value); } } else if ("resource".equalsIgnoreCase(qName)) { id = attributes.getValue("id"); contentType = StringUtils.trimToNull(attributes .getValue("content-type")); contentLength = 0; try { contentLength = Integer.parseInt(attributes .getValue("content-length")); } catch (Exception ignore) { } resourceType = StringUtils.trimToNull(attributes .getValue("resource-type")); if(resourceType == null) { resourceType = ResourceType.TYPE_UPLOAD; } String enc = StringUtils.trimToNull(attributes.getValue("body")); if (enc != null) { byte[] decoded = null; try { decoded = Base64.decodeBase64(enc.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { log.error(e); } body = new byte[(int) contentLength]; System.arraycopy(decoded, 0, body, 0, (int) contentLength); } filePath = StringUtils.trimToNull(attributes.getValue("filePath")); accessMode = AccessMode.INHERITED; String access_mode = attributes.getValue("sakai:access_mode"); if (access_mode != null && !access_mode.trim().equals("")) { accessMode = AccessMode.fromString(access_mode); } if (accessMode == null || AccessMode.SITE == accessMode) { accessMode = AccessMode.INHERITED; } String shidden = attributes.getValue("sakai:hidden"); hidden = shidden != null && !shidden.trim().equals("") && !Boolean.FALSE.toString().equalsIgnoreCase(shidden); if (hidden) { releaseDate = null; retractDate = null; } else { // extract release date String date0 = attributes.getValue("sakai:release_date"); if (date0 != null && !date0.trim().equals("")) { releaseDate = conversionTimeService.newTimeGmt(date0); if (releaseDate.getTime() <= START_OF_TIME) { releaseDate = null; } } // extract retract date String date1 = attributes.getValue("sakai:retract_date"); if (date1 != null && !date1.trim().equals("")) { retractDate = conversionTimeService.newTimeGmt(date1); if (retractDate.getTime() >= END_OF_TIME) { retractDate = null; } } } } else if ("sakai:authzGroup".equalsIgnoreCase(qName)) { if (group == null) { group = new ArrayList<String>(); } group.add(attributes.getValue("sakai:group_name")); } else if ("properties".equalsIgnoreCase(qName)) { } else if ("members".equalsIgnoreCase(qName)) { // ignore } else if ("member".equalsIgnoreCase(qName)) { // ignore } else { log.warn("Unexpected Element \"" + qName + "\""); } } }); } /** * @param sax2 * @throws Exception */ public void check(SAXSerializableResourceAccess sax2) throws Exception { StringBuilder sb = new StringBuilder(); if ((accessMode != null && !accessMode.equals(sax2.accessMode)) || (accessMode == null && sax2.accessMode != null) || (accessMode != null && sax2.accessMode == null)) { sb.append(" ").append( "Access Mode not equal [" + accessMode + "]!=[" + sax2.accessMode + "]").append("\n"); } if (this.hidden != sax2.hidden) { sb.append(" ").append( "Hidden not equal [" + hidden + "]!=[" + sax2.hidden + "]").append( "\n"); } if ((id != null && !id.equals(sax2.id)) || (id == null && sax2.id != null) || (id != null && sax2.id == null)) { sb.append(" ").append("ID not equal [" + id + "]!=[" + sax2.id + "]") .append("\n"); } if ((releaseDate != null && sax2.releaseDate != null && (this.releaseDate .getTime() != sax2.releaseDate.getTime())) || (releaseDate == null && sax2.releaseDate != null) || (releaseDate != null && sax2.releaseDate == null)) { sb.append(" ") .append( "Release not equal [" + releaseDate + "]!=[" + sax2.releaseDate + "]").append("\n"); } if ((retractDate != null && sax2.retractDate != null && (this.retractDate .getTime() != sax2.retractDate.getTime())) || (retractDate == null && sax2.retractDate != null) || (retractDate != null && sax2.retractDate == null)) { sb.append(" ") .append( "Release not equal [" + retractDate + "]!=[" + sax2.retractDate + "]").append("\n"); } if ((resourceType != null && !resourceType.equals(sax2.resourceType)) || (resourceType == null && sax2.resourceType != null) || (resourceType != null && sax2.resourceType == null)) { sb.append(" ").append( "resourceType not equal [" + resourceType + "]!=[" + sax2.resourceType + "]") .append("\n"); } if ((group == null && sax2.group != null) || (group != null && sax2.group == null)) { sb.append(" ").append( "group not equal [" + group + "]!=[" + sax2.group + "]").append("\n"); } if (group != null && sax2.group != null) { if (this.group.size() != sax2.group.size()) { sb.append(" ").append( "group not equal [" + group + "]!=[" + sax2.group + "]").append( "\n"); } else { for (String g : group) { if (!sax2.group.contains(g)) { sb.append(" ").append( "group not present in other object [" + g + "]").append( "\n"); } } for (String g : sax2.group) { if (!group.contains(g)) { sb.append(" ").append( "group not present in this object [" + g + "]").append( "\n"); } } } } if ((body == null && sax2.body != null) || (body != null && sax2.body == null)) { sb.append(" ").append( "group not equal [" + Arrays.toString(body) + "]!=[" + Arrays.toString(sax2.body) + "]").append("\n"); } if (body != null && sax2.body != null) { if (this.body.length != sax2.body.length) { sb.append(" ").append( "group not equal [" + Arrays.toString(body) + "]!=[" + Arrays.toString(sax2.body) + "]").append( "\n"); } else { for (int i = 0; i < body.length; i++) { if (body[i] != sax2.body[i]) { sb.append(" ").append( "group not equal [" + body[i] + "]!=[" + sax2.body[i] + "]").append("\n"); } } } } if (this.contentLength != sax2.contentLength) { sb.append(" ").append( "ContentLength not equal [" + contentLength + "]!=[" + sax2.contentLength + "]").append("\n"); } if(contentType != null && contentType.trim().equals("")) { contentType = null; } if(sax2.contentType != null && sax2.contentType.trim().equals("")) { sax2.contentType = null; } if ((contentType != null && !contentType.equals(sax2.contentType)) || (contentType == null && sax2.contentType != null) || (contentType != null && sax2.contentType == null)) { sb.append(" ").append( "Content Type not equal [" + contentType + "]!=[" + sax2.contentType + "]").append("\n"); } if(filePath != null && filePath.trim().equals("")) { filePath = null; } if(sax2.filePath != null && sax2.filePath.trim().equals("")) { sax2.filePath = null; } if ((filePath != null && !filePath.equals(sax2.filePath)) || (filePath == null && sax2.filePath != null) || (filePath != null && sax2.filePath == null)) { sb.append(" ").append( "FilePath not equal [" + filePath + "]!=[" + sax2.filePath + "]") .append("\n"); } if (sb.length() != 0) { log.error(sb.toString()); throw new Exception("Serialization Items do not match "); } saxSerializableProperties.check((SAXSerializablePropertiesAccess) sax2 .getSerializableProperties()); } }