/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.event.receiver.core.internal.tenantmgt;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.util.http.mapper.MappingData;
import org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils;
import org.wso2.carbon.event.receiver.core.config.EventReceiverConstants;
import org.wso2.carbon.event.receiver.core.internal.ds.EventReceiverServiceValueHolder;
import org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve;
import org.wso2.carbon.tomcat.ext.valves.CompositeValve;
import org.wso2.carbon.user.core.tenant.TenantManager;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import javax.servlet.http.HttpServletRequest;
public class TenantLazyLoaderValve extends CarbonTomcatValve {
private static final Log log = LogFactory.getLog(TenantLazyLoaderValve.class);
@Override
public void invoke(Request request, Response response, CompositeValve compositeValve) {
String requestURI = request.getRequestURI();
String domain = MultitenantUtils.getTenantDomainFromRequestURL(requestURI);
if (domain == null || domain.trim().length() == 0) {
getNext().invoke(request, response, compositeValve);
return;
}
if (!(requestURI.contains("/" + EventReceiverConstants.HTTP_RECEIVER_ENDPOINT_PREFIX + "/"))) {
getNext().invoke(request, response, compositeValve);
return;
}
try {
TenantManager tenantManager = EventReceiverServiceValueHolder.getRealmService().getTenantManager();
int tenantId = tenantManager.getTenantId(domain);
if (tenantId == MultitenantConstants.INVALID_TENANT_ID) {
if (log.isDebugEnabled()) {
log.debug("Tenant does not exist: " + domain);
}
getNext().invoke(request, response, compositeValve);
return;
}
} catch (Exception e) {
log.error("Error occurred while checking tenant existence", e);
getNext().invoke(request, response, compositeValve);
return;
}
ConfigurationContext serverConfigCtx = EventReceiverServiceValueHolder.getConfigurationContextService().getServerConfigContext();
// Fixing NPE when server shutting down while requests keep coming in
if (serverConfigCtx != null) {
if (TenantAxisUtils.getLastAccessed(domain, serverConfigCtx) == -1) { // First time access
try {
if (requestURI.contains("/" + EventReceiverConstants.HTTP_RECEIVER_ENDPOINT_PREFIX + "/")) {
remapRequest(request);
} else {
request.getRequestDispatcher(requestURI).forward(request, response);
}
} catch (Exception e) {
String msg = "Cannot redirect tenant request to " + requestURI + " for tenant " + domain;
log.error(msg, e);
throw new RuntimeException(msg, e);
}
}
setTenantAccessed(domain, serverConfigCtx);
getNext().invoke(request, response, compositeValve);
}
}
private void setTenantAccessed(String domain, ConfigurationContext serverConfigCtx) {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(TenantLazyLoaderValve.class.getClassLoader());
TenantAxisUtils.setTenantAccessed(domain, serverConfigCtx);
} finally {
Thread.currentThread().setContextClassLoader(tccl);
}
}
/**
* This method is used in remapping a request with context at tomcat level. This is mainly used
* with Lazy loading of tenants and Lazy loading of http receivers, where we can remap a request for a
* lazy loaded http receiver so that any request (GET, POST) parameters will not get lost with the
* first request.
*
* @param request - servlet request to be remapped for contexts
* @throws Exception - on error
*/
public static void remapRequest(HttpServletRequest request) throws Exception {
Request connectorReq = (Request) request;
MappingData mappingData = connectorReq.getMappingData();
mappingData.recycle();
connectorReq.getConnector().
getMapper().map(connectorReq.getCoyoteRequest().serverName(),
connectorReq.getCoyoteRequest().decodedURI(), null,
mappingData);
connectorReq.setContext((Context) connectorReq.getMappingData().context);
connectorReq.setWrapper((Wrapper) connectorReq.getMappingData().wrapper);
}
}