package de.skuzzle.polly.core.internal.httpv2;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.log4j.Logger;
import de.skuzzle.polly.core.Polly;
import de.skuzzle.polly.core.configuration.ConfigurationProviderImpl;
import de.skuzzle.polly.core.internal.ModuleStates;
import de.skuzzle.polly.core.internal.ShutdownManagerImpl;
import de.skuzzle.polly.core.internal.mypolly.MyPollyImpl;
import de.skuzzle.polly.core.internal.plugins.PluginManagerImpl;
import de.skuzzle.polly.core.moduleloader.AbstractProvider;
import de.skuzzle.polly.core.moduleloader.ModuleLoader;
import de.skuzzle.polly.core.moduleloader.SetupException;
import de.skuzzle.polly.core.moduleloader.annotations.Module;
import de.skuzzle.polly.core.moduleloader.annotations.Provide;
import de.skuzzle.polly.core.moduleloader.annotations.Require;
import de.skuzzle.polly.http.api.AddHandlerListener;
import de.skuzzle.polly.http.api.Controller;
import de.skuzzle.polly.http.api.DefaultServerFactory;
import de.skuzzle.polly.http.api.HttpEvent;
import de.skuzzle.polly.http.api.HttpEventListener;
import de.skuzzle.polly.http.api.HttpException;
import de.skuzzle.polly.http.api.HttpServer;
import de.skuzzle.polly.http.api.HttpServletServer;
import de.skuzzle.polly.http.api.ServerFactory;
import de.skuzzle.polly.http.api.answers.HttpAnswer;
import de.skuzzle.polly.http.api.answers.HttpAnswerHandler;
import de.skuzzle.polly.http.api.answers.HttpResourceAnswer;
import de.skuzzle.polly.http.api.answers.HttpTemplateAnswer;
import de.skuzzle.polly.http.api.handler.DirectoryEventHandler;
import de.skuzzle.polly.http.api.handler.HttpEventHandler;
import de.skuzzle.polly.http.internal.HttpServerCreator;
import de.skuzzle.polly.sdk.Configuration;
import de.skuzzle.polly.sdk.ConfigurationProvider;
import de.skuzzle.polly.sdk.Disposable;
import de.skuzzle.polly.sdk.MyPolly;
import de.skuzzle.polly.sdk.exceptions.DisposingException;
import de.skuzzle.polly.sdk.httpv2.GsonHttpAnswer;
import de.skuzzle.polly.sdk.httpv2.MenuCategory;
import de.skuzzle.polly.sdk.httpv2.MenuEntry;
import de.skuzzle.polly.sdk.httpv2.WebinterfaceManager;
import de.skuzzle.polly.sdk.resources.PollyBundle;
import de.skuzzle.polly.sdk.resources.Resources;
import de.skuzzle.polly.tools.concurrent.ThreadFactoryBuilder;
@Module(
requires = {
@Require(component = ConfigurationProviderImpl.class),
@Require(component = ShutdownManagerImpl.class),
@Require(component = PluginManagerImpl.class),
@Require(component = NewsManager.class),
@Require(state = ModuleStates.PERSISTENCE_READY),
@Require(state = ModuleStates.PLUGINS_READY)
},
provides = @Provide(component = WebInterfaceManagerImpl.class)
)
public class WebinterfaceProvider extends AbstractProvider {
private final static Logger logger = Logger.getLogger(WebinterfaceProvider.class
.getName());
public final static String HTTP_CONFIG = "http.cfg"; //$NON-NLS-1$
private Configuration serverCfg;
private HttpServletServer server;
private WebInterfaceManagerImpl webinterface;
private NewsManager newsManager;
public WebinterfaceProvider(ModuleLoader loader) {
super("WEBINTERFACE_PROVIDER", loader, false); //$NON-NLS-1$
}
@Override
public void setup() throws SetupException {
this.newsManager = this.requireNow(NewsManager.class, true);
GitHubController.refreshCommits();
ConfigurationProvider configProvider = this.requireNow(
ConfigurationProviderImpl.class, true);
try {
this.serverCfg = configProvider.open(HTTP_CONFIG);
} catch (IOException e) {
throw new SetupException(e);
}
int port = this.serverCfg.readInt(Configuration.HTTP_PORT);
int sessionTimeout = this.serverCfg.readInt(Configuration.HTTP_SESSION_TIMEOUT);
final ExecutorService executor = Executors.newFixedThreadPool(15,
new ThreadFactoryBuilder("HTTP_%n%")); //$NON-NLS-1$
final ServerFactory sf;
final boolean useSSL = this.serverCfg.readBoolean(Configuration.HTTP_USE_SSL);
if (useSSL) {
final String keyStorePath = this.serverCfg.readString(Configuration.KEYSTORE_FILE);
final String keyStorePW = this.serverCfg.readString(Configuration.KEYSTORE_PASSWORD);
final String keyPW = this.serverCfg.readString(Configuration.KEY_PASSWORD);
sf = new SSLServerFactory(port, executor, keyStorePath, keyStorePW, keyPW);
} else {
sf = new DefaultServerFactory(port, executor);
}
this.server = HttpServerCreator.createServletServer(sf);
this.server.setSessionLiveTime(sessionTimeout);
this.server.setEncoding("UTF-8"); //$NON-NLS-1$
this.server.setSessionType(HttpServer.SESSION_TYPE_COOKIE);
this.server.setAnswerHandler(GsonHttpAnswer.class, new GsonHttpAnswerHandler());
this.server.addHttpEventListener(new HttpEventListener() {
@Override
public void onRequest(HttpEvent e) {
logger.debug("HTTP: " + e.getMode() + " " + //$NON-NLS-1$ //$NON-NLS-2$
e.getRequestURI().toString() +
" [ip: " + e.getClientIP() + ", " + //$NON-NLS-1$ //$NON-NLS-2$
e.getSession().get("user") + "], " + //$NON-NLS-1$ //$NON-NLS-2$
e.parameterMap());
}
});
ShutdownManagerImpl sm = this.requireNow(ShutdownManagerImpl.class, true);
sm.addDisposable(new Disposable() {
@Override
public boolean isDisposed() {
return false;
}
@Override
public void dispose() throws DisposingException {
server.shutdown(5);
}
});
this.webinterface = new WebInterfaceManagerImpl(this.server, "webv2",
this.serverCfg.readString(Configuration.HTTP_PUBLIC_HOST), port, useSSL);
final Map<String, List<MenuEntry>> addLater = new HashMap<>();
// Automatically collect all menu entries
this.server.addAddHandlerListener(new AddHandlerListener() {
@Override
public void handlerAdded(Controller c, String url, String name, String[] values) {
if (values.length < 1 || !values[0].equals(WebinterfaceManager.ADD_MENU_ENTRY)) {
return;
} else if (values.length < 4) {
logger.error("Ignoring 'ADD_MENU_EVENT' beacause of missing parameters"); //$NON-NLS-1$
return;
}
final String bundleFamily = values[1];
final String categoryKey = values[2];
final String descriptionKey = values[3];
final String[] permissions;
if (values.length == 4) {
permissions = new String[0];
} else {
permissions = Arrays.copyOfRange(values, 4, values.length);
}
final PollyBundle pb = Resources.get(bundleFamily,
c.getClass().getClassLoader());
final String description = pb.get(descriptionKey);
final String category = pb.get(categoryKey);
name = pb.get(name);
final MenuEntry me = new MenuEntry(name, url, description, permissions);
webinterface.addMenuEntry(category, me);
final List<MenuEntry> addLaterEntries = addLater.get(name.toLowerCase());
if (addLaterEntries != null) {
for (final MenuEntry subEntry : addLaterEntries) {
me.addSubEntry(subEntry);
}
}
}
});
this.server.addAddHandlerListener(new AddHandlerListener() {
@Override
public void handlerAdded(Controller c, String url, String name, String[] values) {
if (values.length < 1 || !values[0].equals(WebinterfaceManager.ADD_SUB_ENTRY)) {
return;
} else if (values.length < 4) {
logger.error("Ignoring 'ADD_SUB_ENTRY' beacause of missing parameters"); //$NON-NLS-1$
return;
}
final String parent = values[1];
final String bundleFamily = values[2];
final String descriptionKey = values[3];
final String[] permissions;
if (values.length == 4) {
permissions = new String[0];
} else {
permissions = Arrays.copyOfRange(values, 4, values.length);
}
final PollyBundle pb = Resources.get(bundleFamily,
c.getClass().getClassLoader());
final String description = pb.get(descriptionKey);
name = pb.get(name);
final MenuEntry me = webinterface.getMenuEntry(parent);
if (me != null) {
me.addSubEntry(new MenuEntry(name, url, description, permissions));
} else {
List<MenuEntry> entries = addLater.get(parent.toLowerCase());
if (entries == null) {
entries = new ArrayList<>();
addLater.put(parent.toLowerCase(), entries);
}
entries.add(new MenuEntry(name, url, description, permissions));
}
}
});
this.provideComponent(this.webinterface);
}
@Override
public void run() throws Exception {
final MyPolly myPolly = this.requireNow(MyPollyImpl.class, false);
this.webinterface.addCategory(new MenuCategory(Integer.MAX_VALUE, MSG.indexAdminCategory));
this.webinterface.addCategory(new MenuCategory(-100, MSG.indexGeneralCategory));
this.server.addController(new IndexController(myPolly, this.newsManager));
this.server.addController(new SessionController(myPolly));
this.server.addController(new UserController(myPolly));
this.server.addController(new RoleController(myPolly));
this.server.addController(new GitHubController(myPolly));
UserController.createUserTable(myPolly);
// replace the default template answer handler
final PluginManagerImpl pluginManager = this.requireNow(
PluginManagerImpl.class, true);
final HttpAnswerHandler replace = new PollyTemplateAnswerHandler(
Polly.PLUGIN_FOLDER, pluginManager.loadedPlugins());
this.server.setAnswerHandler(HttpTemplateAnswer.class, replace);
this.server.addHttpEventHandler("/files",
new DirectoryEventHandler("webv2/files", false));
final ClassLoader cl = this.getClass().getClassLoader();
this.server.addHttpEventHandler("/de/skuzzle/polly/sdk/httpv2", new HttpEventHandler() {
@Override
public HttpAnswer handleHttpEvent(String registered, HttpEvent e,
HttpEventHandler next) throws HttpException {
return new HttpResourceAnswer(200, cl, e.getPlainUri());
}
});
this.server.start();
}
}