/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.sqoop.handler;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.log4j.Logger;
import org.apache.sqoop.audit.AuditLoggerManager;
import org.apache.sqoop.common.SqoopException;
import org.apache.sqoop.connector.ConnectorManager;
import org.apache.sqoop.connector.spi.SqoopConnector;
import org.apache.sqoop.json.JSONUtils;
import org.apache.sqoop.json.JsonBean;
import org.apache.sqoop.json.LinkBean;
import org.apache.sqoop.json.LinksBean;
import org.apache.sqoop.json.ValidationResultBean;
import org.apache.sqoop.model.ConfigUtils;
import org.apache.sqoop.model.MLink;
import org.apache.sqoop.model.MLinkConfig;
import org.apache.sqoop.model.MPersistableEntity;
import org.apache.sqoop.model.MResource;
import org.apache.sqoop.repository.Repository;
import org.apache.sqoop.repository.RepositoryManager;
import org.apache.sqoop.security.Authorization.AuthorizationEngine;
import org.apache.sqoop.security.AuthorizationManager;
import org.apache.sqoop.server.RequestContext;
import org.apache.sqoop.server.RequestHandler;
import org.apache.sqoop.server.common.ServerError;
import org.apache.sqoop.validation.ConfigValidationResult;
import org.json.simple.JSONObject;
public class LinkRequestHandler implements RequestHandler {
private static final Logger LOG = Logger.getLogger(LinkRequestHandler.class);
static final String ENABLE = "enable";
static final String DISABLE = "disable";
static final String LINKS_PATH = "links";
static final String LINK_PATH = "link";
public LinkRequestHandler() {
LOG.info("LinkRequestHandler initialized");
}
@Override
public JsonBean handleEvent(RequestContext ctx) {
switch (ctx.getMethod()) {
case GET:
return getLinks(ctx);
case POST:
return createUpdateLink(ctx, true);
case PUT:
if (ctx.getLastURLElement().equals(ENABLE)) {
return enableLink(ctx, true);
} else if (ctx.getLastURLElement().equals(DISABLE)) {
return enableLink(ctx, false);
} else {
return createUpdateLink(ctx, false);
}
case DELETE:
return deleteLink(ctx);
}
return null;
}
/**
* Delete link in the repository.
*
* @param ctx Context object
* @return Empty bean
*/
private JsonBean deleteLink(RequestContext ctx) {
Repository repository = RepositoryManager.getInstance().getRepository();
String linkIdentifier = ctx.getLastURLElement();
// support linkName or linkId for the api
long linkId = HandlerUtils.getLinkIdFromIdentifier(linkIdentifier, repository);
// Authorization check
AuthorizationEngine.deleteLink(String.valueOf(linkId));
AuditLoggerManager.getInstance().logAuditEvent(ctx.getUserName(),
ctx.getRequest().getRemoteAddr(), "delete", "link", linkIdentifier);
repository.deleteLink(linkId);
MResource resource = new MResource(String.valueOf(linkId), MResource.TYPE.LINK);
AuthorizationManager.getAuthorizationHandler().removeResource(resource);
return JsonBean.EMPTY_BEAN;
}
/**
* Create or Update link in repository.
*
* @param ctx Context object
* @return Validation bean object
*/
private JsonBean createUpdateLink(RequestContext ctx, boolean create) {
Repository repository = RepositoryManager.getInstance().getRepository();
LinkBean linkBean = new LinkBean();
try {
JSONObject postData = JSONUtils.parse(ctx.getRequest().getReader());
linkBean.restore(postData);
} catch (IOException e) {
throw new SqoopException(ServerError.SERVER_0003, "Can't read request content", e);
}
String username = ctx.getUserName();
// Get link object
List<MLink> links = linkBean.getLinks();
if (links.size() != 1) {
throw new SqoopException(ServerError.SERVER_0003,
"Expected one link while parsing JSON request but got " + links.size());
}
MLink postedLink = links.get(0);
// Authorization check
if (create) {
AuthorizationEngine.createLink(String.valueOf(postedLink.getConnectorId()));
} else {
AuthorizationEngine.updateLink(String.valueOf(postedLink.getConnectorId()),
String.valueOf(postedLink.getPersistenceId()));
}
MLinkConfig linkConfig = ConnectorManager.getInstance()
.getConnectorConfigurable(postedLink.getConnectorId()).getLinkConfig();
if (!linkConfig.equals(postedLink.getConnectorLinkConfig())) {
throw new SqoopException(ServerError.SERVER_0003, "Detected incorrect link config structure");
}
// if update get the link id from the request URI
if (!create) {
String linkIdentifier = ctx.getLastURLElement();
// support linkName or linkId for the api
long linkId = HandlerUtils.getLinkIdFromIdentifier(linkIdentifier, repository);
if (postedLink.getPersistenceId() == MPersistableEntity.PERSISTANCE_ID_DEFAULT) {
MLink existingLink = repository.findLink(linkId);
postedLink.setPersistenceId(existingLink.getPersistenceId());
}
}
// Associated connector for this link
SqoopConnector connector = ConnectorManager.getInstance().getSqoopConnector(
postedLink.getConnectorId());
// Validate user supplied config data
ConfigValidationResult connectorLinkConfigValidation = ConfigUtils.validateConfigs(postedLink
.getConnectorLinkConfig().getConfigs(), connector.getLinkConfigurationClass());
// Return back link validation result bean
ValidationResultBean linkValidationBean = new ValidationResultBean(
connectorLinkConfigValidation);
// If we're good enough let's perform the action
if (connectorLinkConfigValidation.getStatus().canProceed()) {
if (create) {
AuditLoggerManager.getInstance().logAuditEvent(ctx.getUserName(),
ctx.getRequest().getRemoteAddr(), "create", "link",
String.valueOf(postedLink.getPersistenceId()));
postedLink.setCreationUser(username);
postedLink.setLastUpdateUser(username);
repository.createLink(postedLink);
linkValidationBean.setId(postedLink.getPersistenceId());
} else {
AuditLoggerManager.getInstance().logAuditEvent(ctx.getUserName(),
ctx.getRequest().getRemoteAddr(), "update", "link",
String.valueOf(postedLink.getPersistenceId()));
postedLink.setLastUpdateUser(username);
repository.updateLink(postedLink);
}
}
return linkValidationBean;
}
private JsonBean getLinks(RequestContext ctx) {
String identifier = ctx.getLastURLElement();
LinkBean linkBean;
Locale locale = ctx.getAcceptLanguageHeader();
Repository repository = RepositoryManager.getInstance().getRepository();
// links by connector
if (ctx.getParameterValue(CONNECTOR_NAME_QUERY_PARAM) != null) {
identifier = ctx.getParameterValue(CONNECTOR_NAME_QUERY_PARAM);
AuditLoggerManager.getInstance().logAuditEvent(ctx.getUserName(),
ctx.getRequest().getRemoteAddr(), "get", "linksByConnector", identifier);
if (repository.findConnector(identifier) != null) {
long connectorId = repository.findConnector(identifier).getPersistenceId();
List<MLink> linkList = repository.findLinksForConnector(connectorId);
// Authorization check
linkList = AuthorizationEngine.filterResource(MResource.TYPE.LINK, linkList);
linkBean = createLinksBean(linkList, locale);
} else {
// this means name nor Id existed
throw new SqoopException(ServerError.SERVER_0005, "Invalid connector: " + identifier
+ " name for links given");
}
} else
// all links in the system
if (ctx.getPath().contains(LINKS_PATH)
|| (ctx.getPath().contains(LINK_PATH) && identifier.equals("all"))) {
AuditLoggerManager.getInstance().logAuditEvent(ctx.getUserName(),
ctx.getRequest().getRemoteAddr(), "get", "links", "all");
List<MLink> linkList = repository.findLinks();
// Authorization check
linkList = AuthorizationEngine.filterResource(MResource.TYPE.LINK, linkList);
linkBean = createLinksBean(linkList, locale);
}
// link by Id
else {
AuditLoggerManager.getInstance().logAuditEvent(ctx.getUserName(),
ctx.getRequest().getRemoteAddr(), "get", "link", identifier);
long linkId = HandlerUtils.getLinkIdFromIdentifier(identifier, repository);
MLink link = repository.findLink(linkId);
// Authorization check
AuthorizationEngine.readLink(String.valueOf(link.getPersistenceId()));
linkBean = createLinkBean(Arrays.asList(link), locale);
}
return linkBean;
}
private LinkBean createLinkBean(List<MLink> links, Locale locale) {
LinkBean linkBean = new LinkBean(links);
addLink(links, locale, linkBean);
return linkBean;
}
private LinksBean createLinksBean(List<MLink> links, Locale locale) {
LinksBean linksBean = new LinksBean(links);
addLink(links, locale, linksBean);
return linksBean;
}
private void addLink(List<MLink> links, Locale locale, LinkBean bean) {
// Add associated resources into the bean
for (MLink link : links) {
long connectorId = link.getConnectorId();
if (!bean.hasConnectorConfigBundle(connectorId)) {
bean.addConnectorConfigBundle(connectorId, ConnectorManager.getInstance()
.getResourceBundle(connectorId, locale));
}
}
}
private JsonBean enableLink(RequestContext ctx, boolean enabled) {
Repository repository = RepositoryManager.getInstance().getRepository();
String[] elements = ctx.getUrlElements();
String linkIdentifier = elements[elements.length - 2];
long linkId = HandlerUtils.getLinkIdFromIdentifier(linkIdentifier, repository);
// Authorization check
AuthorizationEngine.enableDisableLink(String.valueOf(linkId));
repository.enableLink(linkId, enabled);
return JsonBean.EMPTY_BEAN;
}
}