Java tutorial
/* * Weblounge: Web Content Management System * Copyright (c) 2003 - 2011 The Weblounge Team * http://entwinemedia.com/weblounge * * This program 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 2 * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package ch.entwine.weblounge.contentrepository.impl.bundle; import ch.entwine.weblounge.common.content.Resource; import ch.entwine.weblounge.common.content.ResourceContent; import ch.entwine.weblounge.common.content.ResourceReader; import ch.entwine.weblounge.common.content.ResourceURI; import ch.entwine.weblounge.common.content.ResourceUtils; import ch.entwine.weblounge.common.impl.content.ResourceURIImpl; import ch.entwine.weblounge.common.repository.ContentRepositoryException; import ch.entwine.weblounge.common.repository.ResourceSerializer; import ch.entwine.weblounge.common.site.Site; import ch.entwine.weblounge.common.url.PathUtils; import ch.entwine.weblounge.common.url.UrlUtils; import ch.entwine.weblounge.contentrepository.impl.fs.FileSystemContentRepository; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.osgi.framework.Bundle; import org.osgi.service.cm.ConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; /** * Represents a bundle repository that is writable. OSGi bundles are not * writable, so this content repository behaves like a * <code>FileSystemContentRepository</code>, while the initial content is copied * from the respective bundle. */ public class WritableBundleContentRepository extends FileSystemContentRepository { /** The logging facility */ private static final Logger logger = LoggerFactory.getLogger(WritableBundleContentRepository.class); /** The repository type */ public static final String TYPE = "ch.entwine.weblounge.contentrepository.bundle"; /** Option specifying the root directory */ public static final String OPT_ROOT_DIR = BundleContentRepository.CONF_PREFIX + "root"; /** Prefix into the bundle */ protected String bundlePathPrefix = "/repository"; /** The site's bundle */ protected Bundle bundle = null; @Override public String getType() { return TYPE; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.contentrepository.impl.fs.FileSystemContentRepository#connect(ch.entwine.weblounge.common.site.Site) */ @Override public void connect(Site site) throws ContentRepositoryException { // Don't have the super implementation automatically create a home page // for us. Otherwise, the creation of the index from the bundle won't work. createHomepage = false; // Initialize the repository and the repository index super.connect(site); try { initializing = true; // Find the site's bundle bundle = loadBundle(site); if (bundle == null) throw new ContentRepositoryException("Unable to locate bundle for site '" + site + "'"); // Add the bundle contents to the index if needed File rootDirecotry = getRootDirectory(); if (getResourceCount() == 0 || !rootDirecotry.exists() || rootDirecotry.list().length == 0) indexBundleContents(); // If there was no homepage as part of the bundle, create it createHomepage(); } finally { initializing = false; } } /** * {@inheritDoc} * * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary) */ @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public void updated(Dictionary properties) throws ConfigurationException { // Since the bundle content repository is based on the file system content // repository, the configuration properties need to be adjusted String rootDirPath = (String) properties.get(OPT_ROOT_DIR); if (StringUtils.isNotBlank(rootDirPath)) { properties.put(FileSystemContentRepository.OPT_ROOT_DIR, PathUtils.trim(rootDirPath)); logger.info("Bundle content repository data will be stored at {}", rootDirPath); } super.updated(properties); } /** * Initializes the content repository by loading the resources from the bundle * and adding it to the repository index. */ protected void indexBundleContents() throws ContentRepositoryException { logger.info("Indexing bundle content repository {}", this); try { logger.info("Clearing index of bundle content repository {}", this); index.clear(); } catch (IOException e) { logger.error("Error indexing bundle content repository: {}", e.getMessage()); } // See if there are any resources. If that's the case, then we don't need to // do anything. If not, we need to copy everything that's currently in the // bundle. for (ResourceSerializer<?, ?> serializer : getSerializers()) { String resourceDirectoryPath = UrlUtils.concat(repositorySiteRoot.getAbsolutePath(), serializer.getType() + "s"); File resourceDirectory = new File(resourceDirectoryPath); if (resourceDirectory.isDirectory() && resourceDirectory.list().length > 0) { logger.debug("Found existing {}s for site '{}' at {}", new Object[] { serializer.getType(), site, resourceDirectoryPath }); return; } } // If there is no content repository at the target location, copy the // initial bundle contents to the filesystem. Otherwise, keep working with // what's there already. logger.info("Loading resources for '{}' from bundle '{}'", site, bundle.getSymbolicName()); try { for (Iterator<ResourceURI> pi = getResourceURIsFromBundle(); pi.hasNext();) { ResourceURI uri = pi.next(); try { Resource<?> resource = loadResourceFromBundle(uri); if (resource == null) { throw new ContentRepositoryException( "Unable to load " + uri.getType() + " " + uri + " from bundle"); } // Update the uri, it now contains the id in addition to just the path uri = resource.getURI(); // Make sure we are not updating existing resources, since this is the // first time import. if (exists(uri)) { throw new ContentRepositoryException( "Error adding resource " + uri + " to repository: a resource with id '" + uri.getIdentifier() + "' or path '" + uri.getPath() + "' already exists"); } logger.info("Loading {} {}:{}", new Object[] { uri.getType(), site, uri }); Set<? extends ResourceContent> content = resource.contents(); if (content.size() == 0) { put(resource); } else { for (ResourceContent c : content) resource.removeContent(c.getLanguage()); put(resource); for (ResourceContent c : content) { InputStream is = null; try { is = loadResourceContentFromBundle(uri, c); if (is == null && c.getExternalLocation() == null) throw new ContentRepositoryException( "Resource content " + c + " missing from repository"); putContent(uri, c, is); } finally { IOUtils.closeQuietly(is); } } } } catch (IOException e) { logger.error("Error reading " + uri.getType() + " " + uri + ": " + e.getMessage(), e); throw new ContentRepositoryException(e); } } } catch (ContentRepositoryException e) { cleanupAfterFailure(); throw e; } // Log index statistics to console long resourceCount = index.getResourceCount(); long resourceVersionCount = index.getRevisionCount(); logger.info("Index contains {} resources and {} revisions", resourceCount, resourceVersionCount - resourceCount); } /** * Closes the index and removes the bundle directory from disk. */ private void cleanupAfterFailure() { try { index.close(); FileUtils.deleteDirectory(repositorySiteRoot); logger.error("Site index and repository directory have been reset"); } catch (IOException e2) { logger.error("Unable to clean up index and repository directory " + repositorySiteRoot); } } /** * Loads all resources from the bundle and returns their uris as an iterator. * * @return the resource uris * @throws ContentRepositoryException * if reading from the repository fails */ @SuppressWarnings("unchecked") protected Iterator<ResourceURI> getResourceURIsFromBundle() throws ContentRepositoryException { List<ResourceURI> resourceURIs = new ArrayList<ResourceURI>(); // For every serializer, try to load the resources for (ResourceSerializer<?, ?> serializer : getSerializers()) { String resourceDirectory = serializer.getType() + "s"; String resourcePathPrefix = UrlUtils.concat(bundlePathPrefix, resourceDirectory); Enumeration<URL> entries = bundle.findEntries(resourcePathPrefix, "*.xml", true); if (entries != null) { while (entries.hasMoreElements()) { URL entry = entries.nextElement(); String path = FilenameUtils.getPath(entry.getPath()); path = path.substring(resourcePathPrefix.length() - 1); long v = ResourceUtils.getVersion(FilenameUtils.getBaseName(entry.getPath())); ResourceURI resourceURI = new ResourceURIImpl(serializer.getType(), site, path, v); resourceURIs.add(resourceURI); logger.trace("Found revision '{}' of {} {}", new Object[] { v, resourceURI.getType(), entry }); } } } return resourceURIs.iterator(); } /** * Loads the specified resource from the bundle rather than from the content * repository. * * @param uri * the uri * @return the resource * @throws IOException * if reading the resource fails */ protected Resource<?> loadResourceFromBundle(ResourceURI uri) throws IOException { String uriPath = uri.getPath(); if (uriPath == null) throw new IllegalArgumentException("Resource uri needs a path"); String entryPath = UrlUtils.concat(bundlePathPrefix, uri.getType() + "s", uriPath, ResourceUtils.getDocument(uri.getVersion())); URL url = bundle.getEntry(entryPath); if (url == null) return null; try { ResourceSerializer<?, ?> serializer = getSerializerByType(uri.getType()); if (serializer == null) { logger.warn("Unable to read {} {}: no serializer found", uri.getType(), uri); return null; } ResourceReader<?, ?> resourceReader = serializer.getReader(); return resourceReader.read(url.openStream(), site); } catch (SAXException e) { throw new RuntimeException("SAX error while reading " + uri.getType() + " '" + uri + "'", e); } catch (IOException e) { throw new IOException("I/O error while reading " + uri.getType() + " '" + uri + "'", e); } catch (ParserConfigurationException e) { throw new IllegalStateException( "Parser configuration error while reading " + uri.getType() + " '" + uri + "'", e); } catch (Throwable t) { throw new IllegalStateException(t); } } /** * Loads the specified resource from the bundle rather than from the content * repository. * * @param uri * the uri * @return the resource * @throws IOException * if reading the resource fails */ protected InputStream loadResourceContentFromBundle(ResourceURI uri, ResourceContent content) throws IOException { String uriPath = uri.getPath(); if (uriPath == null) throw new IllegalArgumentException("Resource uri needs a path"); String documentName = content.getLanguage().getIdentifier(); if (!"".equals(FilenameUtils.getExtension(content.getFilename()))) documentName += "." + FilenameUtils.getExtension(content.getFilename()); String entryPath = UrlUtils.concat(bundlePathPrefix, uri.getType() + "s", uriPath, documentName); URL url = bundle.getEntry(entryPath); if (url == null) return null; return url.openStream(); } }