/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.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 General Public License for more details.
*
*
* Copyright 2005-2008 Pentaho Corporation. All rights reserved.
*
* @created Jun 17, 2005
* @author Marc Batchelor
*
*/
package org.pentaho.platform.repository.content;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.pentaho.commons.connection.IPentahoStreamSource;
import org.pentaho.platform.api.repository.ContentException;
import org.pentaho.platform.api.repository.IContentItem;
import org.pentaho.platform.api.repository.ISearchable;
import org.pentaho.platform.engine.core.system.PentahoBase;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.repository.hibernate.HibernateUtil;
import org.pentaho.platform.repository.messages.Messages;
import org.pentaho.platform.util.UUIDUtil;
import org.pentaho.platform.util.messages.MessageUtil;
import org.pentaho.platform.util.web.MimeHelper;
public class ContentItem extends PentahoBase implements IContentItem, ISearchable {
private static final long serialVersionUID = 823604019645900631L;
private static final Log logger = LogFactory.getLog(ContentItem.class);
private String id; // Required
private ContentLocation parent; // Required
private String path; // Derived
private String name; // Required
private String title; // required
private String mimeType; // Required
private String url; // Optional
private String extension; // Required
private ContentItemFile latestFile;
private OutputStream outputStream = null;
private int revision = -1; // Hibernate Revision
private int writeMode; // Indicates whether this kind of file is versioned
private int latestVersionNum; // This is now defunct. Don't bother using this anymore
private static final String PATH_BUILDER = "{0}/{1}"; //$NON-NLS-1$
private static final String[] SearchableColumns = { "name", //$NON-NLS-1$
"title", //$NON-NLS-1$
"path" }; //$NON-NLS-1$
private static final String SearchableTable = "org.pentaho.platform.repository.content.ContentItem"; //$NON-NLS-1$
private static final String SearchablePhraseNamedQuery = "org.pentaho.platform.repository.content.ContentItem.itemSearcher"; //$NON-NLS-1$
/* Constructor for Hibernate */
protected ContentItem() {
}
/**
* Constructor
*
* @param cntId
* @param theParent
* @param theName
* @param title
* @param mType
* @param url
*/
protected ContentItem(final String cntId, final ContentLocation theParent, final String theName, final String title,
final String mType, final String extension, final String url, final int writeMode) {
name = theName;
mimeType = mType;
parent = theParent;
id = cntId;
String extnSep = "."; //$NON-NLS-1$
if (!extension.startsWith(extnSep)) {
this.extension = extnSep + extension;
} else {
this.extension = extension;
}
this.title = title;
this.url = url;
this.path = MessageUtil.formatMessage(ContentItem.PATH_BUILDER, parent.getDirPath(), this.getName());
this.writeMode = writeMode;
}
public List getMessages() {
return null;
}
protected ContentItemFile newContentFile(final String actionName) {
String fileGuid = UUIDUtil.getUUIDAsString();
String fileName = fileGuid + this.getExtension();
ContentItemFile theFile = new ContentItemFile(this, fileGuid, parent.getDirPath(), fileName, actionName);
this.latestFile = theFile;
HibernateUtil.makePersistent(theFile);
return theFile;
}
public InputStream getInputStream() throws ContentException {
ContentItemFile cif = getLatestFile();
if (this.latestFile == null) {
throw new ContentException(Messages.getInstance().getErrorString("CONTITEM.ERROR_0001_NO_EXISTING_FILES", this.getName())); //$NON-NLS-1$
}
return cif.getInputStream();
}
public IPentahoStreamSource getDataSource() {
ContentItemFile cif = getLatestFile();
if (this.latestFile == null) {
throw new ContentException(Messages.getInstance().getErrorString("CONTITEM.ERROR_0001_NO_EXISTING_FILES", this.getName())); //$NON-NLS-1$
}
String fullPath = PentahoSystem.getApplicationContext().getFileOutputPath(
"system/content/" + cif.getOsPath() + "/" + cif.getId() + extension); //$NON-NLS-1$ //$NON-NLS-2$
return new FilePentahoStreamSource(fullPath);
}
public static final class FilePentahoStreamSource implements IPentahoStreamSource {
// TODO: Move this into the pentaho-connections project.
File file;
String fullPath;
String mimeType;
public FilePentahoStreamSource(String fp) {
super();
assert (fp != null);
this.fullPath = fp;
this.file = new File(fullPath);
mimeType = MimeHelper.getMimeTypeFromFileName(this.file.getName());
if (mimeType == null) {
this.mimeType = "application/octet-stream"; //$NON-NLS-1$
}
}
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new FileInputStream(this.file));
}
public String getName() {
return file.getName();
}
public OutputStream getOutputStream() throws IOException {
return new FileOutputStream(this.file);
}
public String getContentType() {
return mimeType;
}
}
public Reader getReader() throws ContentException {
ContentItemFile cif = getLatestFile();
if (this.latestFile == null) {
throw new ContentException(Messages.getInstance().getErrorString("CONTITEM.ERROR_0002_NO_EXISTING_FILES", this.getName())); //$NON-NLS-1$
}
return cif.getReader();
}
public OutputStream getOutputStream(final String actionName) throws IOException {
outputStream = null;
if (actionName == null) {
throw new IllegalArgumentException(Messages.getInstance().getErrorString("CONTITEM.ERROR_0006_ACTION_NAME_CANNOT_BE_NULL")); //$NON-NLS-1$
}
switch (getWriteMode()) {
case WRITEMODE_KEEPVERSIONS: {
ContentItemFile cif = newContentFile(actionName);
outputStream = cif.getOutputStream(false);
return outputStream;
}
case WRITEMODE_OVERWRITE: {
ContentItemFile cif = getLatestFile();
if (cif == null) {
cif = newContentFile(actionName);
}
if (cif != null) {
cif.setFileDateTime(new Date());
outputStream = cif.getOutputStream(true);
return outputStream;
}
throw new IOException(Messages.getInstance().getErrorString("CONTITEM.ERROR_0004_OUTPUT_STREAM_NOT_AVAILABLE")); //$NON-NLS-1$
}
case WRITEMODE_APPEND: {
ContentItemFile cif = getLatestFile();
if (cif == null) {
cif = newContentFile(actionName);
}
if (cif != null) {
cif.setFileDateTime(new Date());
outputStream = cif.getOutputStream(true, true);
return outputStream;
}
throw new IOException(Messages.getInstance().getErrorString("CONTITEM.ERROR_0004_OUTPUT_STREAM_NOT_AVAILABLE")); //$NON-NLS-1$
}
default: {
throw new ContentException(Messages.getInstance().getErrorString(
"CONTITEM.ERROR_0003_BAD_WRITE_MODE", Integer.toString(getWriteMode()))); //$NON-NLS-1$
}
}
}
public void closeOutputStream() {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
error(Messages.getInstance().getErrorString("ContentItem.ERROR_0001_CLOSE_OUTPUT_STREAM"), e); //$NON-NLS-1$
}
}
}
public String getActionName() {
return this.getLatestFile() != null ? this.getLatestFile().getActionName() : null;
}
public String getFileId() {
return this.getLatestFile() != null ? this.getLatestFile().getId() : null;
}
public long getFileSize() {
return this.getLatestFile() != null ? this.getLatestFile().getFileSize() : -1;
}
public Date getFileDateTime() {
return this.getLatestFile() != null ? this.getLatestFile().getFileDateTime() : null;
}
/**
* @return Returns the writeMode.
*/
public int getWriteMode() {
return writeMode;
}
/**
* @param writeMode
* The writeMode to set.
*/
public void setWriteMode(final int writeMode) {
this.writeMode = writeMode;
}
/**
* equals override for Hibernate
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ContentItem)) {
return false;
}
final ContentItem that = (ContentItem) other;
return this.getId().equals(that.getId());
}
/**
* hashcode override for Hibernate
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return getId().hashCode();
}
/**
* @return Returns the extension.
*/
public String getExtension() {
return extension;
}
/**
* @param extension
* The extension to set.
*/
public void setExtension(final String extension) {
this.extension = extension;
}
/**
* @return Returns the latestFile.
*/
protected ContentItemFile getLatestFile() {
if (latestFile == null) {
Session session = HibernateUtil.getSession();
Query qry = session
.createQuery("from ContentItemFile cif where cif.parent = :contentParent and cif.fileDateTime = (select max(fileDateTime) from ContentItemFile where parent = :contentParent2) "); //$NON-NLS-1$
qry.setParameter("contentParent", this); //$NON-NLS-1$
qry.setParameter("contentParent2", this); //$NON-NLS-1$
try {
this.latestFile = (ContentItemFile) qry.uniqueResult();
} catch (org.hibernate.TransientObjectException ignored) {
// The object doesn't exist in Hibernate yet - that's
// OK, there's no reason for this exception to escape
// from this method. We are purposely swallowing
// the exception and returning null...
this.latestFile = null;
}
}
return this.latestFile;
}
/**
* @param latestFile
* The latestFile to set.
*/
public void setLatestFile(final ContentItemFile latestFile) {
this.latestFile = latestFile;
}
/**
* @deprecated
* @return Returns the latestVersionNum. Don't set or get this value any more - retained for backward compatibility.
*/
@Deprecated
public int getLatestVersionNum() {
return latestVersionNum;
}
/**
* @deprecated
* @param latestVersionNum
* The latestVersionNum to set. Don't set or get this value any more - retained for backward compatibility.
*/
@Deprecated
public void setLatestVersionNum(final int latestVersionNum) {
this.latestVersionNum = latestVersionNum;
}
/**
* @return Returns the revision.
*/
public int getRevision() {
return revision;
}
/**
* @param revision
* The revision to set.
*/
public void setRevision(final int revision) {
this.revision = revision;
}
public void removeVersion(final ContentItemFile cif) {
try {
cif.deleteOsFile();
} catch (Exception ex) {
ContentItem.logger.error(Messages.getInstance().getErrorString(
"CONTITEM.ERROR_0005_COULD_NOT_DELETE_OS_FILE", cif.getCompleteFileName()), ex); //$NON-NLS-1$
}
HibernateUtil.makeTransient(cif);
if ((latestFile == null) || latestFile.getId().equals(cif.getId())) {
this.latestFile = null;
// Get the now latest file from the DB and sets the variable.
this.getLatestFile();
}
}
public void removeVersion(final String fileId) {
Iterator it = getFileVersions().iterator();
ContentItemFile cif;
while (it.hasNext()) {
cif = (ContentItemFile) it.next();
if (fileId.equalsIgnoreCase(cif.getId())) {
removeVersion(cif);
break;
}
}
}
/**
* Removes all version files.
*/
public void removeAllVersions() {
Iterator it = getFileVersions().iterator();
ContentItemFile cif;
while (it.hasNext()) {
cif = (ContentItemFile) it.next();
cif.deleteOsFile();
HibernateUtil.makeTransient(cif);
}
this.latestFile = null;
}
/*
* Accessors
*/
/**
* @return Returns the parent.
*/
public ContentLocation getParent() {
return parent;
}
/**
* @param theParent
* The parent to set.
*/
public void setParent(final ContentLocation theParent) {
this.parent = theParent;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param fName
* The name to set.
*/
public void setName(final String fName) {
this.name = fName;
}
/**
* @return Returns the id.
*/
public String getId() {
return id;
}
/**
* @param id
* The id to set.
*/
public void setId(final String id) {
this.id = id;
}
/**
* @return Returns the mimeType.
*/
public String getMimeType() {
return mimeType;
}
/**
* @param mimeType
* The mimeType to set.
*/
public void setMimeType(final String mimeType) {
this.mimeType = mimeType;
}
/**
* @return Returns the url.
*/
public String getUrl() {
return url;
}
/**
* @param url
* The url to set.
*/
public void setUrl(final String url) {
this.url = url;
}
/**
* @return Returns the path.
*/
public String getPath() {
return path;
}
/**
* @param path
* The path to set.
*/
public void setPath(final String path) {
this.path = path;
}
/**
* @return Returns the fileVersions.
*/
public List getFileVersions() {
Session session = HibernateUtil.getSession();
Query qry = session
.createQuery("from ContentItemFile cif where cif.parent = :contentParent order by cif.fileDateTime"); //$NON-NLS-1$
qry.setParameter("contentParent", this); //$NON-NLS-1$
return qry.list();
}
/**
* @return Returns the title.
*/
public String getTitle() {
return title;
}
/**
* @param title
* The title to set.
*/
public void setTitle(final String title) {
this.title = title;
}
/**
* @return Returns the log.
*/
@Override
public Log getLogger() {
return ContentItem.logger;
}
/* ISearchable Needs */
public String[] getSearchableColumns() {
return ContentItem.SearchableColumns;
}
public String getSearchableTable() {
return ContentItem.SearchableTable;
}
public String getPhraseSearchQueryName() {
return ContentItem.SearchablePhraseNamedQuery;
}
public void makeTransient() {
this.removeAllVersions();
HibernateUtil.makeTransient(this);
}
@Override
public String toString() {
return MessageFormat.format("{0}, {1}, {2}", new Object[] { this.getTitle(), this.getPath(), this.getMimeType() }); //$NON-NLS-1$
}
}