[Major] Update website tutorial to not use queries
This commit is contained in:
parent
1c2d048c45
commit
f51d5ae81f
|
@ -276,21 +276,6 @@
|
||||||
<strolch.env>dev.eitchpc</strolch.env>
|
<strolch.env>dev.eitchpc</strolch.env>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
|
||||||
<id>m2e.eitchmac</id>
|
|
||||||
<activation>
|
|
||||||
<property>
|
|
||||||
<name>user.name</name>
|
|
||||||
<value>eitch</value>
|
|
||||||
</property>
|
|
||||||
<os>
|
|
||||||
<family>mac</family>
|
|
||||||
</os>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<strolch.env>dev.eitchmac</strolch.env>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -344,10 +329,6 @@
|
||||||
<root>/home/eitch/src/git/strolch-bookshop/runtime</root>
|
<root>/home/eitch/src/git/strolch-bookshop/runtime</root>
|
||||||
<environment>dev</environment>
|
<environment>dev</environment>
|
||||||
</env>
|
</env>
|
||||||
<env id="dev.eitchmac" default="true">
|
|
||||||
<root>/Users/eitch/src/git/strolch-bookshop/runtime</root>
|
|
||||||
<environment>dev</environment>
|
|
||||||
</env>
|
|
||||||
</StrolchBootstrap>
|
</StrolchBootstrap>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -408,8 +389,47 @@
|
||||||
<Role name="User">
|
<Role name="User">
|
||||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||||
</Privilege>
|
</Privilege>
|
||||||
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
|
|
||||||
<Allow>internal</Allow>
|
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
<Allow>li.strolch.bookshop.search.BookSearch</Allow>
|
||||||
|
</Privilege>
|
||||||
|
|
||||||
|
<Privilege name="GetResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="GetOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="GetActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
</Role>
|
</Role>
|
||||||
<Role name="UserPrivileges">
|
<Role name="UserPrivileges">
|
||||||
|
@ -424,9 +444,48 @@
|
||||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||||
<AllAllowed>true</AllAllowed>
|
<AllAllowed>true</AllAllowed>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
|
|
||||||
|
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
|
||||||
|
<Privilege name="GetResource" policy="ModelPrivilege">
|
||||||
<AllAllowed>true</AllAllowed>
|
<AllAllowed>true</AllAllowed>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
|
<Privilege name="GetOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="GetActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
|
||||||
<Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
|
<Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
|
||||||
<AllAllowed>true</AllAllowed>
|
<AllAllowed>true</AllAllowed>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
|
@ -441,12 +500,55 @@
|
||||||
<Allow>li.strolch.runtime.privilege.StrolchSystemActionWithResult</Allow>
|
<Allow>li.strolch.runtime.privilege.StrolchSystemActionWithResult</Allow>
|
||||||
<Allow>li.strolch.persistence.postgresql.PostgreSqlSchemaInitializer</Allow>
|
<Allow>li.strolch.persistence.postgresql.PostgreSqlSchemaInitializer</Allow>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
|
|
||||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||||
<AllAllowed>true</AllAllowed>
|
<AllAllowed>true</AllAllowed>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
|
|
||||||
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
|
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
|
||||||
<AllAllowed>true</AllAllowed>
|
<AllAllowed>true</AllAllowed>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
|
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
|
||||||
|
<Privilege name="GetResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="GetOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="GetActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="AddActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="UpdateActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveResource" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveOrder" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
<Privilege name="RemoveActivity" policy="ModelPrivilege">
|
||||||
|
<AllAllowed>true</AllAllowed>
|
||||||
|
</Privilege>
|
||||||
|
|
||||||
<Privilege name="PrivilegeAction" policy="DefaultPrivilege">
|
<Privilege name="PrivilegeAction" policy="DefaultPrivilege">
|
||||||
<Allow>Persist</Allow>
|
<Allow>Persist</Allow>
|
||||||
<Allow>PersistSessions</Allow>
|
<Allow>PersistSessions</Allow>
|
||||||
|
@ -657,6 +759,11 @@
|
||||||
unlimited JCE libraries for your JVM and when you restart the server, you don't need to log back in, if
|
unlimited JCE libraries for your JVM and when you restart the server, you don't need to log back in, if
|
||||||
your session is still alive.
|
your session is still alive.
|
||||||
</li>
|
</li>
|
||||||
|
<li>In <code>PrivilegeRoles.xml</code> there seems to be a lot of boilerplate. One thing about a highly
|
||||||
|
configurable system is that sometimes the configuration is bigger. In this case we have opted to have
|
||||||
|
the configuration shown and not use default values which you don't see, so that privilege acces is
|
||||||
|
clearly seen.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Your project is now ready to be imported into your favourite IDE. We have used both IntelliJ and Eclipse so
|
<p>Your project is now ready to be imported into your favourite IDE. We have used both IntelliJ and Eclipse so
|
||||||
|
@ -669,55 +776,59 @@
|
||||||
<pre class="pre-scrollable">
|
<pre class="pre-scrollable">
|
||||||
package li.strolch.bookshop.web;
|
package li.strolch.bookshop.web;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
import javax.servlet.ServletContextListener;
|
import javax.servlet.ServletContextListener;
|
||||||
import javax.servlet.annotation.WebListener;
|
import javax.servlet.annotation.WebListener;
|
||||||
|
import java.io.InputStream;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import li.strolch.agent.api.StrolchAgent;
|
import li.strolch.agent.api.StrolchAgent;
|
||||||
import li.strolch.agent.api.StrolchBootstrapper;
|
import li.strolch.agent.api.StrolchBootstrapper;
|
||||||
|
import li.strolch.utils.helper.StringHelper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@WebListener
|
@WebListener
|
||||||
public class StartupListener implements ServletContextListener {
|
public class StartupListener implements ServletContextListener {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(StartupListener.class);
|
private static final Logger logger = LoggerFactory.getLogger(StartupListener.class);
|
||||||
|
private static final String APP_NAME = "Bookshop";
|
||||||
|
|
||||||
private StrolchAgent agent;
|
private StrolchAgent agent;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contextInitialized(ServletContextEvent sce) {
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
|
|
||||||
logger.info("Starting Bookshop...");
|
logger.info("Starting " + APP_NAME + "...");
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
// we load the configuration by reading the boot strap file:
|
|
||||||
String boostrapFileName = "/WEB-INF/" + StrolchBootstrapper.FILE_BOOTSTRAP;
|
String boostrapFileName = "/WEB-INF/" + StrolchBootstrapper.FILE_BOOTSTRAP;
|
||||||
InputStream bootstrapFile = sce.getServletContext().getResourceAsStream(boostrapFileName);
|
InputStream bootstrapFile = sce.getServletContext().getResourceAsStream(boostrapFileName);
|
||||||
StrolchBootstrapper bootstrapper = new StrolchBootstrapper(StartupListener.class);
|
StrolchBootstrapper bootstrapper = new StrolchBootstrapper(StartupListener.class);
|
||||||
|
|
||||||
// now setup, initialize and start Strolch:
|
|
||||||
this.agent = bootstrapper.setupByBoostrapFile(StartupListener.class, bootstrapFile);
|
this.agent = bootstrapper.setupByBoostrapFile(StartupListener.class, bootstrapFile);
|
||||||
this.agent.initialize();
|
this.agent.initialize();
|
||||||
this.agent.start();
|
this.agent.start();
|
||||||
|
} catch (Throwable e) {
|
||||||
} catch (Exception e) {
|
logger.error("Failed to start " + APP_NAME + " due to: " + e.getMessage(), e);
|
||||||
logger.error("Failed to start Bookshop due to: " + e.getMessage(), e);
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Started Bookshop.");
|
long took = System.currentTimeMillis() - start;
|
||||||
|
logger.info("Started " + APP_NAME + " in " + (StringHelper.formatMillisecondsDuration(took)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contextDestroyed(ServletContextEvent sce) {
|
public void contextDestroyed(ServletContextEvent sce) {
|
||||||
if (this.agent != null) {
|
if (this.agent != null) {
|
||||||
|
logger.info("Destroying " + APP_NAME + "...");
|
||||||
|
try {
|
||||||
this.agent.stop();
|
this.agent.stop();
|
||||||
this.agent.destroy();
|
this.agent.destroy();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.error("Failed to stop " + APP_NAME + " due to: " + e.getMessage(), e);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
logger.info("Destroyed Bookshop.");
|
}
|
||||||
|
logger.info("Destroyed " + APP_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -725,7 +836,7 @@ public class StartupListener implements ServletContextListener {
|
||||||
<p>Now configure your IDE to start the web project, and then once it has started, you should see the following
|
<p>Now configure your IDE to start the web project, and then once it has started, you should see the following
|
||||||
in the logs:</p>
|
in the logs:</p>
|
||||||
<pre class="pre-scrollable">
|
<pre class="pre-scrollable">
|
||||||
Bookshop:dev All 8 Strolch Components started. Strolch is now ready to be used. Have fun =))
|
Bookshop:dev All 8 Strolch Components started. Took 44ms. Strolch is now ready to be used. Have fun =))
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>This log tells us the name of the app as defined in the StrolchConfiguration.xml file as well as which
|
<p>This log tells us the name of the app as defined in the StrolchConfiguration.xml file as well as which
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="google-site-verification" content="CPhbjooaiTdROm7Vs4E7kuHZvBfkeLUtonGgcVUbTL8"/>
|
<meta name="google-site-verification" content="CPhbjooaiTdROm7Vs4E7kuHZvBfkeLUtonGgcVUbTL8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
@ -119,10 +119,10 @@ public class BooksResource {
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h3>Query</h3>
|
<h3>Search</h3>
|
||||||
|
|
||||||
<p>The first service we'll add is to query the existing books. The API defines three parameters, with which the
|
<p>The first service we'll add is to query, or search for the existing books. The API defines three parameters,
|
||||||
result can be controlled. The method can be defined as follows:</p>
|
with which the result can be controlled. The method can be defined as follows:</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@GET
|
@GET
|
||||||
|
@ -144,44 +144,64 @@ public class BookShopConstants {
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>As this tutorial progesses, more and more constants will be added here. This class helps with two issues:
|
<p>As this tutorial progresses, more and more constants will be added here. This class helps with two issues:
|
||||||
Through the constants we can easily reason over where certain fields, and types are used and of course
|
Through the constants we can easily reason over where certain fields, and types are used and of course String
|
||||||
String literals in code are a rather bad thing.</p>
|
literals in code are a rather bad thing.</p>
|
||||||
|
|
||||||
<p>Queries in Strolch are their own objects, which allows us to implement privilege validation and thus we need
|
<p>In Strolch there are multiple way to access objects. The old way was using Queries, the new search API is
|
||||||
to create this class as well. Book entities are Resources, thus we will be creating a
|
much more fluent and easier to read and write. The search API, as well as the deprecated query API allows us
|
||||||
<code>ResourceQuery</code>. Since the query is for Resources of type Book, we will define this using a
|
to implement privilege validation and thus one should create corresponding classes for each type of search.
|
||||||
navigation. Thus the resulting query looks as follows:</p>
|
Book entities are Resources, thus we will be creating a <code>ResourceSearch</code>. The search is for
|
||||||
|
Resources of type Book thus the resulting search looks as follows:</p>
|
||||||
<pre>
|
<pre>
|
||||||
public class BooksQuery<U> extends ResourceQuery<U> {
|
public class BooksSearch<U> extends ResourceSearch<U> {
|
||||||
public BooksQuery() {
|
public BookSearch() {
|
||||||
super(new StrolchTypeNavigation(BookShopConstants.TYPE_BOOK));
|
types(TYPE_BOOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BookSearch stringQuery(String value) {
|
||||||
|
if (isEmpty(value))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
// split by spaces
|
||||||
|
value = value.trim();
|
||||||
|
String[] values = value.split(" ");
|
||||||
|
|
||||||
|
// add where clauses for id, name and description
|
||||||
|
where(id().containsIgnoreCase(values) //
|
||||||
|
.or(name().containsIgnoreCase(values)) //
|
||||||
|
.or(param(BAG_PARAMETERS, PARAM_DESCRIPTION).containsIgnoreCase(values)));
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<p>Note how we added a special method <code>stringQuery(String)</code> - this method defines where a search
|
||||||
|
string entered by the user will be used to match a book. In this case for <code>ID</code>, <code>name</code>
|
||||||
|
and the <code>description</code> parameter.</p>
|
||||||
|
|
||||||
<p>So that our users can call this query, we must give them this as a privilege. This is done by adding the full
|
<p>So that our users can call this query, we must give them this as a privilege. This is done by adding the full
|
||||||
class name to the <code>PrivilegeRoles.xml</code> file as follows:</p>
|
class name to the <code>PrivilegeRoles.xml</code> file as follows:</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
...
|
...
|
||||||
<Role name="User">
|
<Role name="User">
|
||||||
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
|
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||||
<Allow>internal</Allow>
|
<Allow>internal</Allow>
|
||||||
<Allow>li.strolch.bookshop.query.BooksQuery</Allow>
|
<Allow>li.strolch.bookshop.search.BookSearch</Allow>
|
||||||
</Privilege>
|
</Privilege>
|
||||||
</Role>
|
</Role>
|
||||||
...
|
...
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p><b>Note:</b> The <code>internal</code> allow value is a special privilege which is used internally when a
|
<p><b>Note:</b> The <code>internal</code> allow value is a special privilege which is used internally when a
|
||||||
service or something performs internal queries. This means that a service can perform a query for object to
|
service or something performs internal queries. This means that a service can perform a query
|
||||||
which the user might not have access, but without which the service could not be completed. We will use this
|
for object to which the user might not have access, but without which the service could not be
|
||||||
in a later stage.
|
completed. We will use this in a later stage. </p>
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>Now we all parts we need to implement the query method. The method will include opening a transaction,
|
<p>Now we have all parts we need to implement the query method. The method will include opening a transaction,
|
||||||
instantiating the query, executing the query, and returning the result:</p>
|
instantiating the search, executing the search, and returning the result:</p>
|
||||||
<pre class="pre-scrollable">
|
<pre class="pre-scrollable">
|
||||||
@Path("books")
|
@Path("books")
|
||||||
public class BooksResource {
|
public class BooksResource {
|
||||||
|
@ -198,46 +218,31 @@ public class BooksResource {
|
||||||
int limit = StringHelper.isNotEmpty(limitS) ? Integer.valueOf(limitS) : 0;
|
int limit = StringHelper.isNotEmpty(limitS) ? Integer.valueOf(limitS) : 0;
|
||||||
|
|
||||||
// open the TX with the certificate, using this class as context
|
// open the TX with the certificate, using this class as context
|
||||||
|
Paging<Resource> paging;
|
||||||
try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getClass())) {
|
try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getClass())) {
|
||||||
|
|
||||||
// prepare the query
|
// perform a book search
|
||||||
ResourceQuery<Resource> query = new BooksQuery<JsonObject>();
|
paging = new BookSearch() //
|
||||||
|
.stringQuery(queryS) //
|
||||||
// prepare selections
|
.search(tx) //
|
||||||
if (StringHelper.isEmpty(queryS)) {
|
.orderByName(false) //
|
||||||
query.withAny();
|
.toPaging(offset, limit);
|
||||||
} else {
|
|
||||||
OrSelection or = new OrSelection();
|
|
||||||
or.with(ParameterSelection.stringSelection(BookShopConstants.BAG_PARAMETERS,
|
|
||||||
BookShopConstants.PARAM_DESCRIPTION, queryS, StringMatchMode.ci()));
|
|
||||||
or.with(new NameSelection(queryS, StringMatchMode.ci()));
|
|
||||||
|
|
||||||
// add selections
|
|
||||||
query.with(or);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform the query
|
|
||||||
List<Resource> books = tx.doQuery(query);
|
|
||||||
|
|
||||||
// perform paging
|
|
||||||
Paging<Resource> paging = Paging.asPage(books, offset, limit);
|
|
||||||
List<Resource> page = paging.getPage();
|
|
||||||
|
|
||||||
// return result
|
|
||||||
ResourceVisitor<JsonObject> visitor = new StrolchRootElementToJsonVisitor().flat().asResourceVisitor();
|
ResourceVisitor<JsonObject> visitor = new StrolchRootElementToJsonVisitor().flat().asResourceVisitor();
|
||||||
return ResponseUtil.listToResponse(StrolchRestfulConstants.DATA, page, a -> a.accept(visitor));
|
return ResponseUtil.toResponse(paging, e -> e.accept(visitor));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p><b>Note:</b> We automatically transform the Resource objects to JSON using the <code>StrolchElementToJsonVisitor</code>.
|
<p><b>Note:</b> We automatically transform the Resource objects to JSON using the <code>StrolchElementToJsonVisitor</code>.
|
||||||
By calling the method <code>.flat()</code> we have a more compact JSON format. Paging is handled by a util
|
By calling the method <code>.flat()</code> we have a more compact JSON format. Paging is handled
|
||||||
class.</p>
|
by a util class.</p>
|
||||||
|
|
||||||
<p>As a rule we use the format where we return two fields: <code>msg</code> is a dash if all is ok, otherwise an
|
<p>The helper class <code>ResponseUtil</code> takes care of creating the JsonObject and the proper page. As a
|
||||||
|
rule we use the format where we return two fields: <code>msg</code> is a dash if all is ok, otherwise an
|
||||||
error message will be present. Data is always in the <code>data</code> field. This is just a personal taste,
|
error message will be present. Data is always in the <code>data</code> field. This is just a personal taste,
|
||||||
and can be changed to one't taste.</p>
|
and can be changed to one's own taste.</p>
|
||||||
|
|
||||||
<h3>Get</h3>
|
<h3>Get</h3>
|
||||||
|
|
||||||
|
@ -270,8 +275,8 @@ public Response get(@Context HttpServletRequest request, @PathParam("id") String
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>Note how we simply retrieve the book as a Resource from the TX. This is a good moment to familiarize yourself
|
<p>Note how we simply retrieve the book as a Resource from the TX. This is a good moment to familiarize yourself
|
||||||
with the API of the <code>StrolchTransaction</code>. There are methods to retrieve elements, and also
|
with the API of the <code>StrolchTransaction</code>. There are methods to retrieve elements, and also perform
|
||||||
perform queries. We will use more of these methods later.</p>
|
queries. We will use more of these methods later.</p>
|
||||||
|
|
||||||
<p>Further it can be noted that a simple retrieval isn't validated against the user's privileges, the user is
|
<p>Further it can be noted that a simple retrieval isn't validated against the user's privileges, the user is
|
||||||
authenticated, which is enough for the moment.</p>
|
authenticated, which is enough for the moment.</p>
|
||||||
|
@ -314,7 +319,7 @@ public Response create(@Context HttpServletRequest request, String data) {
|
||||||
|
|
||||||
The service is implemented as follows:
|
The service is implemented as follows:
|
||||||
<pre class="pre-scrollable">
|
<pre class="pre-scrollable">
|
||||||
public class CreateBookService extends AbstractService<JsonServiceArgument, JsonServiceResult> {
|
public class CreateBookService extends AbstractService<JsonServiceArgument, JsonServiceResult> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -556,21 +561,22 @@ public class RemoveBookService extends AbstractService<StringServiceArgument,
|
||||||
<h3>Notes:</h3>
|
<h3>Notes:</h3>
|
||||||
<p>One should now see a pattern emerge:</p>
|
<p>One should now see a pattern emerge:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The REST API delegates to the Services, or queries, with the exception of the retrieval of a single
|
<li>The REST API delegates to the Services, or Searches, with the exception of the retrieval of a single
|
||||||
object by id.
|
object by id.
|
||||||
</li>
|
</li>
|
||||||
<li>Services should do initial validation of the input. Not much validation was done here, but more could be
|
<li>Services should do initial validation of the input. Not much validation was done here, but more could be
|
||||||
done.
|
done.
|
||||||
</li>
|
</li>
|
||||||
<li>Commands are reusable objects to perform recurring work.</li>
|
<li>Commands are reusable objects to perform recurring work.</li>
|
||||||
<li>Queries and Services are privileged actions for which a user must have the privilege to perform the
|
<li>Searches and Services are privileged actions for which a user must have the privilege to perform the
|
||||||
action.
|
action.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>The book services are quite simple, but as more requirements arise, it should be easy to implement them in
|
<p>The book services are quite simple, but as more requirements arise, it should be easy to implement them in
|
||||||
the service layer. Thus should a service be required to be performed by an integration layer, then they can
|
the service layer. Thus should a service be required to be performed by an integration layer, then they can
|
||||||
simply call the services, since the input is defined and validation is done there.</p>
|
simply call the services, since the input is defined and validation is done there (i.e. NOT in the REST
|
||||||
|
API).</p>
|
||||||
|
|
||||||
<p>This concludes the CRUD of books.</p>
|
<p>This concludes the CRUD of books.</p>
|
||||||
|
|
||||||
|
@ -614,7 +620,7 @@ public class RemoveBookService extends AbstractService<StringServiceArgument,
|
||||||
s.parentNode.insertBefore(g, s);
|
s.parentNode.insertBefore(g, s);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<noscript><p><img src="http://piwik.eitchnet.ch/piwik.php?idsite=2" style="border:0;" alt=""/></p></noscript>
|
<noscript><p><img src="http://piwik.eitchnet.ch/piwik.php?idsite=2" style="border:0;" alt="" /></p></noscript>
|
||||||
<!-- End Piwik Code -->
|
<!-- End Piwik Code -->
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in New Issue