/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geogig.geoserver.config;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import com.google.common.base.Preconditions;
/**
* Simple bean that contains PostgreSQL specific configuration parameters for connecting to a GeoGig
* PostgreSQL backend. Instances of this bean can be wrapped inside a Wicket IModel implementation
* and used to build GeoGig repository URI location in a PostgreSQL database. Note that the URI
* location cannot be built solely from an instance of this bean, but must be supplied a repository
* ID.
*
* @see org.geogig.geoserver.web.repository.GeoGigRepositoryInfoFormComponent
*/
public class PostgresConfigBean implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(PostgresConfigBean.class);
private static final String SCHEME = "postgresql";
private static final String USER = "user";
private static final String PASSWORD = "password";
private static final String SLASH = "/";
private static final String UTF8 = StandardCharsets.UTF_8.name();
private String host = "localhost", database, schema = "public", username = "postgres", password;
private Integer port = 5432;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
public String getSchema() {
return schema;
}
public void setSchema(String schema) {
this.schema = schema;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public URI buildUriForRepo(String repoId) {
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
// set the schema
builder.scheme(SCHEME);
// set the host
builder.host(host);
// set the port
if (port > 0) {
builder.port(port);
}
// build the path in the form of "/databaseName/schema/repoID"
StringBuilder sb = new StringBuilder(128);
sb.append(SLASH).append(database);
if (null != schema) {
sb.append(SLASH).append(schema);
}
sb.append(SLASH).append(repoId);
builder.path(sb.toString());
// set the query parameters
String encodedUsername = username;
String encodedPassword = password;
try {
// try to URLEncode the username value, using UTF-8 encoding
encodedUsername = URLEncoder.encode(username, UTF8);
} catch (UnsupportedEncodingException uee) {
LOGGER.warn(String.format("Error encoding PostgreSQL username in UTF-8, attempting to use unencoded value: %s",
username), uee);
}
try {
encodedPassword = URLEncoder.encode(password, UTF8);
} catch (UnsupportedEncodingException uee) {
LOGGER.warn("Error encoding PostgreSQL password value, attempting to use unencoded value", uee);
}
builder.queryParam(USER, encodedUsername);
builder.queryParam(PASSWORD, encodedPassword);
// return the URI
return builder.build(true).toUri();
}
public static PostgresConfigBean newInstance() {
return new PostgresConfigBean();
}
public static PostgresConfigBean from(URI location) {
Preconditions.checkNotNull(location, "Cannot parse NULL URI location");
Preconditions.checkNotNull(location.getScheme(), "Cannot parse NULL URI scheme");
if (!"postgresql".equals(location.getScheme())) {
// don't parse, return new object
return newInstance();
}
UriComponents uri = UriComponentsBuilder.fromUri(location).build();
// build a bean from the parts
String host = uri.getHost();
int port = uri.getPort();
// get the path and parse database, repo and schema
String uriPath = uri.getPath();
// Path might have a leading '/'. If it does, skip it
int startIndex = uriPath.startsWith(SLASH) ? 1 : 0;
String[] paths = uriPath.substring(startIndex).split(SLASH);
// first is always the database
String database = paths[0];
// second part is repoId if no other parts exist, otherwise it's schema
String schema = null;
if (paths.length > 2) {
schema = paths[1];
}
// get the query parameters and pull out user and password
MultiValueMap<String, String> queryParams = uri.getQueryParams();
String username = queryParams.getFirst(USER);
String password = queryParams.getFirst(PASSWORD);
try {
// username should be URLEncoded, decode it here
username = URLDecoder.decode(username, UTF8);
} catch (UnsupportedEncodingException uee) {
LOGGER.warn(String.format("Error decoding PostgreSQL username value, attempting to use undecoded value: %s",
username), uee);
}
try {
// password should be URLEncoded, decode it here
password = URLDecoder.decode(password, UTF8);
} catch (UnsupportedEncodingException uee) {
LOGGER.warn("Error decoding PostgreSQL password value, attempting to use undecoded value", uee);
}
PostgresConfigBean bean = new PostgresConfigBean();
bean.setHost(host);
bean.setPort(port);
bean.setDatabase(database);
bean.setSchema(schema);
bean.setUsername(username);
bean.setPassword(password);
return bean;
}
public static String parseRepoId(URI location) {
// get the path and parse database, repo and schema
String uriPath = location.getPath();
// URI might have a leading '/'. If it does, skip it
int startIndex = uriPath.startsWith(SLASH) ? 1 : 0;
String[] paths = uriPath.substring(startIndex).split(SLASH);
// last part is the repoID
return paths[paths.length - 1];
}
@Override
public int hashCode() {
// hash all the fields, if they aren't null, otherwise use some prime numbers as place holders
return (host != null) ? host.hashCode() : 17 ^
((port != null) ? port.hashCode() : 37) ^
((username != null) ? username.hashCode() : 57) ^
((schema != null) ? schema.hashCode() : 97) ^
((password != null) ? password.hashCode() : 137) ^
((database != null) ? database.hashCode() : 197);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PostgresConfigBean other = (PostgresConfigBean) obj;
if (!Objects.equals(this.host, other.host)) {
return false;
}
if (!Objects.equals(this.database, other.database)) {
return false;
}
if (!Objects.equals(this.schema, other.schema)) {
return false;
}
if (!Objects.equals(this.username, other.username)) {
return false;
}
if (!Objects.equals(this.password, other.password)) {
return false;
}
if (!Objects.equals(this.port, other.port)) {
return false;
}
return true;
}
}