/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.jooby.ebean;
import com.google.inject.Binder;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import io.ebean.EbeanServer;
import io.ebean.config.ContainerConfig;
import io.ebean.config.ServerConfig;
import org.jooby.Env;
import org.jooby.internal.ebean.EbeanEnhancer;
import org.jooby.internal.ebean.EbeanManaged;
import org.jooby.jdbc.Jdbc;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
/**
* <h1>ebean module</h1>
* <p>
* Object-Relational-Mapping via Ebean. It configures and exposes {@link EbeanServer} instances.
* </p>
* <p>
* This module extends {@link Jdbc} module, before going forward, make sure you read the doc of the
* {@link Jdbc} module first.
* </p>
*
* <h2>usage</h2>
* <pre>
* {
* use(new Ebeanby().doWith(conf {@literal ->} {
* conf.addClass(Pet.class);
* }));
*
* get("/pets", req {@literal ->} {
* EbeanServer ebean = req.require(EbeanServer.class);
* return ebean.createQuery(Pet.class)
* .findList();
* });
* }
* </pre>
*
* <p>
* Usage is pretty straightforward, but of course we need to setup/configure the enhancement.
* </p>
*
* <h2>enhancement</h2>
* <p>
* The enhancement process comes in two flavors:
* </p>
* <ul>
* <li>Runtime: via a JVM Agent</li>
* <li>Build time: via Maven plugin</li>
* </ul>
*
* <h3>recommended setup</h3>
* <p>
* The recommended setup consist of setting up both: runtime and build time enhancement.
* </p>
*
* <p>
* The runtime enhancer increases developer productivity, it let you start your app from IDE
* and/or <code>mvn jooby:run</code>. All you have to do is to add the agent dependencies to your
* classpath:
* </p>
*
* <pre>
* <dependency>
* <groupId>org.avaje.ebeanorm</groupId>
* <artifactId>avaje-ebeanorm-agent</artifactId>
* <scope>test</scope>
* </dependency>
*
* <dependency>
* <groupId>org.avaje</groupId>
* <artifactId>avaje-agentloader</artifactId>
* <scope>test</scope>
* </dependency>
* </pre>
*
* <p>
* Did you see the <code>test scope</code>? We don't want to use the runtime enhancer while
* running in prod. Instead, we want to use the build time enhancer.
* </p>
* <p>
* All you have to do is to add <code>avaje-ebeanorm-mavenenhancer</code> to your
* <code>pom.xml</code> as described in the
* <a href="http://ebean-orm.github.io/docs#enhance_maven">official doc</a>.
* </p>
*
* <p>
* Alternative, and because we want to keep our <code>pom.xml</code> small, you can drop a
* <code>ebean.activator</code> file inside the <code>src/etc/mvn</code> folder. The presence of the
* file <code>src/etc/mvn/ebean.activator</code> will trigger the
* <code>avaje-ebeanorm-mavenenhancer</code> plugin.
* </p>
*
* <h2>configuration</h2>
* <p>
* Configuration is done via <code>.conf</code>, for example:
* </p>
*
* <pre>
* ebean.ddl.generate=false
* ebean.ddl.run=false
*
* ebean.debug.sql=true
* ebean.debug.lazyload=false
*
* ebean.disableClasspathSearch = false
* </pre>
*
* <p>
* Or programmatically:
* </p>
*
* <pre>
* {
* use(new Ebeanby().doWith(conf {@literal ->} {
* conf.setDisableClasspathSearch(false);
* }));
* }
* </pre>
*
* @author edgar
* @since 0.10.0
*/
public class Ebeanby extends Jdbc {
private Set<String> packages = new HashSet<>();
static {
// Turn off ebean shutdown hook:
System.setProperty("ebean.registerShutdownHook", "false");
}
/**
* Creates a new {@link Ebeanby} using the given name to setup a {@link Jdbc} datasource.
*
* @param name Name of this ebean module.
*/
public Ebeanby(final String name) {
super(name);
}
/**
* Creates a new {@link Ebeanby} using the default {@link Jdbc} name: <code>db</code>.
*/
public Ebeanby() {
}
/**
* <p>
* Add one ore more packages. Packages are used by the agent enhancement (if present) and to
* search for entities via class path search when classes have not been explicitly specified.
* </p>
*
* @param packages Packages to enhancement and search for.
* @return This module.
*/
public Ebeanby packages(final String... packages) {
Arrays.stream(packages).forEach(this.packages::add);
return this;
}
@Override
public void configure(final Env env, final Config conf, final Binder binder) {
configure(env, conf, binder, (name, ds) -> {
ServerConfig config = new ServerConfig();
this.packages.add(conf.getString("application.ns"));
EbeanEnhancer.newEnhancer().run(packages);
config.setName(name);
packages.forEach(config::addPackage);
Config cprops = conf.getConfig("ebean");
if (conf.hasPath("ebean." + name)) {
cprops = conf.getConfig("ebean." + name)
.withFallback(cprops)
.withoutPath(name);
}
Properties props = props(cprops);
ContainerConfig container = new ContainerConfig();
container.loadFromProperties(props);
config.setContainerConfig(container);
config.setDataSource(ds);
config.loadFromProperties(props);
config.setDefaultServer(cprops.getBoolean("defaultServer"));
config.setRegister(cprops.getBoolean("register"));
callback(config, conf);
EbeanManaged server = new EbeanManaged(conf, config);
env.onStart(server::start);
env.onStop(server::stop);
env.serviceKey().generate(EbeanServer.class, name,
k -> binder.bind(k).toProvider(server).asEagerSingleton());
});
}
@Override
public Config config() {
return ConfigFactory.parseResources(getClass(), "ebean.conf").withFallback(super.config());
}
private Properties props(final Config config) {
Properties props = new Properties();
config.entrySet().forEach(prop -> {
Object value = prop.getValue().unwrapped();
props.setProperty("ebean." + prop.getKey(), value.toString());
});
return props;
}
}