public abstract class BaseResource extends ServerResource {

	protected static final Logger LOGGER = Logger.getLogger("org.restlet");

	protected static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
	protected static final String NO_TITLE = null;

	protected RdfStore getMetadataRdfStore() {
		return ((MarketPlaceApplication) getApplication()).getMetadataRdfStore();
	}

	protected FileStore getMetadataFileStore() {
		return ((MarketPlaceApplication) getApplication()).getMetadataFileStore();
	}

	protected QueryBuilder getQueryBuilder() {
		return ((MarketPlaceApplication) getApplication()).getQueryBuilder();
	}

	protected String getDataDir() {
		return ((MarketPlaceApplication) getApplication()).getDataDir();
	}

	protected EndorserWhitelist getWhitelist() {
		return ((MarketPlaceApplication) getApplication()).getWhitelist();
	}

	protected freemarker.template.Configuration getFreeMarkerConfiguration() {
		return ((MarketPlaceApplication) getApplication())
				.getFreeMarkerConfiguration();
	}

	protected TemplateRepresentation createTemplateRepresentation(String tpl,
			Map<String, Object> info, MediaType mediaType) {

		freemarker.template.Configuration freeMarkerConfig = getFreeMarkerConfiguration();

		return new TemplateRepresentation(tpl, freeMarkerConfig, info,
				mediaType);
	}

	protected Map<String, Object> createInfoStructure(String title) {

		Map<String, Object> info = new HashMap<String, Object>();

		// Add the standard base URL declaration.
		info.put("baseurl", getBaseUrl(getRequest()));

		// Add the title if appropriate.
		if (title != null && !"".equals(title)) {
			info.put("title", title);
		}

		return info;
	}

	protected String commitMetadataEntry(File uploadedFile, Document doc) {
		writeMetadataToFileStore(doc);

		String metadataPath = null;
		try {
			metadataPath = writeMetadataToRdfStore(doc);
		} catch (ResourceException e) {
			// transaction has failed, so rollback
			deleteMetadataFromFileStore(doc);
			throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e);
		}

		if (!uploadedFile.delete()) {
			LOGGER.severe("uploaded file could not be deleted: " + uploadedFile);
		}

		return metadataPath;
	}

	protected void writeMetadataToFileStore(Document doc) {

		String[] coordinates = getMetadataEntryCoordinates(doc);

		String identifier = coordinates[0];
		String endorser = coordinates[1];
		String created = coordinates[2];

		String key =
			identifier + File.separator
		+ endorser + File.separator
		+ created;

		getMetadataFileStore().store(key, doc);
	}

	protected void deleteMetadataFromFileStore(Document doc) {

		String[] coordinates = getMetadataEntryCoordinates(doc);

		String identifier = coordinates[0];
		String endorser = coordinates[1];
		String created = coordinates[2];

		String key = identifier + File.separator + endorser
				+ File.separator + created;

		getMetadataFileStore().remove(key);
	}

	// Create a deep copy of the document and strip signature elements.
	protected String createRdfEntry(Document doc) {
		Document copy = (Document) doc.cloneNode(true);
		MetadataUtils.stripSignatureElements(copy);
		String rdfEntry = XMLUtils.documentToString(copy);
		String[] coords = getMetadataEntryCoordinates(copy);
		rdfEntry = rdfEntry.replaceFirst("<rdf:Description rdf:about=\"#"
				+ coords[0] + "\">", "<rdf:Description rdf:about=\"#"
				+ coords[0] + "/" + coords[1] + "/" + coords[2] + "\">");
		return rdfEntry;
	}

	private void tagEntry(String[] coordinates){
		String newPath = coordinates[0] + "/" + coordinates[1] + "/" + coordinates[2];
		getMetadataRdfStore().tag(newPath, "latest");
	}

	private void removeTag(String[] coordinates, String previousLatest){
		if(previousLatest != null && !previousLatest.equals("null")){
			String path = coordinates[0] + "/" + coordinates[1] + "/" + previousLatest;
			getMetadataRdfStore().removeTag(path, "latest");
		}
	}

	private String getPreviousLatest(String[] coordinates) {
		String previousLatest = null;

		String identifier = coordinates[0];
        String endorser = coordinates[1];

        String query = getQueryBuilder().buildLatestEntryQuery(
        		identifier, endorser);

        try {
        	List<Map<String, String>> results = query(query);

        	if(results.size() > 0){
        		previousLatest = results.get(0).get("latest");
        	}
        } catch(MarketplaceException e){
        	throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
        }

		return previousLatest;
	}

	protected String[] getMetadataEntryCoordinates(Document doc) {

		String[] coords = new String[3];

		coords[0] = XPathUtils.getValue(doc, IDENTIFIER_ELEMENT);
		coords[1] = XPathUtils.getValue(doc, EMAIL);
		coords[2] = XPathUtils.getValue(doc, CREATED_DATE);

		return coords;
	}

	protected String writeMetadataToRdfStore(Document datumDoc) {

		String[] coordinates = getMetadataEntryCoordinates(datumDoc);

		String identifier = coordinates[0];
		String endorser = coordinates[1];
		String created = coordinates[2];

		String metadataPath = identifier + "/" + endorser + "/" + created;
		String previousLatest = getPreviousLatest(coordinates);

		String rdfEntry = createRdfEntry(datumDoc);
		if (!storeMetadatum(metadataPath, rdfEntry)) {
			throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
		}

		removeTag(coordinates, previousLatest);
		tagEntry(coordinates);

		return metadataPath;
	}

    // always has a trailing slash!
    public static String getBaseUrl(Request request) {

        Series headers = (Series) request.getAttributes().get("org.restlet.http.headers");

        String scheme = null;
        String authority = null;
        if (headers != null) {
            scheme = headers.getFirstValue("X-Forwarded-Proto");
            authority = headers.getFirstValue("Host");
        }

        Reference ref = request.getRootRef();
        if (authority != null) {
            ref.setAuthority(authority);
        }
        if (scheme != null) {
            ref.setScheme(scheme);
        }

        String url = ref.toString();
        if (url.endsWith("/")) {
            return url;
        } else {
            return url + "/";
        }
    }

    /**
	 * Stores a new metadata entry
	 *
	 * @param metadataPath
	 *            identifier of the metadata entry
	 * @param rdf
	 *            the metadata entry to store
	 */
	protected boolean storeMetadatum(String metadataPath, String rdf) {
		boolean success = getMetadataRdfStore().store(metadataPath, rdf);

		return success;
	}

	/**
	 * Retrieve a particular metadata entry
	 *
	 * @param metadataPath
	 *            identifier of the metadata entry
	 * @return metadata entry
	 */
	protected String getMetadatum(String metadataPath) {
		String model = getMetadataFileStore().read(metadataPath);

		return model;
	}

	/**
	 * Remove a metadata entry
	 *
	 * @param iri
	 *            identifier of the metadata entry
	 */
	protected void removeMetadatum(String metadataPath) {
		getMetadataRdfStore().remove(metadataPath);
	}

	protected String queryResultsAsXml(String query)
			throws MarketplaceException {
		String result = getMetadataRdfStore()
				.getRdfEntriesAsXml(query);

		return result;
	}

	protected String queryResultsAsJson(String query)
			throws MarketplaceException {
		String result = getMetadataRdfStore().getRdfEntriesAsJson(
				query);

		return result;
	}

	/**
	 * Query the metadata
	 *
	 * @param queryString
	 *            the query
	 * @return the resultset as a Java Collection
	 */
	protected List<Map<String, String>> query(String query)
			throws MarketplaceException {
		List<Map<String, String>> list = getMetadataRdfStore().getRdfEntriesAsMap(
				query);

		return list;
	}

	protected Representation createStatusRepresentation(String title,
			String message) {
		Representation status = null;
		if (getRequest().getClientInfo().getAcceptedMediaTypes().size() > 0
			&& getRequest().getClientInfo().getAcceptedMediaTypes()
				.get(0).getMetadata().equals(MediaType.TEXT_HTML)) {
			Map<String, Object> dataModel = createInfoStructure(title);
			dataModel.put("statusName", getResponse().getStatus().getReasonPhrase());
			dataModel.put("statusDescription", message);
			status = createTemplateRepresentation("status.ftl", dataModel,
				MediaType.TEXT_HTML);
		} else {
			status = new StringRepresentation(message, TEXT_PLAIN);
		}

		return status;
	}

} public final class Configuration {

	private static final Logger LOGGER = Logger.getLogger("org.restlet");
	
    private static final String CONFIG_FILENAME = "marketplace.cfg";

    private static final Properties PROPERTIES;

    static {
        List<File> configFileLocations = getConfigurationFileLocations();
        PROPERTIES = getConfigurationProperties(configFileLocations);
    }

    private Configuration() {

    }

    private static List<File> getConfigurationFileLocations() {

        ArrayList<File> locations = new ArrayList<File>();

        // Possible locations for the configuration file are the current working
        // directory, the user's home directory, or the standard system
        // location, in that order.
        File[] dirs = { new File(System.getProperty("user.dir")),
                new File(System.getProperty("user.home")),
                new File("/etc/stratuslab/") };

        for (File dir : dirs) {
            locations.add(new File(dir, CONFIG_FILENAME));
        }

        return Collections.unmodifiableList(locations);
    }

    private static Properties getConfigurationProperties(
            List<File> configFileLocations) {

        for (File f : configFileLocations) {
            if (f.canRead()) {
            	LOGGER.info("using configuration: " + f.getAbsolutePath());
                Properties properties = loadProperties(f);
                validateConfiguration(properties);
                return properties;
            }
        }
        throw new MarketplaceException("cannot locate configuration file");
    }

    private static Properties loadProperties(File configFile) {

        Properties properties = new Properties();

        try {
            Reader reader = new InputStreamReader(
            		new FileInputStream(configFile), "UTF-8");
            try {
                properties.load(reader);
            } catch (IOException consumed) {
                // TODO: Add logging.
            } finally {
                try {
                    reader.close();
                } catch (IOException consumed) {
                    // TODO: Add logging.
                }
            }
        } catch (FileNotFoundException consumed) {
            // Return empty properties file.
        } catch (UnsupportedEncodingException e) {
        	// Return empty properties file.
        }

        return properties;
    }

    public static String getParameterValue(Parameter parameter) {
        return parameter.getProperty(PROPERTIES);
    }

    public static boolean getParameterValueAsBoolean(Parameter parameter) {
        return Boolean.parseBoolean(parameter.getProperty(PROPERTIES));
    }

    public static int getParameterValueAsInt(Parameter parameter) {
        return Integer.parseInt(parameter.getProperty(PROPERTIES));
    }

    public static long getParameterValueAsLong(Parameter parameter) {
        return Long.parseLong(parameter.getProperty(PROPERTIES));
    }

    public static File getParameterValueAsFile(Parameter parameter) {
        return new File(parameter.getProperty(PROPERTIES));
    }

    private static void validateConfiguration(Properties properties) {
        checkAllParametersAreValid(properties);
        checkAllParametersAreKnown(properties);
    }

    private static void checkAllParametersAreValid(Properties properties) {
        for (Parameter p : Parameter.values()) {
            String value = p.getProperty(properties);
            if (value != null) {
                p.validate(value);
            }
        }
    }

    private static void checkAllParametersAreKnown(Properties properties) {
        for (Object key : properties.keySet()) {
            Parameter.parameterFromKey(key);
        }
    }

} public enum Parameter {

    STORE_TYPE(true, "memory",
            "Storage type for image metadata database (memory or native)") {
        @Override
        public void validate(String value) {
            super.validate(value);
            if("mysql".equals(value) || "postgres".equals(value)){
                throw new IllegalArgumentException(getKey() +
                        " RDBMS support has been removed, please reconfigure"
                        + " the Marketplace to use store.type=native, restart, and"
                        + " execute the marketplace-rebuild-db script.");
            } else if (!("memory".equals(value) || "native".equals(value))) {
                throw new IllegalArgumentException(getKey()
                        + " must be 'memory' or 'native'");
            }
        }
    },

    COUCHBASE_BUCKET(false, "default", "Couchbase bucket name ('default')"),
    
    COUCHBASE_PASSWORD(false, "", "Couchbase password"),
    
    COUCHBASE_MARKETPLACEID(false, "", "Identifier for this Marketplace host"),
    
    COUCHBASE_URIS(false, "http://127.0.0.1:8091/pools", "List of Couchbase URIs") {
    	@Override
    	public void validate(String value) {
    		super.validate(value);
    		String[] uris = value.split("\\s+");
    		for(String uri : uris){
    			try {
    				new URI(uri);
    			} catch (URISyntaxException e) {
    				throw new IllegalArgumentException("invalid Couchbase URI: "
    						+ value, e);
    			}
    		}
    	}
    },
    
    DATA_DIR(true, "/var/lib/stratuslab/metadata/data",
            "Directory containing raw metadata data entries.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            File datadir = new File(value);
            if (!datadir.isAbsolute()) {
                throw new IllegalArgumentException(getKey()
                        + " must be an absolute directory");
            }
        }
    },

    ENDORSER_REMINDER(false, "false", "Flag to determine if email reminder "
            + "should be sent to endorsers periodically.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isBoolean(value);
        }
    },
    
    FILESTORE_TYPE(true, "file",
    "Storage type for image metadata files (file, couchbase)") {
    	@Override
    	public void validate(String value) {
    		super.validate(value);
    		if (!("file".equals(value) || "couchbase".equals(value))) {
    			throw new IllegalArgumentException(getKey()
    					+ " must be 'file' or 'couchbase'");
    		}
    	}
    },

    GIT_KEY(false, "Private key for accessing Git repository"),
    
    GIT_KEY_PASSPHRASE(false, "Private key passphrase"),
    
    GIT_KNOWN_HOSTS(false, "Known hosts file"),
    
    GIT_URI(false, "URI of Git repository"),
    
    GIT_USER(false, "User credential for Git repository"),
    
    GIT_PASSWORD(false, "Password credential for Git repository"),
    
    PENDING_DIR(true, "/var/lib/stratuslab/marketplace/pending",
            "Directory for pending (unconfirmed) entries.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            File datadir = new File(value);
            if (!datadir.isAbsolute()) {
                throw new IllegalArgumentException(getKey()
                        + " must be an absolute directory");
            }
        }
    },

    METADATA_MAX_BYTES(true, "10240000",
            "Size limit for a single metadata entry.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isPositiveInt(getKey(), value);
        }
    },

    VALIDATE_EMAIL(true, "true", "Flag to determine email must be validated.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isBoolean(value);
        }
    },

    RDBMS_DBNAME(false, "marketplace", "Database name."),

    RDBMS_HOST(false, "localhost", "RDBMS hostname."),

    RDBMS_PORT(false, "3306", "Port on for RDBMS server.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isValidPort(value);
        }
    },

    RDBMS_DBUSER(false, "sesame", "RDBMS username."),

    RDBMS_DBPASS(false, "sesame", "RDBMS password."),
    
    REPLICATION_ENABLED(false, "false", "Enable replication.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isBoolean(value);
        }
    },

    REPLICATION_TYPE(false, "public",
    "Type of replication (public, private)") {
    	@Override
    	public void validate(String value) {
    		super.validate(value);
    		if (!("public".equals(value) || "private".equals(value))) {
    			throw new IllegalArgumentException(getKey()
    					+ " must be 'public' or 'private'");
    		}
    	}
    },
    
    ADMIN_EMAIL(true, "Email address for account approvals, etc.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isValidEmail(value);
        }
    },

    MAIL_HOST(true, "Host for SMTP server for email notifications."),

    MAIL_PORT(false, "Port on SMTP server (defaults to standard ports).") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isValidPort(value);
        }
    },

    MAIL_USER(true, "Username for SMTP server."),

    MAIL_PASSWORD(false, "Password for SMTP server."),

    MAIL_SSL(false, "Use SSL for SMTP server (default is 'true').") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isBoolean(value);
        }
    },

    MAIL_DEBUG(false, "Debug mail sending (default is 'false').") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isBoolean(value);
        }
    },

    MARKETPLACE_ENDPOINT(false, "http://marketplace.stratuslab.eu", 
    		"Endpoint for this Marketplace instance.") {
    	public void validate(String value) {
            super.validate(value);
            try {
                new URL(value);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException("invalid master URL: "
                        + value, e);
            }
        }
    },
    
    MARKETPLACE_TYPE(false, "Type of Marketplace instance."),
    
    MASTER_URL(true, "URL of the master instance.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            try {
                new URL(value);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException("invalid master URL: "
                        + value, e);
            }
        }
    },
    
    SUPPORT_EMAIL(true, "Email address for support requests.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isValidEmail(value);
        }
    },

    SUPPORT_URL(true, "URL where support can be obtained.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            try {
                new URL(value);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException("invalid support URL: "
                        + value, e);
            }
        }
    },

    WHITELIST_ENABLED(false, "false", "Enable endorser whitelist.") {
        @Override
        public void validate(String value) {
            super.validate(value);
            isBoolean(value);
        }
    },

    WHITELIST_LOCATION(false, "/etc/stratuslab/marketplace.whitelist",
            "Endorser whitelist location."),

    WHITELIST_TRUSTSTORE(false, "/etc/stratuslab/marketplace.truststore",
            "Marketplace truststore location."),

    WHITELIST_CRL(false, "/etc/stratuslab/marketplace.crl",
            "Location of CRLs."),
    
    WHITELIST_PASSWORD(false, "Truststore password."),
    
    STYLE_PATH(true, "/eu/stratuslab/style/css/",
            "Path for CSS and style information."),

    IMG_PATH(true, "/eu/stratuslab/style/img/", "Path for image files."),
            
    JS_PATH(true, "/eu/stratuslab/style/js/", "Path for JavaScript files.");

    private final String key;
    private final boolean required;
    private final String description;
    private final String defaultValue;

    private Parameter(boolean required, String description) {
        this(required, null, description);
    }

    private Parameter(boolean required, String defaultValue, String description) {
        this.key = nameToKey(this.name());
        this.required = required;
        this.defaultValue = defaultValue;
        this.description = description;
    }

    public String getKey() {
        return key;
    }

    public boolean isRequired() {
        return required;
    }

    public String getDescription() {
        return description;
    }

    public String getDefaultValue() {
        return defaultValue;
    }

    public String getProperty(Properties properties) {
        String value = (String) properties.get(key);
        return (value != null) ? value : defaultValue;
    }

    public void validate(String value) {
        if (isRequired()) {
            if (value == null || "".equals(value)) {
                throw new IllegalArgumentException("required parameter ("
                        + getKey() + ") is not defined");
            }
        }
    }

    public static Parameter parameterFromKey(Object key) {
        String skey = key.toString().toUpperCase().replace('.', '_');
        return Parameter.valueOf(skey);
    }

    public static String nameToKey(String name) {
        return name.toLowerCase().replace('_', '.');
    }

    private static void isBoolean(String s) {
        if (!("true".equals(s) || "false".equals(s))) {
            throw new IllegalArgumentException(
                    "value must be 'true' or 'false'");
        }
    }

    private static void isPositiveInt(String key, String s) {
        try {
            int i = Integer.parseInt(s);
            if (i <= 0) {
                throw new IllegalArgumentException(key
                        + " must be a positive integer");
            }
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(key
                    + " must be a positive integer: " + e.getMessage(), e);
        }
    }

    private static void isValidPort(String s) {
    	final int minPort = 1;
    	final int maxPort = 65535;
    	
        try {
            int port = Integer.parseInt(s);
            if (port < minPort || port > maxPort) {
                throw new IllegalArgumentException("invalid port number("
                        + port + ")");
            }
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

    private static void isValidEmail(String s) {
        try {
            new InternetAddress(s);
            if (!Pattern.matches(".+@.+", s)) {
                throw new IllegalArgumentException("invalid email address: "
                        + s);

            }
        } catch (AddressException e) {
            throw new IllegalArgumentException("invalid email address: "
                    + e.getMessage(), e);
        }
    }

} public interface QueryBuilder {
	
	String buildLatestEntryQuery(String identifier, String email);
	
	String buildGetMetadataQuery(String status, String access,
			Map<String, String> requestQueryValues, 
			Map<String, Object> requestAttributes);
	
	String buildGetMetadataCountQuery(String status, String access,
			Map<String, String> requestQueryValues, 
			Map<String, Object> requestAttributes);
	
	String buildGetTotalRecordsQuery(String status, String access);
	
	String buildEndorserQuery(String email, String historyRange);
	
	String buildEndorsersQuery();
	
	String buildExpiryQuery(String expiryDate);

	String buildReminderQuery(String email);
	
	String buildTagQuery(String email, String tag);
	
	String buildEndorserTagsQuery(String email);
	
} public class SparqlBuilder implements QueryBuilder {

	protected static final int ARG_EMAIL = 1;
	protected static final int ARG_DATE = 2;
	protected static final int ARG_OTHER = 3;
	
	private static final String CREATED = "created";
	private static final String IDENTIFIER = "identifier";
	private static final String EMAIL = "email";
	
	private static final String WHERE = " WHERE {";
	
	public String buildLatestEntryQuery(String identifier, String endorser) {
		String query = String.format(SparqlUtils.LATEST_ENTRY_QUERY_TEMPLATE, 
        		identifier, endorser);
		
		return query;
	}

	public String buildGetMetadataQuery(String status, String access,
			Map<String, String> requestQueryValues,
			Map<String, Object> requestAttributes) {
				
		StringBuilder dataQuery = new StringBuilder(SparqlUtils.SELECT_ALL);
		buildBaseQuery(dataQuery, status, access,
				requestQueryValues, requestAttributes);
		
		//Build the paging query
		String paging = buildPagingFilter(requestQueryValues);
    	dataQuery.append(paging);
        
        return dataQuery.toString();
	}

	public String buildGetMetadataCountQuery(String status, String access,
			Map<String, String> requestQueryValues,
			Map<String, Object> requestAttributes) {
		
		StringBuilder countQuery = new StringBuilder(SparqlUtils.SELECT_COUNT);
		countQuery.append("{" + SparqlUtils.SELECT_ALL);
        buildBaseQuery(countQuery, status, access,
				requestQueryValues, requestAttributes);
        countQuery.append("}");

        return countQuery.toString();        
    }
	
	private void buildBaseQuery(StringBuilder query, String status, String access,
			Map<String, String> requestQueryValues,
			Map<String, Object> requestAttributes){
		
		boolean hasDate = false;
	    
		addQueryParametersToRequest(requestAttributes, requestQueryValues);
		String filter = buildFilterFromUrlQuery(requestAttributes);
    	    	
    	if(filter.length() > 0){
    		if(filter.contains(CREATED)){
    			hasDate = true;
    		}
    	}
    	    	
    	//Build the paging query
    	String searching = buildSearchingFilter(requestQueryValues);
    	   	         
        String where = WHERE
            + SparqlUtils.WHERE_BLOCK;
        
        StringBuilder wherePredicate = new StringBuilder(
                    where);

        wherePredicate.append(filter);

        if (!hasDate) {
                wherePredicate
                        .append(SparqlUtils.getLatestFilter());
        }
                     
        setStatus(wherePredicate, status);
        setAccess(wherePredicate, access);
                        
        wherePredicate.append(searching);
        wherePredicate.append(" }");

        query.append(wherePredicate);
        query.append(SparqlUtils.GROUP_BY);
    }
	
	private void setStatus(StringBuilder wherePredicate, String status){
		if(status.equals("expired")){ //implies validity date in the past and not deprecated
        	wherePredicate
              	.append(SparqlUtils.getExpiredFilter(
              			MarketplaceUtils.getCurrentDate()));
        	wherePredicate.append(SparqlUtils.DEPRECATED_OFF);
        } else if(status.equals("valid")){ //implies in date and not deprecated
        	  wherePredicate
              	.append(SparqlUtils.getValidFilter(
              			MarketplaceUtils.getCurrentDate()));
        	  wherePredicate.append(SparqlUtils.DEPRECATED_OFF);
        } else if(status.equals("deprecated")){ //implies contains a deprecated field
        	appendDeprecated(wherePredicate);
        }
	}
	
	private void setAccess(StringBuilder wherePredicate, String access){
		if(access.equals("pdisk")){ 
        	wherePredicate
              	.append(SparqlUtils.ACCESS_PRIVATE_FILTER);
        } else if(access.equals("web")){ 
        	  wherePredicate
              	.append(SparqlUtils.ACCESS_PUBLIC_FILTER);
        }
	}
	
	private void addQueryParametersToRequest(Map<String, Object> attributes, Map<String, String> formValues) {
    	
		//Create filter from request parameters.    	    	 	
    	if(formValues.containsKey(IDENTIFIER)){
    		attributes.put(IDENTIFIER, formValues.get(IDENTIFIER));
    	}
    	if(formValues.containsKey(EMAIL)){
    		attributes.put(EMAIL, formValues.get(EMAIL));
    	}
    	if(formValues.containsKey(CREATED)){
    		attributes.put(CREATED, formValues.get(CREATED));
    	}   
    }
	
	private String buildFilterFromUrlQuery(Map<String, Object> attributes){
    	StringBuilder filter = new StringBuilder();
    	
    	for (Map.Entry<String, Object> arg : attributes.entrySet()) {
    		String key = arg.getKey();
    		if (!key.startsWith("org.restlet")) {
    			switch (classifyArg((String) arg.getValue())) {
    			case ARG_EMAIL:
    				filter.append(
    						SparqlUtils.buildFilterEq(EMAIL, (String)arg.getValue()));
    				break;
    			case ARG_DATE:
    				filter.append(
    						SparqlUtils.buildFilterEq(CREATED, (String)arg.getValue()));
    				break;
    			case ARG_OTHER:
    				filter.append(
    						SparqlUtils.buildFilterEq(IDENTIFIER, (String)arg.getValue()));
    				break;
    			default:
    				break;
    			}
    		}
    	}
    	
    	return filter.toString();
    }
    
    private int classifyArg(String arg) {
        if (arg == null || arg.equals("null") || arg.equals("")) {
            return -1;
        } else if (PatternUtils.isEmail(arg)) {
            return ARG_EMAIL;
        } else if (PatternUtils.isDate(arg)) {
            return ARG_DATE;
        } else {
            return ARG_OTHER;
        }
    }
	
    /*
     * Build a paging filter (ORDER BY ... LIMIT ...)
     * 
     * @return SPARQL LIMIT clause
     */
    private String buildPagingFilter(Map<String, String> requestQueryValues){
    	
    	String iDisplayStart = requestQueryValues.get("iDisplayStart");
		String iDisplayLength = requestQueryValues.get("iDisplayLength");
		String iSortCol = requestQueryValues.get("iSortCol_0");
		String sSortDir = requestQueryValues.get("sSortDir_0");   	
    	String paging = "";
    	
    	if(iDisplayStart != null && iDisplayLength != null){
    		int sort = (iSortCol != null) ? 
    				Integer.parseInt(iSortCol) : SparqlUtils.DEFAULT_SORT_COL;
    		String sortCol = SparqlUtils.getColumn(sort);
    		      		
    		paging = SparqlUtils.buildLimit(sortCol, iDisplayLength, iDisplayStart, sSortDir);
    	}
    	
    	return paging;
    }
             
    private String buildSearchingFilter(Map<String, String> requestQueryValues){
    	String sSearch = requestQueryValues.get("sSearch");
    	
    	String[] sSearchCols = new String[SparqlUtils.getColumnCount()];
    	
        for(int i = 0; i < SparqlUtils.getColumnCount();i++){
                sSearchCols[i] = requestQueryValues.get("sSearch_" + i);
        }
        
        String searching = buildSearchAllFilter(sSearch)
        					+ buildSearchColsFilter(sSearchCols);      
        
        return searching;
    }
           
    private String buildSearchAllFilter(String sSearch){
    	String searching = "";
    	
    	if(sSearch != null && sSearch.length() > 0){
            String[] searchTerms = sSearch.split(" ");
            StringBuilder searchAllFilter = new StringBuilder(" FILTER (");
            for(int i = 0; i < searchTerms.length; i++){

                    searchAllFilter.append("(");
                    for(int j = 1; j < SparqlUtils.getColumnCount(); j++){
                            searchAllFilter.append(SparqlUtils.buildRegex(
                                            SparqlUtils.getColumn(j), searchTerms[i]));

                            if(j < SparqlUtils.getColumnCount() - 1){
                                    searchAllFilter.append(" || ");
                            }
                    }
                    searchAllFilter.append(")");

                    if(i < searchTerms.length -1){
                                    searchAllFilter.append(" && ");
                    }
            }
            searchAllFilter.append(") . ");
            searching = searchAllFilter.toString();
    	}
    	
    	return searching;    	
    }
    
    private String buildSearchColsFilter(String[] sSearchCols) {
    	StringBuilder searchColumnsPredicate = new StringBuilder();
        
        for(int i = 1; i <= SparqlUtils.SEARCHABLE_COLUMNS; i++){
                if ( sSearchCols[i] != null && sSearchCols[i].length() > 0 )
                {
                        if ( searchColumnsPredicate.length() == 0 )
                        {
                                searchColumnsPredicate.append(" FILTER (");
                        }
                        else
                        {
                                searchColumnsPredicate.append(" && ");
                        }
                        String word = sSearchCols[i];
                        searchColumnsPredicate.append(SparqlUtils.buildRegex(
                                        SparqlUtils.getColumn(i), word));
                }
        }
        
        if(searchColumnsPredicate.length() != 0){
                searchColumnsPredicate.append(" ) . ");
        }
        
        return searchColumnsPredicate.toString();
    }
    
	public String buildGetTotalRecordsQuery(String status, String access) {
		String q = SparqlUtils.SELECT_COUNT
        + WHERE
        + SparqlUtils.COUNT_WHERE_BLOCK
        + SparqlUtils.getLatestFilter();
		
		StringBuilder wherePredicate = new StringBuilder();
		setStatus(wherePredicate, status);
		
    	StringBuilder query = new StringBuilder(q);
    	query.append(wherePredicate);    	
    	
    	query.append(" }");
    	
    	return query.toString();
	}
	
	private void appendDeprecated(StringBuilder filter) {
		filter.append(SparqlUtils.DEPRECATED_ON);
	}

	public String buildEndorserQuery(String email, String historyRange) {
		return String.format(SparqlUtils.ENDORSER_HISTORY_QUERY_TEMPLATE, email, historyRange);
	}
	
	public String buildEndorsersQuery(){
		return SparqlUtils.EMAIL_QUERY;
	}
	
	public String buildReminderQuery(String email){
		StringBuilder filterPredicate = new StringBuilder();

		filterPredicate.append(
				SparqlUtils.buildFilterEq("email", email));
		//Build the full SPARQL query
		StringBuilder query = new StringBuilder(SparqlUtils.SELECT_ALL);

		String where = WHERE + SparqlUtils.WHERE_BLOCK;
		
		StringBuilder filter = new StringBuilder(where);

		filter.append(filterPredicate.toString());
		
		filter
		.append(SparqlUtils.getLatestFilter());
        filter.append(SparqlUtils.getValidFilter(
        		MarketplaceUtils.getCurrentDate()));
		
		filter.append(" " + SparqlUtils.DEPRECATED_OFF);
		filter.append(" }");

		query.append(filter);

		return query.toString();
	}
	
	public String buildExpiryQuery(String expiryDate){
		String select = "SELECT " +
		"?identifier ?email ?created ?valid";
		String where = WHERE + SparqlUtils.WHERE_BLOCK;
		
		//Build the full SPARQL query
		StringBuilder query = new StringBuilder(select);

		StringBuilder filter = new StringBuilder(
				where);

		filter
		.append(SparqlUtils.getLatestFilter());
		filter.append(
				SparqlUtils.getValidFilter(MarketplaceUtils.getCurrentDate()));	
		
		filter.append(SparqlUtils.getExpiredFilter(expiryDate));
				
		filter.append(" " + SparqlUtils.DEPRECATED_OFF);
		filter.append(" }");

		query.append(filter);

		return query.toString();
	}
	
	public String buildTagQuery(String tag, String email) {
		return String.format(SparqlUtils.TAG_QUERY_TEMPLATE, tag, email);
	}
	
	public String buildEndorserTagsQuery(String email){
		return String.format(SparqlUtils.ENDORSER_TAG_QUERY_TEMPLATE, email);
	}
	
} public class EndorserResource extends BaseResource {

	private static final int DEFAULT_RANGE = 30;
	
    private String query = null;
    private String email = null;
            
    @Override
    protected void doInit() {
        email = (String) getRequest().getAttributes().get("email");
        
        String range = getRangeFromRequest();
        
        query = getQueryBuilder().buildEndorserQuery(email, getHistoryRange(range));
    }

    @Get("html")
    public Representation toHtml() {
        List<Map<String, String>> results = new ArrayList<Map<String, String>>();
        
        try {
        	results = query(query);
        } catch(MarketplaceException e){
        	LOGGER.severe(e.getMessage());
        }
        
        for(int i = 0; i < results.size(); i++){
        	Map<String, String> resultRow = results.get(i);
        	String deprecated = resultRow.get("deprecated");
        	String location = resultRow.get("location");
        	
        	if(deprecated.equals("null")){
        		resultRow.put("deprecated", "");
        	}
        	        	
        	if(location.equals("null")){
        		resultRow.put("location", "");
        	}
        	
        	results.set(i, resultRow);
        }
        	
        Map<String, Object> data = createInfoStructure("History for " + this.email);
        data.put("content", results);

        // Load the FreeMarker template
        // Wraps the bean with a FreeMarker representation
        Representation representation = createTemplateRepresentation(
                "endorser.ftl", data, MediaType.TEXT_HTML);

        return representation;
    }

    /**
     * Returns endorser details.
     */
    @Get("xml")
    public Representation toXml() {
        String results = "";
		try {
			results = queryResultsAsXml(query);
		} catch (MarketplaceException e) {
			LOGGER.severe(e.getMessage());
		}
        StringRepresentation representation = new StringRepresentation(results,
                MediaType.APPLICATION_XML);

        // Returns the XML representation of this document.
        return representation;
    }
    
    private String getRangeFromRequest(){
		Form form = getRequest().getResourceRef().getQueryAsForm();
		String range = form.getFirstValue("range", "30");

		return range;
	}
    
    private String getHistoryRange(String range){
		Date today = new Date();
		Calendar cal = Calendar.getInstance();  
		cal.setTime(today);    
		
		int r = DEFAULT_RANGE;
		
		try {
			r = Integer.parseInt(range);
		} catch(NumberFormatException n){
			LOGGER.warning("incorrect range value entered: " + range);
		}
		
		cal.add(Calendar.DATE, -r);
		Date expiration = cal.getTime();
				
		return MarketplaceUtils.getFormattedDate(expiration);
	}
} public class EndorsersResource extends BaseResource {
   
	@Get("html")
    public Representation toHtml() {
		
		List<Map<String, String>> results = new ArrayList<Map<String, String>>();
        try {
        	results = query(getQueryBuilder().buildEndorsersQuery());
        } catch(MarketplaceException e){
        	throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e);
        }
        
        for(int i = 0; i < results.size(); i++){
        	Map<String, String> resultRow = results.get(i);
        	String subject = resultRow.get("subject");
        	
        	String name = "";
        	String[] cns = X500NameUtils.getAttributeValues(subject, JavaAndBCStyle.CN);
        	if(cns.length > 0){
        		name = cns[0];
        	} else {
        		name = subject;
        	}
        		
        	resultRow.put("name", name);
        	results.set(i, resultRow);
        }
        Map<String, Object> data = createInfoStructure("Endorsers");
        data.put("content", results);

        // Load the FreeMarker template
        // Wraps the bean with a FreeMarker representation
        Representation representation = createTemplateRepresentation(
                "endorsers.ftl", data, MediaType.TEXT_HTML);

        return representation;
    }

    /**
     * Returns a listing of all endorsers.
     */
    @Get("xml")
    public Representation toXml() {
        // Generate the right representation according to its media type.
        String results = "";
		try {
			results = queryResultsAsXml(getQueryBuilder().buildEndorsersQuery());
		} catch (MarketplaceException e) {
			LOGGER.severe(e.getMessage());
		}
        StringRepresentation representation = new StringRepresentation(results,
                MediaType.APPLICATION_XML);

        // Returns the XML representation of this document.
        return representation;
    }
    
} public class ErrorResource extends BaseResource {

    @Get("html")
    public Representation toHtml() {

        Map<String, Object> info = createInfoStructure(NO_TITLE);
        setStatus(Status.SERVER_ERROR_INTERNAL);
        return createTemplateRepresentation("error.ftl", info, MediaType.TEXT_HTML);
    } 

} public class HomeResource extends BaseResource {

    @Get("html")
    public Representation toHtml() {

        Map<String, Object> info = createInfoStructure(NO_TITLE);

        return createTemplateRepresentation("home.ftl", info, MediaType.TEXT_HTML);
    }

} public class MDataResource extends MDataResourceBase {

	@Post("www_form")
	public Representation acceptDatatablesQueryString(Representation entity){
		if (entity == null) {
            throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                    "post with null entity");
        }

		String query = "";
    	try {
    		query = entity.getText();
    	} catch(IOException e){}

    	getRequest().getResourceRef().setQuery(query);

    	return toJSON();
	}

	@Post("multipart")
    public Representation acceptMetadataWebUpload(Representation entity) {
		if (entity == null) {
            throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                    "post with null entity");
        }

		isUploadPermitted();

        File upload = processMultipartForm();

        return acceptMetadataEntry(upload);
    }

	@Post("application_rdf|application_xml")
    public Representation acceptMetadataUpload(Representation entity) {
		if (entity == null) {
            throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                    "post with null entity");
        }

		isUploadPermitted();

		File upload = MetadataFileUtils.writeContentsToDisk(entity);

        return acceptMetadataEntry(upload);
    }

	private void isUploadPermitted(){
		String type = Configuration.getParameterValue(MARKETPLACE_TYPE);

		if(type != null && type.equalsIgnoreCase("replica")){
			throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN,
            "read-only repository");
		}
	}

    // Currently this method will only process the first uploaded file. This is
    // done to simplify the logic for treating a post request. This should be
    // extended in the future to handle multiple files.
    private File processMultipartForm() {

        File storeDirectory = Configuration
                .getParameterValueAsFile(PENDING_DIR);

        int fileSizeLimit = Configuration
                .getParameterValueAsInt(METADATA_MAX_BYTES);

        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(fileSizeLimit);

        RestletFileUpload upload = new RestletFileUpload(factory);

        List<FileItem> items;

        try {
            Request request = getRequest();
            items = upload.parseRequest(request);
        } catch (FileUploadException e) {
            throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, e
                    .getMessage(), e);
        }

        for (FileItem fi : items) {
            if (fi.getName() != null) {
                String uuid = UUID.randomUUID().toString();
                File file = new File(storeDirectory, uuid);
                try {
                    fi.write(file);
                    return file;
                } catch (Exception consumed) {
                }
            }
        }

        throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                "no valid file uploaded");
    }

    @Get("html")
    public Representation toHtml() throws IOException {

    	Map<String, Object> data = createInfoStructure("Metadata");

    	// Load the FreeMarker template
        // Wraps the bean with a FreeMarker representation
        Representation representation = createTemplateRepresentation(
                "metadata.ftl", data, MediaType.TEXT_HTML);

        return representation;
    }

    /**
     * Returns a listing of all registered metadata or a particular entry if
     * specified.
     */
    @Get("xml")
    public Representation toXml() {

    	String status = getRequestFlag("status", "valid");
    	String access = getRequestFlag("location", "all");

    	List<Map<String, String>> metadata = getMetadata(status, access,
    			getRequestQueryValues());
    	metadata.remove(0);

        List<String> pathsToMetadata = buildPathsToMetadata(metadata);

        String xmlOutput = buildXmlOutput(pathsToMetadata);

        // Returns the XML representation of this document.
        StringRepresentation representation = new StringRepresentation(xmlOutput,
                MediaType.APPLICATION_XML);

        return representation;
    }

    private List<String> buildPathsToMetadata(List<Map<String, String>> metadata){

    	List<String> pathsToMetadata = new ArrayList<String>(metadata.size());
        for (Map<String, String> entry : metadata) {
        	String path = entry.get("identifier")
        	+ File.separator + entry.get("email")
        	+ File.separator + entry.get("created");

            pathsToMetadata.add(path);
        }

        return pathsToMetadata;
    }

	private String buildXmlOutput(List<String> pathsToMetadata) {

		StringBuilder output = new StringBuilder(XML_HEADER);

		output.append("<metadata>");
		for (String path : pathsToMetadata) {
			String datum = getMetadatum(path);
			if (datum != null) {
				if (datum.startsWith(XML_HEADER)) {
					datum = datum.substring(XML_HEADER.length());
				}

				output.append(datum);
			}
		}
		output.append("</metadata>");

		return output.toString();
	}

    /**
     * Returns a listing of all registered metadata or a particular entry if
     * specified.
     */
    @Get("json")
    public Representation toJSON() {
    	String status = getRequestFlag("status", "valid");
    	String access = getRequestFlag("location", "all");

    	List<Map<String, String>> metadata = null;

    	String msg = "no metadata matching query found";

    	try {
    		metadata = getMetadata(status, access,
    				getRequestQueryValues());
    	} catch(ResourceException r){
    		metadata = new ArrayList<Map<String, String>>();
        	if(r.getCause() != null){
        		msg = "ERROR: " + r.getCause().getMessage();
        	}
    	}

		String iTotalDisplayRecords = "0";
		String iTotalRecords = "0";
		if (metadata.size() > 0) {
			Map<String, String> recordCounts = (Map<String, String>) metadata
					.remove(0);
			iTotalDisplayRecords = recordCounts.get("iTotalDisplayRecords");
			iTotalRecords = recordCounts.get("iTotalRecords");
		}

		Map<String, Object> json = buildJsonHeader(iTotalRecords,
				iTotalDisplayRecords, msg);
		List<ArrayList<String>> jsonResults = buildJsonResults(metadata);

		json.put("aaData", jsonResults);

		// Returns the XML representation of this document.
		return new StringRepresentation(JSONValue.toJSONString(json),
				MediaType.APPLICATION_JSON);
    }

   private Map<String, Object> buildJsonHeader(String iTotalRecords,
    		String iTotalDisplayRecords, String msg){
    	Map<String, Object> json = new HashMap<String, Object>();

    	Integer sEcho = (getRequestQueryValues().get("sEcho") != null
    			? Integer.parseInt(getRequestQueryValues().get("sEcho")) : 0);

        json.put("sEcho", sEcho);
        json.put("iTotalRecords", Long.valueOf(iTotalRecords));
        json.put("iTotalDisplayRecords", Long.valueOf(iTotalDisplayRecords));
        json.put("rMsg", msg);

    	return json;
    }

    private List<ArrayList<String>> buildJsonResults(List<Map<String, String>> metadata){
    	int numberOfRows = metadata.size();

    	List<ArrayList<String>> aaData = new ArrayList<ArrayList<String>>(numberOfRows);

    	for(int i = 0; i < metadata.size(); i++){
    		Map<String, String> resultRow = (Map<String, String>)metadata.get(i);

    		ArrayList<String> row = new ArrayList<String>(resultRow.size());
			row.add(""); //empty cell used for display
			row.add(resultRow.get("os"));
            row.add(resultRow.get("osversion"));
            row.add(resultRow.get("arch"));
            row.add(resultRow.get("email"));
            row.add(resultRow.get("kind"));
            row.add(resultRow.get("created"));
            row.add(resultRow.get("identifier"));
            row.add(resultRow.get("location"));
            row.add(resultRow.get("description"));
            row.add(resultRow.get("title"));

            aaData.add(row);
    	}

    	return aaData;
    }

    private String getRequestFlag(String flag, String defaultValue){
    	Map<String, String> requestValues = getRequestQueryValues();

    	String value = (requestValues.containsKey(flag)) ?
    			getFlagValue(requestValues.get(flag), defaultValue) : defaultValue;

        return value;
    }

    /*
     * Gets the value of the request flag from the query
     *
     * @param the value taken from the request
     *
     * @return the value of flag
     */
    private String getFlagValue(String flag, String defaultValue){
    	return (flag == null) ? defaultValue : flag;
    }

    private Map<String, String> getRequestQueryValues(){
    	Form form = getRequest().getResourceRef().getQueryAsForm();

    	return form.getValuesMap();
    }
} public class MDatumResource extends BaseResource {

	private static final String ENCODING = "UTF-8";

	private static final String METADATA_ROUTE = "/metadata/";

    private String datum = null;
    private String identifier = null;
    private String url = null;

    @Override
    protected void doInit() {
    	String metadataPath = null;
        ConcurrentMap<String, Object> attrs = getRequest().getAttributes();
    	String email = (String) attrs.get("email");

    	if(attrs.containsKey("tag")){
    		String tag = getTag((String) attrs.get("tag"));

    		url = getTaggedEntry(tag, email);
    		metadataPath = url;
    	} else {
    		url = getRequest().getResourceRef().getPath();

            int i = url.indexOf(METADATA_ROUTE);
            if (i >= 0) {
                i += METADATA_ROUTE.length();
                metadataPath = url.substring(i);
            }
    	}

        if (metadataPath != null) {
            datum = getMetadatum(metadataPath);
            identifier = metadataPath.substring(0, metadataPath.indexOf('/'));
        }
    }

    private String getTag(String tag){
    	String decodedTag = "";

    	try {
			decodedTag = URLDecoder.decode(tag, ENCODING)
					.replaceAll("^\"|\"$", "");
		} catch (UnsupportedEncodingException e) {
			LOGGER.severe("Unable to decode tag query: " + e.getMessage());
		}

    	return decodedTag;
    }

    private String getTaggedEntry(String tag, String email){

    	List<Map<String, String>> results = new ArrayList<Map<String, String>>();
        try {
        	String query = getQueryBuilder().buildTagQuery(tag, email);
        	results = query(query);
        } catch(MarketplaceException e){
        	throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e);
        }

        String identifier = "";
        String created = "";

        if (results.size() > 0) {
        	Map<String, String> resultRow = results.get(0);
        	identifier = resultRow.get("identifier");
        	created = resultRow.get("created");
        }

        if (identifier == "null" || created == "null")
        	throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND,
                    "metadata entry not found.\n");

    	return identifier + "/" + email + "/" + created;
    }

    @Get("xml")
    public Representation toXml() {
    	if (this.datum == null) {
            throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND,
                    "metadata entry not found.\n");
        }

    	StringRepresentation representation = new StringRepresentation(
                new StringBuilder(datum), MediaType.APPLICATION_RDF_XML);

        Disposition disposition = new Disposition();
        disposition.setFilename(identifier + ".xml");
        disposition.setType(Disposition.TYPE_ATTACHMENT);
        representation.setDisposition(disposition);

        // Returns the XML representation of this document.
        return representation;
    }

    @Get("json")
    public Representation toJSON() {
        if (this.datum == null) {
            throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND,
                    "metadata entry not found.\n");
        }

        Model rdfModel = ModelFactory.createMemModelMaker()
                .createDefaultModel();
        rdfModel.read(new ByteArrayInputStream((MetadataFileUtils.stripSignature(datum))
				        .getBytes(Charset.forName(ENCODING))), MARKETPLACE_URI);

        ByteArrayOutputStream jsonOut = new ByteArrayOutputStream();
        RDFDataMgr.write(jsonOut, rdfModel, RDFFormat.RDFJSON);

        StringRepresentation representation = new StringRepresentation(jsonOut.toString(),
        		MediaType.APPLICATION_JSON);

        Disposition disposition = new Disposition();
        disposition.setFilename(identifier + ".json");
        representation.setDisposition(disposition);
        disposition.setType(Disposition.TYPE_ATTACHMENT);

        return representation;
    }

    @Get("html")
    public Representation toHtml() {
    	if (this.datum == null) {
            throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND,
                    "metadata entry not found.\n");
        }
        TransformerFactory tFactory = TransformerFactory.newInstance();

        StringBuilder stringBuilder = new StringBuilder();

        try {
            Transformer transformer = tFactory
                    .newTransformer(new javax.xml.transform.stream.StreamSource(
                            getClass().getResourceAsStream("/rdf.xsl")));

            StringWriter xmlOutWriter = new StringWriter();

            transformer.transform(new javax.xml.transform.stream.StreamSource(
                    new StringReader(MetadataFileUtils.stripSignature(datum))),
                    new javax.xml.transform.stream.StreamResult(xmlOutWriter));

            stringBuilder.append(xmlOutWriter.toString());
        } catch (TransformerConfigurationException e) {
            LOGGER.severe("Error parsing metadata stylesheet: " + e.getMessage());
        } catch (TransformerException e) {
        	LOGGER.severe("Error parsing metadata stylesheet: " + e.getMessage());
        }

        Map<String, Object> data = createInfoStructure("Metadata");
        data.put("identifier", identifier);
        data.put("content", stringBuilder.toString());
        data.put("url", url);

        // Load the FreeMarker template
        // Wraps the bean with a FreeMarker representation
        Representation representation = createTemplateRepresentation(
                "mdatum.ftl", data, MediaType.TEXT_HTML);

        return representation;
    }

    private static String getEntryId(final String url){
        // return url.replaceFirst("[^?]*/(.*?)(?:\\?.*)","$1);" <-- incorrect
        return url.replaceFirst(".*/([^/?]+).*", "$1");
    }
} public class QueryResource extends BaseResource {
	
	private static final Logger LOGGER = Logger.getLogger("org.restlet");
	
	@SuppressWarnings("unchecked")
	@Get("html")
    public Representation toHtml() {
        Representation representation = null;

        try {
        	SparqlQuery query = new SparqlQuery(
        			getQueryFromRequest(), 
        			SparqlQuery.OUTPUT_MAP);
        	
        	List<Map<String, String>> results = (List<Map<String, String>>)executeQuery(query);
        	
			// return representation;
			Map<String, Object> queryMap = createInfoStructure("Query");
			queryMap.put("query", query.getQuery());
			queryMap.put(
					"results",
					(results == null) ? new ArrayList<List<Map<String, String>>>()
							: results);
            
        	representation = createTemplateRepresentation("query.ftl", queryMap,
                    MediaType.TEXT_HTML);
        	
        } catch (MalformedQueryException e) {
        	throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, e
                    .getMessage(), e);
        }
        
        return representation;
    }

	private String getQueryFromRequest(){
		Form form = getRequest().getResourceRef().getQueryAsForm();
		String query = form.getFirstValue("query");

		return query;
	}
	
   	@Get("xml")
    public Representation toXml() {
		Representation representation = null;

        try {
        	SparqlQuery query = new SparqlQuery(
        			getQueryFromRequest(), 
        			SparqlQuery.OUTPUT_XML);
        	
        	String results = (String)executeQuery(query);
        	if(results == null){
        		results = "";
        	}
        	
            representation = new StringRepresentation(results,
                    MediaType.APPLICATION_SPARQL_RESULTS_XML);
        } catch (MalformedQueryException e) {
        	throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, e
                    .getMessage(), e);
        }
        
        return representation;	
    }
	
	@Get("json")
    public Representation toJson() {
        Representation representation = null;

        try {
        	SparqlQuery query = new SparqlQuery(
        			getQueryFromRequest(), 
        			SparqlQuery.OUTPUT_JSON);
        	
        	String results = (String)executeQuery(query);
        	if(results == null){
        		results = "";
        	}
        	        	        	
            representation = new StringRepresentation(results,
                    MediaType.APPLICATION_SPARQL_RESULTS_JSON);
        } catch (MalformedQueryException e) {
        	throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, e
                    .getMessage(), e);
        }
        
        return representation;
    }
       
	private Object executeQuery(SparqlQuery query){
		//check that query is allowed.
    	if(!query.isAllowed()){
    		throw new ResourceException(
    				Status.CLIENT_ERROR_BAD_REQUEST,
    				"query not allowed.");
    	}
         
    	Object results = null;
		try {
			results = getFormattedQueryResults(query);
		} catch (MarketplaceException e) {
			LOGGER.severe(e.getMessage());
		}
		
		return results;
	}
	
	private Object getFormattedQueryResults(SparqlQuery query) 
	throws MarketplaceException {
		Object results = null;
		
		if (!query.isEmpty()) {
			switch (query.getOutputFormat()) {
			case SparqlQuery.OUTPUT_JSON:
				results = queryResultsAsJson(query.getQuery());
				break;

			case SparqlQuery.OUTPUT_XML:
				results = queryResultsAsXml(query.getQuery());
				break;
				
			case SparqlQuery.OUTPUT_MAP:
				results = query(query.getQuery());
				break;
				
			default:
				results = null;
				break;
				
			}
		}
		
		return results;
	}
	
	static class SparqlQuery {	

		public static final int OUTPUT_MAP = 0;
		private static final int OUTPUT_XML = 1;
		private static final int OUTPUT_JSON = 2;
		
		private String query = "";
		private boolean allowed = true;
		private boolean empty = true;
		
		private int outputFormat;
		
		SparqlQuery(String query, int outputFormat) throws MalformedQueryException {
			this.query = query;
			this.outputFormat = outputFormat;
			
			if(query == null){
				this.query = "";
				this.empty = true;
			} else if (!"".equals(query)) {
				this.empty = false;
				isQueryWellFormed();
				isQueryAllowed();
			}
			
		}

		private void isQueryAllowed() {
			ParserSPARQL11 jenaParser = new ParserSPARQL11();
			Query select = jenaParser.parse(new Query(), query);

			if(!select.isSelectType() || 
					select.hasDatasetDescription()){
				this.allowed = false;
			} else {
				Element queryElement = select.getQueryPattern();
				if(queryElement.getClass() == ElementGroup.class){
					List<Element> elements = ((ElementGroup)queryElement).getElements();
					for(Element e: elements){
						if(e.getClass() == ElementNamedGraph.class){
							this.allowed = false;
						}
					}
				}
			}
		}
		
		private void isQueryWellFormed() 
			throws MalformedQueryException{
			QueryParser parser = QueryParserUtil
				.createParser(QueryLanguage.SPARQL);

				parser.parseQuery(query, MARKETPLACE_URI);
		}
		
		public int getOutputFormat(){
			return this.outputFormat;
		}
		
		public String getQuery(){
			return this.query;
		}
		
		public boolean isAllowed() {
			return this.allowed;
		}
						
		public boolean isEmpty(){
			return this.empty;
		}
		
	}

} public class TagsResource extends BaseResource{
	
	private String query = null;
    private String email = null;
    
	@Override
    protected void doInit() {
        email = (String) getRequest().getAttributes().get("email");
        query = getQueryBuilder().buildEndorserTagsQuery(email);
    }

    @Get("html")
    public Representation toHtml() {
        List<Map<String, String>> results = new ArrayList<Map<String, String>>();
        
        try {
        	results = query(query);      	
        } catch(MarketplaceException e){
        	LOGGER.severe(e.getMessage());
        }
        	
        Map<String, Object> data = createInfoStructure("Current tags for " + this.email);
        data.put("content", results);
        data.put("email", email);

        // Load the FreeMarker template
        // Wraps the bean with a FreeMarker representation
        Representation representation = createTemplateRepresentation(
                "tags.ftl", data, MediaType.TEXT_HTML);

        return representation;
    }

    /**
     * Returns the endorser's tags details.
     */
    @Get("xml")
    public Representation toXml() {
        String results = "";
		try {
			results = queryResultsAsXml(query);
		} catch (MarketplaceException e) {
			LOGGER.severe(e.getMessage());
		}
        StringRepresentation representation = new StringRepresentation(results,
                MediaType.APPLICATION_XML);

        // Returns the XML representation of this document.
        return representation;
    }

} public class UploadResource extends BaseResource {

    @Get("html")
    public Representation toHtml() {

        Map<String, Object> map = createInfoStructure("Upload");

        return createTemplateRepresentation("upload.ftl",
                map, MediaType.TEXT_HTML);
    }

} public class SyncResource extends BaseResource {

	private String dataDir = Configuration.getParameterValue(DATA_DIR);
	
	private static final String ENCODING = "UTF-8";
	
	@Post
	public Representation syncEntry(Representation entity){
		Representation rep = null;
		
		Form form = new Form(entity);  
        String key = form.getFirstValue("path");
                
        if(key != null){
        	boolean success = syncMetadata(key); 

        	if(success){
        		setStatus(Status.SUCCESS_ACCEPTED);
        		rep = new StringRepresentation("synced metadata entry");
        	} else {
        		setStatus(Status.SERVER_ERROR_INTERNAL);
            	rep = new StringRepresentation("unable to sync metadata entry");
        	}
        } else {
        	rep = syncMetadataList();
        }
                
        return rep;
	}
	
	private Representation syncMetadataList() {
        Representation rep = null;
		boolean processed = false;
        
		File syncFile = new File(dataDir
				+ File.separator + ".sync");
		if(syncFile.exists()){    
			processed = processSyncList(syncFile);
			
			if(processed){
				setStatus(Status.SUCCESS_OK);
				rep = new StringRepresentation("sync list processed");
			} else {
				setStatus(Status.SERVER_ERROR_INTERNAL);
				rep = new StringRepresentation("unable to process sync list");
			}
			
		} else {
			setStatus(Status.SUCCESS_ACCEPTED);
			rep = new StringRepresentation("no sync list to process");
		}

		return rep;	
	}
	
	private boolean processSyncList(File syncFileList) {
		BufferedReader syncFileListReader = null;
		boolean success = true;
		
		try {
			syncFileListReader = new BufferedReader(
					new InputStreamReader(new FileInputStream(syncFileList), ENCODING));

			String syncFilePath;
			while ((syncFilePath = syncFileListReader.readLine()) != null){
				success = syncMetadata(syncFilePath);
			}

		} catch (IOException e) {
			success = false;
			LOGGER.warning("Unable to process sync list: " + e.getMessage());
		} finally {
			MetadataFileUtils.closeReliably(syncFileListReader);
		}

		return success;
	}
	
	private boolean syncMetadata(String key){
		boolean success = true;
		
		String metadata = getMetadataFileStore().read(key);
		if(metadata != null){
			success = storeMetadata(metadata);
		} else {
			success = false;
			LOGGER.warning("Unable to sync metadata: " + key);
		}
		
		return success;
	}
	
	private boolean storeMetadata(String metadata){
		boolean success = true;
		
		InputStream stream = null;
		Document metadataXml = null;

		try {
			stream = new ByteArrayInputStream(metadata.getBytes(ENCODING));
			metadataXml = MetadataFileUtils.extractXmlDocument(stream);
			writeMetadataToRdfStore(metadataXml);
		}catch(UnsupportedEncodingException e){
		  success = false;
		} finally {
			MetadataFileUtils.closeReliably(stream);
		}
		
		return success;
	}
	
} public class ActionRouter extends Router {

    public ActionRouter() {
        super();

        // Routing is tolerant of extraneous leading and trailing slashes.
        // TODO: Determine a better mechanism for being tolerant of slashes.
        TemplateRoute route;

        route = attach("/{uuid}/{command}/", ActionResource.class);
        route.getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);

        route = attach("/{uuid}/{command}", ActionResource.class);
        route.getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);

        route = attach("{uuid}/{command}/", ActionResource.class);
        route.getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);

        route = attach("{uuid}/{command}", ActionResource.class);
        route.getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);

        attachDefault(InvalidActionResource.class);
    }

} public class CouchbaseStore extends FileStore {

	private static final Logger LOGGER = Logger.getLogger("org.restlet");
	
	private static final String DESIGN_DOC = "marketplace_views";
	private static final String VIEW = "by_timestamp";
	
	CouchbaseClient client = null;
	private String marketplaceId;
	private String lastKeyPath;
	
	private FileStore fileStore;
	
	
	public CouchbaseStore(String dataDir){
		String bucket = Configuration.getParameterValue(COUCHBASE_BUCKET);
		String password = Configuration.getParameterValue(COUCHBASE_PASSWORD);
		marketplaceId = Configuration.getParameterValue(COUCHBASE_MARKETPLACEID);
		
		lastKeyPath = dataDir + File.separator + ".lastkey";
		
		fileStore = new FlatFileStore(dataDir);
		
		String[] uris = Configuration.getParameterValue(COUCHBASE_URIS).split("\\s+");
		
		List<URI> hosts = new LinkedList<URI>();

	    try {
	    	for(String uri : uris){
	    		hosts.add(URI.create(uri));	
	    	}
			
			client = new CouchbaseClient(hosts, bucket, password);
			
			createView();
	    } catch (Exception e){
	    	LOGGER.severe("Error connecting to Couchbase: " + e.getMessage());
		}
	}
	
	private void createView() {
		DesignDocument designDoc = new DesignDocument(DESIGN_DOC);

	    String viewName = VIEW;
	    String mapFunction =
	            "function (doc, meta) {\n" +
	            "    emit(doc.uploaded);\n" +
	            "}";

	    ViewDesign viewDesign = new ViewDesign(viewName,mapFunction);
	    designDoc.getViews().add(viewDesign);
	    client.createDesignDoc( designDoc );
	}

	@Override
	public void store(String key, Document metadata) {
		String contents = XMLUtils.documentToString(metadata);
		String uploaded = Long.toString(System.currentTimeMillis());
		
		Map<String, String> jsonDocument = new HashMap<String, String>();
		jsonDocument.put("uploaded", uploaded);
		jsonDocument.put("marketplaceId", marketplaceId);
		jsonDocument.put("metadata", contents);
		
		try {
			if(client.get(key) == null){
				client.set(key, JSONValue.toJSONString(jsonDocument));
			}
		} catch(Exception e){
			LOGGER.severe("unable to store to couchbase: " + e.getMessage());
			throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e.getMessage());
		}
		
		fileStore.store(key, metadata);
	}

	@Override
	public void remove(String key) {
		client.delete(key);
	}

	@Override
	public String read(String key) {
		String metadata = null;
		
		metadata = fileStore.read(key);
		
		if(metadata == null){
			try {
				String jsonDocument = (String)client.get(key);
		
				@SuppressWarnings("unchecked")
				Map<String, String> document = (HashMap<String, String>)JSONValue.parse(jsonDocument);
		
				metadata = (String)document.get("metadata");
			} catch(Exception e){
				LOGGER.severe("unable to read from couchbase: " + e.getMessage());
				throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e.getMessage());
			}
		}
		
		return metadata;
	}

	public List<String> updates(int limit){
		String startKey = getLastKey();
		String lastKey = "";
		
		View view = client.getView(DESIGN_DOC, VIEW);
		Query query = new Query();
		query.setLimit(limit);
		
		if(startKey != null && !startKey.equals("")){
			query.setRangeStart(ComplexKey.of(startKey));
			query.setSkip(1);
		}
		query.setIncludeDocs(true);
		
		List<String> documents = new ArrayList<String>();
		
		try {
			ViewResponse response = client.query(view, query);
			for (ViewRow row : response) {
				String rawDocument = (String) row.getDocument();

				@SuppressWarnings("unchecked")
				Map<String, String> document = (HashMap<String, String>) JSONValue
						.parse(rawDocument);

				if (!document.get("marketplaceId").equals(marketplaceId)) {
					documents.add(document.get("metadata"));
				}

				lastKey = row.getKey();
			}
			setLastKey(lastKey);
		} catch (Exception e) {
			LOGGER.severe("unable to retrieve updates: " + e.getMessage());
		}
		
		return documents;
	}
	
	private String getLastKey() {
		String lastKey = null;
		FileInputStream in = null;
		
		try {
			in = new FileInputStream(lastKeyPath);
			Properties props = new Properties();
        
			props.load(in);
			MetadataFileUtils.closeReliably(in);
			
			lastKey = props.getProperty("lastKey");
		} catch (FileNotFoundException f) {
			MetadataFileUtils.closeReliably(in);
		}catch (IOException e) {
			MetadataFileUtils.closeReliably(in);
		}
        
		return lastKey;
	}
	
	private void setLastKey(String lastKey){
		if (!lastKey.equals("")) {
			Properties props = new Properties();

			FileOutputStream out = null;

			try {
				out = new FileOutputStream(new File(lastKeyPath));
				props.setProperty("lastKey", lastKey);
				props.store(out, null);
				MetadataFileUtils.closeReliably(out);
			} catch (FileNotFoundException f) {
				MetadataFileUtils.closeReliably(out);
			} catch (IOException e) {
				MetadataFileUtils.closeReliably(out);
			}
		}
    }
	
	@Override
	public void shutdown() {
		if (client != null) {
			client.shutdown();
		} else {
			LOGGER.warning("no connection to Couchbase");
		}
	}

} public abstract class FileStore {
	
	public abstract void store(String key, Document metadata);
	public abstract void remove(String key);
	public abstract String read(String key);
	public abstract List<String> updates(int limit);
	public abstract void shutdown();

} public class FlatFileStore extends FileStore {
	
	private String dataDir;
	
	private static final Logger LOGGER = Logger.getLogger("org.restlet");
	
	public FlatFileStore(String dataDir){
		this.dataDir = dataDir + File.separator + "metadata";
		MetadataFileUtils.createIfNotExists(dataDir);
	}
	
	@Override
	public void store(String key, Document metadata) {
		File rdfFile = new File(dataDir, File.separator + key + ".xml");

		File rdfFileParent = rdfFile.getParentFile();
		if (!rdfFileParent.exists()) {
			if (!rdfFileParent.mkdirs()) {
				LOGGER.severe("Unable to create directory structure for file.");
				throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
			}
		}

		String contents = XMLUtils.documentToString(metadata);
		MetadataUtils.writeStringToFile(contents, rdfFile);			
	}

	@Override
	public void remove(String key) {
		File rdfFile = new File(dataDir, File.separator + key + ".xml");

		if (rdfFile.exists()) {
			if (!rdfFile.delete()) {
				throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
			}
		}
	}
	
	@Override
	public String read(String key) {
		String model = null;
		try {
			model = MetadataFileUtils.readFileAsString(dataDir 
					+ File.separator + key + ".xml");
		} catch (IOException e) {
			LOGGER.severe("Unable to read metadata: " + key);
		}

		return model;
	}

	@Override
	public void shutdown() {
				
	}

	@Override
	public List<String> updates(int limit) {
		throw new UnsupportedOperationException("Not implemented.");
	}
	
} public class GitStore extends FileStore {

	private FileStore fileStore;
	
	private HashMap<String, Document> localUpdates = new HashMap<String, Document>();
	
	private GitManager manager;
	
	private static final Logger LOGGER = Logger.getLogger("org.restlet");
	
	private FileMonitor monitor;
	
	public GitStore(String dataDir, FileStore store) {
		String gitDir = dataDir + File.separator + "metadata";
		MetadataFileUtils.createIfNotExists(gitDir);
		monitor = new FileMonitor(gitDir, ".xml");
		manager = new GitManager(gitDir);
		fileStore = store;
    }
	
	GitStore(FileStore store, FileMonitor monitor, GitManager manager){
		this.fileStore = store;
		this.monitor = monitor;
		this.manager = manager;
	}
	
	private String getKeyFromPath(String path) {
		String[] elements = path.split(File.separatorChar == '\\' ? "\\\\"
				: File.separator);
		int length = elements.length;
		String key = elements[length - 3] + "/" + elements[length - 2] + "/"
				+ FilenameUtils.getBaseName(elements[length - 1]);

		return key;
	}
	
	@Override
	public void store(String key, Document metadata) {
		fileStore.store(key,  metadata);
		localUpdates.put(key, metadata);
		
		manager.addToRepo(key);
	}

	@Override
	public void remove(String key) {
		manager.removeFromRepo(key);
		fileStore.remove(key);
	}

	@Override
	public String read(String key) {
		return fileStore.read(key);
	}

	@Override
	public List<String> updates(int limit) {
		List<String> updates = new ArrayList<String>();
		int i = 1;

		File file = monitor.getFile();

		while(i < limit && file != null){
			try {
				String key = getKeyFromPath(file.getCanonicalPath());

				if (!localUpdates.containsKey(key)) {
					String document = MetadataFileUtils.
							readFileAsString(file.getCanonicalPath());
					updates.add(document);

					i++;
				} else {
					localUpdates.remove(key);
				}
				file = monitor.getFile();
				
			} catch (IOException e) {
				LOGGER.severe("Unable to read file: " + e.getMessage());
			}
		}

		return updates;
	}

	@Override
	public void shutdown() {
		manager.close();
		fileStore.shutdown();
	}
	
} public abstract class RdfStore {
	
	public abstract void shutdown();
	public abstract void initialize();
    public abstract boolean store(String identifier, String entry);
    public abstract void tag(String identifier, String tag);
    public abstract void removeTag(String identifier, String tag);
    public abstract List<Map<String, String>> getRdfEntriesAsMap(String query) throws MarketplaceException;
    public abstract String getRdfEntriesAsXml(String query) throws MarketplaceException;
    public abstract String getRdfEntriesAsJson(String query) throws MarketplaceException;
    public abstract String getRdfEntry(String uri) throws MarketplaceException;
    public abstract void remove(String identifier);
    public abstract int size();
} public interface RdfStoreFactory {
	
	String SESAME_PROVIDER = "sesame";
	String MYSQL_BACKEND = "mysql";
	String POSTGRESQL_BACKEND = "postgres";
	String MEMORY_BACKEND = "memory";
	String REMOTE_BACKEND = "remote";
	
	RdfStore createRdfStore(String provider, String type);
} public class RdfStoreFactoryImpl implements RdfStoreFactory {
    
    public RdfStore createRdfStore(String provider, String type){
		
		SesameBackend backend = null;

		if(type.equals("native")) {
			backend = new SesameNativeBackend();
		} else {
			backend = new SesameMemoryBackend();
		}

		return new SesameRdfStore(backend);
	}
} public class Processor {
	
	private Application application;
	
	public Processor(Application app){
		application = app;
	}
	
	public void processEntry(String entry) {
		Representation rdf = new StringRepresentation(entry,
				MediaType.APPLICATION_RDF_XML);
		Request request = new Request(Method.POST, "http://localhost.replica");
		request.setEntity(rdf);
		request.setRootRef(new Reference("http://localhost.replica"));
		
		Response response = new Response(request);
		
		ServerResource resource = new MDataResource();
		resource.setApplication(application);
		
		resource.init(null, request, response);
		
		if (response.getStatus().isSuccess()) {
			resource.handle();
		}		
		
		resource.release();
	}
} public class RdfStoreUpdater {
	private static final Logger LOGGER = Logger.getLogger("org.restlet");
    private static final int DEFAULT_LIMIT = 1000;
	
	private int limit;	
	private FileStore store;
	private Processor processor;
	
	private boolean updating = false;

	public RdfStoreUpdater(FileStore fileStore, Processor processor) {
		this(fileStore, processor, DEFAULT_LIMIT);		
	}

	public RdfStoreUpdater(FileStore fileStore, Processor processor, int limit){
		store = fileStore;
		this.processor = processor;
		this.limit = limit;
	}
	
	public void update() {
		if (!updating) {
			LOGGER.info("update started");
			List<String> documents;
		
			do {
				documents = store.updates(limit);
				
				for (String document : documents){
					processor.processEntry(document);
				}
			
			} while (documents.size() == limit);
		
			updating = false;
			LOGGER.info("update completed");
		}
	}
	
} @SuppressWarnings("restriction")
public class EndorserWhitelist {
	
	private static final Logger LOGGER = Logger.getLogger("org.restlet");
	
	private boolean enabled = false;
	
	private List<X500Principal> whitelist = new ArrayList<X500Principal>();
	private List<String> crls = new ArrayList<String>();
	private String crlFileExt;
	private String crlLocation;
	
    private X509CertChainValidator validator = null;
    
    public static final Long DEFAULT_UPDATE_INTERVAL = //
        10L * 60L * 1000L; // 10min
    
    public EndorserWhitelist(){
    	
    	enabled = Configuration.getParameterValueAsBoolean(WHITELIST_ENABLED);	
		
		String location = Configuration.getParameterValue(WHITELIST_LOCATION);
		String truststore = Configuration.getParameterValue(WHITELIST_TRUSTSTORE);
		String password = Configuration.getParameterValue(WHITELIST_PASSWORD);
		crlLocation = Configuration.getParameterValue(WHITELIST_CRL);
        
		if(Configuration.getParameterValueAsBoolean(WHITELIST_ENABLED)){
			
			loadWhitelist(location);
			
			if(new File(truststore).isFile()){
				
				loadCrls();
				
				if(password != null){
					validator = createKeystoreValidator(truststore, password.toCharArray());
				} else {
					LOGGER.severe("No password specified");
					enabled = false;
				}
				
			} else if (new File(truststore).isDirectory()) {
				validator = createOpensslValidator(truststore);
			} else {
				LOGGER.severe("Truststore (" + truststore + ")  not found");
				enabled = false;
			}
		}
		
		if(enabled){
			LOGGER.warning("Endorser whitelist enabled.");
		}
    }

	private X509CertChainValidator createOpensslValidator(String truststore) {
		
		OpensslCertChainValidator v = new OpensslCertChainValidator(
				truststore, 
				NamespaceCheckingMode.EUGRIDPMA_AND_GLOBUS, 
				DEFAULT_UPDATE_INTERVAL);
			
		return v;
	}

	private X509CertChainValidator createKeystoreValidator(String truststore, char[] password) {
		KeystoreCertChainValidator v = null;
		
		try {
			String type = "JKS";
			if(truststore.endsWith(".jks")){
				type = "JKS";
			} else if(truststore.endsWith(".p12")){
				type = "PKCS12";
			}
			
			v = new KeystoreCertChainValidator(
					truststore, 
					password,
					type, 
					DEFAULT_UPDATE_INTERVAL);
			
			v.setCrls(crls);
			v.setCRLUpdateInterval(DEFAULT_UPDATE_INTERVAL);
			
		} catch (KeyStoreException e) {
			LOGGER.severe("Error creating cert validator: " + e.getMessage());
			this.enabled = false;
		} catch (IOException e) {
			LOGGER.severe("Error creating cert validator: " + e.getMessage());
			this.enabled = false;
		}
		
		return v;
	}

	public EndorserWhitelist(String truststore, String password, String crl,
			List<X500Principal> list) {
		
		whitelist = list;
		crls.add(crl);
		
		validator = createKeystoreValidator(truststore, password.toCharArray());
	}

	private void loadCrls() {
		
		List<File> crlFiles = getCrlFiles(crlLocation);			
		
		for ( File f : crlFiles ){
			crls.add(f.getAbsolutePath());
		}	
		
	}

	private List<File> getCrlFiles(String crlLocation) {
		
		List<File> crlFiles = new ArrayList<File>();
		
		File crl = new File(crlLocation);
				
		if(crl.isDirectory()){
			crlFiles = Arrays.asList(crl.listFiles());
		} else if (crl.isFile()) {
	        crlFiles.add(crl);
		} else {
			crlFiles = getFilesFromPattern(crl);			
		}
		
		return crlFiles;
	}

	private List<File> getFilesFromPattern(File crls) {
		
		List<File> crlFiles = new ArrayList<File>(); 
		
		String[] filePattern = crls.getName().split("\\*");
		
		if(filePattern.length == 2){
			crlFileExt = filePattern[1];
			
			File crlsDir = crls.getParentFile();
			
			if(crlsDir != null){
				crlFiles = Arrays.asList(crlsDir.listFiles(new CrlFileFilter()));
			}
		}
		
		return crlFiles;
	}

	private void loadWhitelist(String location) {
		
		List<X500Principal> lines = new ArrayList<X500Principal>();

		try {
			Reader fileReader = new InputStreamReader(
					new FileInputStream(location), "UTF-8");
			BufferedReader bufferedReader = new BufferedReader(fileReader);

			String line = null;
			while ((line = bufferedReader.readLine()) != null) {
				lines.add(new X500Principal(line));
			}

			MetadataFileUtils.closeReliably(bufferedReader);
			MetadataFileUtils.closeReliably(fileReader);

		}catch(FileNotFoundException e){
			LOGGER.severe("Unable to find whitelist file: " + location);
			this.enabled = false;
		} catch (IOException e) {
			LOGGER.severe("Error reading whitelist file. " + e.getMessage());
			this.enabled = false;
		}

		this.whitelist = lines;
	}

	public boolean isEnabled(){
		return enabled;
	}
	
	public boolean isCertVerified(Document doc){
		
		boolean verified = false;
		try {
			Node signature = extractSignature(doc);
			KeyInfo keyInfo = MetadataUtils.extractKeyInfoFromNode(signature);
			X509Certificate[] chain = extractX509CertChainFromKeyInfo(keyInfo);        
            
			ValidationResult result = validator.validate(chain);
			verified = result.isValid();
						
		} catch(MetadataException e){
			verified = false;
		}
		
		return verified;
	}
	
	public boolean isEndorserWhitelisted(Document doc){
		
		String endorser = getCertSubject(doc);
		
		if(!isCertVerified(doc)){
			return false;
		}
		
		boolean isListed = isEndorserListed(endorser);
		if(isListed){
			LOGGER.info("Endorser " + endorser + " found in whitelist.");
		}
		
		return isListed;
	}
	
	private String getCertSubject(Document doc){
		
		String endorser = "";
		
		NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,
        "Signature");
    	
		if (nl.getLength() > 0) {
    		Node node = nl.item(0);
    	
    		X509Certificate cert = MetadataUtils.extractX509CertFromNode(node);
    		Map<String, String> endorsement = MetadataUtils.extractEndorserInfoFromCert(cert);
    	
    		endorser = endorsement.get("subject");
    	}
		
		return endorser;	
	}
	
	private boolean isEndorserListed(String endorser){
		
		for(X500Principal s : this.whitelist){
			if(s.getName().equals(endorser)){
				return true;
			}
		}
		
		return false;
	}
	
	public class CrlFileFilter implements FileFilter {

		public boolean accept(File arg0) {

			if (arg0.getName().endsWith(crlFileExt)){
				return true;
			}

			return false;
		}

	}
	
	private static Node extractSignature(Document doc){
    	NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,
        "Signature");
    	Node node = null;
    	
    	if (nl.getLength() > 0) {
    		node = nl.item(0);
    	} else {
			throw new MetadataException("no signature");
		}
    	
    	return node;
    }
    
   public static X509Certificate[] extractX509CertChainFromKeyInfo(
            KeyInfo keyInfo) {
        List<X509Certificate> chain = MetadataUtils.extractX509CertChainFromKeyInfo(keyInfo);
        
        X509Certificate[] certs = new X509Certificate[chain.size()];
        for(int i = 0; i < chain.size(); i++){
        	certs[i] = chain.get(i);
        }
        
        return certs;
    }
    	
} public final class MetadataFileUtils {

	private static final String ENCODING = "UTF-8";
	
	private MetadataFileUtils(){}
	
	public static File writeContentsToDisk(Representation entity) {

        char[] buffer = new char[4096];

        File storeDirectory = Configuration
                .getParameterValueAsFile(PENDING_DIR);

        File output = new File(storeDirectory, UUID.randomUUID().toString());

        Reader reader = null;
        Writer writer = null;

        try {
        	reader = Channels.newReader(entity.getChannel(), ENCODING);
        	writer = new OutputStreamWriter(
            		new FileOutputStream(output), ENCODING);

            int nchars = reader.read(buffer);
            while (nchars >= 0) {
                writer.write(buffer, 0, nchars);
                nchars = reader.read(buffer);
            }

        } catch (IOException consumed) {

        } finally {
            closeReliably(reader);
            closeReliably(writer);
        }
        return output;
    }
	
	public static String readFileAsString(String filePath)
	throws IOException {
		
		StringBuilder text = new StringBuilder();
	    String nl = System.getProperty("line.separator");
	    Scanner scanner = new Scanner(new FileInputStream(filePath), 
	    		ENCODING);
	    try {
	      while (scanner.hasNextLine()){
	        text.append(scanner.nextLine() + nl);
	      }
	    }
	    finally{
	      scanner.close();
	    }
	    
	    return text.toString();
	}

	public static void closeReliably(Closeable closeable) {
		
		if (closeable != null) {
			try {
				closeable.close();
			} catch (IOException consumed) {
			}
		}
	}
	
	public static String stripSignature(String signedString) {
		
        DocumentBuilder db = XMLUtils.newDocumentBuilder(false);
        Document datumDoc = null;
        String rdfEntry = "";
        try {
            datumDoc = db.parse(new ByteArrayInputStream(signedString
                    .getBytes(ENCODING)));

            // Create a deep copy of the document and strip signature elements.
            Document copy = (Document) datumDoc.cloneNode(true);
            MetadataUtils.stripSignatureElements(copy);
            rdfEntry = XMLUtils.documentToString(copy);
        } catch (SAXException e) {
            throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                    "Unable to parse metadata: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new ResourceException(e);
        }

        return rdfEntry;
    }

	public static Document extractXmlDocument(String rdf){
		Document datumDoc = null;

		try {
			
			datumDoc = extractXmlDocument(new ByteArrayInputStream(
					rdf.getBytes(ENCODING)));
			
		} catch (UnsupportedEncodingException e) {
			throw new ResourceException(e);
		}	

		return datumDoc;
	}
	
	public static Document extractXmlDocument(InputStream stream) {

        DocumentBuilder db = XMLUtils.newDocumentBuilder(false);
        Document datumDoc = null;

        try {

        	datumDoc = db.parse(stream);

        } catch (SAXException e) {
            throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                    "unable to parse metadata: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new ResourceException(e);
        }

        return datumDoc;
    }
	
	public static boolean createIfNotExists(String path) {
		
        File dir = new File(path);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                return false;
            }
        }
        
        return true;
    }

} public class Reminder extends BaseResource {

	private String marketplaceEndpoint;
		
	private static final int MAX_EXPIRY_RANGE = 3;
	
	public Reminder(Application app){
		setApplication(app);
		
		String endpoint = Configuration.getParameterValue(MARKETPLACE_ENDPOINT);
		if (endpoint.endsWith("/")) {
			    marketplaceEndpoint = endpoint.substring(0, endpoint.length() - 1);
		} else {
			marketplaceEndpoint = endpoint;
		}
		
	}
	
	public void remind() {
		String cn = "CN=";
		
		List<Map<String, String>> results = new ArrayList<Map<String, String>>();
		
		try {
        	results = query(getQueryBuilder().buildEndorsersQuery());
        } catch(MarketplaceException e){
        	LOGGER.severe(e.getMessage());
        }
		for(int i = 0; i < results.size(); i++){
        	Map<String, String> resultRow = results.get(i);
        	
        	String subject = resultRow.get("subject");
        	String name = subject.substring(subject.indexOf(cn)+cn.length(), 
        			subject.indexOf(",", subject.indexOf(cn)+cn.length()));
        	String email = resultRow.get("email");
        	
        	StringBuilder mailContents = new StringBuilder("Dear " + name + ",\n\n" +
        			"You are currently endorsing the following images in the StratusLab Marketplace.\n" +
                    "If you no longer wish to endorse these images please " +
                    "deprecate the corresponding Marketplace entries.\n\n" +
                    "You can deprecate an entry using the following command:\n\t" +
                    "stratus-deprecate-metadata --email=" + email + " <image id>\n\n");

            List<Map<String, String>> entries = new ArrayList<Map<String, String>>();
            
            try {
				entries = query(getQueryBuilder().buildReminderQuery(email));
			} catch (MarketplaceException e) {
				LOGGER.warning(e.getMessage());
			}

			buildAndSendReminderEmail(email, entries, mailContents);
		}
	}
		
	public void expiry() {
		List<Map<String, String>> results = new ArrayList<Map<String, String>>();
		
		try {
			String expiringEntries = getQueryBuilder().buildExpiryQuery(getExpiryDate());
			
			results = query(expiringEntries);
        } catch(MarketplaceException e){
        	LOGGER.severe(e.getMessage());
        }
        
        HashMap<String, List<Map<String, String>>> entries = 
        	new HashMap<String, List<Map<String, String>>>();
        
		for(int i = 0; i < results.size(); i++){
			Map<String, String> resultRow = results.get(i);
        	
        	String email = resultRow.get("email");
        	
        	List<Map<String, String>> store = new ArrayList<Map<String, String>>();
        	
        	if(entries.containsKey(email)){
        		store = entries.get(email);
        	}
        	
        	store.add(resultRow);
        	entries.put(email, store);
		}
		
		buildAndSendExpiryEmail(entries);
	}
		
	private void buildAndSendReminderEmail(String email, 
			List<Map<String, String>> entries, StringBuilder mailContents){
		if(entries.size() > 0){
			for(int j = 0; j < entries.size(); j++){
				Map<String, String> entryRow = (Map<String, String>)entries.get(j);

				String identifier = entryRow.get("identifier");
				String created = entryRow.get("created");
				String os = entryRow.get("os");
				String osversion = entryRow.get("osversion");
				String location = entryRow.get("location");
				
				mailContents.append(identifier 
						+ "\n\tcreated:\t" + created
						+ "\n\tos:     \t" + os + " " + osversion + "\n");

				if(!location.equals("null") && location.length() > 0){
					mailContents.append("\tlocation:\t" + location + "\n");
				}

				mailContents.append("\n");
			}
			
			try {
				Notifier.sendNotification(email, mailContents.toString());
			} catch (Exception e) {
				LOGGER.warning(e.getMessage());
			}
		}
	}
	
	private void buildAndSendExpiryEmail(Map<String, List<Map<String, String>>> entries){
			
		for(Map.Entry<String, List<Map<String, String>>> entry : entries.entrySet()){
			String endorser = entry.getKey();
			List<Map<String, String>> expiring = entry.getValue();
						
			StringBuilder mailContents = new StringBuilder("Dear Marketplace user,\n\n" +
        			"The following images in the StratusLab Marketplace ("
					+ marketplaceEndpoint + ")"    			
        			+ " are due to expire shortly. " 
        			+ "Once the validity date has passed the entry will no longer be visible. "
                    + "If you wish to continue endorsing these images please "
                    + "re-sign the metadata with an extended validity period and upload to the Marketplace.\n\n");
			
			for(Map<String, String> e: expiring){
				String identifier = e.get("identifier");
				String created = e.get("created");
				String valid = e.get("valid");
				
				mailContents.append("\n" + identifier + "\texpires: " + valid);
				mailContents.append("\nRetrieve entry: " + marketplaceEndpoint
				+ "/metadata/" + identifier + "/" + endorser + "/" + created + "?media=xml\n"); 
			}
			
			try {
				Notifier.sendNotification(endorser, mailContents.toString());
			} catch (Exception e) {
				LOGGER.warning(e.getMessage());
			}			
		}
	}
	
	private String getExpiryDate(){
		Date today = new Date();
		Calendar cal = Calendar.getInstance();  
		cal.setTime(today);    
		cal.add(Calendar.DATE, MAX_EXPIRY_RANGE);
		Date expiration = cal.getTime();
				
		return MarketplaceUtils.getFormattedDate(expiration);
	}
		
}
import static eu.stratuslab.marketplace.server.cfg.Parameter.DATA_DIR;
import static eu.stratuslab.marketplace.server.cfg.Parameter.ENDORSER_REMINDER;
import static eu.stratuslab.marketplace.server.cfg.Parameter.PENDING_DIR;
import static eu.stratuslab.marketplace.server.cfg.Parameter.STORE_TYPE;
import static eu.stratuslab.marketplace.server.cfg.Parameter.FILESTORE_TYPE;
import static eu.stratuslab.marketplace.server.cfg.Parameter.REPLICATION_ENABLED;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import eu.stratuslab.marketplace.server.resources.BaseResource;
import org.json.simple.JSONValue;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.LocalReference;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.ext.freemarker.ContextTemplateLoader;
import org.restlet.ext.freemarker.TemplateRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Directory;
import org.restlet.routing.Filter;
import org.restlet.routing.Router;
import org.restlet.routing.Template;
import org.restlet.routing.TemplateRoute;
import org.restlet.service.StatusService;
import eu.stratuslab.marketplace.server.cfg.Configuration;
import eu.stratuslab.marketplace.server.cfg.Parameter;
import eu.stratuslab.marketplace.server.query.QueryBuilder;
import eu.stratuslab.marketplace.server.query.SparqlBuilder;
import eu.stratuslab.marketplace.server.resources.EndorserResource;
import eu.stratuslab.marketplace.server.resources.EndorsersResource;
import eu.stratuslab.marketplace.server.resources.ErrorResource;
import eu.stratuslab.marketplace.server.resources.HomeResource;
import eu.stratuslab.marketplace.server.resources.MDataResource;
import eu.stratuslab.marketplace.server.resources.MDatumResource;
import eu.stratuslab.marketplace.server.resources.QueryResource;
import eu.stratuslab.marketplace.server.resources.TagsResource;
import eu.stratuslab.marketplace.server.resources.UploadResource;
import eu.stratuslab.marketplace.server.resources.SyncResource;
import eu.stratuslab.marketplace.server.routers.ActionRouter;
import eu.stratuslab.marketplace.server.store.file.CouchbaseStore;
import eu.stratuslab.marketplace.server.store.file.FileStore;
import eu.stratuslab.marketplace.server.store.file.FlatFileStore;
import eu.stratuslab.marketplace.server.store.file.GitStore;
import eu.stratuslab.marketplace.server.store.rdf.RdfStore;
import eu.stratuslab.marketplace.server.store.rdf.RdfStoreFactory;
import eu.stratuslab.marketplace.server.store.rdf.RdfStoreFactoryImpl;
import eu.stratuslab.marketplace.server.store.rdf.Processor;
import eu.stratuslab.marketplace.server.store.rdf.RdfStoreUpdater;
import eu.stratuslab.marketplace.server.utils.EndorserWhitelist;
import eu.stratuslab.marketplace.server.utils.MetadataFileUtils;
import eu.stratuslab.marketplace.server.utils.Reminder;
/**
 * Created as part of the StratusLab project (http://stratuslab.eu),
 * co-funded by the European Commission under the Grant Agreement
 * INSFO-RI-261552.
 *
 * Copyright (c) 2011
 *
 * 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 eu.stratuslab.marketplace.server;





public class MarketPlaceApplication extends Application {

    private static final Logger LOGGER = Logger.getLogger("org.restlet");

    private final ScheduledExecutorService scheduler = Executors
            .newScheduledThreadPool(2);
    private final ScheduledExecutorService indexUpdater = Executors.newScheduledThreadPool(1);

    private ScheduledFuture<?> reminderHandle;
    private ScheduledFuture<?> indexUpdateHandle;

    private static final int REMINDER_INTERVAL = 30;
    private static final int EXPIRY_INTERVAL = 1;

    private Reminder reminder;
    private Reminder expiry;

    private RdfStore store = null;
    private FileStore fileStore = null;
    private QueryBuilder queryBuilder = null;

    private String dataDir = null;

    private freemarker.template.Configuration freeMarkerConfiguration = null;

    private EndorserWhitelist whitelist;

    private RdfStoreUpdater rdfUpdater;

	private boolean invalidConfig = false;

	public MarketPlaceApplication() {
		try {
			String storeType = Configuration.getParameterValue(STORE_TYPE);
			String fileStoreType = Configuration.getParameterValue(FILESTORE_TYPE);
			String dataDir = Configuration.getParameterValue(DATA_DIR);
			init(dataDir, storeType, fileStoreType);
		} catch(ExceptionInInitializerError e){
			LOGGER.severe("incorrect configuration: " + e.getCause().getMessage());
			invalidConfig = true;
		}
	}

    public MarketPlaceApplication(String dataDir, String storeType, String fileStoreType) {
        init(dataDir, storeType, fileStoreType);
    }

    private void init(String dataDir, String storeType, String fileStoreType) {
        setName("StratusLab Marketplace");
        setDescription("Marketplace for StratusLab images");
        setOwner("StratusLab");
        setAuthor("Stuart Kenny");

        getMetadataService().addExtension("multipart",
                MediaType.MULTIPART_FORM_DATA, false);
        getMetadataService().addExtension("www_form",
                MediaType.APPLICATION_WWW_FORM, false);
        getMetadataService().addExtension("application_rdf",
                MediaType.APPLICATION_RDF_XML, true);
        getMetadataService().addExtension("application_xml",
                MediaType.APPLICATION_XML, false);

        getMetadataService().setDefaultMediaType(MediaType.APPLICATION_XML);

        setStatusService(new MarketPlaceStatusService());

        getTunnelService().setUserAgentTunnel(true);

        this.dataDir = dataDir;

        boolean success = MetadataFileUtils.createIfNotExists(dataDir);
        if(!success){
        	LOGGER.severe("Unable to create directory: " + dataDir);
        }

        success = MetadataFileUtils.createIfNotExists(
        		Configuration.getParameterValue(PENDING_DIR));
        if(!success){
        	LOGGER.severe("Unable to create directory: "
        			+ Configuration.getParameterValue(PENDING_DIR));
        }

        whitelist = new EndorserWhitelist();

        RdfStoreFactory factory = new RdfStoreFactoryImpl();
        store = factory.createRdfStore(RdfStoreFactory.SESAME_PROVIDER,
                storeType);
        store.initialize();

        FileStore internalStore = null;

        if(fileStoreType.equals("file")){
        	internalStore = new FlatFileStore(dataDir);
        } else if(fileStoreType.equals("couchbase")){
        	internalStore = new CouchbaseStore(dataDir);
        }

        if(Configuration.getParameterValueAsBoolean(REPLICATION_ENABLED)){
        	 fileStore = new GitStore(dataDir, internalStore);


        	final Runnable update = new Runnable() {
        		public void run() {
        			rdfIndexUpdate();
        		}
        	};

        	rdfUpdater = new RdfStoreUpdater(fileStore, new Processor(this));
        	indexUpdateHandle = indexUpdater.scheduleWithFixedDelay(update,
        			1, 5, TimeUnit.MINUTES);
        } else {
        	fileStore = internalStore;
        }

        queryBuilder = new SparqlBuilder();

        final Runnable remind = new Runnable() {
            public void run() {
                remind();
            }
        };

        final Runnable expires = new Runnable() {
            public void run() {
                expiry();
            }
        };

        reminder = new Reminder(this);
        expiry = new Reminder(this);

        if (Configuration.getParameterValueAsBoolean(ENDORSER_REMINDER)) {
        	reminderHandle = scheduler.scheduleWithFixedDelay(remind, REMINDER_INTERVAL,
        			REMINDER_INTERVAL, TimeUnit.DAYS);
            reminderHandle = scheduler.scheduleWithFixedDelay(expires, EXPIRY_INTERVAL,
            		EXPIRY_INTERVAL, TimeUnit.DAYS);
        }

    }

    /**
     * Creates a root Restlet that will receive all incoming calls.
     */
    @Override
    public Restlet createInboundRoot() {
        Context context = getContext();

        // Create the FreeMarker configuration.
        freeMarkerConfiguration = MarketPlaceApplication
                .createFreeMarkerConfig(context);

        // Create a router Restlet that defines routes.
        Router router = new Router(context);

        if(invalidConfig){
        	router.attach("/", ErrorResource.class);