588 lines
19 KiB
Java
588 lines
19 KiB
Java
package org.gcube.application.geoportal.client;
|
||
|
||
import java.io.IOException;
|
||
import java.rmi.RemoteException;
|
||
import java.util.Iterator;
|
||
|
||
import javax.ws.rs.client.Entity;
|
||
import javax.ws.rs.client.WebTarget;
|
||
import javax.ws.rs.core.MediaType;
|
||
import javax.ws.rs.core.Response;
|
||
|
||
import org.bson.Document;
|
||
import org.gcube.application.geoportal.client.utils.Serialization;
|
||
import org.gcube.application.geoportal.common.faults.InvalidRequestException;
|
||
import org.gcube.application.geoportal.common.model.configuration.Configuration;
|
||
import org.gcube.application.geoportal.common.model.document.Project;
|
||
import org.gcube.application.geoportal.common.model.document.access.Access;
|
||
import org.gcube.application.geoportal.common.model.document.relationships.RelationshipNavigationObject;
|
||
import org.gcube.application.geoportal.common.model.rest.CreateRelationshipRequest;
|
||
import org.gcube.application.geoportal.common.model.rest.DeleteRelationshipRequest;
|
||
import org.gcube.application.geoportal.common.model.rest.QueryRequest;
|
||
import org.gcube.application.geoportal.common.model.rest.RegisterFileSetRequest;
|
||
import org.gcube.application.geoportal.common.model.rest.StepExecutionRequest;
|
||
import org.gcube.application.geoportal.common.rest.InterfaceConstants;
|
||
import org.gcube.application.geoportal.common.rest.Projects;
|
||
import org.gcube.common.clients.Call;
|
||
import org.gcube.common.clients.delegates.ProxyDelegate;
|
||
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
|
||
|
||
import lombok.NonNull;
|
||
import lombok.RequiredArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
|
||
/**
|
||
* Instantiates a new default documents client.
|
||
*
|
||
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
|
||
*
|
||
* May 2, 2023
|
||
* @param <T> the generic type
|
||
*/
|
||
|
||
/**
|
||
* Instantiates a new default documents client.
|
||
*
|
||
* @param delegate the delegate
|
||
* @param profileID the profile ID
|
||
* @param managedClass the managed class
|
||
*/
|
||
@RequiredArgsConstructor
|
||
@Slf4j
|
||
public class DefaultDocumentsClient<T extends Project> implements Projects<T> {
|
||
|
||
@NonNull
|
||
protected final ProxyDelegate<WebTarget> delegate;
|
||
@NonNull
|
||
protected final String profileID;
|
||
@NonNull
|
||
protected final Class<T> managedClass;
|
||
|
||
/**
|
||
* Gets the managed class.
|
||
*
|
||
* @return the managed class
|
||
*/
|
||
public Class<T> getManagedClass() {
|
||
return managedClass;
|
||
}
|
||
|
||
/**
|
||
* Creates the new.
|
||
*
|
||
* @param toCreate the to create
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T createNew(Document toCreate) throws RemoteException {
|
||
try {
|
||
log.debug("Creating Profiled Document (class {}, useCaseDescriptor {}) with content {} ", getManagedClass(),
|
||
profileID, toCreate);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).request(MediaType.APPLICATION_JSON)
|
||
.post(Entity.entity(toCreate, MediaType.APPLICATION_JSON)), getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Registered {} profiled {} ", toReturn.getId(), profileID);
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Delete by id.
|
||
*
|
||
* @param id the id
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public void deleteById(String id) throws RemoteException {
|
||
deleteById(id, false);
|
||
}
|
||
|
||
/**
|
||
* Delete by id.
|
||
*
|
||
* @param id the id
|
||
* @param force the force
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public void deleteById(String id, Boolean force) throws RemoteException {
|
||
try {
|
||
log.debug("Deleting ID {} useCaseDescriptor {} force {} ", id, profileID, force);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons
|
||
.check(endpoint.path(profileID).path(id).queryParam(InterfaceConstants.Parameters.FORCE, force)
|
||
.request(MediaType.APPLICATION_JSON).delete(), null);
|
||
};
|
||
delegate.make(call);
|
||
log.info("Deleted ID {} useCaseDescriptor {} force {} ", id, profileID, force);
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Gets the by id.
|
||
*
|
||
* @param id the id
|
||
* @return the by id
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T getById(String id) throws RemoteException {
|
||
try {
|
||
log.info("Loading Document ID {} (class {}, useCaseDescriptor {})", id, getManagedClass(), profileID);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(
|
||
endpoint.path(profileID).path(id).request(MediaType.APPLICATION_JSON).get(), getManagedClass());
|
||
};
|
||
return delegate.make(call);
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Gets the configuration.
|
||
*
|
||
* @return the configuration
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public Configuration getConfiguration() throws RemoteException {
|
||
try {
|
||
log.info("Loading Configuration for useCaseDescriptor {}", profileID);
|
||
Call<WebTarget, Configuration> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID)
|
||
.path(InterfaceConstants.Methods.CONFIGURATION_PATH).request(MediaType.APPLICATION_JSON).get(),
|
||
Configuration.class);
|
||
};
|
||
return delegate.make(call);
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Query.
|
||
*
|
||
* @param request the request
|
||
* @return the iterator
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public Iterator<T> query(QueryRequest request) throws RemoteException {
|
||
return queryForClass(request, getManagedClass());
|
||
}
|
||
|
||
/**
|
||
* Query for class.
|
||
*
|
||
* @param <C> the generic type
|
||
* @param request the request
|
||
* @param clazz the clazz
|
||
* @return the iterator
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public <C> Iterator<C> queryForClass(QueryRequest request, Class<C> clazz) throws RemoteException {
|
||
String jsonString = queryForJSON(request);
|
||
log.debug("Deserializing query Result as {} ", clazz);
|
||
try {
|
||
return Serialization.readCollection(jsonString, clazz);
|
||
} catch (IOException e) {
|
||
log.error("Unable to deserialize result as " + clazz, e);
|
||
log.debug("Query request was {} ", request);
|
||
log.debug("Query result was {} ", jsonString);
|
||
throw new RemoteException("Invalid format for submitted query");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Query for JSON.
|
||
*
|
||
* @param request the request
|
||
* @return the string
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public String queryForJSON(QueryRequest request) throws RemoteException {
|
||
try {
|
||
log.debug("Querying useCaseDescriptor {} for {}", profileID, request);
|
||
Call<WebTarget, String> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(InterfaceConstants.Methods.QUERY_PATH)
|
||
.request(MediaType.APPLICATION_JSON).post(Entity.entity(request, MediaType.APPLICATION_JSON)),
|
||
String.class);
|
||
};
|
||
return delegate.make(call);
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Perform step.
|
||
*
|
||
* @param id the id
|
||
* @param request the request
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T performStep(String id, StepExecutionRequest request) throws RemoteException {
|
||
try {
|
||
log.debug("Executing step on {} (class {}, useCaseDescriptor {}) with request {} ", id, getManagedClass(),
|
||
profileID, request);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(InterfaceConstants.Methods.STEP).path(id)
|
||
.request(MediaType.APPLICATION_JSON).post(Entity.entity(request, MediaType.APPLICATION_JSON)),
|
||
getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Executed STEP {} on {} [useCaseDescriptor {}, class {}] ", request.getStepID(), id, profileID,
|
||
getManagedClass());
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Register file set.
|
||
*
|
||
* @param id the id
|
||
* @param req the req
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
* @throws InvalidRequestException the invalid request exception
|
||
*/
|
||
@Override
|
||
public T registerFileSet(String id, RegisterFileSetRequest req) throws RemoteException, InvalidRequestException {
|
||
try {
|
||
log.debug("Registering FileSet on {} (class {}, useCaseDescriptor {}) with request {} ", id,
|
||
getManagedClass(), profileID, req);
|
||
req.validate();
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID)
|
||
.path(InterfaceConstants.Methods.REGISTER_FILES_PATH).path(id)
|
||
.request(MediaType.APPLICATION_JSON).post(Entity.entity(req, MediaType.APPLICATION_JSON)),
|
||
getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Registered FileSet on {} [useCaseDescriptor {}, class {}] with {}", id, profileID,
|
||
getManagedClass(), req);
|
||
return toReturn;
|
||
} catch (InvalidRequestException e) {
|
||
log.error("Invalid Request ", e);
|
||
throw e;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Delete file set.
|
||
*
|
||
* @param id the id
|
||
* @param path the path
|
||
* @param force the force
|
||
* @param ignoreErrors the ignore errors
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T deleteFileSet(String id, String path, Boolean force, Boolean ignoreErrors) throws RemoteException {
|
||
try {
|
||
log.debug(
|
||
"Deleting Fileset for ID {} [useCaseDescriptor {} , class {}] at {} (force {} ) (ignoreErrors {} )",
|
||
id, profileID, getManagedClass(), path, force, ignoreErrors);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(InterfaceConstants.Methods.DELETE_FILES_PATH)
|
||
.path(id).queryParam(InterfaceConstants.Parameters.FORCE, force)
|
||
.queryParam(InterfaceConstants.Parameters.IGNORE_ERRORS, ignoreErrors)
|
||
.request(MediaType.APPLICATION_JSON).post(Entity.entity(path, MediaType.APPLICATION_JSON)),
|
||
getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Deleted ID {} useCaseDescriptor {} force {} ", id, profileID, force);
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Force unlock.
|
||
*
|
||
* @param id the id
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T forceUnlock(String id) throws RemoteException {
|
||
try {
|
||
log.warn("Force Unlock of {} [useCaseDescriptor {} , class {}]", id, profileID, getManagedClass());
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(InterfaceConstants.Methods.FORCE_UNLOCK)
|
||
.path(id).request(MediaType.APPLICATION_JSON).put(Entity.json("")), getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Unlocked ID {} useCaseDescriptor {}", id, profileID);
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Sets the access policy.
|
||
*
|
||
* @param id the id
|
||
* @param toSet the to set
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T setAccessPolicy(String id, Access toSet) throws RemoteException {
|
||
try {
|
||
log.info("Setting Access of {} [useCaseDescriptor {} , class {}] as {}", id, profileID, getManagedClass(),
|
||
toSet);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(
|
||
endpoint.path(profileID).path(InterfaceConstants.Methods.SET_PROJECT_ACCESS_POLICY).path(id)
|
||
.request(MediaType.APPLICATION_JSON).put(Entity.json(toSet)),
|
||
getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.debug("Updated Access of ID {} useCaseDescriptor {}", id, profileID);
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Update document.
|
||
*
|
||
* @param id the id
|
||
* @param updatedDocument the updated document
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T updateDocument(String id, Document updatedDocument) throws RemoteException {
|
||
try {
|
||
log.debug("Updating {} [useCaseDescriptor {} , class {}] with ", id, profileID, getManagedClass(),
|
||
updatedDocument);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(id).request(MediaType.APPLICATION_JSON)
|
||
.put(Entity.entity(updatedDocument, MediaType.APPLICATION_JSON)), getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Updated ID {} useCaseDescriptor {}", id, profileID);
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Patch document. Added by Francesco Mangiacrapa
|
||
*
|
||
* @param id the id
|
||
* @param path the path
|
||
* @param updatedDocument the updated document
|
||
* @return the t
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public T patchDocument(String id, String path, Document updatedDocument) throws RemoteException {
|
||
try {
|
||
log.debug("Patching {} [useCaseDescriptor {} , class {}] with ", id, profileID, getManagedClass(),
|
||
updatedDocument);
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
|
||
WebTarget webTarget = endpoint.path(profileID).path(id);
|
||
webTarget.queryParam(InterfaceConstants.Parameters.PATH, path);
|
||
webTarget.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
|
||
Response response = webTarget.request(MediaType.APPLICATION_JSON).method("PATCH",
|
||
Entity.entity(updatedDocument, MediaType.APPLICATION_JSON));
|
||
return ResponseCommons.check(response, getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Updated ID {} useCaseDescriptor {}", id, profileID);
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Sets the relation.
|
||
*
|
||
* @param request the request
|
||
* @return the project
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public Project setRelation(CreateRelationshipRequest request) throws RemoteException {
|
||
try {
|
||
log.debug("Setting relationship {}:{} --{}--> {}:{}", profileID, request.getProjectId(),
|
||
request.getRelationshipId(), request.getTargetUCD(), request.getTargetId());
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(InterfaceConstants.Methods.RELATIONSHIP)
|
||
.path(request.getProjectId()).path(request.getRelationshipId())
|
||
.queryParam(InterfaceConstants.Parameters.TARGET_ID, request.getTargetId())
|
||
.queryParam(InterfaceConstants.Parameters.TARGET_UCD, request.getTargetUCD())
|
||
.request(MediaType.APPLICATION_JSON).put(Entity.json("")), getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Set relationship {}:{} --{}--> {}:{}", profileID, request.getProjectId(),
|
||
request.getRelationshipId(), request.getTargetUCD(), request.getTargetId());
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Delete relation.
|
||
*
|
||
* @param request the request
|
||
* @return the project
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public Project deleteRelation(DeleteRelationshipRequest request) throws RemoteException {
|
||
try {
|
||
log.debug("Deleting relationship {}:{} --{}--> {}:{}", profileID, request.getProjectId(),
|
||
request.getRelationshipId(), request.getTargetUCD(), request.getTargetId());
|
||
Call<WebTarget, T> call = endpoint -> {
|
||
return ResponseCommons.check(endpoint.path(profileID).path(InterfaceConstants.Methods.RELATIONSHIP)
|
||
.path(request.getProjectId()).path(request.getRelationshipId())
|
||
.queryParam(InterfaceConstants.Parameters.TARGET_ID, request.getTargetId())
|
||
.queryParam(InterfaceConstants.Parameters.TARGET_UCD, request.getTargetUCD())
|
||
.request(MediaType.APPLICATION_JSON).delete(), getManagedClass());
|
||
};
|
||
T toReturn = delegate.make(call);
|
||
log.info("Deleted relationship {}:{} --{}--> {}:{}", profileID, request.getProjectId(),
|
||
request.getRelationshipId(), request.getTargetUCD(), request.getTargetId());
|
||
return toReturn;
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Gets the relationship chain.
|
||
*
|
||
* @param id the id
|
||
* @param relationId the relation id
|
||
* @return the relationship chain
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public Iterator<RelationshipNavigationObject> getRelationshipChain(String id, String relationId)
|
||
throws RemoteException {
|
||
return getRelationshipChain(id, relationId, null);
|
||
}
|
||
|
||
/**
|
||
* Gets the relationship chain.
|
||
*
|
||
* @param id the id
|
||
* @param relationId the relation id
|
||
* @param deep the deep
|
||
* @return the relationship chain
|
||
* @throws RemoteException the remote exception
|
||
*/
|
||
@Override
|
||
public Iterator<RelationshipNavigationObject> getRelationshipChain(String id, String relationId, Boolean deep)
|
||
throws RemoteException {
|
||
try {
|
||
log.debug("Get relationship chain ID {} for {} [useCaseDescriptor {} , class {}]", relationId, id,
|
||
profileID, getManagedClass());
|
||
Call<WebTarget, Iterator<RelationshipNavigationObject>> call = endpoint -> {
|
||
|
||
WebTarget target = endpoint.path(profileID).path(InterfaceConstants.Methods.RELATIONSHIP).path(id)
|
||
.path(relationId);
|
||
|
||
if (deep != null)
|
||
target = target.queryParam(InterfaceConstants.Parameters.DEEP, deep);
|
||
|
||
String jsonChain = ResponseCommons.check(target.request(MediaType.APPLICATION_JSON).get(),
|
||
String.class);
|
||
|
||
return Serialization.readCollection(jsonChain, RelationshipNavigationObject.class);
|
||
};
|
||
return delegate.make(call);
|
||
} catch (RemoteException e) {
|
||
log.error("Unexpected error ", e);
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("Unexpected error ", e);
|
||
throw new RemoteException("Unexpected Error", e);
|
||
}
|
||
}
|
||
}
|