blob: 8c53ac0c24899744e02d92c0ee2bae5ab2229a34 [file] [log] [blame]
/**
*
* Copyright 2009-2010 Rickard Öberg AB
*
* 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 org.apache.zest.library.eventsourcing.domain.rest.server;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.zest.api.injection.scope.Service;
import org.apache.zest.api.service.qualifier.Tagged;
import org.apache.zest.functional.Iterables;
import org.apache.zest.io.Outputs;
import org.apache.zest.library.eventsourcing.domain.api.UnitOfWorkDomainEventsValue;
import org.apache.zest.library.eventsourcing.domain.source.EventSource;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.CharacterSet;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.ext.atom.*;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.WriterRepresentation;
import org.restlet.resource.ResourceException;
import static org.apache.zest.functional.Iterables.iterable;
/**
* Get events in various formats.
* <p>
* The feed is paged, with one
* current set page, one working set page, and the rest being archive pages that never change. The links "next", "previous",
* "first" and "last" are used as expected per the Atom spec.
* </p>
* <pre><code>
* / = current set of most recent events (event range: count-pagesize to count)
* /n,m = events from index n to index m. These are archive pages.
* /n = working set page, where n is the first event index to be presented
* </code></pre>
*/
public class DomainEventSourceResource
extends Restlet
{
EventSource source;
public DomainEventSourceResource( @Service @Tagged("domain") EventSource source )
{
this.source = source;
}
@Override
public void handle( Request request, Response response )
{
long eventCount = source.count();
long pageSize = 10;
long startEvent = -1;
long endEvent = -1;
long limit = pageSize;
final List<UnitOfWorkDomainEventsValue> eventsValues = new ArrayList<UnitOfWorkDomainEventsValue>();
final Feed feed = new Feed();
feed.setBaseReference( request.getResourceRef().getParentRef() );
List<Link> links = feed.getLinks();
String remainingPart = request.getResourceRef().getRemainingPart();
if (remainingPart.equals( "/" ))
{
// Current set - always contains the last "pageSize" events
startEvent = Math.max( 0, eventCount - pageSize - 1 );
feed.setTitle( new Text( "Current set" ) );
if (startEvent > 0)
{
long previousStart = Math.max(0, startEvent-pageSize);
long previousEnd = startEvent-1;
Link link = new Link( new Reference( previousStart+","+previousEnd ), new Relation( "previous" ), MediaType.APPLICATION_ATOM );
link.setTitle( "Previous page" );
links.add( link );
}
} else
{
// Archive
String[] indices = remainingPart.substring(1).split( "," );
if (indices.length == 1)
{
// Working set
startEvent = Long.parseLong( indices[0] );
endEvent = startEvent + pageSize - 1;
limit = pageSize;
feed.setTitle( new Text("Working set") );
} else if (indices.length == 2)
{
feed.setTitle( new Text("Archive page") );
startEvent = Long.parseLong( indices[0] );
endEvent = Long.parseLong( indices[1] );
limit = 1+endEvent-startEvent;
} else
throw new ResourceException( Status.CLIENT_ERROR_NOT_FOUND );
if (startEvent > 0)
{
long previousStart = Math.max(0, startEvent-pageSize);
long previousEnd = startEvent-1;
Link link = new Link( new Reference( previousStart+","+previousEnd ), new Relation( "previous" ), MediaType.APPLICATION_ATOM );
link.setTitle( "Previous page" );
links.add( link );
}
long nextStart = endEvent+1;
long nextEnd = nextStart+pageSize-1;
if (nextStart < eventCount)
if (nextEnd >= eventCount)
{
Link next = new Link( new Reference( nextStart+"" ), new Relation( "next" ), MediaType.APPLICATION_ATOM );
next.setTitle( "Working set" );
links.add( next );
} else
{
Link next = new Link( new Reference( nextStart+","+nextEnd ), new Relation( "next" ), MediaType.APPLICATION_ATOM );
next.setTitle( "Next page" );
links.add( next );
}
}
try
{
source.events( startEvent, limit ).transferTo( Outputs.collection( eventsValues ) );
} catch (Throwable throwable)
{
throw new ResourceException( Status.SERVER_ERROR_INTERNAL, throwable );
}
Link last = new Link( new Reference( "0,"+(pageSize-1) ), new Relation( "last" ), MediaType.APPLICATION_ATOM );
last.setTitle( "Last archive page" );
links.add( last );
Link first = new Link( new Reference( "." ), new Relation( "first" ), MediaType.APPLICATION_ATOM );
first.setTitle( "Current set" );
links.add( first );
/*
if (previousPage != -1)
{
Link link = new Link( new Reference( ""+previousPage ), new Relation( "prev-archive" ), MediaType.APPLICATION_ATOM );
link.setTitle( "Previous archive page" );
links.add( link );
}
if (nextPage != -1)
{
Link link = new Link( new Reference( "" + nextPage ), new Relation( "next-archive" ), MediaType.APPLICATION_ATOM );
link.setTitle( "Next archive page" );
links.add( link );
}
else if (startEvent != workingSetOffset)
{
Link next = new Link( new Reference( "" ), new Relation( "next" ), MediaType.APPLICATION_ATOM );
next.setTitle( "Next page" );
links.add( next );
}
*/
Date lastModified = null;
for (UnitOfWorkDomainEventsValue eventsValue : eventsValues)
{
Entry entry = new Entry();
entry.setTitle( new Text( eventsValue.usecase().get() + "(" + eventsValue.user().get() + ")" ) );
entry.setPublished( new Date( eventsValue.timestamp().get() ) );
entry.setModificationDate( lastModified = new Date( eventsValue.timestamp().get() ) );
entry.setId( Long.toString( startEvent + 1 ) );
startEvent++;
Content content = new Content();
content.setInlineContent( new StringRepresentation( eventsValue.toString(), MediaType.APPLICATION_JSON ) );
entry.setContent( content );
feed.getEntries().add( entry );
}
feed.setModificationDate( lastModified );
MediaType mediaType = request.getClientInfo().getPreferredMediaType( Iterables.toList( iterable( MediaType.TEXT_HTML, MediaType.APPLICATION_ATOM ) ));
if (MediaType.APPLICATION_ATOM.equals( mediaType ))
{
WriterRepresentation representation = new WriterRepresentation( MediaType.APPLICATION_ATOM )
{
@Override
public void write( final Writer writer ) throws IOException
{
feed.write( writer );
}
};
representation.setCharacterSet( CharacterSet.UTF_8 );
response.setEntity( representation );
} else
{
WriterRepresentation representation = new WriterRepresentation(MediaType.TEXT_HTML)
{
@Override
public void write( Writer writer ) throws IOException
{
writer.append( "<html><head><title>Events</title></head><body>" );
for( Link link : feed.getLinks() )
{
writer.append( "<a href=\"").append( link.getHref().getPath()).append( "\">" );
writer.append( link.getTitle() );
writer.append( "</a><br/>" );
}
writer.append( "<ol>" );
for( Entry entry : feed.getEntries() )
{
writer.append( "<li>" ).append( entry.getTitle().toString() ).append( "</li>" );
}
writer.append( "</ol></body>" );
}
};
representation.setCharacterSet( CharacterSet.UTF_8 );
response.setEntity( representation );
}
/*
} else
{
throw new ResourceException( Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE );
}
*/
}
}