Skip to content

Commit

Permalink
#12: Add new (non-temp) directory-based repository implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
guusdk committed Oct 9, 2018
1 parent f5fef26 commit 294b0a6
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 34 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,17 @@ A full set of usage instructions are provided by adding the ``--help`` argument:
end users. Defaults to http
--domain <arg> The domain that will be used for the
component with the XMPP domain.
--fileRepo <arg> Store files in a directory provided by
the file system. Provide the desired
path as a value. Path must exist.
-h,--help Displays this help text.
--maxFileSize <arg> The maximum allowed size per file, in
bytes. Use -1 to disable file size
limit. Defaults to 5242880 (five MB).
--sharedSecret <arg> The shared secret, that authenticates
this component with the XMPP domain.
--tempFileRepo Store files in the temporary directory
provided by the file system.
--webHost <arg> The hostname or IP address on which the
webserver will be ran. Defaults to an
arbitrary, non-local address of this
Expand All @@ -86,4 +91,5 @@ A full set of usage instructions are provided by adding the ``--help`` argument:





27 changes: 9 additions & 18 deletions src/main/java/nl/goodbytes/xmpp/xep0363/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.xmpp.packet.IQ;
import org.xmpp.packet.PacketError;

import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -45,31 +44,22 @@ public class Component extends AbstractComponent

private static final Logger Log = LoggerFactory.getLogger( Component.class );
private final String name;
private final String endpoint;

/**
* Instantiates a new component.
*
* The URL that's provided in the second argument is used as the base URL for all client interaction. More
* specifically, this value is used to generate the slot filenames. The URL should be accessible for end-users.
*
* @param name The component name (cannot be null or an empty String).
* @param endpoint The base URL for HTTP interaction (cannot be null).
*/
public Component( String name, URL endpoint )
public Component( String name )
{
super();

if ( name == null || name.trim().isEmpty() )
{
throw new IllegalArgumentException( "Argument 'name' cannot be null or an empty String." );
}
if ( endpoint == null )
{
throw new IllegalArgumentException( "Argument 'endpoint' cannot be null." );
}

this.name = name.trim();
this.endpoint = endpoint.toExternalForm();
}

@Override
Expand Down Expand Up @@ -196,21 +186,22 @@ protected IQ handleIQGet( IQ iq ) throws Exception
return response;
}

final URL url = new URL( URI.create( endpoint + "/" + slot.getUuid() + "/" + fileName ).toASCIIString() );
final URL putUrl = slot.getPutUrl();
final URL getUrl = slot.getGetUrl();

Log.info( "Entity '{}' obtained slot for '{}' ({} bytes): {}", iq.getFrom(), fileName, fileSize, url.toExternalForm() );
Log.info( "Entity '{}' obtained slot for '{}' ({} bytes). PUT-URL: {} GET-URL: {}", iq.getFrom(), fileName, fileSize, putUrl, getUrl );

final IQ response = IQ.createResultIQ( iq );
final Element slotElement = response.setChildElement( "slot", iq.getChildElement().getNamespaceURI() );
if ( isPre030Style )
{
slotElement.addElement( "put" ).setText( url.toExternalForm() );
slotElement.addElement( "get" ).setText( url.toExternalForm() );
slotElement.addElement( "put" ).setText( putUrl.toExternalForm() );
slotElement.addElement( "get" ).setText( getUrl.toExternalForm() );
}
else
{
slotElement.addElement( "put" ).addAttribute( "url", url.toExternalForm() );
slotElement.addElement( "get" ).addAttribute( "url", url.toExternalForm() );
slotElement.addElement( "put" ).addAttribute( "url", putUrl.toExternalForm() );
slotElement.addElement( "get" ).addAttribute( "url", getUrl.toExternalForm() );
}
return response;
}
Expand Down
52 changes: 38 additions & 14 deletions src/main/java/nl/goodbytes/xmpp/xep0363/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package nl.goodbytes.xmpp.xep0363;

import nl.goodbytes.xmpp.xep0363.repository.DirectoryRepository;
import nl.goodbytes.xmpp.xep0363.repository.TempDirectoryRepository;
import org.apache.commons.cli.*;
import org.eclipse.jetty.server.Server;
Expand All @@ -27,6 +28,9 @@
import org.slf4j.LoggerFactory;

import java.net.*;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Enumeration;
Expand All @@ -49,8 +53,9 @@ public class Launcher
private final String announcedWebHost;
private final Integer announcedWebPort;
private final Repository repository;
private final Long maxFileSize;

public Launcher( String xmppHost, Integer xmppPort, String domain, String sharedSecret, String webHost, Integer webPort, String announcedWebProtocol, String announcedWebHost, Integer announcedWebPort, Repository repository )
public Launcher( String xmppHost, Integer xmppPort, String domain, String sharedSecret, String webHost, Integer webPort, String announcedWebProtocol, String announcedWebHost, Integer announcedWebPort, Repository repository, Long maxFileSize )
{
this.xmppHost = xmppHost != null ? xmppHost : "localhost";
this.xmppPort = xmppPort != null ? xmppPort : 5275;
Expand All @@ -62,6 +67,7 @@ public Launcher( String xmppHost, Integer xmppPort, String domain, String shared
this.announcedWebHost = announcedWebHost != null ? announcedWebHost : this.webHost;
this.announcedWebPort = announcedWebPort != null ? announcedWebPort : this.webPort;
this.repository = repository != null ? repository : new TempDirectoryRepository();
this.maxFileSize = maxFileSize != null ? maxFileSize : SlotManager.DEFAULT_MAX_FILE_SIZE;
}

public static void main( String[] args )
Expand Down Expand Up @@ -159,6 +165,13 @@ public static void main( String[] args )
.desc( "Store files in the temporary directory provided by the file system." )
.build()
);
repoType.addOption(
Option.builder()
.longOpt( "fileRepo" )
.hasArg()
.desc( "Store files in a directory provided by the file system. Provide the desired path as a value. Path must exist." )
.build()
);
options.addOptionGroup( repoType );

options.addOption(
Expand Down Expand Up @@ -199,21 +212,23 @@ public static void main( String[] args )
{
repository = new TempDirectoryRepository();
}
else
else if (line.hasOption( "fileRepo"))
{
repository = null;
final String directory = line.getOptionValue( "fileRepo" );
final Path path;
try {
path = Paths.get( directory );
} catch ( InvalidPathException e ) {
throw new ParseException( "Invalid value for 'fileRepo' option: " + e.getMessage() );
}
repository = new DirectoryRepository( path );
}
Log.info( "webPort: {}", webPort );
Log.info( "announcedWebPort: {}", announcedWebPort );

final Launcher launcher = new Launcher( xmppHost, xmppPort, domain, sharedSecret, webHost, webPort, announcedWebProtocol, announcedWebHost, announcedWebPort, repository );

if ( maxFileSize != null )
else
{
SlotManager.getInstance().setMaxFileSize( maxFileSize );
repository = null;
}
Log.info( "maxFileSize: {}", SlotManager.getInstance().getMaxFileSize() );

final Launcher launcher = new Launcher( xmppHost, xmppPort, domain, sharedSecret, webHost, webPort, announcedWebProtocol, announcedWebHost, announcedWebPort, repository, maxFileSize );
launcher.start();
}
}
Expand Down Expand Up @@ -287,6 +302,17 @@ private static String getPublicAddress()

public void start()
{
Log.info( "Starting external component with endpoint {}://{}:{}", announcedWebProtocol, announcedWebHost, announcedWebPort );
SlotManager.getInstance().setWebProtocol( announcedWebProtocol );
SlotManager.getInstance().setWebHost( announcedWebHost );
SlotManager.getInstance().setWebPort( announcedWebPort );

if ( maxFileSize != null )
{
SlotManager.getInstance().setMaxFileSize( maxFileSize );
}
Log.info( "maxFileSize: {}", SlotManager.getInstance().getMaxFileSize() );

Server jetty = null;
ExternalComponentManager manager = null;
try
Expand All @@ -310,9 +336,7 @@ public void start()

Log.info( "Webserver started at {}:{}", connector.getHost(), connector.getLocalPort() );

final URL endpoint = new URL( announcedWebProtocol, announcedWebHost, announcedWebPort, "" );
Log.info( "Starting external component with endpoint {}", endpoint.toExternalForm() );
final Component component = new Component( domain, endpoint );
final Component component = new Component( domain );
manager = new ExternalComponentManager( xmppHost, xmppPort );
if ( sharedSecret != null )
{
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/nl/goodbytes/xmpp/xep0363/Servlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URI;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.UUID;


Expand Down Expand Up @@ -190,7 +191,15 @@ protected void doPut( HttpServletRequest req, HttpServletResponse resp ) throws
}
}

resp.setHeader( "Location", URI.create( req.getRequestURL().toString() ).toASCIIString() );
try
{
resp.setHeader( "Location", slot.getGetUrl().toExternalForm() );
}
catch ( URISyntaxException | MalformedURLException e )
{
Log.warn( "Unable to calculate GET URL for {}", slot, e );
}

resp.setStatus( HttpServletResponse.SC_CREATED );
Log.info( "... responded with CREATED. Stored data from the request body in the repository." );
}
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/nl/goodbytes/xmpp/xep0363/Slot.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

import org.xmpp.packet.JID;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Date;
import java.util.UUID;

Expand Down Expand Up @@ -61,4 +65,46 @@ public UUID getUuid()
{
return uuid;
}

public URL getPutUrl() throws URISyntaxException, MalformedURLException
{
return getURL();
}

public URL getGetUrl() throws URISyntaxException, MalformedURLException
{
return getURL();
}

private URL getURL() throws URISyntaxException, MalformedURLException
{
// First, use URI to properly encode all components.
final URI uri = new URI(
SlotManager.getInstance().getWebProtocol(),
null, // userinfo
SlotManager.getInstance().getWebHost(),
SlotManager.getInstance().getWebPort(),
"/" + uuid.toString() + "/" + filename,
null, // query
null // fragment
);

// Then, ensure that the URL contains US-ASCII characters only, to prevent issues with some clients.
final String usascii = uri.toASCIIString();

// Finally, transform the result into an URL.
return new URL( usascii );
}

@Override
public String toString()
{
return "Slot{" +
"uuid=" + uuid +
", creationDate=" + creationDate +
", filename='" + filename + '\'' +
", creator=" + creator +
", size=" + size +
'}';
}
}
34 changes: 34 additions & 0 deletions src/main/java/nl/goodbytes/xmpp/xep0363/SlotManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.common.cache.CacheBuilder;
import org.xmpp.packet.JID;

import java.net.URL;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

Expand All @@ -39,6 +40,9 @@ public class SlotManager
private long maxFileSize = DEFAULT_MAX_FILE_SIZE;
private long putExpiryValue = 5;
private TimeUnit putExpiryUnit = TimeUnit.MINUTES;
private String webProtocol;
private String webHost;
private Integer webPort;


private SlotManager()
Expand Down Expand Up @@ -91,4 +95,34 @@ public Slot consumeSlotForPut( UUID uuid )

return slot;
}

public void setWebProtocol( final String webProtocol )
{
this.webProtocol = webProtocol;
}

public String getWebProtocol()
{
return webProtocol;
}

public void setWebHost( final String webHost )
{
this.webHost = webHost;
}

public String getWebHost()
{
return webHost;
}

public void setWebPort( final int webPort )
{
this.webPort = webPort;
}

public int getWebPort()
{
return webPort;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package nl.goodbytes.xmpp.xep0363.repository;

import java.io.IOException;
import java.nio.file.Path;

/**
* A repository of files, backed by a regular directory.
*
* @author Guus der Kinderen, [email protected]
*/
public class DirectoryRepository extends AbstractFileSystemRepository
{
private final Path path;
public DirectoryRepository( final Path path )
{
super();

this.path = path;
}

@Override
protected Path initializeRepository() throws IOException
{
return path;
}
}

0 comments on commit 294b0a6

Please sign in to comment.