/*
* Copyright (C) 2003-2009 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.faq.service;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.exoplatform.commons.utils.MimeTypeResolver;
import org.exoplatform.container.configuration.ConfigurationManager;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ValueParam;
import org.exoplatform.ks.common.conf.ManagedPlugin;
import org.exoplatform.management.annotations.Managed;
import org.exoplatform.management.annotations.ManagedDescription;
import org.exoplatform.management.jmx.annotations.NameTemplate;
import org.exoplatform.management.jmx.annotations.Property;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
/**
* A plugin to initialize FAQ data from xml export. By default the data will be imported to root category only if it does not already exist
* <value-params> are :
* <ul>
* <li><b>location</b> : configuration url for the data to import</li>
* <li><b>forceXML</b> (optional) : indicates if XML data should override existing data</li>
* </ul>
*/
@Managed
@NameTemplate( { @Property(key = "service", value = "faq"), @Property(key = "view", value = "plugins"), @Property(key = "name", value = "{Name}") })
@ManagedDescription("Plugin that allows to initialize default data for the FAQ")
public class InitialDataPlugin extends ManagedPlugin {
private static final String TEXT_XML = "text/xml";
private static final String APPLICATION_ZIP = "application/zip";
private static Log log = ExoLogger.getLogger(InitialDataPlugin.class);
private String location;
private boolean forceXML = false;
public InitialDataPlugin(InitParams params) {
ValueParam vp1 = params.getValueParam("location");
if (vp1 == null) {
log.warn("value-param 'location' is missing for " + getName() + ". The plugin will not be used");
} else {
this.location = vp1.getValue();
}
ValueParam vp2 = params.getValueParam("forceXML");
if (vp2 != null) {
try {
forceXML = Boolean.valueOf(vp2.getValue());
} catch (Exception e) {
log.warn("value-param 'forceXML' is erroneous for " + getName() + ". Expected 'true' or 'false', received '" + vp2.getValue() + "'. Using " + forceXML);
}
}
}
public void setLocation(String location) {
this.location = location;
}
public void setForceXML(boolean forceXML) {
this.forceXML = forceXML;
}
@Managed
@ManagedDescription("The location where FAQ an XML export file will be looked")
public String getLocation() {
return location;
}
@Managed
@ManagedDescription("Indicate if the data loaded should override any data found in the existing database")
public boolean isForceXML() {
return forceXML;
}
public boolean importData(FAQService service, ConfigurationManager configurationService) throws RuntimeException {
try {
if (location == null) {
log.warn("No data location provided for " + this);
return false;
}
boolean isZip = isZip(location);
if (!isZip) {
throw new RuntimeException("the .zip FAQ export format is expected");
}
String categoryId = readCategoryFromZipEntry(configurationService);
if (categoryId == null) {
throw new RuntimeException("Could not extract category id from .zip . Is it a real FAQ export?");
}
Category oCat = service.getCategoryById(Utils.CATEGORY_HOME + "/" + categoryId);
if (oCat != null) {
log.info("FAQ data in " + location + " was not imported. The category '" + oCat.getName() + "' already exists.");
return false;
} else {
InputStream inputStream = configurationService.getInputStream(location);
boolean result = service.importData(Utils.CATEGORY_HOME, inputStream, isZip);
return result;
}
} catch (Exception e) {
log.error("The plugin " + getName() + " failed to initialize data " + e);
throw new RuntimeException(e.getCause());
}
}
private String readCategoryFromZipEntry(ConfigurationManager configurationService) throws Exception {
InputStream inputStream = null;
ZipInputStream zipStream = null;
try {
inputStream = configurationService.getInputStream(location);
zipStream = new ZipInputStream(inputStream);
ZipEntry entry;
while ((entry = zipStream.getNextEntry()) != null) {
String name = entry.getName();
zipStream.closeEntry();
String result = name.substring(0, name.lastIndexOf(".xml"));
return result;
}
return null;
} finally {
safeClose(inputStream);
safeClose(zipStream);
}
}
private void safeClose(InputStream inputStream) {
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable t) {
;// ignore
}
}
}
/**
* Look for the name of the category in the input file
* @param configurationService
* @return
*/
private String readCategoryFromXml(ConfigurationManager configurationService) throws Exception, UnsupportedEncodingException, IOException {
InputStream inputStream = null;
try {
String importedCategoryId = null;
inputStream = configurationService.getInputStream(location);
boolean keepReading = true;
StringBuffer sbuf = new StringBuffer();
byte[] buf = new byte[1024];
int len;
String patternStr = "name=\"(\\S*)\""; // match stuf like sv:name="CategoryXYZ" and captures CategoryXYZ
Pattern pattern = Pattern.compile(patternStr);
while (((len = inputStream.read(buf)) > 0) && keepReading) {
sbuf.append(new String(buf, "UTF-8"));
String content = sbuf.substring(0);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
importedCategoryId = matcher.group(1);
break; // stop reading
}
}
return importedCategoryId;
} finally {
safeClose(inputStream);
}
}
private void createCategory(FAQService service, String categoryName) throws Exception {
Category categ = new Category();
categ.setCreatedDate(new Date());
categ.setName(categoryName);
categ.setModerators(new String[0]);
categ.setIndex(11L);
service.saveCategory(Utils.CATEGORY_HOME, categ, true);
}
boolean isZip(String fileName) {
MimeTypeResolver mimeTypeResolver = new MimeTypeResolver();
String mimeType = mimeTypeResolver.getMimeType(fileName);
if (APPLICATION_ZIP.equals(mimeType)) {
return true;
} else if (TEXT_XML.equals(mimeType)) {
return false;
}
throw new RuntimeException("The format " + mimeType + " is not supported. Expecting " + APPLICATION_ZIP + " or " + TEXT_XML);
}
public String toString() {
return getName() + " (forceXML=" + forceXML + ",location=" + location + ")";
}
}