/*******************************************************************************
* Copyright (c) 2015 IBH SYSTEMS GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBH SYSTEMS GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.packagedrone.repo.web.sitemap.servlet;
import static java.util.Optional.ofNullable;
import java.io.IOException;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLOutputFactory;
import org.eclipse.packagedrone.repo.web.sitemap.UrlSetContextCreator;
import org.eclipse.packagedrone.repo.web.sitemap.SitemapGenerator;
import org.eclipse.packagedrone.repo.web.sitemap.SitemapIndexWriter;
import org.eclipse.packagedrone.repo.web.sitemap.UrlSetContext;
import org.eclipse.packagedrone.repo.web.sitemap.UrlSetWriter;
import org.eclipse.packagedrone.utils.Exceptions.ThrowingRunnable;
import org.osgi.framework.FrameworkUtil;
import org.osgi.util.tracker.ServiceTracker;
public class SitemapProcessor
{
private final Supplier<String> prefixSupplier;
private final String sitemapUrl;
private final XMLOutputFactory outputFactory;
private final ServiceTracker<SitemapGenerator, SitemapGenerator> tracker;
public SitemapProcessor ( final Supplier<String> prefixSupplier, final String sitemapUrl, final XMLOutputFactory outputFactory )
{
this.prefixSupplier = prefixSupplier;
this.sitemapUrl = sitemapUrl;
this.outputFactory = outputFactory;
this.tracker = new ServiceTracker<> ( FrameworkUtil.getBundle ( SitemapProcessor.class ).getBundleContext (), SitemapGenerator.class, null );
this.tracker.open ();
}
public void dispose ()
{
this.tracker.close ();
}
public boolean process ( final HttpServletResponse response, final String path ) throws IOException
{
if ( path == null || path.isEmpty () )
{
processRoot ( response );
return true;
}
else
{
return processSub ( response, path );
}
}
private void processRoot ( final HttpServletResponse response ) throws IOException
{
final String prefix = ofNullable ( this.prefixSupplier.get () ).orElse ( "http://localhost" ) + this.sitemapUrl;
response.setContentType ( "text/xml" );
try ( SitemapIndexWriter writer = new SitemapIndexWriter ( response.getWriter (), prefix, this.outputFactory ) )
{
for ( final SitemapGenerator generator : this.tracker.getTracked ().values () )
{
generator.gatherRoots ( writer );
}
}
}
private class ContextCreator implements UrlSetContextCreator
{
private final HttpServletResponse response;
private boolean used;
private ThrowingRunnable finish;
public ContextCreator ( final HttpServletResponse response )
{
this.response = response;
}
public boolean isUsed ()
{
return this.used;
}
@Override
public UrlSetContext createUrlSet ()
{
if ( this.used )
{
throw new IllegalStateException ( "Context creator may only be used once" );
}
this.used = true;
try
{
final String prefix = ofNullable ( SitemapProcessor.this.prefixSupplier.get () ).orElse ( "http://localhost" );
this.response.setContentType ( "text/xml" );
final UrlSetWriter result = new UrlSetWriter ( this.response.getWriter (), prefix, SitemapProcessor.this.outputFactory );
this.finish = result::finish;
return result;
}
catch ( final IOException e )
{
throw new RuntimeException ( e );
}
}
public void finish ()
{
if ( this.finish != null )
{
try
{
this.finish.run ();
}
catch ( final Exception e )
{
// ignore
}
}
}
}
private boolean processSub ( final HttpServletResponse response, final String path ) throws IOException
{
final ContextCreator creator = new ContextCreator ( response );
for ( final SitemapGenerator generator : this.tracker.getTracked ().values () )
{
generator.render ( path, creator );
if ( creator.isUsed () )
{
creator.finish ();
return true;
}
}
return false;
}
}