/*
* Copyright (c) 2013-2014 the original author or authors
*
* 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 io.werval.spi.server;
import java.util.ArrayList;
import java.util.List;
import io.werval.api.exceptions.PassivationException;
import io.werval.spi.ApplicationSPI;
import io.werval.spi.dev.DevShellSPI;
import io.werval.util.Reflectively;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.werval.util.IllegalArguments.ensureNotNull;
/**
* Base class to write HttpServer implementations.
* <p>
* Primary responsibility is error handling across lifecycle.
* <p>
* See {@link HttpServerHelper} for composable helper methods to use in implementations.
*/
public abstract class HttpServerAdapter
implements HttpServer
{
private static final Logger LOG = LoggerFactory.getLogger( HttpServerAdapter.class );
private final Thread shutdownHook;
protected ApplicationSPI app;
protected DevShellSPI devSpi;
protected HttpServerAdapter()
{
this.shutdownHook = new Thread(
new Runnable()
{
@Override
public void run()
{
passivate();
}
},
"werval-server-shutdown"
);
}
protected HttpServerAdapter( ApplicationSPI app, DevShellSPI devSpi )
{
this();
setApplicationSPI( app );
setDevShellSPI( devSpi );
}
@Override
@Reflectively.Invoked( by = "DevShell" )
public final void setApplicationSPI( ApplicationSPI application )
{
ensureNotNull( "ApplicationSPI", application );
this.app = application;
}
@Override
@Reflectively.Invoked( by = "DevShell" )
public final void setDevShellSPI( DevShellSPI devSpi )
{
this.devSpi = devSpi;
}
@Override
@Reflectively.Invoked( by = "DevShell" )
// Fail-fast
public final void activate()
{
long start = System.currentTimeMillis();
// Activate Application
app.activate();
// Notify Global object that the HttpServer will start listening to network connections
app.global().beforeHttpBind( app );
// Activate HttpServer
activateHttpServer();
// Notify Global object that the HttpServer started listening to network connections
app.global().afterHttpBind( app );
// Log
if( LOG.isInfoEnabled() )
{
String address = app.config().string( "werval.http.address" );
int port = app.config().intNumber( "werval.http.port" );
LOG.info(
"Http Service Activated on http://{}:{}/ - Took {}ms",
address, port, System.currentTimeMillis() - start
);
}
}
@Override
@Reflectively.Invoked( by = "DevShell" )
public final void passivate()
{
// Record all passivation errors here to report them at once at the end
List<Exception> passivationErrors = new ArrayList<>();
// Notify Global object that the HttpServer will stop listening to network connections
try
{
app.global().beforeHttpUnbind( app );
}
catch( Exception ex )
{
passivationErrors.add(
new PassivationException( "Exception(s) on Global.beforeHttpUnbind(): " + ex.getMessage(), ex )
);
}
// Passivate HttpServer
try
{
passivateHttpServer();
LOG.info( "Http Service Passivated" );
}
catch( Exception ex )
{
passivationErrors.add(
new PassivationException( "Exception(s) on HttpServer.passivate(): " + ex.getMessage(), ex )
);
}
// Notify Global object that the HttpServer stopped listening to network connections
try
{
app.global().afterHttpUnbind( app );
}
catch( Exception ex )
{
passivationErrors.add(
new PassivationException( "Exception(s) on Global.afterHttpUnbind(): " + ex.getMessage(), ex )
);
}
// Passivate Application
try
{
app.passivate();
}
catch( Exception ex )
{
passivationErrors.add(
new PassivationException( "Exception(s) on Application.passivate(): " + ex.getMessage(), ex )
);
}
// Log errors
if( !passivationErrors.isEmpty() )
{
PassivationException ex = new PassivationException( "There were errors during passivation" );
for( Exception passivationError : passivationErrors )
{
ex.addSuppressed( passivationError );
}
LOG.error( ex.getMessage(), ex );
}
}
/**
* Override this method and activate your HttpServer implementation in it.
*/
protected void activateHttpServer()
{
throw new UnsupportedOperationException( "Override activateHttpServer()!" );
}
/**
* Override this method and passivate your HttpServer implementation in it.
*/
protected void passivateHttpServer()
{
throw new UnsupportedOperationException( "Override passivateHttpServer()!" );
}
@Override
@Reflectively.Invoked( by = "DevShell" )
public final void registerPassivationShutdownHook()
{
try
{
Runtime.getRuntime().addShutdownHook( shutdownHook );
}
catch( IllegalArgumentException ex )
{
throw new IllegalStateException( "HttpServer passivation hook previously registered", ex );
}
}
}