View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3.examples;
18  
19  import org.apache.commons.jexl3.JexlBuilder;
20  import org.apache.commons.jexl3.JexlContext;
21  import org.apache.commons.jexl3.JexlEngine;
22  import org.apache.commons.jexl3.JexlFeatures;
23  import org.apache.commons.jexl3.JexlScript;
24  import org.apache.commons.jexl3.MapContext;
25  import org.apache.commons.jexl3.introspection.JexlPermissions;
26  import org.apache.commons.jexl3.introspection.JexlPermissions.ClassPermissions;
27  import org.junit.Assert;
28  import org.junit.Test;
29  
30  import java.net.URI;
31  import java.util.Arrays;
32  import java.util.List;
33  import java.util.stream.Collectors;
34  import java.util.stream.Stream;
35  
36  import static java.lang.Boolean.TRUE;
37  
38  /**
39   * A test around scripting streams.
40   */
41  public class StreamTest {
42      /** Our engine instance. */
43      private final JexlEngine jexl;
44  
45      public StreamTest() {
46          // Restricting features; no loops, no side effects
47          JexlFeatures features = new JexlFeatures()
48                  .loops(false)
49                  .sideEffectGlobal(false)
50                  .sideEffect(false);
51          // Restricted permissions to a safe set but with URI allowed
52          JexlPermissions permissions = new ClassPermissions(java.net.URI.class);
53          // Create the engine
54          jexl = new JexlBuilder().permissions(permissions).create();
55      }
56  
57      /**
58       * A MapContext that can operate on streams.
59       */
60      public static class StreamContext extends MapContext {
61          /**
62           * This allows using a JEXL lambda as a mapper.
63           * @param stream the stream
64           * @param mapper the lambda to use as mapper
65           * @return the mapped stream
66           */
67          public Stream<?> map(Stream<?> stream, final JexlScript mapper) {
68              return stream.map( x -> mapper.execute(this, x));
69          }
70  
71          /**
72           * This allows using a JEXL lambda as a filter.
73           * @param stream the stream
74           * @param filter the lambda to use as filter
75           * @return the filtered stream
76           */
77          public Stream<?> filter(Stream<?> stream, final JexlScript filter) {
78              return stream.filter(x -> x != null && TRUE.equals(filter.execute(this, x)));
79          }
80      }
81  
82      @Test
83      public void testURIStream() throws Exception {
84          // let's assume a collection of uris need to be processed and transformed to be simplified ;
85          // we want only http/https ones, only the host part and using an https scheme
86          List<URI> uris = Arrays.asList(
87                  URI.create("http://user@www.apache.org:8000?qry=true"),
88                  URI.create("https://commons.apache.org/releases/prepare.html"),
89                  URI.create("mailto:henrib@apache.org")
90          );
91          // Create the test control, the expected result of our script evaluation
92          List<?> control =  uris.stream()
93                  .map(uri -> uri.getScheme().startsWith("http")? "https://" + uri.getHost() : null)
94                  .filter(x -> x != null)
95                  .collect(Collectors.toList());
96          Assert.assertEquals(2, control.size());
97  
98          // Create scripts:
99          // uri is the name of the variable used as parameter; the beans are exposed as properties
100         // note that it is also used in the backquoted string
101         JexlScript mapper = jexl.createScript("uri.scheme =^ 'http'? `https://${uri.host}` : null", "uri");
102         // using the bang-bang / !! - JScript like -  is the way to coerce to boolean in the filter
103         JexlScript transform = jexl.createScript(
104                 "list.stream().map(mapper).filter(x -> !!x).collect(Collectors.toList())", "list");
105 
106         // Execute scripts:
107         JexlContext sctxt = new StreamContext();
108         // expose the static methods of Collectors; java.util.* is allowed by permissions
109         sctxt.set("Collectors", Collectors.class);
110         // expose the mapper script as a global variable in the context
111         sctxt.set("mapper", mapper);
112 
113         Object transformed = transform.execute(sctxt, uris);
114         Assert.assertTrue(transformed instanceof List<?>);
115         Assert.assertEquals(control, transformed);
116     }
117 }