Java tutorial
/** * Copyright (c) Cohesive Integrations, LLC * Copyright (c) Codice Foundation * * This 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 3 of the License, or any later version. * * 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. A copy of the GNU Lesser General Public License is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. * **/ package net.di2e.ecdr.commons; import java.io.IOException; import java.io.InputStream; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.activation.MimeType; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.HashCodeBuilder; import org.slf4j.LoggerFactory; import org.slf4j.ext.XLogger; import ddf.catalog.data.Attribute; import ddf.catalog.data.AttributeDescriptor; import ddf.catalog.data.BinaryContent; import ddf.catalog.data.Metacard; import ddf.catalog.data.MetacardType; import ddf.catalog.data.impl.AttributeImpl; import ddf.catalog.data.impl.MetacardTypeImpl; public class CDRMetacard implements Metacard, Serializable { private static final long serialVersionUID = 1L; private transient Map<String, Attribute> map = null; private transient Metacard wrappedMetacard; private transient MetacardType type; private String sourceId; public static final String METACARD_CONTENT_COLLECTION_ATTRIBUTE = "content-collections"; public static final String RESOURCE_MIME_TYPE = "resource-mime-type"; public static final String RESOURCE_TITLE = "resource-title"; static final String THUMBNAIL_LINK = "thumbnail-link"; public static final String THUMBNAIL_LENGTH = "thumbnail-length"; public static final String THUMBNAIL_MIMETYPE = "thumbnail-mimetype"; public static final String THUMBNAIL_LINK_TITLE = "thumbnail-link-title"; static final String METADATA_LINK = "metadata-link"; public static final String WRAP_METADATA = "wrap-metadata"; private static final String THUMBNAIL_N_A = "N/A"; private static final XLogger LOGGER = new XLogger(LoggerFactory.getLogger(CDRMetacard.class)); /** * Creates a {@link Metacard} with a and empty {@link Attribute}s. */ public CDRMetacard() { this(CDRMetacardType.CDR_METACARD); } /** * Creates a {@link Metacard} with the provided {@link MetacardType} and empty {@link Attribute} s. * * @param type * the {@link MetacardType} */ public CDRMetacard(MetacardType mt) { map = new HashMap<String, Attribute>(); if (mt != null) { this.type = mt; } else { throw new IllegalArgumentException(MetacardType.class.getName() + " instance should not be null."); } } /** * Creates a {@link Metacard} with the provided {@link Metacard}. * * @param metacard * the {@link Metacard} to create this new {@code Metacard} from */ public CDRMetacard(Metacard metacard) { this(CDRMetacardType.CDR_METACARD); if (metacard instanceof CDRMetacard) { map.putAll(((CDRMetacard) metacard).getMap()); setSourceId(metacard.getSourceId()); } else { this.wrappedMetacard = metacard; if (metacard.getMetacardType() != null) { this.type = metacard.getMetacardType(); } else { throw new IllegalArgumentException(MetacardType.class.getName() + " instance should not be null."); } } } public Map<String, Attribute> getMap() { return map; } public boolean hasLocation() { return getLocation() != null; } public boolean hasResource() { return getResourceURI() != null; } public String getResourceMIMETypeString() { Attribute attribute = getAttribute(RESOURCE_MIME_TYPE); if (attribute != null) { Serializable mimeType = attribute.getValue(); if (mimeType != null && mimeType instanceof String) { return (String) mimeType; } } return "application/unknown"; } public long getResourceSizeLong() { String size = getResourceSize(); if (StringUtils.isNotBlank(size)) { try { return Long.parseLong(getResourceSize()); } catch (NumberFormatException e) { LOGGER.debug("Could not parse resource size into integer from Metacard: " + size); } } return -1; } public boolean hasThumbnail() { // Order is important here especially if the original Metacard is using // Thumbnail links and doesn't pull the thumbnail until the getThumbnail // method is called return getAttribute(THUMBNAIL_LINK) != null || getThumbnail() != null; } public long getThumbnailLength() { long thumbnailSize = -1; Attribute attribute = getAttribute(THUMBNAIL_LENGTH); if (attribute != null) { thumbnailSize = (Long) attribute.getValue(); } if (thumbnailSize < 0) { byte[] thumbnail = getThumbnail(); thumbnailSize = thumbnail == null ? -1 : thumbnail.length; } return thumbnailSize; } public String getAtomId() { return "urn:catalog:id:" + getId(); } public URI getMetadataURL() { URI uri = null; String data = requestString(METADATA_LINK); if (data != null) { try { uri = new URI(data); } catch (URISyntaxException e) { LOGGER.warn("failed parsing URI string, returning null"); } } return uri; } public URI getThumbnailURL() { URI uri = null; String data = requestString(THUMBNAIL_LINK); if (data != null) { try { uri = new URI(data); } catch (URISyntaxException e) { LOGGER.warn("failed parsing URI string, returning null"); } } return uri; } public MimeType getThumbnailMIMEType() { Attribute attribute = getAttribute(THUMBNAIL_MIMETYPE); if (attribute != null) { return (MimeType) attribute.getValue(); } return null; } public String getThumbnailLinkTitle() { Attribute attribute = getAttribute(THUMBNAIL_LINK_TITLE); if (attribute != null) { return (String) attribute.getValue(); } return null; } @Override public String getMetadata() { String metadata = null; // We can't call getString here because it will get in an endless loop because of the explicit checks in // getAttribute (for METADATA), so instead we check the values ourselves then try to pull from the link (on // demand) Attribute metadataAttribute = (wrappedMetacard != null) ? wrappedMetacard.getAttribute(METADATA) : map.get(METADATA); if (metadataAttribute == null || StringUtils.isBlank(getAttributeValue(metadataAttribute, String.class))) { URI metadataURI = getMetadataURL(); if (metadataURI != null) { try (InputStream in = metadataURI.toURL().openStream()) { metadata = IOUtils.toString(in); if (getAttribute(CDRMetacard.WRAP_METADATA) != null) { StringBuilder sb = new StringBuilder(); sb.append("<xml>"); sb.append(metadata); sb.append("</xml>"); metadata = sb.toString(); } setAttribute(new AttributeImpl(Metacard.METADATA, metadata)); } catch (MalformedURLException e) { LOGGER.warn("Cannot read metadata due to Invalid metadata URL[" + metadataURI + "]: " + e.getMessage(), e); } catch (IOException e) { LOGGER.warn("Could not read metadata from remote URL[" + metadataURI + "] due to: " + e.getMessage(), e); } } } else { metadata = getAttributeValue(metadataAttribute, String.class); } return metadata; } @Override public Date getModifiedDate() { Date modified = requestDate(Metacard.MODIFIED); return modified == null ? new Date() : modified; } @Override public String getResourceSize() { String size = requestString(Metacard.RESOURCE_SIZE); return StringUtils.isNotBlank(size) && !THUMBNAIL_N_A.equalsIgnoreCase(size) ? size : null; } @Override public byte[] getThumbnail() { byte[] thumbnail = null; // We can't call getData here because it will get in an endless loop because of the explicit checks in // getAttribute (for THUMBNAIL), so instead we check the values ourselves then try to pull from the link (on // demand) Attribute thumbnailAttribute = (wrappedMetacard != null) ? wrappedMetacard.getAttribute(THUMBNAIL) : map.get(THUMBNAIL); if (thumbnailAttribute == null || getAttributeValue(thumbnailAttribute, byte[].class) == null) { URI thumbnailURI = getThumbnailURL(); if (thumbnailURI != null) { try (InputStream in = thumbnailURI.toURL().openStream()) { thumbnail = IOUtils.toByteArray(in); setAttribute(new AttributeImpl(Metacard.THUMBNAIL, thumbnail)); } catch (MalformedURLException e) { LOGGER.warn("Cannot read thumbnail due to invalid thumbnail URL[" + thumbnailURI + "]: " + e.getMessage(), e); } catch (IOException e) { LOGGER.warn("Could not read thumbnail from remote URL[" + thumbnailURI + "] due to: " + e.getMessage(), e); } } } else { thumbnail = getAttributeValue(thumbnailAttribute, byte[].class); } return thumbnail; } @Override public String getTitle() { String title = requestString(Metacard.TITLE); return title == null ? "No Title" : title; } @Override public Date getCreatedDate() { return requestDate(Metacard.CREATED); } /** * Sets the date/time the {@link Metacard} was created. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#CREATED}, created)) * </code> * * @param created * {@link Date} when this {@link Metacard} was created. * * @see Metacard#CREATED */ public void setCreatedDate(Date created) { setAttribute(Metacard.CREATED, created); } /** * Sets the date/time the {@link Metacard} was last modifed. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#MODIFIED}, modified)) * </code> * * @param modified * {@link Date} when this {@link Metacard} was last modified. * * @see Metacard#MODIFIED */ public void setModifiedDate(Date modified) { setAttribute(Metacard.MODIFIED, modified); } @Override public Date getExpirationDate() { return requestDate(Metacard.EXPIRATION); } /** * Sets the date/time this {@link Metacard} is no longer valid and could be removed. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#EXPIRATION}, expiration)) * </code> * * @param expiration * {@link Date} when the {@link Metacard} expires and should be removed from any stores. * * @see Metacard#EXPIRATION */ public void setExpirationDate(Date expiration) { setAttribute(Metacard.EXPIRATION, expiration); } @Override public Date getEffectiveDate() { return requestDate(Metacard.EFFECTIVE); } /** * Sets the date/time the {@link Metacard} was last known to be valid. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#EFFECTIVE}, effective)) * </code> * * @param effective * {@link Date} when the information represented by the {@link Metacard} was last known to be valid. * * @see Metacard#EFFECTIVE */ public void setEffectiveDate(Date effective) { setAttribute(Metacard.EFFECTIVE, effective); } @Override public String getId() { return requestString(Metacard.ID); } /** * Sets the ID of the {@link Metacard}. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#ID}, id)) * </code> * * @param id * unique identifier of the Metacard. * * @see Metacard#ID */ public void setId(String id) { setAttribute(Metacard.ID, id); } @Override public String getLocation() { return requestString(Metacard.GEOGRAPHY); } /** * Sets the WKT representation of the geometry. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#GEOGRAPHY}, wkt)) * </code> * * @param wkt * WKT-defined geospatial {@link String}, returns null if not applicable * * @see Metacard#GEOGRAPHY */ public void setLocation(String wkt) { setAttribute(Metacard.GEOGRAPHY, wkt); } @Override public String getSourceId() { return wrappedMetacard != null ? wrappedMetacard.getSourceId() : sourceId; } public void setSourceId(String id) { if (wrappedMetacard != null) { wrappedMetacard.setSourceId(id); } else { this.sourceId = id; } } /** * Sets the thumbnail associated with this {@link Metacard}. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#THUMBNAIL}, bytes)) * </code> * * @param bytes * thumbnail for the {@link Metacard}. * * @see Metacard#THUMBNAIL */ public void setThumbnail(byte[] bytes) { setAttribute(Metacard.THUMBNAIL, bytes); } /** * Sets the title of this {@link Metacard}. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#TITLE}, id)) * </code> * * @param title * Title of the {@link Metacard} * * @see Metacard#TITLE */ public void setTitle(String title) { setAttribute(Metacard.TITLE, title); } /** * Sets the metadata associated with this {@link Metacard}. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#METADATA}, metadata)) * </code> * * @param metadata * Associated metadata of the {@link Metacard} * * @see Metacard#METADATA */ public void setMetadata(String metadata) { setAttribute(Metacard.METADATA, metadata); } @Override public MetacardType getMetacardType() { return type; } /** * Sets the {@link MetacardType} of the {@link Metacard}. * * @param type * {@link MetacardType} of the {@link Metacard} * * @see MetacardType */ public void setType(MetacardType mt) { this.type = mt; } @Override public URI getContentTypeNamespace() { URI uri = null; String uriString = requestString(Metacard.TARGET_NAMESPACE); if (uriString != null && !uriString.isEmpty()) { uri = URI.create(uriString); } return uri; } /** * Some types of metadata use different content types. If utilized, sets the {@link URI} of the content type. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#TARGET_NAMESPACE}, targetNamespace)) * </code> * * @param targetNamespace * {@link URI} of the sub-type, null if unused * * @see Metacard#TARGET_NAMESPACE */ public void setTargetNamespace(URI targetNamespace) { setAttribute(Metacard.TARGET_NAMESPACE, targetNamespace.toASCIIString()); } @Override public String getContentTypeName() { String content = requestString(Metacard.CONTENT_TYPE); return StringUtils.isNotBlank(content) ? content : "Unknown"; } /** * Sets the name of the content type of the {@link Metacard}. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#CONTENT_TYPE}, contentType)) * </code> * * @param contentType * name of content type of the {@link Metacard} * * @see Metacard#CONTENT_TYPE */ public void setContentTypeName(String contentType) { setAttribute(Metacard.CONTENT_TYPE, contentType); } @Override public String getContentTypeVersion() { String version = requestString(Metacard.CONTENT_TYPE_VERSION); return StringUtils.isNotBlank(version) ? version : "Unknown"; } /** * Sets the version of the content type of the {@link Metacard}. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#CONTENT_TYPE_VERSION}, contentTypeVersion)) * </code> * * @param contentTypeVersion * version of content type of the {@link Metacard} * * @see Metacard#CONTENT_TYPE_VERSION */ public void setContentTypeVersion(String contentTypeVersion) { setAttribute(Metacard.CONTENT_TYPE_VERSION, contentTypeVersion); } @Override public URI getResourceURI() { URI uri = null; String data = requestString(Metacard.RESOURCE_URI); if (data != null) { try { uri = new URI(data); } catch (URISyntaxException e) { LOGGER.warn("failed parsing URI string, returning null"); } } return uri; } /** * * * @see Metacard#RESOURCE_URI */ public void setResourceURI(URI uri) { if (uri == null) { return; } setAttribute(RESOURCE_URI, uri.toString()); } public void setMetadataLinkURI(URI uri) { if (uri == null) { return; } setAttribute(METADATA_LINK, uri.toString()); } public void setThumbnailLinkURI(URI uri) { if (uri == null) { return; } setAttribute(THUMBNAIL_LINK, uri.toString()); } /** * Sets the size of the resource which may or may not contain a unit. <br/> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#RESOURCE_SIZE}, dadSize)) * </code> * * @param dadSize * {@link String} representation of the size * * @see Metacard#RESOURCE_SIZE */ public void setResourceSize(String dadSize) { setAttribute(RESOURCE_SIZE, dadSize); } /** * Returns the security relevant markings on the {@link ddf.catalog.data.Metacard}. * * @return security markings */ public Map<String, List<String>> getSecurity() { return requestData(Metacard.SECURITY, HashMap.class); } /** * Sets the security markings on this {@link Metacard}. <br /> * Convenience method for <code> * {@link #setAttribute setAttribute}(new {@link AttributeImpl}({@link Metacard#SECURITY}, security)) * </code> * * @param security */ public void setSecurity(HashMap<String, List<String>> security) { setAttribute(Metacard.SECURITY, security); } /** * The brains of the operation -- does the interaction with the map or the wrapped metacard. * * @param <T> * the type of the Attribute value expected * @param attributeName * the name of the {@link Attribute} to retrieve * @param returnType * the class that the value of the {@link AttributeType} is expected to be bound to * @return the value of the requested {@link Attribute} name */ protected <T> T requestData(String attributeName, Class<T> returnType) { Attribute attribute = getAttribute(attributeName); if (attribute == null) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Attribute " + attributeName + " was not found, returning null"); } return null; } return getAttributeValue(attribute, returnType); } protected <T> T getAttributeValue(Attribute attribute, Class<T> returnType) { Serializable data = attribute.getValue(); if (returnType.isAssignableFrom(data.getClass())) { return returnType.cast(data); } else { if (LOGGER.isDebugEnabled()) { LOGGER.trace(data.getClass().toString() + " can not be assigned to " + returnType.toString()); } } return null; } /** * Get {@link Date} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, Date.class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected Date requestDate(String key) { return requestData(key, Date.class); } /** * Get {@link Double} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, Double.class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected Double requestDouble(String key) { return requestData(key, Double.class); } /** * Get {@link BinaryContent} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, BinaryContent.class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected InputStream requestInputStream(String key) { BinaryContent data = requestData(key, BinaryContent.class); if (data != null) { return data.getInputStream(); } return null; } /** * Get {@link Integer} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, Integer.class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected Integer requestInteger(String key) { return requestData(key, Integer.class); } /** * Get {@link Long} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, Long.class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected Long requestLong(String key) { return requestData(key, Long.class); } /** * Get {@link String} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, String.class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected String requestString(String key) { return requestData(key, String.class); } /** * Get {@link byte[]} data from the map or wrapped metacard. <br/> * Convenience method for <code> * {@link #requestData requestData}(key, byte[].class)) * </code> * * @param key * the name of the {@link Attribute} to retrieve * @return the value of the requested {@link Attribute} name * */ protected byte[] requestBytes(String key) { return requestData(key, byte[].class); } @Override public Attribute getAttribute(String name) { if (Metacard.THUMBNAIL.equals(name)) { return new AttributeImpl(name, this.getThumbnail()); } else if (Metacard.METADATA.equals(name)) { return new AttributeImpl(name, this.getMetadata()); } else { return (wrappedMetacard != null) ? wrappedMetacard.getAttribute(name) : map.get(name); } } /** * Set an attribute via a name/value pair. * * @param name * the name of the {@link Attribute} * @param value * the value of the {@link Attribute} */ public void setAttribute(String name, Serializable value) { setAttribute(new AttributeImpl(name, value)); } @Override public void setAttribute(Attribute attribute) { if (attribute == null) { return; } if (wrappedMetacard != null) { wrappedMetacard.setAttribute(attribute); } else { String name = attribute.getName(); Serializable value = attribute.getValue(); if (name != null) { if (value != null) { map.put(name, attribute); } else { map.remove(name); } } } } private void writeObject(ObjectOutputStream stream) throws IOException { /* * defaultWriteObject() is invoked for greater flexibility and compatibility. See the *Serialization Note* in * class Javadoc. */ stream.defaultWriteObject(); /* * Cannot allow unknown implementations of MetacardType to be serialized. Must convert them to our * implementation to guarantee it is serializing the logical representation and not the physical representation. */ if (type instanceof MetacardTypeImpl) { stream.writeObject(type); } else { MetacardTypeImpl mt = new MetacardTypeImpl(type.getName(), type.getAttributeDescriptors()); stream.writeObject(mt); } if (map != null) { stream.writeInt(map.size()); for (Attribute attribute : this.map.values()) { stream.writeObject(attribute); } } else { if (wrappedMetacard != null && wrappedMetacard.getMetacardType() != null) { MetacardType metacardType = wrappedMetacard.getMetacardType(); List<Attribute> attributes = new ArrayList<Attribute>(); if (metacardType.getAttributeDescriptors() == null) { // no descriptors, means no attributes can be defined. // no attributes defined, means no attributes written to // disk stream.writeInt(0); } else { for (AttributeDescriptor ad : metacardType.getAttributeDescriptors()) { Attribute attribute = wrappedMetacard.getAttribute(ad.getName()); if (attribute != null) { attributes.add(attribute); } } // Must loop again because the size of the attributes list // is not known until list has been fully populated. stream.writeInt(attributes.size()); for (Attribute attribute : attributes) { stream.writeObject(attribute); } } } } } /** * Deserializes this instance. * * @param stream * the {@link ObjectInputStream} that contains the bytes of the object * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { /* * defaultReadObject() is invoked for greater flexibility and compatibility. See the *Serialization Note* in * class Javadoc. */ stream.defaultReadObject(); map = new HashMap<String, Attribute>(); wrappedMetacard = null; type = (MetacardType) stream.readObject(); if (type == null) { throw new InvalidObjectException(MetacardType.class.getName() + " instance cannot be null."); } int numElements = stream.readInt(); for (int i = 0; i < numElements; i++) { Attribute attribute = (Attribute) stream.readObject(); if (attribute != null) { AttributeDescriptor attributeDescriptor = getMetacardType() .getAttributeDescriptor(attribute.getName()); if (attributeDescriptor != null && attribute.getValue() != null) { attributeDescriptor.getType().getAttributeFormat(); attributeDescriptor.getType().getClass(); } } setAttribute(attribute); } } public int hashCode() { return new HashCodeBuilder(17, 37).append(this.getId()).append(this.getMetacardType()) .append(this.getMetadata()).toHashCode(); } }