diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a88e34..c5aaa5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v2.0.6 - 2020.11.18 +## Fixes +- creating stacks or loading JSON data is possible after the database has been provisioned + +## New features +- stop and start the database +- using GraalVM 20.3.0 CE LTS (allowing for compression of Linux executable) + # v2.0.5 - 2020.11.13 ## Fixes - better error messaging for creation and termination of databases diff --git a/README.md b/README.md index 561d9dd..63f4a77 100644 --- a/README.md +++ b/README.md @@ -20,23 +20,23 @@ __... in 5 minutes.__ [![DRAGON Stack - React Frontend / Autonomous Backend](https://img.youtube.com/vi/DzI9yyAiRjY/0.jpg)](https://www.youtube.com/watch?v=DzI9yyAiRjY) ## Download -The latest stable release is v2.0.5. +The latest stable release is v2.0.6. ### Linux and OCI Cloud Shell ``` -rm -f ./dragon-linux-x86_64-2.0.5 -wget https://github.com/loiclefevre/dragon/releases/download/v2.0.5/dragon-linux-x86_64-2.0.5 +rm -f ./dragon-linux-x86_64-2.0.6 +wget https://github.com/loiclefevre/dragon/releases/download/v2.0.6/dragon-linux-x86_64-2.0.6 chmod +x dragon-linux-* ``` Learn about [OCI Cloud shell](https://docs.cloud.oracle.com/en-us/iaas/Content/API/Concepts/cloudshellintro.htm). ### Windows ``` -powershell wget https://github.com/loiclefevre/dragon/releases/download/v2.0.5/dragon-windows-x86_64-2.0.5.exe -OutFile dragon-windows-x86_64-2.0.5.exe +powershell wget https://github.com/loiclefevre/dragon/releases/download/v2.0.6/dragon-windows-x86_64-2.0.6.exe -OutFile dragon-windows-x86_64-2.0.6.exe ``` ### MAC OS ``` -curl -L -O https://github.com/loiclefevre/dragon/releases/download/v2.0.5/dragon-osx-x86_64-2.0.5 +curl -L -O https://github.com/loiclefevre/dragon/releases/download/v2.0.6/dragon-osx-x86_64-2.0.6 chmod +x dragon-osx-* ``` @@ -136,17 +136,17 @@ Example from OCI Cloud Shell (Linux): Linux and OCI Cloud Shell: ``` -$ ./dragon-linux-x86_64-2.0.5 +$ ./dragon-linux-x86_64-2.0.6 ``` Windows: ``` -> dragon-windows-x86_64-2.0.5.exe +> dragon-windows-x86_64-2.0.6.exe ``` MAC OS: ``` -$ ./dragon-osx-x86_64-2.0.5 +$ ./dragon-osx-x86_64-2.0.6 ``` ### Loading JSON data @@ -155,19 +155,19 @@ If you need to create JSON collections during the provisioning process, you may To load JSON data as well as provisioning (Linux and OCI Cloud Shell): ``` -$ ./dragon-linux-x86_64-2.0.5 -loadjson +$ ./dragon-linux-x86_64-2.0.6 -loadjson ``` To load JSON data as well as provisioning and finally create a React application (Linux and OCI Cloud Shell): ``` -$ ./dragon-linux-x86_64-2.0.5 -loadjson -create-react-app myfrontend +$ ./dragon-linux-x86_64-2.0.6 -loadjson -create-react-app myfrontend ``` ### Destroying your database To destroy your database (Linux and OCI Cloud Shell): ``` -$ ./dragon-linux-x86_64-2.0.5 -destroy +$ ./dragon-linux-x86_64-2.0.6 -destroy ``` ## Stacks @@ -222,7 +222,7 @@ For [Spring-Boot](https://spring.io/projects/spring-boot) web application: Simple to use, it works, it is optimized already, __no__ administrative burdens, develop right away! -Converged, it means, you get the consistency of a _relational_ database, the _flexibility_ of a JSON database, the simplicity of _Machine Learning_ in the database, the location capabilities of a _spatial_ database, the power of a property _graph_ database, the indexing capabilities of a _full-text_ database, the _automatic elasticity_ as well as the costing model (always free version, pay by the second...) of a cloud native database, the _performance_ of the underlying infrastructure Exadata, the strongest _security_ of the database market, and the vast developer friendly ecosystem brought by Oracle Cloud Infrastructure. +__[Converged](https://www.youtube.com/watch?v=yBWgb_oh39U)__, it means, you get the consistency of a _relational_ database, the _flexibility_ of a JSON database, the simplicity of _Machine Learning_ in the database, the location capabilities of a _spatial_ database, the power of a property _graph_ database, the indexing capabilities of a _full-text_ database, the _automatic elasticity_ as well as the costing model (always free version, pay by the second...) of a cloud native database, the _performance_ of the underlying infrastructure Exadata, the strongest _security_ of the database market, and the vast developer friendly ecosystem brought by Oracle Cloud Infrastructure. Autonomous Database can be: diff --git a/pom.xml b/pom.xml index da27db7..8987f10 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.oracle dragon - 2.0.5 + 2.0.6 Dragon Stack @@ -18,7 +18,7 @@ ${targetJavaVersion} UTF-8 2.4 - 20.2.0 + 20.3.0 1.25.4 @@ -151,20 +151,23 @@ false - dragon-${os.detected.classifier}-${project.version} + dragon-${os.detected.classifier}-${project.version} com.oracle.dragon.DragonStack --no-fallback -H:+ReportExceptionStackTraces - -H:+TraceClassInitialization --allow-incomplete-classpath --report-unsupported-elements-at-runtime --enable-all-security-services -H:EnableURLProtocols=https -H:ConfigurationFileDirectories=../src/main/resources/META-INF/native-image --initialize-at-build-time=sun.instrument.InstrumentationImpl - --rerun-class-initialization-at-runtime=org.bouncycastle.jcajce.provider.drbg.DRBG$Default,org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV + + diff --git a/src/main/java/com/oracle/dragon/stacks/CodeGenerator.java b/src/main/java/com/oracle/dragon/stacks/CodeGenerator.java index 0738c9b..9a6267d 100644 --- a/src/main/java/com/oracle/dragon/stacks/CodeGenerator.java +++ b/src/main/java/com/oracle/dragon/stacks/CodeGenerator.java @@ -25,6 +25,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static com.oracle.dragon.util.DSSession.EXECUTABLE_NAME; + public class CodeGenerator { private final StackType type; private final String name; @@ -90,6 +92,7 @@ public void work() throws DSException { st.add("name", name); st.add("path", dest.getAbsolutePath()); st.add("override", override); + st.add("executable", EXECUTABLE_NAME); System.out.println(st.render()); @@ -143,6 +146,7 @@ public void work() throws DSException { .collect(Collectors.joining("\n")), '<', '>'); st.add("name", name); st.add("path", dest.getAbsolutePath()); + st.add("executable", EXECUTABLE_NAME); for (String key : patchParameters.keySet()) { st.add(key, patchParameters.get(key)); diff --git a/src/main/java/com/oracle/dragon/util/DSSession.java b/src/main/java/com/oracle/dragon/util/DSSession.java index f5062c8..cf5b3be 100644 --- a/src/main/java/com/oracle/dragon/util/DSSession.java +++ b/src/main/java/com/oracle/dragon/util/DSSession.java @@ -62,7 +62,7 @@ public class DSSession { /** * Current version. */ - public static final String VERSION = "2.0.5"; + public static final String VERSION = "2.0.6"; public static final String CONFIGURATION_FILENAME = "dragon.config"; public static final String LOCAL_CONFIGURATION_FILENAME = "local_dragon.config.json"; @@ -102,7 +102,9 @@ public enum Operation { CreateDatabase, DestroyDatabase, LoadData, - UpgradeDragon + UpgradeDragon, + StopDatabase, + StartDatabase } public enum Section { @@ -119,7 +121,10 @@ public enum Section { CreateStack("Stack creation"), Upgrade("DRAGON upgrade"), PostProcessingStack("Stack post processing"), - CreateKeys("Keys creation"); + CreateKeys("Keys creation"), + DatabaseShutdown("Database shutdown"), + DatabaseStart("Database startup"), + ; private final String name; @@ -221,14 +226,19 @@ enum LicenseType { private File dataPath = new File("."); + public static final String EXECUTABLE_NAME; + static { + final ProcessHandle processHandle = ProcessHandle.current(); + EXECUTABLE_NAME = processHandle.info().command().get(); + final String osName = System.getProperty("os.name").toLowerCase(); if (osName.startsWith("windows")) { platform = Platform.Windows; OCICloudShell = false; try { - final ProcessHandle processHandle = ProcessHandle.current(); + final String shell = processHandle.parent().get().info().command().get(); vscode = processHandle.parent().get().parent().get().parent().get().info().command().get().toLowerCase().endsWith("code.exe"); } catch (NoSuchElementException ignored) { @@ -334,6 +344,32 @@ public void analyzeCommandLineParameters(String[] args) throws DSException { } break; + case "-stop-db": + case "--stop-db": + if (localConfiguration != null) { + if (operation == Operation.CreateDatabase) { + operation = Operation.StopDatabase; + } else { + section.printlnKO("conflicting command: " + arg); + displayUsage(); + System.exit(-9999); + } + } + break; + + case "-start-db": + case "--start-db": + if (localConfiguration != null) { + if (operation == Operation.CreateDatabase) { + operation = Operation.StartDatabase; + } else { + section.printlnKO("conflicting command: " + arg); + displayUsage(); + System.exit(-9999); + } + } + break; + case "-info": case "--info": info = true; @@ -424,6 +460,8 @@ private void displayUsage() { println(" \t . JSON file names must match [_[0-9]+].json"); println(ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "create" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "react" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "app" + ANSI_RESET + " [name] \tcreates a " + ANSI_VSC_BLUE + "React" + ANSI_RESET + " frontend (default name: frontend)"); println(ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "create" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "spring" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "boot" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "petclinic" + ANSI_RESET + " [name]\tcreates the " + ANSI_BRIGHT_GREEN + "Spring Boot" + ANSI_RESET + " Petclinic (default name: petclinic)"); + println(ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "stop" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "db" + ANSI_RESET + " \t"+ANSI_RED+"stops"+ANSI_RESET+" the database"); + println(ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "start" + ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "db" + ANSI_RESET + " \t"+ANSI_BRIGHT_GREEN+"starts"+ANSI_RESET+" the database"); println(ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "destroy" + ANSI_RESET + " \tto destroy the database"); println(ANSI_VSC_DASH + "-" + ANSI_VSC_BLUE + "upgrade" + ANSI_RESET + " \tto download the latest version for your platform... (if available)"); displayHowToReportIssue(); @@ -751,12 +789,12 @@ private void initializeClients() throws DSException { public void work() throws DSException { switch (operation) { case CreateDatabase: - // prevent creating a new database if already one exists! + // prevent creating a new database if one already exists! // TODO: check if the one inside the local config do really exists... if (localConfiguration == null) { initializeClients(); createADB(); - } else { + } else if(!createStack) { throw new DatabaseAlreadyDeployedException(localConfiguration.getDbName()); } break; @@ -785,9 +823,23 @@ public void work() throws DSException { case UpgradeDragon: checkForUpgrade(); break; + + case StopDatabase: + if (localConfiguration != null && localConfiguration.getDbName().equals(dbName)) { + initializeClients(); + stopDatabase(); + } + break; + + case StartDatabase: + if (localConfiguration != null && localConfiguration.getDbName().equals(dbName)) { + initializeClients(); + startDatabase(); + } + break; } - if (operation == Operation.CreateDatabase && createStack) { + if ((operation == Operation.CreateDatabase || operation == Operation.LoadData) && createStack) { final CodeGenerator c = new CodeGenerator(stackType, stackName, stackOverride, localConfiguration); c.work(); } @@ -996,11 +1048,181 @@ private boolean isAboveVersion(String latestVersion) { return current.compareTo(maybeNewVersion) < 0; } + private void stopDatabase() throws DSException { + section = Section.DatabaseShutdown; + section.print("checking existing databases"); + + final ListAutonomousDatabasesRequest listADB = ListAutonomousDatabasesRequest.builder().compartmentId(configFile.get(CONFIG_COMPARTMENT_ID)).build(); + final ListAutonomousDatabasesResponse listADBResponse = dbClient.listAutonomousDatabases(listADB); + + boolean dbNameExists = false; + String adbId = null; + AutonomousDatabaseSummary.LifecycleState currentLifecycleState = null; + for (AutonomousDatabaseSummary adb : listADBResponse.getItems()) { + //System.out.println(adb.getLifecycleState()+", "+adb.getIsFreeTier()+", "+dbName); + + if (adb.getLifecycleState() != AutonomousDatabaseSummary.LifecycleState.Terminated) { + if (adb.getDbName().equals(dbName)) { + if (databaseType.isFree() && !adb.getIsFreeTier()) continue; + dbNameExists = true; + adbId = adb.getId(); + currentLifecycleState = adb.getLifecycleState(); + break; + } + } + } + + if (!dbNameExists) { + section.printlnOK("nothing to do"); + } else { + section.print("pending"); + + if(currentLifecycleState == AutonomousDatabaseSummary.LifecycleState.Stopped) { + section.printlnOK("already stopped"); + return; + } + + workRequestClient = new WorkRequestClient(provider); + StopAutonomousDatabaseResponse responseTerminate = dbClient.stopAutonomousDatabase(StopAutonomousDatabaseRequest.builder().autonomousDatabaseId(adbId).build()); + String workRequestId = responseTerminate.getOpcWorkRequestId(); + + GetWorkRequestRequest getWorkRequestRequest = GetWorkRequestRequest.builder().workRequestId(workRequestId).build(); + boolean exit = false; + long startTime = System.currentTimeMillis(); + float pendingProgressMove = 0f; + do { + GetWorkRequestResponse getWorkRequestResponse = workRequestClient.getWorkRequest(getWorkRequestRequest); + switch (getWorkRequestResponse.getWorkRequest().getStatus()) { + case Succeeded: + section.printlnOK(getDurationSince(startTime)); + exit = true; + break; + case Failed: + section.printlnKO(); + final ListWorkRequestErrorsResponse response = workRequestClient.listWorkRequestErrors(ListWorkRequestErrorsRequest.builder().workRequestId(workRequestId).opcRequestId(getWorkRequestResponse.getOpcRequestId()).build()); + final StringBuilder errors = new StringBuilder(); + int i = 0; + for (WorkRequestError e : response.getItems()) { + if (i > 0) errors.append("\n"); + errors.append(e.getMessage()); + i++; + } + throw new OCIDatabaseShutdownFailedException(dbName, errors.toString()); + case Accepted: + section.print(String.format("accepted [%s]", getDurationSince(startTime))); + break; + case InProgress: + section.print(String.format("in progress %.0f%% [%s]", Math.min(getWorkRequestResponse.getWorkRequest().getPercentComplete() + pendingProgressMove, 99f), getDurationSince(startTime))); + pendingProgressMove += Math.random() * 2f; + break; + } + + sleep(1000L); + + } while (!exit); + + DatabaseWaiters waiter = dbClient.getWaiters(); + try { + final GetAutonomousDatabaseResponse responseGet = waiter.forAutonomousDatabase(GetAutonomousDatabaseRequest.builder().autonomousDatabaseId(adbId).build(), + new AutonomousDatabase.LifecycleState[]{AutonomousDatabase.LifecycleState.Stopped}).execute(); + } catch (Exception e) { + section.printlnKO(); + throw new OCIDatabaseWaitForShutdownFailedException(e); + } + } + + } + + private void startDatabase() throws DSException { + section = Section.DatabaseStart; + section.print("checking existing databases"); + + final ListAutonomousDatabasesRequest listADB = ListAutonomousDatabasesRequest.builder().compartmentId(configFile.get(CONFIG_COMPARTMENT_ID)).build(); + final ListAutonomousDatabasesResponse listADBResponse = dbClient.listAutonomousDatabases(listADB); + + boolean dbNameExists = false; + String adbId = null; + AutonomousDatabaseSummary.LifecycleState currentLifecycleState = null; + for (AutonomousDatabaseSummary adb : listADBResponse.getItems()) { + //System.out.println(adb.getLifecycleState()+", "+adb.getIsFreeTier()+", "+dbName); + + if (adb.getLifecycleState() != AutonomousDatabaseSummary.LifecycleState.Terminated) { + if (adb.getDbName().equals(dbName)) { + if (databaseType.isFree() && !adb.getIsFreeTier()) continue; + dbNameExists = true; + adbId = adb.getId(); + currentLifecycleState = adb.getLifecycleState(); + break; + } + } + } + + if (!dbNameExists) { + section.printlnOK("nothing to do"); + } else { + section.print("pending"); + + if(currentLifecycleState == AutonomousDatabaseSummary.LifecycleState.Available ) { + section.printlnOK("already started"); + return; + } + + workRequestClient = new WorkRequestClient(provider); + StartAutonomousDatabaseResponse responseTerminate = dbClient.startAutonomousDatabase(StartAutonomousDatabaseRequest.builder().autonomousDatabaseId(adbId).build()); + String workRequestId = responseTerminate.getOpcWorkRequestId(); + + GetWorkRequestRequest getWorkRequestRequest = GetWorkRequestRequest.builder().workRequestId(workRequestId).build(); + boolean exit = false; + long startTime = System.currentTimeMillis(); + float pendingProgressMove = 0f; + do { + GetWorkRequestResponse getWorkRequestResponse = workRequestClient.getWorkRequest(getWorkRequestRequest); + switch (getWorkRequestResponse.getWorkRequest().getStatus()) { + case Succeeded: + section.printlnOK(getDurationSince(startTime)); + exit = true; + break; + case Failed: + section.printlnKO(); + final ListWorkRequestErrorsResponse response = workRequestClient.listWorkRequestErrors(ListWorkRequestErrorsRequest.builder().workRequestId(workRequestId).opcRequestId(getWorkRequestResponse.getOpcRequestId()).build()); + final StringBuilder errors = new StringBuilder(); + int i = 0; + for (WorkRequestError e : response.getItems()) { + if (i > 0) errors.append("\n"); + errors.append(e.getMessage()); + i++; + } + throw new OCIDatabaseStartFailedException(dbName, errors.toString()); + case Accepted: + section.print(String.format("accepted [%s]", getDurationSince(startTime))); + break; + case InProgress: + section.print(String.format("in progress %.0f%% [%s]", Math.min(getWorkRequestResponse.getWorkRequest().getPercentComplete() + pendingProgressMove, 99f), getDurationSince(startTime))); + pendingProgressMove += Math.random() * 2f; + break; + } + + sleep(1000L); + + } while (!exit); + + DatabaseWaiters waiter = dbClient.getWaiters(); + try { + final GetAutonomousDatabaseResponse responseGet = waiter.forAutonomousDatabase(GetAutonomousDatabaseRequest.builder().autonomousDatabaseId(adbId).build(), + new AutonomousDatabase.LifecycleState[]{AutonomousDatabase.LifecycleState.Available}).execute(); + } catch (Exception e) { + section.printlnKO(); + throw new OCIDatabaseWaitForStartFailedException(e); + } + } + + } + private void createADB() throws DSException { section = Section.DatabaseCreation; section.print("checking existing databases"); - final ListAutonomousDatabasesRequest listADB = ListAutonomousDatabasesRequest.builder().compartmentId(configFile.get("compartment_id")).build(); + final ListAutonomousDatabasesRequest listADB = ListAutonomousDatabasesRequest.builder().compartmentId(configFile.get(CONFIG_COMPARTMENT_ID)).build(); final ListAutonomousDatabasesResponse listADBResponse = dbClient.listAutonomousDatabases(listADB); final Set existingFreeADB = new TreeSet<>(); boolean dbNameAlreadyExists = false; @@ -1471,7 +1693,7 @@ private void destroyDatabase() throws OCIDatabaseTerminationFailedException, OCI section = Section.DatabaseTermination; section.print("checking existing databases"); - final ListAutonomousDatabasesRequest listADB = ListAutonomousDatabasesRequest.builder().compartmentId(configFile.get("compartment_id")).build(); + final ListAutonomousDatabasesRequest listADB = ListAutonomousDatabasesRequest.builder().compartmentId(configFile.get(CONFIG_COMPARTMENT_ID)).build(); final ListAutonomousDatabasesResponse listADBResponse = dbClient.listAutonomousDatabases(listADB); boolean dbNameExists = false; diff --git a/src/main/java/com/oracle/dragon/util/exception/ErrorCode.java b/src/main/java/com/oracle/dragon/util/exception/ErrorCode.java index e9a5dfa..82ce216 100644 --- a/src/main/java/com/oracle/dragon/util/exception/ErrorCode.java +++ b/src/main/java/com/oracle/dragon/util/exception/ErrorCode.java @@ -42,7 +42,11 @@ public enum ErrorCode { UpgradeFailed(-39), UpgradeTimeout(-40), StackFileDownload(-41), DatabaseAlreadyDeployed(-42), - UnmanagedDatabaseCantBeDestroyed(-43); + UnmanagedDatabaseCantBeDestroyed(-43), + OCIDatabaseWaitForShutdownFailed(-44), + OCIDatabaseShutdownFailed(-45), + OCIDatabaseWaitForStartFailed(-46), + OCIDatabaseStartFailed(-47); public final int internalErrorCode; diff --git a/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseShutdownFailedException.java b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseShutdownFailedException.java new file mode 100644 index 0000000..101c9ea --- /dev/null +++ b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseShutdownFailedException.java @@ -0,0 +1,7 @@ +package com.oracle.dragon.util.exception; + +public class OCIDatabaseShutdownFailedException extends DSException { + public OCIDatabaseShutdownFailedException(String dbName, String error) { + super(ErrorCode.OCIDatabaseShutdownFailed, String.format("The shutdown of your %s database failed:\n%s", dbName, error)); + } +} diff --git a/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseStartFailedException.java b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseStartFailedException.java new file mode 100644 index 0000000..75260ce --- /dev/null +++ b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseStartFailedException.java @@ -0,0 +1,7 @@ +package com.oracle.dragon.util.exception; + +public class OCIDatabaseStartFailedException extends DSException { + public OCIDatabaseStartFailedException(String dbName, String error) { + super(ErrorCode.OCIDatabaseStartFailed, String.format("The startup of your %s database failed:\n%s", dbName, error)); + } +} diff --git a/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseWaitForShutdownFailedException.java b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseWaitForShutdownFailedException.java new file mode 100644 index 0000000..2f7c608 --- /dev/null +++ b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseWaitForShutdownFailedException.java @@ -0,0 +1,7 @@ +package com.oracle.dragon.util.exception; + +public class OCIDatabaseWaitForShutdownFailedException extends DSException { + public OCIDatabaseWaitForShutdownFailedException(Exception e) { + super(ErrorCode.OCIDatabaseWaitForShutdownFailed,"Unable to wait for complete shutdown of the database!",e); + } +} diff --git a/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseWaitForStartFailedException.java b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseWaitForStartFailedException.java new file mode 100644 index 0000000..ce170dd --- /dev/null +++ b/src/main/java/com/oracle/dragon/util/exception/OCIDatabaseWaitForStartFailedException.java @@ -0,0 +1,7 @@ +package com.oracle.dragon.util.exception; + +public class OCIDatabaseWaitForStartFailedException extends DSException { + public OCIDatabaseWaitForStartFailedException(Exception e) { + super(ErrorCode.OCIDatabaseWaitForStartFailed,"Unable to wait for complete startup of the database!",e); + } +} diff --git a/src/main/resources/stacks/create-react-app/data/src/App.js.st b/src/main/resources/stacks/create-react-app/data/src/App.js.st index a42c94f..3fbfe08 100644 --- a/src/main/resources/stacks/create-react-app/data/src/App.js.st +++ b/src/main/resources/stacks/create-react-app/data/src/App.js.st @@ -101,7 +101,7 @@ function App() { target="_blank" rel="noopener noreferrer" > - IƱigo Quilez + Iñigo Quilez

diff --git a/www/help.png b/www/help.png index 4de6e09..47a0fc7 100644 Binary files a/www/help.png and b/www/help.png differ