package com.idega.block.article.importer; import java.io.IOException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.httpclient.HttpException; import org.apache.webdav.lib.PropertyName; import org.apache.webdav.lib.WebdavResource; import org.apache.webdav.lib.WebdavResources; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import com.idega.block.article.data.ArticleEntity; import com.idega.block.article.data.CategoryBugRemover; import com.idega.block.article.data.dao.ArticleDao; import com.idega.block.article.data.dao.CategoryDao; import com.idega.content.business.categories.CategoriesEngine; import com.idega.content.business.categories.CategoryBean; import com.idega.content.data.ContentCategory; import com.idega.core.business.DefaultSpringBean; import com.idega.core.localisation.business.ICLocaleBusiness; import com.idega.idegaweb.IWMainApplicationSettings; import com.idega.idegaweb.IWMainSlideStartedEvent; import com.idega.idegaweb.UnavailableIWContext; import com.idega.slide.business.IWSlideService; import com.idega.util.ArrayUtil; import com.idega.util.CoreConstants; import com.idega.util.ListUtil; import com.idega.util.StringUtil; import com.idega.util.expression.ELUtil; /** * Imports articles and their categories to database. * @author martynas * Last changed: 2011.05.18 * You can report about problems to: <a href="mailto:martynas@idega.com">Martynas StakÄ—</a> * You can expect to find some test cases notice in the end of the file. */ @Service @Scope(BeanDefinition.SCOPE_SINGLETON) public class ArticlesImporter extends DefaultSpringBean implements ApplicationListener { @Autowired private CategoriesEngine categoryEngine; @Autowired private CategoryDao categoryDao; private static Logger LOGGER = Logger.getLogger(ArticlesImporter.class.getName()); public static final String CATEGORIES_IMPORTED_APP_PROP = "is_categories_imported", ARTICLES_IMPORTED_APP_PROP = "articles_imported", CATEGORIES_BUG_FIXED_PROP = "is_categories_bug_fixed"; /** * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) */ @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof IWMainSlideStartedEvent) { IWMainApplicationSettings settings = getApplication().getSettings(); CategoryBugRemover cbr = new CategoryBugRemover(); try { if (!cbr.isBadColunmsExist()) settings.setProperty(CATEGORIES_BUG_FIXED_PROP, Boolean.TRUE.toString()); } catch (Exception e) { LOGGER.log(Level.WARNING, "Failed to check for wrong tables", e); } if (!settings.getBoolean(CATEGORIES_BUG_FIXED_PROP)) { cbr.removeBug(); settings.setProperty(CATEGORIES_IMPORTED_APP_PROP, Boolean.FALSE.toString()); settings.setProperty(ARTICLES_IMPORTED_APP_PROP, Boolean.FALSE.toString()); getLogger().info("Articles importer bug is fixed, terminating the import - need to restart the server"); return; } Boolean isCategoriesImported = settings.getBoolean(CATEGORIES_IMPORTED_APP_PROP, Boolean.FALSE); if (!isCategoriesImported) { getLogger().info("Importing the categories..."); isCategoriesImported = importCategories(); getLogger().info("Finished importing the categories. The result is successfull: " + isCategoriesImported); settings.setProperty(CATEGORIES_IMPORTED_APP_PROP, isCategoriesImported.toString()); } Boolean isArticlesImported = settings.getBoolean(ARTICLES_IMPORTED_APP_PROP, Boolean.FALSE); if (!isArticlesImported && isCategoriesImported) { getLogger().info("Importing the articles..."); isArticlesImported = importArticles(); getLogger().info("Finished importing articles. The result is successfull: " + isArticlesImported); settings.setProperty(ARTICLES_IMPORTED_APP_PROP, isArticlesImported.toString()); } } } /** * Method for importing categories, which are in categories.xml, but not in database * @return true, if imported, false, if at least one category was not imported */ public boolean importCategories() { List<Locale> localeList = ICLocaleBusiness.getListOfLocalesJAVA(); if (localeList == null) { getLogger().warning("There are no locales in the system"); return Boolean.FALSE; } if (categoryEngine == null) { getLogger().warning("Categories engine is not initialized"); return Boolean.FALSE; } int numberOfImportedCategories = 0; for (Locale locale : localeList) { List<ContentCategory> categoryList = null; try { categoryList = categoryEngine.getCategoriesByLocale(locale.toString()); } catch (UnavailableIWContext e){ LOGGER.log(Level.WARNING, "Failed to import because categories.xml does not exist", e); return Boolean.FALSE; } if (ListUtil.isEmpty(categoryList)) continue; for (ContentCategory category : categoryList) { if (category == null) continue; String categoryId = category.getId(); if (StringUtil.isEmpty(categoryId)) { getLogger().warning("Category " + category + " has no ID, unable to import it!"); continue; } Boolean isAdded = categoryDao.addCategory(categoryId) != null; if (isAdded) { getLogger().info("Category with ID '" + categoryId + "' was imported"); numberOfImportedCategories++; } else { getLogger().warning("Failed to import the category with ID '" + categoryId + "'. Number of successfully imported categories: " + numberOfImportedCategories); return Boolean.FALSE; } } } getLogger().info("Number of imported categories: " + numberOfImportedCategories); return Boolean.TRUE; } /** * Method for importing articles and their categories from default /files/cms/article path. * @return true, if imported, false if at least one article was not imported. */ private boolean importArticles() { IWSlideService iWSlideService = getServiceInstance(IWSlideService.class); try { /*Getting the articles folders*/ WebdavResource resource = iWSlideService.getWebdavResourceAuthenticatedAsRoot(CoreConstants.CONTENT_PATH .concat(CoreConstants.ARTICLE_CONTENT_PATH)); boolean importResult = importArticleFolders(resource); resource.close(); return importResult; } catch (HttpException e) { LOGGER.log(Level.WARNING, "Failed to import articles cause of:", e); } catch (RemoteException e) { LOGGER.log(Level.WARNING, "Failed to import articles cause of:", e); } catch (IOException e) { LOGGER.log(Level.WARNING, "No such folder or you do not have a permission to access it: ", e); } return Boolean.FALSE; } /** * Browse recursively thought folders, searches for articles folders, imports articles if found some. * @param resource Path where to start looking for articles to import. * @return true, if articles found and imported, false if not all articles imported. */ private boolean importArticleFolders(WebdavResource resource) { if (resource == null) { getLogger().warning("Resource is undefined!"); return Boolean.FALSE; } if (!resource.exists()) { getLogger().warning("Resource " + resource.getPath() + " does not exist!"); return Boolean.FALSE; } getLogger().info("Will try to import article(s) from " + resource.getPath() + " and it's folder(s)"); // Importing articles from the current folder importArticles(resource); // Now will try to look for the articles in the current folder try { WebdavResource[] foldersAndFilesResources = resource.listWebdavResources(); if (ArrayUtil.isEmpty(foldersAndFilesResources)) { getLogger().info("There are no files nor folders inside " + resource.getPath()); return Boolean.TRUE; } boolean result = Boolean.FALSE; for (WebdavResource wr: foldersAndFilesResources) { if (wr == null) continue; if (importArticleFolders(wr)) { result = Boolean.TRUE; getLogger().info("SUCCESSFULLY imported article(s) from the folder: " + wr.getPath()); } if (wr != null) wr.close(); } /*Trying to solve out of memory exception*/ foldersAndFilesResources = null; resource = null; return result; } catch (HttpException e) { LOGGER.log(Level.WARNING, "Http:Exception: ",e); } catch (IOException e) { LOGGER.log(Level.WARNING, "No such folder or you do not have a permission to access it: ", e); } return Boolean.FALSE; } /** * Checks, if it is folder containing article folders. * @param resource Folder, that might be containing article folders. * @return true, if it is folder of articles, false, if not. */ private boolean isArticlesFolder(WebdavResource resource){ if (resource == null || !resource.exists()) { getLogger().warning("Resource " + resource + " does not exist!"); return Boolean.FALSE; } /*Checking is it folder*/ if (!resource.isCollection()) return Boolean.FALSE; try { WebdavResources webdavResources = resource.getChildResources(); if (webdavResources == null) { getLogger().warning("Folder " + resource + " does not have files"); return Boolean.FALSE; } String[] arrayOfResourcesInStringRepresentation = webdavResources.list(); if (ArrayUtil.isEmpty(arrayOfResourcesInStringRepresentation)) { getLogger().warning("Folder " + resource + " does not have files"); return Boolean.FALSE; } for (String s: arrayOfResourcesInStringRepresentation) { int index = s.indexOf("?jsessionid"); if (index > 0) s = s.substring(0, index); if (s.endsWith(CoreConstants.ARTICLE_FILENAME_SCOPE)) return Boolean.TRUE; } getLogger().info("Did not find any folders ending with '" + CoreConstants.DOT + CoreConstants.ARTICLE_FILENAME_SCOPE + "' inside " + resource.getPath()); /*Trying to solve out of memory exception*/ arrayOfResourcesInStringRepresentation = null; resource = null; webdavResources = null; } catch (HttpException e) { LOGGER.log(Level.WARNING, "Http:Exception: ",e); } catch (IOException e) { LOGGER.log(Level.WARNING, "No such folder or you do not have a permission to access it: ", e); } return Boolean.FALSE; } /** * Imports articles to database table "IC_ARTICLE" or updates it if exist. * @param resource Folder of articles folder. * @return true, if imported, false if failed or not articles folder. */ private boolean importArticles(WebdavResource resource){ if (!isArticlesFolder(resource)) { getLogger().warning("Folder " + resource.getPath() + " is not folder of articles!"); return Boolean.FALSE; } String uri = null; try { uri = resource.getPath(); WebdavResources filesAndFolders = resource.getChildResources(); if (filesAndFolders == null) { getLogger().warning("There are no files nor folders inside the current folder: " + resource.getPath() + ". Skipping this folder"); return Boolean.FALSE; } WebdavResource[] arrayOfResources = filesAndFolders.listResources(); if (ArrayUtil.isEmpty(arrayOfResources)) { getLogger().warning("There are no files nor folders inside the current folder: " + resource.getPath() + ". Skipping this folder"); return Boolean.FALSE; } boolean isImportSuccesful = Boolean.TRUE; String propertyName = new PropertyName("DAV", "categories").toString(); for (WebdavResource r: arrayOfResources) { uri = null; String name = r.getName(); if (StringUtil.isEmpty(name)) { getLogger().warning("Name is undefined for the resource " + resource.getPath()); continue; } if (name.endsWith(CoreConstants.ARTICLE_FILENAME_SCOPE)) { try { uri = r.getPath(); // Loading categories @SuppressWarnings("unchecked") Enumeration<String> resourceEnumeration = r.propfindMethod(uri, propertyName); Collection<String> articleCategories = new ArrayList<String>(); if (resourceEnumeration != null){ while (resourceEnumeration.hasMoreElements()) { Collection<String> tmpCategories = CategoryBean.getCategoriesFromString(resourceEnumeration.nextElement()); if (!ListUtil.isEmpty(tmpCategories)) articleCategories.addAll(tmpCategories); } if (!ListUtil.isEmpty(articleCategories)) getLogger().info("Found categories for the article (" + uri + "): " + articleCategories); } if (ListUtil.isEmpty(articleCategories)) articleCategories = Collections.emptyList(); else articleCategories = new ArrayList<String>(articleCategories); // Fixing URI if (uri.contains(CoreConstants.WEBDAV_SERVLET_URI)) uri = uri.substring(CoreConstants.WEBDAV_SERVLET_URI.length()); if (uri.endsWith(CoreConstants.SLASH)) uri = uri.substring(0, uri.lastIndexOf(CoreConstants.SLASH)); // Writing to the DB getLogger().info("Importing article " + uri); ArticleEntity article = getArticleDao().getByUri(uri); if (article == null) { article = new ArticleEntity(); article.setModificationDate(new Date(r.getCreationDate())); article.setUri(uri); } if (!ListUtil.isEmpty(articleCategories)) { categoryDao.addCategories(articleCategories); article.setCategories(categoryDao.getCategories(articleCategories)); } article = getArticleDao().updateArticle(article); if (article != null && article.getId() != null) { getLogger().info("Article " + uri + " was SUCCESSFULLY imported"); } else { getLogger().warning("FAILED to import the article: " + uri); isImportSuccesful = Boolean.FALSE; break; } } finally { r.close(); } } } /*Trying to solve out of memory exception*/ if (!isImportSuccesful) { for (WebdavResource r : arrayOfResources) { if (r != null) { r.close(); } } } arrayOfResources = null; filesAndFolders = null; resource = null; return isImportSuccesful; } catch (HttpException e) { LOGGER.log(Level.WARNING, "Failed to import articles from (" + uri + ")", e); } catch (IOException e) { LOGGER.log(Level.WARNING, "No such folder (" + uri + ") or you do not have a permission to access it!", e); } return Boolean.FALSE; } private ArticleDao getArticleDao() { ArticleDao articleDao = ELUtil.getInstance().getBean(ArticleDao.BEAN_NAME); return articleDao; } /* * Test cases isArticlesFolder(WebdavResource resource): * Passed articles folder, returned: true * Passed not articles folder, returned: false * Passed null, returned: false * * Test cases importArticles(WebdavResource resource): * Passed articles folder, without categories, * returned: true; * imported: true; * * Test cases importArticles(WebdavResource resource): * Passed articles folder, with category, * returned: true; * imported: true; * * Test cases importArticles(WebdavResource resource): * Passed articles folder, with categories, * returned: true; * imported: true; * * Test cases importArticles(WebdavResource resource): * Passed same as before articles folder, with categories, * returned: true; * imported: false, changed nothing, but executed; * * Passed not articles folder, * returned: false; * imported: false; * * Passed null, * returned: false * imported: false * * Test cases importArticles(): * Passed directory /files/cms/article/ with 669 articles in different folders, ~3 articles in one folder * returned: true * imported: true * * Passed directory /files/cms/article/ with 999 articles in different folders, ~3 articles in one folder. Also different categories or no categories * returned: true * imported: true * * Test cases importCategories(): * Passed empty directory, * imported: false; * returned: true; */ }