/* * Copyright (C) 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.sites.liberation.export; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.sites.liberation.util.EntryType.ATTACHMENT; import static com.google.sites.liberation.util.EntryType.getType; import static com.google.sites.liberation.util.EntryType.isPage; import static com.google.sites.liberation.util.EntryUtils.getParentId; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import com.google.common.collect.Sets; import com.google.gdata.client.sites.SitesService; import com.google.gdata.data.sites.AttachmentEntry; import com.google.gdata.data.sites.BaseContentEntry; import com.google.gdata.data.sites.BasePageEntry; import com.google.gdata.util.common.base.Nullable; import com.google.inject.Inject; import com.google.sites.liberation.util.ProgressListener; import com.google.sites.liberation.util.UrlUtils; /** * Implements {@link SiteExporter} to export an entire Site * to a given root folder. * * @author bsimon@google.com (Benjamin Simon) */ final class SiteExporterImpl implements SiteExporter { private static final Logger LOGGER = Logger.getLogger( SiteExporterImpl.class.getCanonicalName()); private final AbsoluteLinkConverter linkConverter; private final AppendableFactory appendableFactory; private final AttachmentDownloader attachmentDownloader; private final EntryStoreFactory entryStoreFactory; private final FeedProvider feedProvider; private final PageExporter pageExporter; private final RevisionsExporter revisionsExporter; /** * Creates a new SiteExporter with the given dependencies. */ @Inject SiteExporterImpl(AbsoluteLinkConverter linkConverter, AppendableFactory appendableFactory, AttachmentDownloader attachmentDownloader, EntryStoreFactory entryStoreFactory, FeedProvider feedProvider, PageExporter pageExporter, RevisionsExporter revisionsExporter) { this.linkConverter = checkNotNull(linkConverter); this.appendableFactory = checkNotNull(appendableFactory); this.attachmentDownloader = checkNotNull(attachmentDownloader); this.entryStoreFactory = checkNotNull(entryStoreFactory); this.feedProvider = checkNotNull(feedProvider); this.pageExporter = checkNotNull(pageExporter); this.revisionsExporter = checkNotNull(revisionsExporter); } @Override public void exportSite(String host, @Nullable String domain, String webspace, boolean exportRevisions, SitesService sitesService, File rootDirectory, ProgressListener progressListener) { checkNotNull(host, "host"); checkNotNull(webspace, "webspace"); checkNotNull(sitesService, "sitesService"); checkNotNull(rootDirectory, "rootDirectory"); checkNotNull(progressListener, "progressListener"); Set<BaseContentEntry<?>> pages = Sets.newHashSet(); Set<AttachmentEntry> attachments = Sets.newHashSet(); EntryStore entryStore = entryStoreFactory.newEntryStore(); URL feedUrl = UrlUtils.getFeedUrl(host, domain, webspace); URL siteUrl = UrlUtils.getSiteUrl(host, domain, webspace); progressListener.setStatus("Retrieving site data (this may take a few minutes)."); Iterable<BaseContentEntry<?>> entries = feedProvider.getEntries(feedUrl, sitesService); int num = 1; for (BaseContentEntry<?> entry : entries) { if (entry != null) { if (num % 20 == 0) { progressListener.setStatus("Retrieved " + num + " entries."); } entryStore.addEntry(entry); if (isPage(entry)) { pages.add((BasePageEntry<?>) entry); } else if (getType(entry) == ATTACHMENT) { // TODO(gk5885): remove extra cast for // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302214 attachments.add((AttachmentEntry) entry); } else { progressListener.setStatus("The class of page is not supported!" + "The class of page:" + entry.getClass()); } num++; } else { LOGGER.log(Level.WARNING, "Error parsing entries!"); } } int totalEntries = pages.size() + attachments.size(); if (totalEntries > 0) { int currentEntries = 0; for (BaseContentEntry<?> page : pages) { progressListener.setStatus("Exporting page: " + page.getTitle().getPlainText() + '.'); linkConverter.convertLinks(page, entryStore, siteUrl, false); File relativePath = getPath(page, entryStore); if (relativePath != null) { File directory = new File(rootDirectory, relativePath.getPath()); directory.mkdirs(); exportPage(page, directory, entryStore, exportRevisions); if (exportRevisions) { revisionsExporter.exportRevisions(page, entryStore, directory, sitesService, siteUrl); } } progressListener.setProgress(((double) ++currentEntries) / totalEntries); } for (AttachmentEntry attachment : attachments) { progressListener.setStatus("Downloading attachment: " + attachment.getTitle().getPlainText() + '.'); downloadAttachment(attachment, rootDirectory, entryStore, sitesService); progressListener.setProgress(((double) ++currentEntries) / totalEntries); } progressListener.setStatus("Export complete."); } else { progressListener.setStatus("No data returned. " + "Can you get anything from " + feedUrl.toString()+"."); } } private void exportPage(BaseContentEntry<?> page, File directory, EntryStore entryStore, boolean revisionsExported) { File file = new File(directory, "index.html"); Appendable out = null; try { out = appendableFactory.getAppendable(file); pageExporter.exportPage(page, entryStore, out, revisionsExported); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed writing to file: " + file.getPath(), e); } finally { if (out instanceof Closeable) { try { ((Closeable) out).close(); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed closing file: " + file.getPath(), e); } } } } private void downloadAttachment(AttachmentEntry attachment, File rootDirectory, EntryStore entryStore, SitesService sitesService) { BasePageEntry<?> parent = entryStore.getParent(attachment.getId()); if (parent != null) { File relativePath = getPath(parent, entryStore); if (relativePath != null) { File folder = new File(rootDirectory, relativePath.getPath()); folder.mkdirs(); File file = new File(folder, attachment.getTitle().getPlainText()); attachmentDownloader.download(attachment, file, sitesService); } } } /** * Returns the site-relative folder path corresponding to the given page, or * {@code null} if any of the page's ancestors are missing. */ private File getPath(BaseContentEntry<?> entry, EntryStore entryStore) { String parentId = getParentId(entry); if (parentId == null) { return new File(((BasePageEntry<?>) entry).getPageName().getValue()); } BasePageEntry<?> parent = (BasePageEntry<?>) entryStore.getEntry(parentId); if (parent == null) { return null; } return new File(getPath(parent, entryStore), ((BasePageEntry<?>) entry).getPageName().getValue()); } }