/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.server.headlessclient;
import java.rmi.RemoteException;
import java.util.Iterator;
import javax.servlet.http.HttpSession;
import org.apache.wicket.AbortException;
import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.Session;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.servlet.AbortWithWebErrorCodeException;
import org.apache.wicket.request.target.basic.RedirectRequestTarget;
import org.apache.wicket.request.target.coding.HybridUrlCodingStrategy;
import com.servoy.j2db.FormManager;
import com.servoy.j2db.IMainContainer;
import com.servoy.j2db.IWebClientApplication;
import com.servoy.j2db.Messages;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.IRepository;
import com.servoy.j2db.persistence.Solution;
import com.servoy.j2db.persistence.SolutionMetaData;
import com.servoy.j2db.scripting.StartupArguments;
import com.servoy.j2db.server.headlessclient.MainPage.ShowUrlInfo;
import com.servoy.j2db.server.shared.ApplicationServerRegistry;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.Settings;
import com.servoy.j2db.util.Utils;
public class SolutionLoader extends WebPage
{
private static final long serialVersionUID = 1L;
public SolutionLoader(PageParameters pp)
{
SolutionMetaData theReq = null;
try
{
if (ApplicationServerRegistry.get().getDataServer().isInGlobalMaintenanceMode() ||
ApplicationServerRegistry.get().getDataServer().isInServerMaintenanceMode())
{
// do this before redirect & register client - where it is usually detected, because when clustered
// this should result in a valid switch to another server in the cluster by the load balancer; if we wait until
// after redirect, a page expired will happen on the other server
// throw new AbortWithHttpStatusException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, false); this works, but doesn't show maintenance error page for non-clustered case
Session.get().invalidate();
RequestCycle.get().setRedirect(false);
throw new RestartResponseException(new ServoyServerInMaintenanceMode());
}
}
catch (RemoteException e)
{
// will not happen
throw new RuntimeException(e);
}
FeedbackPanel feedback = new FeedbackPanel("feedback");
add(feedback);
StartupArguments argumentsScope = new StartupArguments(pp);
String solutionName = argumentsScope.getSolutionName();
String method = argumentsScope.getMethodName();
String firstArgument = argumentsScope.getFirstArgument();
try
{
IRepository repository = ApplicationServerRegistry.get().getLocalRepository();
SolutionMetaData smd = (SolutionMetaData)repository.getRootObjectMetaData(solutionName, IRepository.SOLUTIONS);
if (smd == null ||
smd.getSolutionType() == SolutionMetaData.SOLUTION ||
smd.getSolutionType() == SolutionMetaData.WEB_CLIENT_ONLY ||
((smd.getSolutionType() == SolutionMetaData.MOBILE || smd.getSolutionType() == SolutionMetaData.MODULE) && ApplicationServerRegistry.get().isDeveloperStartup()))
{
theReq = smd;
}
else
{
Debug.log("Not loading solution " + smd.getName() + ", it is not configured for webclient usage");
theReq = null;
}
if (theReq != null)
{
Solution sol = (Solution)repository.getActiveRootObject(solutionName, IRepository.SOLUTIONS);
if (sol.getLoginSolutionName() == null && sol.getLoginFormID() <= 0 && theReq.getMustAuthenticate() &&
!((WebClientSession)getSession()).isSignedIn())
{
String authType = pp.getString("sv_auth_type"); //$NON-NLS-1$
boolean authorized = false;
if ((authType != null && authType.equals("basic")) || //$NON-NLS-1$
(authType == null && Utils.getAsBoolean(Settings.getInstance().getProperty("servoy.webclient.basic.authentication", "false")))) //$NON-NLS-1$ //$NON-NLS-2$
{
String authorizationHeader = ((WebRequest)RequestCycle.get().getRequest()).getHttpServletRequest().getHeader("Authorization"); //$NON-NLS-1$
if (authorizationHeader != null)
{
String authorization = authorizationHeader.substring(6);
// TODO: which encoding to use? see http://tools.ietf.org/id/draft-reschke-basicauth-enc-05.xml
authorization = new String(Utils.decodeBASE64(authorization));
int index = authorization.indexOf(':');
if (index > 0)
{
String username = authorization.substring(0, index);
String password = authorization.substring(index + 1);
authorized = ((WebClientSession)getSession()).authenticate(username, password);
}
}
if (!authorized)
{
((WebResponse)RequestCycle.get().getResponse()).getHttpServletResponse().setHeader("WWW-Authenticate", "Basic realm=\"webclient\""); //$NON-NLS-1$ //$NON-NLS-2$
throw new AbortWithWebErrorCodeException(401);
}
}
if (!authorized)
{
//signin first
throw new RestartResponseAtInterceptPageException(SignIn.class);
}
}
WebClientSession session;
HttpSession httpSession;
synchronized (sol)
{
// create the http session
httpSession = ((WebRequest)RequestCycle.get().getRequest()).getHttpServletRequest().getSession();
Session.unset();
session = (WebClientSession)getSession();
session.bind();
session.getClientInfo();
}
synchronized (httpSession)
{
IWebClientApplication sc = session.getWebClient();
if (sc != null && sc.getSolution() != null && sc.getFlattenedSolution().getMainSolutionMetaData().getName().equals(solutionName))
{
// make sure it is registered as a start of a request.
session.getWebClient().onBeginRequest(session);
FormManager formManager = ((FormManager)sc.getFormManager());
String currentPageMapName = getPageMap().getName();
if (currentPageMapName != null && !Utils.equalObjects(sc.getMainPage().getPageMap().getName(), currentPageMapName))
{
IMainContainer newContainer = formManager.getOrCreateMainContainer(currentPageMapName);
formManager.setCurrentContainer(newContainer, currentPageMapName);
}
// remove the method/argument from the page parameters, they shouldn't be used to generate a redirect url.
pp.remove("method"); //$NON-NLS-1$
pp.remove("m"); //$NON-NLS-1$
pp.remove("argument"); //$NON-NLS-1$
pp.remove("a"); //$NON-NLS-1$
// also remove client method arguments to avoid stackoverflow for deeplinked authenticate solutions (js_login called inside deeplinked method)
sc.handleArguments(null);
if (method != null)
{
try
{
sc.getScriptEngine().getScopesScope().executeGlobalFunction(null, method,
(firstArgument == null ? null : new Object[] { firstArgument, argumentsScope.toJSMap() }), false, false);
}
catch (Exception e1)
{
sc.reportError(Messages.getString("servoy.formManager.error.ExecutingOpenSolutionMethod", new Object[] { method }), e1); //$NON-NLS-1$
}
}
if (formManager.getCurrentContainer().getController() == null)
{
Iterator<Form> e = sc.getFlattenedSolution().getForms(true);
// add all forms first, they may be referred to in the login form
Form first = sc.getFlattenedSolution().getForm(sc.getSolution().getFirstFormID());
boolean formCanBeInstantiated = sc.getFlattenedSolution().formCanBeInstantiated(first);
while (!formCanBeInstantiated && e.hasNext())
{
Form form = e.next();
formCanBeInstantiated = sc.getFlattenedSolution().formCanBeInstantiated(form);
if (formCanBeInstantiated) first = form;
}
if (first != null)
{
formManager.showFormInCurrentContainer(first.getName());
}
}
}
else
{
sc = session.startSessionClient(theReq, method, argumentsScope);
}
if (sc.isValid())
{
Page page = sc.getMainPage();
// do get it from the real wicket session so that a lock is set on this page. (or waited for the lock)
Page p = session.getPage(page.getPageMapName(), page.getId(), page.getCurrentVersionNumber());
if (p instanceof MainPage)
{
page = p;
ShowUrlInfo urlScript = ((MainPage)p).getShowUrlInfo();
if (urlScript != null && "_self".equals(urlScript.getTarget()))
{
// a redirect was found to it self, just redirect directly to that one.
// clear the current main pages show url script first.
((MainPage)p).getShowUrlScript();
RequestCycle.get().setRequestTarget(new RedirectRequestTarget(urlScript.getUrl()));
return;
}
}
HybridUrlCodingStrategy.setInitialPageParameters(page, pp);
setResponsePage(page);
setRedirect(true);
//setRedirect(Utils.getAsBoolean(sc.getSettings().getProperty("servoy.webclient.nice.urls", "false")));
}
}
}
}
catch (RestartResponseAtInterceptPageException restart)
{
setRedirect(false);
throw restart;
}
catch (AbortException abort)
{
setRedirect(true);
throw abort;
}
catch (Exception e)
{
Debug.error(e);
error(e.toString());
}
}
}