/*
* Copyright (c) 2013 GigaSpaces Technologies Ltd. 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.
*/
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import models.User;
import org.apache.commons.io.FileUtils;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import play.Application;
import play.GlobalSettings;
import play.api.Play;
import play.api.mvc.Results;
import play.api.mvc.SimpleResult;
import play.core.j.JavaResults;
import play.libs.Akka;
import play.libs.Json;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;
import scala.Tuple2;
import scala.collection.JavaConversions;
import server.ApplicationContext;
import server.exceptions.ExceptionResponse;
import server.exceptions.ExceptionResponseDetails;
import utils.StringUtils;
import utils.Utils;
import akka.util.Duration;
import annotations.AnonymousUsers;
import beans.config.Conf;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.net.SMTPAppender;
/**
* On system startup trigger event onStart or onStop.
*
* @author Igor Goldenberg
*/
public class Global extends GlobalSettings
{
private static org.slf4j.Logger logger = LoggerFactory.getLogger( Global.class );
private Conf conf;
@Override
public void onStart(Application app)
{
loadSpringContext( app );
// print cloudify configuration
logger.info("printing configuration");
conf = ApplicationContext.get().conf();
logger.info( Json.stringify( Json.toJson( conf ) ) );
// initialize the server pool.
// letting the bean do it is incorrect..
new Thread( new Runnable() { // guy - lets use this for now to fix #33
@Override
public void run()
{
ApplicationContext.get().getServerPool().init();
}
}).start();
// create Admin user if not exists
if ( User.find.where().eq("admin", Boolean.TRUE ).findRowCount() <= 0 )
{
logger.info( "no admin user. creating from configuration" );
User adminUser = User.newUser("Cloudify", "Administrator",
conf.server.admin.username,
conf.server.admin.password );
adminUser.setAdmin(true);
adminUser.save();
}else{
logger.info( "found admin user" );
}
uploadInitialData( app );
logger.info("starting destroy server job");
Akka.system().scheduler().schedule(
Duration.Zero(),
Duration.create(1, TimeUnit.MINUTES),
ApplicationContext.get().getDestroyServersTask()
);
try{
if (!conf.mails.logErrors.isValid() || !conf.smtp.enabled ) {
logger.info("disabling log errors SMTP appender as smtp configuration or emails configuration is unsatisfactory");
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
for (Logger _logger : loggerContext.getLoggerList()) {
_logger.detachAppender("EMAIL");
}
}else{
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
for (Logger _logger : loggerContext.getLoggerList()) {
SMTPAppender smtpAppender = ( SMTPAppender )_logger.getAppender("EMAIL");
if ( smtpAppender != null ){
smtpAppender.setPassword( conf.smtp.password );
smtpAppender.setSTARTTLS( conf.smtp.tls );
smtpAppender.setUsername( conf.smtp.user );
smtpAppender.setFrom( conf.smtp.user );
smtpAppender.setSMTPHost( conf.smtp.host );
smtpAppender.setSMTPPort( conf.smtp.port );
String subject = smtpAppender.getSubject();
smtpAppender.setSubject( conf.application.name + " " + subject );
smtpAppender.addTo( conf.mails.logErrors.email );
smtpAppender.start();
logger.info(_logger.getName() + " has appender " + smtpAppender.getName() + " fully configured");
break;
}
// }else{
// logger.info("logger [" + _logger.getName() + "] does not have emails appender");
// }
}
}
}catch(Exception e){
logger.error("unable to reconfigure logback");
}
logger.error("testing");
try{
ApplicationContext.get().getMailSender().sendChangelog();
}catch(Exception e){
logger.error("unable to send changelog email",e);
}
if( conf.settings.isRecoverExecutions ){
try{
restoreExecutions();
}
catch( Throwable t ){
logger.error( t.toString(), t );
}
}
}
private static void restoreExecutions() {
if( logger.isDebugEnabled() ){
logger.debug( "---restoreExecutions" );
}
ApplicationContext.get().getRestoreExecutionService().restoreExecutions();
}
private void uploadInitialData( Application app )
{
if ( !conf.settings.initialData.load ) {
logger.info( "configuration set to not load initial data. skipping initial data" );
return;
}
File file = app.getFile( "conf/initialData/initial-data.json" );
try {
ApplicationContext.get().getInitialData().load( FileUtils.readFileToString( file ) );
} catch ( Exception e ) {
logger.info( "unable to read initial data from : " + file );
}
}
@Override
public Result onError( Http.RequestHeader requestHeader, Throwable throwable )
{
logger.error( "experienced error [{}]", Utils.requestToString( requestHeader ), throwable );
// todo : maybe this should be a method implemented in the exception.
// I assume there is an easier way to do this, but this is what I have so far.
// the code below simply detects this is our exception, and it sets headers so the GUI can respond accordingly.
Results.Status status = JavaResults.InternalServerError();
SimpleResult result;
if ( throwable.getCause() != null && throwable.getCause() instanceof ExceptionResponse ){ // customize response according to this exception
ExceptionResponseDetails res = ( ( ExceptionResponse) throwable.getCause() ).getResponseDetails();
//
//
Tuple2<String, String> ac = new Tuple2<String, String>( res.getHeaderKey(), res.toJson());
ArrayList<Tuple2<String, String>> list = new ArrayList<Tuple2<String, String>>();
list.add(ac);
scala.collection.immutable.List<Tuple2<String, String>> headers =
JavaConversions.asScalaBuffer( list ).toList();
//
//
// guy -- important.. even though Intellij marks this as error, it is not an error.. ignore it.
status.header().headers().$plus( ac );
result = status.withHeaders( headers );
// return result;
// return play.mvc.Results.internalServerError();
}else{
return null;
}
final SimpleResult finalResult = result;
return new Result() {
@Override
public play.api.mvc.Result getWrappedResult()
{
return finalResult;
}
};
}
@Override
public Action onRequest( Http.Request request, Method actionMethod )
{
if( conf.settings.globalSecurityCheck ){
if ( !actionMethod.isAnnotationPresent( AnonymousUsers.class ) ){
// must verify login
String authToken = request.queryString().get("authToken")[0];
User.validateAuthToken( authToken );
}
}
return super.onRequest( request, actionMethod );
}
@Override
public void onStop(Application app)
{
ApplicationContext.get().getServerBootstrapper().close();
}
private ClassPathXmlApplicationContext loadSpringContext( Application app ){
try{
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.getEnvironment().setActiveProfiles("standalone");
ctx.load("*Context.xml");
ctx.refresh();
logger.info("loading spring context");
String contextPath = app.configuration().getString("spring.context");
String contextProfiles = app.configuration().getString("spring.profiles");
if (StringUtils.isEmptyOrSpaces(contextProfiles) || StringUtils.isEmptyOrSpaces(contextPath) ){
throw new RuntimeException("you need to configure spring.context and spring.profiles");
}
logger.info("spring context is at : [" + contextPath + "]");
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
applicationContext.getEnvironment().setActiveProfiles(contextProfiles.split(","));
applicationContext.setConfigLocation(contextPath);
applicationContext.refresh();
return applicationContext;
}catch(RuntimeException e){
logger.error("unable to load context",e);
throw e;
}
}
}