package org.openlca.ilcd.io;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response.Status.Family;
import org.openlca.ilcd.commons.IDataSet;
import org.openlca.ilcd.commons.Ref;
import org.openlca.ilcd.descriptors.DataStockList;
import org.openlca.ilcd.descriptors.DescriptorList;
import org.openlca.ilcd.sources.Source;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.WebResource.Builder;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataMultiPart;
/**
* A client interface of a Soda4LCA service end-point.
*/
public class SodaClient implements DataStore {
private Logger log = LoggerFactory.getLogger(this.getClass());
private final SodaConnection con;
private Client client;
private List<Cookie> cookies = new ArrayList<>();
private boolean isConnected = false;
private XmlBinder binder = new XmlBinder();
public SodaClient(SodaConnection con) {
this.con = con;
}
public void connect() throws DataStoreException {
log.info("Create ILCD network connection {}", con);
client = Client.create();
authenticate();
isConnected = true;
}
private void authenticate() throws DataStoreException {
if (con.user == null || con.user.trim().isEmpty()
|| con.password == null || con.password.trim().isEmpty()) {
log.info("no user or password -> anonymous access");
return;
}
log.info("Authenticate user: {}", con.user);
ClientResponse response = client.resource(con.url).path("authenticate")
.path("login").queryParam("userName", con.user)
.queryParam("password", con.password).get(ClientResponse.class);
eval(response);
log.trace("Server response: {}", response.getEntity(String.class));
for (NewCookie c : response.getCookies()) {
cookies.add(c.toCookie());
}
}
public AuthInfo getAuthentication() throws DataStoreException {
checkConnection();
log.trace("Get authentication information.");
WebResource r = resource("authenticate", "status");
ClientResponse response = cookies(r).get(ClientResponse.class);
eval(response);
AuthInfo authInfo = response.getEntity(AuthInfo.class);
return authInfo;
}
public DataStockList getDataStockList() throws DataStoreException {
checkConnection();
log.trace("get data stocks");
WebResource r = resource("datastocks");
return cookies(r).get(DataStockList.class);
}
@Override
public <T> T get(Class<T> type, String id) throws DataStoreException {
checkConnection();
WebResource r = resource(Dir.get(type), id).queryParam("format", "xml");
log.info("Get resource: {}", r.getURI());
ClientResponse response = cookies(r).get(ClientResponse.class);
eval(response);
try {
return binder.fromStream(type, response.getEntityInputStream());
} catch (Exception e) {
throw new DataStoreException("Failed to load resource " + id
+ " of type " + type, e);
}
}
@Override
public void put(IDataSet ds) throws DataStoreException {
checkConnection();
WebResource r = resource(Dir.get(ds.getClass()));
log.info("Publish resource: {}/{}", r.getURI(), ds.getUUID());
try {
byte[] bytes = binder.toByteArray(ds);
Builder builder = cookies(r).type(MediaType.APPLICATION_XML);
if (con.dataStockId != null) {
log.trace("post to data stock {}", con.dataStockId);
builder = builder.header("stock", con.dataStockId);
}
ClientResponse response = builder.post(ClientResponse.class, bytes);
eval(response);
log.trace("Server response: {}", fetchMessage(response));
} catch (Exception e) {
throw new DataStoreException("Failed to upload data set + " + ds +
": " + e.getMessage(), e);
}
}
@Override
public void put(Source source, File[] files) throws DataStoreException {
checkConnection();
log.info("Publish source with files {}", source);
try {
FormDataMultiPart multiPart = new FormDataMultiPart();
if (con.dataStockId != null) {
log.trace("post to data stock {}", con.dataStockId);
multiPart.field("stock", con.dataStockId);
}
byte[] bytes = binder.toByteArray(source);
ByteArrayInputStream xmlStream = new ByteArrayInputStream(bytes);
FormDataBodyPart xmlPart = new FormDataBodyPart("file", xmlStream,
MediaType.MULTIPART_FORM_DATA_TYPE);
multiPart.bodyPart(xmlPart);
addFiles(files, multiPart);
WebResource r = resource("sources/withBinaries");
ClientResponse resp = cookies(r).type(
MediaType.MULTIPART_FORM_DATA_TYPE)
.post(ClientResponse.class, multiPart);
eval(resp);
log.trace("Server response: {}", fetchMessage(resp));
} catch (Exception e) {
throw new DataStoreException("Failed to upload source with file: "
+ e.getMessage(), e);
}
}
private void addFiles(File[] files, FormDataMultiPart multiPart)
throws Exception {
if (files == null)
return;
for (File file : files) {
if (file == null)
continue;
FileInputStream is = new FileInputStream(file);
FormDataBodyPart part = new FormDataBodyPart(file.getName(),
is, MediaType.MULTIPART_FORM_DATA_TYPE);
multiPart.bodyPart(part);
}
}
public InputStream getExternalDocument(String sourceId, String fileName)
throws DataStoreException {
checkConnection();
WebResource r = resource("sources", sourceId, fileName);
log.info("Get external document {} for source {}", fileName, sourceId);
try {
return cookies(r).type(MediaType.APPLICATION_OCTET_STREAM).get(
InputStream.class);
} catch (Exception e) {
throw new DataStoreException("Failed to get file " + fileName +
"for source " + sourceId + ": " + e.getMessage(), e);
}
}
@Override
public <T> boolean delete(Class<T> type, String id) {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> Iterator<T> iterator(Class<T> type) {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> boolean contains(Class<T> type, String id)
throws DataStoreException {
checkConnection();
WebResource r = resource(Dir.get(type), id)
.queryParam("format", "xml");
log.trace("Contains resource {} ?", r.getURI());
ClientResponse response = cookies(r).head();
log.trace("Server response: {}", response);
return response.getStatus() == Status.OK.getStatusCode();
}
/** Includes also the version in the check. */
public boolean contains(Ref ref) throws DataStoreException {
if (ref == null || ref.type == null || ref.uuid == null)
return false;
checkConnection();
WebResource r = resource(Dir.get(ref.getDataSetClass()), ref.uuid)
.queryParam("format", "xml");
if (ref.version != null)
r = r.queryParam("version", ref.version);
ClientResponse response = cookies(r).head();
return response.getStatus() == Status.OK.getStatusCode();
}
public DescriptorList search(Class<?> type, String name)
throws DataStoreException {
checkConnection();
String term = null;
if (name == null)
term = "";
else
term = name.trim();
WebResource r = initSearchRequest(type)
.queryParam("search", "true")
.queryParam("name", term);
log.trace("Search resources: {}", r.getURI());
DescriptorList list = cookies(r).get(DescriptorList.class);
return list;
}
private WebResource resource(String... path) {
WebResource r = client.resource(con.url);
for (String p : path) {
r = r.path(p);
}
return r;
}
private Builder cookies(WebResource r) {
Builder b = r.getRequestBuilder();
for (Cookie c : cookies)
b.cookie(c);
return b;
}
private void checkConnection() throws DataStoreException {
if (!isConnected) {
connect();
}
}
private WebResource initSearchRequest(Class<?> type) {
if (con.dataStockId == null)
return resource(Dir.get(type));
else
return resource("datastocks", con.dataStockId, Dir.get(type));
}
private void eval(ClientResponse response) throws DataStoreException {
if (response == null)
throw new DataStoreException("Client response is NULL.");
Status status = Status.fromStatusCode(response.getStatus());
Family family = status.getFamily();
if (family == Family.CLIENT_ERROR || family == Family.SERVER_ERROR) {
String message = status.getReasonPhrase() + ": "
+ fetchMessage(response);
throw new DataStoreException(message);
}
}
private String fetchMessage(ClientResponse response) {
if (response.hasEntity())
return response.getEntity(String.class);
return "";
}
@Override
public void close() throws IOException {
client.destroy();
}
}