http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/core/src/test/java/org/gridgain/grid/kernal/processors/rest/handlers/cache/GridCacheCommandHandlerSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/gridgain/grid/kernal/processors/rest/handlers/cache/GridCacheCommandHandlerSelfTest.java b/modules/core/src/test/java/org/gridgain/grid/kernal/processors/rest/handlers/cache/GridCacheCommandHandlerSelfTest.java deleted file mode 100644 index 3074419..0000000 --- a/modules/core/src/test/java/org/gridgain/grid/kernal/processors/rest/handlers/cache/GridCacheCommandHandlerSelfTest.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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.gridgain.grid.kernal.processors.rest.handlers.cache; - -import org.apache.ignite.*; -import org.apache.ignite.cache.*; -import org.apache.ignite.configuration.*; -import org.apache.ignite.internal.*; -import org.apache.ignite.lang.*; -import org.apache.ignite.spi.discovery.tcp.*; -import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.*; -import org.gridgain.grid.kernal.processors.cache.*; -import org.gridgain.grid.kernal.processors.rest.*; -import org.gridgain.grid.kernal.processors.rest.handlers.*; -import org.gridgain.grid.kernal.processors.rest.request.*; -import org.apache.ignite.internal.util.future.*; -import org.apache.ignite.internal.util.typedef.*; -import org.gridgain.testframework.junits.common.*; - -import java.lang.reflect.*; -import java.util.*; - -/** - * Tests command handler directly. - */ -public class GridCacheCommandHandlerSelfTest extends GridCommonAbstractTest { - /** - * Constructor. - */ - public GridCacheCommandHandlerSelfTest() { - super(true); - } - - /** {@inheritDoc} */ - @Override protected IgniteConfiguration getConfiguration() throws Exception { - // Discovery config. - TcpDiscoverySpi disco = new TcpDiscoverySpi(); - - disco.setIpFinder(new TcpDiscoveryVmIpFinder(true)); - - // Cache config. - CacheConfiguration cacheCfg = defaultCacheConfiguration(); - - cacheCfg.setCacheMode(GridCacheMode.LOCAL); - cacheCfg.setQueryIndexEnabled(false); - - // Grid config. - IgniteConfiguration cfg = super.getConfiguration(); - - cfg.setLocalHost("localhost"); - cfg.setRestEnabled(true); - cfg.setDiscoverySpi(disco); - cfg.setCacheConfiguration(cacheCfg); // Add 'null' cache configuration. - - return cfg; - } - - /** - * Tests the cache failure during the execution of the CACHE_GET command. - * - * @throws Exception If failed. - */ - public void testCacheGetFailsSyncNotify() throws Exception { - GridRestCommandHandler hnd = new TestableGridCacheCommandHandler(((GridKernal)grid()).context(), "getAsync", - true); - - GridRestCacheRequest req = new GridRestCacheRequest(); - - req.command(GridRestCommand.CACHE_GET); - - req.key("k1"); - - try { - hnd.handleAsync(req).get(); - - fail("Expected exception not thrown."); - } - catch (IgniteCheckedException e) { - info("Got expected exception: " + e); - } - } - - /** - * Tests the cache failure during the execution of the CACHE_GET command. - * - * @throws Exception If failed. - */ - public void testCacheGetFailsAsyncNotify() throws Exception { - GridRestCommandHandler hnd = new TestableGridCacheCommandHandler(((GridKernal)grid()).context(), "getAsync", - false); - - GridRestCacheRequest req = new GridRestCacheRequest(); - - req.command(GridRestCommand.CACHE_GET); - - req.key("k1"); - - try { - hnd.handleAsync(req).get(); - - fail("Expected exception not thrown."); - } - catch (IgniteCheckedException e) { - info("Got expected exception: " + e); - } - } - - /** - * Test cache handler append/prepend commands. - * - * @throws Exception In case of any exception. - */ - @SuppressWarnings("NullableProblems") - public void testAppendPrepend() throws Exception { - assertEquals("as" + "df", testAppend("as", "df", true)); - assertEquals("df" + "as", testAppend("as", "df", false)); - - List<String> curList = new ArrayList<>(Arrays.asList("a", "b")); - List<String> newList = new ArrayList<>(Arrays.asList("b", "c")); - - assertEquals(Arrays.asList("a", "b", "b", "c"), testAppend(curList, newList, true)); - assertEquals(Arrays.asList("b", "c", "a", "b"), testAppend(curList, newList, false)); - - Set<String> curSet = new HashSet<>(Arrays.asList("a", "b")); - Set<String> newSet = new HashSet<>(Arrays.asList("b", "c")); - Set<String> resSet = new HashSet<>(Arrays.asList("a", "b", "c")); - - assertEquals(resSet, testAppend(curSet, newSet, true)); - assertEquals(resSet, testAppend(curSet, newSet, false)); - assertEquals(resSet, testAppend(newSet, curList, true)); - assertEquals(resSet, testAppend(newSet, curList, false)); - assertEquals(resSet, testAppend(curSet, newList, true)); - assertEquals(resSet, testAppend(curSet, newList, false)); - - Map<String, String> curMap = F.asMap("a", "1", "b", "2", "c", "3"); - Map<String, String> newMap = F.asMap("a", "#", "b", null, "c", "%", "d", "4"); - - assertEquals(F.asMap("a", "#", "c", "%", "d", "4"), testAppend(curMap, newMap, true)); - assertEquals(F.asMap("a", "1", "b", "2", "c", "3", "d", "4"), testAppend(curMap, newMap, false)); - - try { - testAppend("as", Arrays.asList("df"), true); - - fail("Expects failed with incompatible types message."); - } - catch (IgniteCheckedException e) { - info("Got expected exception: " + e); - - assertTrue(e.getMessage().startsWith("Incompatible types")); - } - } - - /** - * Test cache handler append/prepend commands with specified environment. - * - * @param curVal Current value in cache. - * @param newVal New value to append/prepend. - * @param append Append or prepend flag. - * @param <T> Cache value type. - * @return Resulting value in cache. - * @throws IgniteCheckedException In case of any grid exception. - */ - private <T> T testAppend(T curVal, T newVal, boolean append) throws IgniteCheckedException { - GridRestCommandHandler hnd = new GridCacheCommandHandler(((GridKernal)grid()).context()); - - String key = UUID.randomUUID().toString(); - - GridRestCacheRequest req = new GridRestCacheRequest(); - - req.command(append ? GridRestCommand.CACHE_APPEND : GridRestCommand.CACHE_PREPEND); - - req.key(key); - req.value(newVal); - - assertFalse("Expects failure due to no value in cache.", (Boolean)hnd.handleAsync(req).get().getResponse()); - - T res; - - try { - // Change cache state. - cache().putx(key, curVal); - - // Validate behavior for initialized cache (has current value). - assertTrue("Expects succeed.", (Boolean)hnd.handleAsync(req).get().getResponse()); - } - finally { - res = (T)cache().remove(key); - } - - return res; - } - - /** - * Test command handler. - */ - private static class TestableGridCacheCommandHandler extends GridCacheCommandHandler { - /** */ - private final String failMtd; - - /** */ - private final boolean sync; - - /** - * Constructor. - * - * @param ctx Context. - * @param failMtd Method to fail. - * @param sync Sync notification flag. - */ - TestableGridCacheCommandHandler(final GridKernalContext ctx, final String failMtd, final boolean sync) { - super(ctx); - - this.failMtd = failMtd; - this.sync = sync; - } - - /** - * @param cacheName Name of the cache. - * - * @return Instance of a GridCache proxy. - */ - @Override protected GridCacheProjectionEx<Object, Object> localCache(String cacheName) throws IgniteCheckedException { - final GridCacheProjectionEx<Object, Object> cache = super.localCache(cacheName); - - return (GridCacheProjectionEx<Object, Object>)Proxy.newProxyInstance(getClass().getClassLoader(), - new Class[] {GridCacheProjectionEx.class}, - new InvocationHandler() { - @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable { - if (failMtd.equals(mtd.getName())) { - IgniteFuture<Object> fut = new GridFinishedFuture<>(ctx, - new IgniteCheckedException("Operation failed")); - - fut.syncNotify(sync); - - return fut; - } - // Rewriting flagsOn result to keep intercepting invocations after it. - else if ("flagsOn".equals(mtd.getName())) - return proxy; - else if ("forSubjectId".equals(mtd.getName())) - return proxy; - - return mtd.invoke(cache, args); - } - }); - } - } -}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/core/src/test/java/org/gridgain/testsuites/bamboo/GridRestHandlerTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/gridgain/testsuites/bamboo/GridRestHandlerTestSuite.java b/modules/core/src/test/java/org/gridgain/testsuites/bamboo/GridRestHandlerTestSuite.java index b9c84b3..91123f5 100644 --- a/modules/core/src/test/java/org/gridgain/testsuites/bamboo/GridRestHandlerTestSuite.java +++ b/modules/core/src/test/java/org/gridgain/testsuites/bamboo/GridRestHandlerTestSuite.java @@ -18,7 +18,7 @@ package org.gridgain.testsuites.bamboo; import junit.framework.*; -import org.gridgain.grid.kernal.processors.rest.handlers.cache.*; +import org.apache.ignite.internal.processors.rest.handlers.cache.*; /** * REST support tests. http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java new file mode 100644 index 0000000..e36da80 --- /dev/null +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java @@ -0,0 +1,50 @@ +/* + * 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.apache.ignite.internal.processors.rest.protocols.http.jetty; + +import net.sf.json.*; +import net.sf.json.processors.*; + +import java.util.*; + +/** + * Jetty protocol json configuration. + */ +public class GridJettyJsonConfig extends JsonConfig { + /** + * Constructs default jetty json config. + */ + public GridJettyJsonConfig() { + registerJsonValueProcessor(UUID.class, new ToStringJsonProcessor()); + } + + /** + * Helper class for simple to-string conversion for the beans. + */ + private static class ToStringJsonProcessor implements JsonValueProcessor { + /** {@inheritDoc} */ + @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) { + throw new UnsupportedOperationException("Serialize array to string is not supported: " + val); + } + + /** {@inheritDoc} */ + @Override public Object processObjectValue(String key, Object val, JsonConfig jsonCfg) { + return val == null ? null : val.toString(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java new file mode 100644 index 0000000..c22bc2f --- /dev/null +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java @@ -0,0 +1,595 @@ +/* + * 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.apache.ignite.internal.processors.rest.protocols.http.jetty; + +import net.sf.json.*; +import org.apache.ignite.*; +import org.apache.ignite.lang.*; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.handler.*; +import org.apache.ignite.internal.processors.rest.*; +import org.apache.ignite.internal.processors.rest.request.*; +import org.apache.ignite.plugin.security.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.jetbrains.annotations.*; + +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.*; +import java.net.*; +import java.util.*; + +import static org.apache.ignite.internal.processors.rest.GridRestResponse.*; +import static org.apache.ignite.internal.processors.rest.GridRestCommand.*; + +/** + * Jetty REST handler. The following URL format is supported: + * {@code /gridgain?cmd=cmdName¶m1=abc¶m2=123} + */ +public class GridJettyRestHandler extends AbstractHandler { + /** Logger. */ + private final IgniteLogger log; + + /** Request handlers. */ + private GridRestProtocolHandler hnd; + + /** Default page. */ + private volatile String dfltPage; + + /** Favicon. */ + private volatile byte[] favicon; + + /** Authentication checker. */ + private final IgniteClosure<String, Boolean> authChecker; + + /** + * Creates new HTTP requests handler. + * + * @param hnd Handler. + * @param authChecker Authentication checking closure. + * @param log Logger. + */ + GridJettyRestHandler(GridRestProtocolHandler hnd, IgniteClosure<String, Boolean> authChecker, IgniteLogger log) { + assert hnd != null; + assert log != null; + + this.hnd = hnd; + this.log = log; + this.authChecker = authChecker; + + // Init default page and favicon. + try { + initDefaultPage(); + + if (log.isDebugEnabled()) + log.debug("Initialized default page."); + } + catch (IOException e) { + U.warn(log, "Failed to initialize default page: " + e.getMessage()); + } + + try { + initFavicon(); + + if (log.isDebugEnabled()) + log.debug(favicon != null ? "Initialized favicon, size: " + favicon.length : "Favicon is null."); + } + catch (IOException e) { + U.warn(log, "Failed to initialize favicon: " + e.getMessage()); + } + } + + /** + * @throws IOException If failed. + */ + private void initDefaultPage() throws IOException { + assert dfltPage == null; + + InputStream in = getClass().getResourceAsStream("rest.html"); + + if (in != null) { + LineNumberReader rdr = new LineNumberReader(new InputStreamReader(in)); + + try { + StringBuilder buf = new StringBuilder(2048); + + for (String line = rdr.readLine(); line != null; line = rdr.readLine()) { + buf.append(line); + + if (!line.endsWith(" ")) + buf.append(" "); + } + + dfltPage = buf.toString(); + } + finally { + U.closeQuiet(rdr); + } + } + } + + /** + * @throws IOException If failed. + */ + private void initFavicon() throws IOException { + assert favicon == null; + + InputStream in = getClass().getResourceAsStream("favicon.ico"); + + if (in != null) { + BufferedInputStream bis = new BufferedInputStream(in); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + try { + byte[] buf = new byte[2048]; + + while (true) { + int n = bis.read(buf); + + if (n == -1) + break; + + bos.write(buf, 0, n); + } + + favicon = bos.toByteArray(); + } + finally { + U.closeQuiet(bis); + } + } + } + + /** {@inheritDoc} */ + @Override public void handle(String target, Request req, HttpServletRequest srvReq, HttpServletResponse res) + throws IOException, ServletException { + if (log.isDebugEnabled()) + log.debug("Handling request [target=" + target + ", req=" + req + ", srvReq=" + srvReq + ']'); + + if (target.startsWith("/gridgain")) { + processRequest(target, srvReq, res); + + req.setHandled(true); + } + else if (target.startsWith("/favicon.ico")) { + if (favicon == null) { + res.setStatus(HttpServletResponse.SC_NOT_FOUND); + + req.setHandled(true); + + return; + } + + res.setStatus(HttpServletResponse.SC_OK); + + res.setContentType("image/x-icon"); + + res.getOutputStream().write(favicon); + res.getOutputStream().flush(); + + req.setHandled(true); + } + else { + if (dfltPage == null) { + res.setStatus(HttpServletResponse.SC_NOT_FOUND); + + req.setHandled(true); + + return; + } + + res.setStatus(HttpServletResponse.SC_OK); + + res.setContentType("text/html"); + + res.getWriter().write(dfltPage); + res.getWriter().flush(); + + req.setHandled(true); + } + } + + /** + * Process HTTP request. + * + * @param act Action. + * @param req Http request. + * @param res Http response. + */ + private void processRequest(String act, HttpServletRequest req, HttpServletResponse res) { + res.setContentType("application/json"); + res.setCharacterEncoding("UTF-8"); + + GridRestCommand cmd = command(req); + + if (cmd == null) { + res.setStatus(HttpServletResponse.SC_BAD_REQUEST); + + return; + } + + if (!authChecker.apply(req.getHeader("X-Signature"))) { + res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + return; + } + + GridRestResponse cmdRes; + + Map<String, Object> params = parameters(req); + + try { + GridRestRequest cmdReq = createRequest(cmd, params, req); + + if (log.isDebugEnabled()) + log.debug("Initialized command request: " + cmdReq); + + cmdRes = hnd.handle(cmdReq); + + if (cmdRes == null) + throw new IllegalStateException("Received null result from handler: " + hnd); + + byte[] sesTok = cmdRes.sessionTokenBytes(); + + if (sesTok != null) + cmdRes.setSessionToken(U.byteArray2HexString(sesTok)); + + res.setStatus(HttpServletResponse.SC_OK); + } + catch (Throwable e) { + res.setStatus(HttpServletResponse.SC_OK); + + U.error(log, "Failed to process HTTP request [action=" + act + ", req=" + req + ']', e); + + cmdRes = new GridRestResponse(STATUS_FAILED, e.getMessage()); + } + + JsonConfig cfg = new GridJettyJsonConfig(); + JSON json; + + try { + json = JSONSerializer.toJSON(cmdRes, cfg); + } + catch (JSONException e) { + U.error(log, "Failed to convert response to JSON: " + cmdRes, e); + + json = JSONSerializer.toJSON(new GridRestResponse(STATUS_FAILED, e.getMessage()), cfg); + } + + try { + if (log.isDebugEnabled()) + log.debug("Parsed command response into JSON object: " + json.toString(2)); + + res.getWriter().write(json.toString()); + + if (log.isDebugEnabled()) + log.debug("Processed HTTP request [action=" + act + ", jsonRes=" + cmdRes + ", req=" + req + ']'); + } + catch (IOException e) { + U.error(log, "Failed to send HTTP response: " + json.toString(2), e); + } + } + + /** + * Creates REST request. + * + * @param cmd Command. + * @param params Parameters. + * @return REST request. + * @throws IgniteCheckedException If creation failed. + */ + @Nullable private GridRestRequest createRequest(GridRestCommand cmd, Map<String, Object> params, + ServletRequest req) throws IgniteCheckedException { + GridRestRequest restReq; + + switch (cmd) { + case CACHE_GET: + case CACHE_GET_ALL: + case CACHE_PUT: + case CACHE_PUT_ALL: + case CACHE_REMOVE: + case CACHE_REMOVE_ALL: + case CACHE_ADD: + case CACHE_CAS: + case CACHE_METRICS: + case CACHE_REPLACE: + case CACHE_DECREMENT: + case CACHE_INCREMENT: + case CACHE_APPEND: + case CACHE_PREPEND: { + GridRestCacheRequest restReq0 = new GridRestCacheRequest(); + + restReq0.cacheName((String) params.get("cacheName")); + restReq0.key(params.get("key")); + restReq0.value(params.get("val")); + restReq0.value2(params.get("val2")); + + Object val1 = params.get("val1"); + + if (val1 != null) + restReq0.value(val1); + + restReq0.cacheFlags(intValue("cacheFlags", params, 0)); + restReq0.ttl(longValue("exp", params, null)); + restReq0.initial(longValue("init", params, null)); + restReq0.delta(longValue("delta", params, null)); + + if (cmd == CACHE_GET_ALL || cmd == CACHE_PUT_ALL || cmd == CACHE_REMOVE_ALL) { + List<Object> keys = values("k", params); + List<Object> vals = values("v", params); + + if (keys.size() < vals.size()) + throw new IgniteCheckedException("Number of keys must be greater or equals to number of values."); + + Map<Object, Object> map = U.newHashMap(keys.size()); + + Iterator<Object> keyIt = keys.iterator(); + Iterator<Object> valIt = vals.iterator(); + + while (keyIt.hasNext()) + map.put(keyIt.next(), valIt.hasNext() ? valIt.next() : null); + + restReq0.values(map); + } + + restReq = restReq0; + + break; + } + + case TOPOLOGY: + case NODE: { + GridRestTopologyRequest restReq0 = new GridRestTopologyRequest(); + + restReq0.includeMetrics(Boolean.parseBoolean((String) params.get("mtr"))); + restReq0.includeAttributes(Boolean.parseBoolean((String) params.get("attr"))); + + restReq0.nodeIp((String) params.get("ip")); + + restReq0.nodeId(uuidValue("id", params)); + + restReq = restReq0; + + break; + } + + case EXE: + case RESULT: + case NOOP: { + GridRestTaskRequest restReq0 = new GridRestTaskRequest(); + + restReq0.taskId((String) params.get("id")); + restReq0.taskName((String) params.get("name")); + + restReq0.params(values("p", params)); + + restReq0.async(Boolean.parseBoolean((String) params.get("async"))); + + restReq0.timeout(longValue("timeout", params, 0L)); + + restReq = restReq0; + + break; + } + + case LOG: { + GridRestLogRequest restReq0 = new GridRestLogRequest(); + + restReq0.path((String) params.get("path")); + + restReq0.from(intValue("from", params, -1)); + restReq0.to(intValue("to", params, -1)); + + restReq = restReq0; + + break; + } + + case VERSION: { + restReq = new GridRestRequest(); + + break; + } + + default: + throw new IgniteCheckedException("Invalid command: " + cmd); + } + + restReq.address(new InetSocketAddress(req.getRemoteAddr(), req.getRemotePort())); + + restReq.command(cmd); + + if (params.containsKey("gridgain.login") || params.containsKey("gridgain.password")) { + GridSecurityCredentials cred = new GridSecurityCredentials( + (String)params.get("gridgain.login"), (String)params.get("gridgain.password")); + + restReq.credentials(cred); + } + + String clientId = (String) params.get("clientId"); + + try { + if (clientId != null) + restReq.clientId(UUID.fromString(clientId)); + } + catch (Exception ignored) { + // Ignore invalid client id. Rest handler will process this logic. + } + + String destId = (String) params.get("destId"); + + try { + if (destId != null) + restReq.destinationId(UUID.fromString(destId)); + } + catch (IllegalArgumentException ignored) { + // Don't fail - try to execute locally. + } + + String sesTokStr = (String) params.get("sessionToken"); + + try { + if (sesTokStr != null) + restReq.sessionToken(U.hexString2ByteArray(sesTokStr)); + } + catch (IllegalArgumentException ignored) { + // Ignore invalid session token. + } + + return restReq; + } + + /** + * Retrieves long value from parameters map. + * + * @param key Key. + * @param params Parameters map. + * @param dfltVal Default value. + * @return Long value from parameters map or {@code dfltVal} if null + * or not exists. + * @throws IgniteCheckedException If parsing failed. + */ + @Nullable private static Long longValue(String key, Map<String, Object> params, Long dfltVal) throws IgniteCheckedException { + assert key != null; + + String val = (String) params.get(key); + + try { + return val == null ? dfltVal : Long.valueOf(val); + } + catch (NumberFormatException ignore) { + throw new IgniteCheckedException("Failed to parse parameter of Long type [" + key + "=" + val + "]"); + } + } + + /** + * Retrieves int value from parameters map. + * + * @param key Key. + * @param params Parameters map. + * @param dfltVal Default value. + * @return Integer value from parameters map or {@code dfltVal} if null + * or not exists. + * @throws IgniteCheckedException If parsing failed. + */ + @Nullable private static Integer intValue(String key, Map<String, Object> params, Integer dfltVal) throws IgniteCheckedException { + assert key != null; + + String val = (String) params.get(key); + + try { + return val == null ? dfltVal : Integer.valueOf(val); + } + catch (NumberFormatException ignore) { + throw new IgniteCheckedException("Failed to parse parameter of Integer type [" + key + "=" + val + "]"); + } + } + + /** + * Retrieves UUID value from parameters map. + * + * @param key Key. + * @param params Parameters map. + * @return UUID value from parameters map or {@code null} if null + * or not exists. + * @throws IgniteCheckedException If parsing failed. + */ + @Nullable private static UUID uuidValue(String key, Map<String, Object> params) throws IgniteCheckedException { + assert key != null; + + String val = (String) params.get(key); + + try { + return val == null ? null : UUID.fromString(val); + } + catch (NumberFormatException ignore) { + throw new IgniteCheckedException("Failed to parse parameter of UUID type [" + key + "=" + val + "]"); + } + } + + /** + * Gets values referenced by sequential keys, e.g. {@code key1...keyN}. + * + * @param keyPrefix Key prefix, e.g. {@code key} for {@code key1...keyN}. + * @param params Parameters map. + * @return Values. + */ + @Nullable protected List<Object> values(String keyPrefix, Map<String, Object> params) { + assert keyPrefix != null; + + List<Object> vals = new LinkedList<>(); + + for (int i = 1; ; i++) { + String key = keyPrefix + i; + + if (params.containsKey(key)) + vals.add(params.get(key)); + else + break; + } + + return vals; + } + + /** + * @param req Request. + * @return Command. + */ + @Nullable GridRestCommand command(ServletRequest req) { + String cmd = req.getParameter("cmd"); + + return cmd == null ? null : GridRestCommand.fromKey(cmd.toLowerCase()); + } + + /** + * Parses HTTP parameters in an appropriate format and return back map of + * values to predefined list of names. + * + * @param req Request. + * @return Map of parsed parameters. + */ + @SuppressWarnings({"unchecked"}) + private Map<String, Object> parameters(ServletRequest req) { + Map<String, String[]> params = req.getParameterMap(); + + if (F.isEmpty(params)) + return Collections.emptyMap(); + + Map<String, Object> map = U.newHashMap(params.size()); + + for (Map.Entry<String, String[]> entry : params.entrySet()) + map.put(entry.getKey(), parameter(entry.getValue())); + + return map; + } + + /** + * @param obj Parameter object. + * @return Parameter value. + */ + @Nullable private String parameter(Object obj) { + if (obj instanceof String) + return (String)obj; + else if (obj instanceof String[] && ((String[])obj).length > 0) + return ((String[])obj)[0]; + + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java new file mode 100644 index 0000000..d345e2a --- /dev/null +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java @@ -0,0 +1,409 @@ +/* + * 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.apache.ignite.internal.processors.rest.protocols.http.jetty; + +import org.apache.ignite.*; +import org.apache.ignite.internal.*; +import org.apache.ignite.spi.*; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.util.*; +import org.eclipse.jetty.util.log.*; +import org.eclipse.jetty.util.thread.*; +import org.eclipse.jetty.xml.*; +import org.apache.ignite.internal.processors.rest.*; +import org.apache.ignite.internal.processors.rest.protocols.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.jetbrains.annotations.*; +import org.xml.sax.*; + +import java.io.*; +import java.net.*; +import java.util.*; + +import static org.apache.ignite.IgniteSystemProperties.*; +import static org.apache.ignite.spi.IgnitePortProtocol.*; + +/** + * Jetty REST protocol implementation. + */ +public class GridJettyRestProtocol extends GridRestProtocolAdapter { + /** + * + */ + static { + if (!IgniteSystemProperties.getBoolean(GG_JETTY_LOG_NO_OVERRIDE)) { + Properties p = new Properties(); + + p.setProperty("org.eclipse.jetty.LEVEL", "WARN"); + p.setProperty("org.eclipse.jetty.util.log.LEVEL", "OFF"); + p.setProperty("org.eclipse.jetty.util.component.LEVEL", "OFF"); + + StdErrLog.setProperties(p); + + try { + Class<?> logCls = Class.forName("org.apache.log4j.Logger"); + + String ctgrJetty = "org.eclipse.jetty"; // WARN for this category. + String ctgrJettyUtil = "org.eclipse.jetty.util.log"; // ERROR for this... + String ctgrJettyUtilComp = "org.eclipse.jetty.util.component"; // ...and this. + + Object logJetty = logCls.getMethod("getLogger", String.class).invoke(logCls, ctgrJetty); + Object logJettyUtil = logCls.getMethod("getLogger", String.class).invoke(logCls, ctgrJettyUtil); + Object logJettyUtilComp = logCls.getMethod("getLogger", String.class).invoke(logCls, ctgrJettyUtilComp); + + Class<?> lvlCls = Class.forName("org.apache.log4j.Level"); + + Object warnLvl = lvlCls.getField("WARN").get(null); + Object errLvl = lvlCls.getField("ERROR").get(null); + + logJetty.getClass().getMethod("setLevel", lvlCls).invoke(logJetty, warnLvl); + logJettyUtil.getClass().getMethod("setLevel", lvlCls).invoke(logJetty, errLvl); + logJettyUtilComp.getClass().getMethod("setLevel", lvlCls).invoke(logJetty, errLvl); + } + catch (Exception ignored) { + // No-op. + } + } + } + + /** Jetty handler. */ + private GridJettyRestHandler jettyHnd; + + /** HTTP server. */ + private Server httpSrv; + + /** + * @param ctx Context. + */ + public GridJettyRestProtocol(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public String name() { + return "Jetty REST"; + } + + /** {@inheritDoc} */ + @SuppressWarnings("BusyWait") + @Override public void start(GridRestProtocolHandler hnd) throws IgniteCheckedException { + assert ctx.config().getClientConnectionConfiguration() != null; + + InetAddress locHost; + + try { + locHost = U.resolveLocalHost(ctx.config().getLocalHost()); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to resolve local host to bind address: " + ctx.config().getLocalHost(), e); + } + + System.setProperty(GG_JETTY_HOST, locHost.getHostAddress()); + + jettyHnd = new GridJettyRestHandler(hnd, new C1<String, Boolean>() { + @Override public Boolean apply(String tok) { + return F.isEmpty(secretKey) || authenticate(tok); + } + }, log); + + String jettyPath = config().getRestJettyPath(); + + final URL cfgUrl; + + if (jettyPath == null) { + cfgUrl = null; + + if (log.isDebugEnabled()) + log.debug("Jetty configuration file is not provided, using defaults."); + } + else { + cfgUrl = U.resolveGridGainUrl(jettyPath); + + if (cfgUrl == null) + throw new IgniteSpiException("Invalid Jetty configuration file: " + jettyPath); + else if (log.isDebugEnabled()) + log.debug("Jetty configuration file: " + cfgUrl); + } + + loadJettyConfiguration(cfgUrl); + + AbstractNetworkConnector connector = getJettyConnector(); + + try { + host = InetAddress.getByName(connector.getHost()); + } + catch (UnknownHostException e) { + throw new IgniteCheckedException("Failed to resolve Jetty host address: " + connector.getHost(), e); + } + + int initPort = connector.getPort(); + + int lastPort = initPort + config().getRestPortRange() - 1; + + for (port = initPort; port <= lastPort; port++) { + connector.setPort(port); + + if (startJetty()) { + if (log.isInfoEnabled()) + log.info(startInfo()); + + return; + } + } + + U.warn(log, "Failed to start Jetty REST server (possibly all ports in range are in use) " + + "[firstPort=" + initPort + ", lastPort=" + lastPort + ']'); + } + + /** + * Checks {@link org.apache.ignite.IgniteSystemProperties#GG_JETTY_PORT} system property + * and overrides default connector port if it present. + * Then initializes {@code port} with the found value. + * + * @param con Jetty connector. + */ + private void override(AbstractNetworkConnector con) { + String host = System.getProperty(GG_JETTY_HOST); + + if (!F.isEmpty(host)) + con.setHost(host); + + int currPort = con.getPort(); + + Integer overridePort = Integer.getInteger(GG_JETTY_PORT); + + if (overridePort != null && overridePort != 0) + currPort = overridePort; + + con.setPort(currPort); + port = currPort; + } + + /** + * @throws IgniteCheckedException If failed. + * @return {@code True} if Jetty started. + */ + @SuppressWarnings("IfMayBeConditional") + private boolean startJetty() throws IgniteCheckedException { + try { + httpSrv.start(); + + if (httpSrv.isStarted()) { + for (Connector con : httpSrv.getConnectors()) { + int connPort = ((NetworkConnector)con).getPort(); + + if (connPort > 0) + ctx.ports().registerPort(connPort, TCP, getClass()); + } + + return true; + } + + return false; + } + catch (SocketException ignore) { + if (log.isDebugEnabled()) + log.debug("Failed to bind HTTP server to configured port."); + + stopJetty(); + + return false; + } + catch (MultiException e) { + if (log.isDebugEnabled()) + log.debug("Caught multi exception: " + e); + + for (Object obj : e.getThrowables()) + if (!(obj instanceof SocketException)) + throw new IgniteCheckedException("Failed to start Jetty HTTP server.", e); + + if (log.isDebugEnabled()) + log.debug("Failed to bind HTTP server to configured port."); + + stopJetty(); + + return false; + } + catch (Exception e) { + throw new IgniteCheckedException("Failed to start Jetty HTTP server.", e); + } + } + + /** + * Loads jetty configuration from the given URL. + * + * @param cfgUrl URL to load configuration from. + * @throws IgniteCheckedException if load failed. + */ + private void loadJettyConfiguration(@Nullable URL cfgUrl) throws IgniteCheckedException { + if (cfgUrl == null) { + HttpConfiguration httpCfg = new HttpConfiguration(); + + httpCfg.setSecureScheme("https"); + httpCfg.setSecurePort(8443); + httpCfg.setSendServerVersion(true); + httpCfg.setSendDateHeader(true); + + String srvPortStr = System.getProperty(GG_JETTY_PORT, "8080"); + + int srvPort; + + try { + srvPort = Integer.valueOf(srvPortStr); + } + catch (NumberFormatException ignore) { + throw new IgniteCheckedException("Failed to start Jetty server because GRIDGAIN_JETTY_PORT system property " + + "cannot be cast to integer: " + srvPortStr); + } + + httpSrv = new Server(new QueuedThreadPool(20, 200)); + + ServerConnector srvConn = new ServerConnector(httpSrv, new HttpConnectionFactory(httpCfg)); + + srvConn.setHost(System.getProperty(GG_JETTY_HOST, "localhost")); + srvConn.setPort(srvPort); + srvConn.setIdleTimeout(30000L); + srvConn.setReuseAddress(true); + + httpSrv.addConnector(srvConn); + + httpSrv.setStopAtShutdown(false); + } + else { + XmlConfiguration cfg; + + try { + cfg = new XmlConfiguration(cfgUrl); + } + catch (FileNotFoundException e) { + throw new IgniteSpiException("Failed to find configuration file: " + cfgUrl, e); + } + catch (SAXException e) { + throw new IgniteSpiException("Failed to parse configuration file: " + cfgUrl, e); + } + catch (IOException e) { + throw new IgniteSpiException("Failed to load configuration file: " + cfgUrl, e); + } + catch (Exception e) { + throw new IgniteSpiException("Failed to start HTTP server with configuration file: " + cfgUrl, e); + } + + try { + httpSrv = (Server)cfg.configure(); + } + catch (Exception e) { + throw new IgniteCheckedException("Failed to start Jetty HTTP server.", e); + } + } + + assert httpSrv != null; + + httpSrv.setHandler(jettyHnd); + + override(getJettyConnector()); + } + + /** + * Checks that the only connector configured for the current jetty instance + * and returns it. + * + * @return Connector instance. + * @throws IgniteCheckedException If no or more than one connectors found. + */ + private AbstractNetworkConnector getJettyConnector() throws IgniteCheckedException { + if (httpSrv.getConnectors().length == 1) { + Connector connector = httpSrv.getConnectors()[0]; + + if (!(connector instanceof AbstractNetworkConnector)) + throw new IgniteCheckedException("Error in jetty configuration. Jetty connector should extend " + + "AbstractNetworkConnector class." ); + + return (AbstractNetworkConnector)connector; + } + else + throw new IgniteCheckedException("Error in jetty configuration [connectorsFound=" + + httpSrv.getConnectors().length + "connectorsExpected=1]"); + } + + /** + * Stops Jetty. + */ + private void stopJetty() { + // Jetty does not really stop the server if port is busy. + try { + if (httpSrv != null) { + // If server was successfully started, deregister ports. + if (httpSrv.isStarted()) + ctx.ports().deregisterPorts(getClass()); + + // Record current interrupted status of calling thread. + boolean interrupted = Thread.interrupted(); + + try { + httpSrv.stop(); + } + finally { + // Reset interrupted flag on calling thread. + if (interrupted) + Thread.currentThread().interrupt(); + } + } + } + catch (InterruptedException ignored) { + if (log.isDebugEnabled()) + log.debug("Thread has been interrupted."); + + Thread.currentThread().interrupt(); + } + catch (Exception e) { + U.error(log, "Failed to stop Jetty HTTP server.", e); + } + } + + /** {@inheritDoc} */ + @Override public void stop() { + stopJetty(); + + httpSrv = null; + jettyHnd = null; + + if (log.isInfoEnabled()) + log.info(stopInfo()); + } + + /** {@inheritDoc} */ + @Override protected String getAddressPropertyName() { + return GridNodeAttributes.ATTR_REST_JETTY_ADDRS; + } + + /** {@inheritDoc} */ + @Override protected String getHostNamePropertyName() { + return GridNodeAttributes.ATTR_REST_JETTY_HOST_NAMES; + } + + /** {@inheritDoc} */ + @Override protected String getPortPropertyName() { + return GridNodeAttributes.ATTR_REST_JETTY_PORT; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(GridJettyRestProtocol.class, this); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/favicon.ico ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/favicon.ico b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/favicon.ico new file mode 100644 index 0000000..6f6ed44 Binary files /dev/null and b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/favicon.ico differ http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/package.html ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/package.html b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/package.html new file mode 100644 index 0000000..e8d499d --- /dev/null +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/package.html @@ -0,0 +1,23 @@ +<!-- + 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. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<body> + <!-- Package description. --> + REST HTTP Jetty protocol handler. +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html new file mode 100644 index 0000000..06cb905 --- /dev/null +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html @@ -0,0 +1,96 @@ +<!-- + 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. + --> + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> + <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/> + <meta http-equiv="Expires" content="-1"> + <meta http-equiv="Pragma" content="no-cache"> + <meta http-equiv="Cache-Control" content="no-cache"> + <meta http-equiv="Content-language" content="en-US"> + <meta http-equiv="Content-Type" content="text/html; CHARSET=UTF-8"> + <meta http-equiv="Author" content="GridGain Systems, Inc., http://www.gridgain.com/"> + <title>GridGain - In-Memory Data Fabric</title> + <style type="text/css"> + body { + font-family: helvetica, verdana, arial, sans-serif; + font-size: 14px; + line-height: 1.5em; + color: #333; + margin: 10px; + } + a, a:visited, a:active, a:hover { + color: #333; + text-decoration: none; + border-bottom: 1px dotted; + } + + a.img_link, a.img_link:visited, a.img_link:active, a.img_link:hover { + text-decoration: none; + border-bottom: 0; + } + + img { + border: none; + } + </style> + <link rel="icon" type="image/x-icon" href="/favicon.ico"/> +</head> +<body> + <br> + <br> + <center> + <a class="img_link" href="http://www.gridgain.com" title="GridGain Systems"> + <img src="http://gridgain.com/wp-content/uploads/2014/09/gridgain-red-logo-standard.png" alt="GridGain - In-Memory Data Fabric"> + </a> + <p> + <div style="width: 650px; text-align: justify; padding-top: 20px"> + <h2>REST API</h2> + GridGain REST API supports + external connectivity to GridGain via REST over HTTP. It comes in handy whenever GridGain Java API is not + available directly, but it is still needed to execute GridGain tasks or retrieve cached data. For example, + you can conveniently use GridGain REST API over HTTP from other non-JVM languages, such as Ruby, PHP or Python, + or any other language, whenever local instance of GridGain is not available. + <p> + Note that PHP REST example is included with GridGain distribution. + <p> + All REST HTTP commands have the following format: <code>http://1.2.3.4:8080/gridgain?cmd=CMD&...</code>, where + <code>'cmd'</code> is the name of the command followed by other command parameters. Every command may have + different parameters, some of which may be mandatory and some optional. The commands parameters may be + passed either via HTTP GET or POST, whichever one is preferred. + </div> + <br> + <br> + <a target=facebook class='img_link' href='http://www.facebook.com/profile.php?id=6458239494'> + <img border=0 align=absmiddle src='http://www.gridgain.com/images/facebook_small.png'> + </a> + + <a class='img_link' target=meetup href='http://www.meetup.com/GridGain-Bay-Area-Meetup-Group/'> + <img border=0 align=absmiddle src='http://www.gridgain.com/images/meetup_logo_small.png' alt='Join GridGain Meetup'/> + </a> + + <a class='img_link' target=twitter href='http://www.twitter.com/gridgain'> + <img border=0 align=absmiddle src='http://www.gridgain.com/images/twitter.gif' alt='Follow GridGain on Twitter'/> + </a> + + <a class='img_link' target=vimeo href='http://www.vimeo.com/gridgain'> + <img border=0 align=absmiddle src='http://www.gridgain.com/images/vimeo.png' alt='Follow GridGain on Vimeo'/> + </a> + </center> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java b/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java deleted file mode 100644 index e03e69e..0000000 --- a/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.gridgain.grid.kernal.processors.rest.protocols.http.jetty; - -import net.sf.json.*; -import net.sf.json.processors.*; - -import java.util.*; - -/** - * Jetty protocol json configuration. - */ -public class GridJettyJsonConfig extends JsonConfig { - /** - * Constructs default jetty json config. - */ - public GridJettyJsonConfig() { - registerJsonValueProcessor(UUID.class, new ToStringJsonProcessor()); - } - - /** - * Helper class for simple to-string conversion for the beans. - */ - private static class ToStringJsonProcessor implements JsonValueProcessor { - /** {@inheritDoc} */ - @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) { - throw new UnsupportedOperationException("Serialize array to string is not supported: " + val); - } - - /** {@inheritDoc} */ - @Override public Object processObjectValue(String key, Object val, JsonConfig jsonCfg) { - return val == null ? null : val.toString(); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b0e45a2/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java deleted file mode 100644 index 5666a47..0000000 --- a/modules/rest-http/src/main/java/org/gridgain/grid/kernal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * 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.gridgain.grid.kernal.processors.rest.protocols.http.jetty; - -import net.sf.json.*; -import org.apache.ignite.*; -import org.apache.ignite.lang.*; -import org.eclipse.jetty.server.*; -import org.eclipse.jetty.server.handler.*; -import org.gridgain.grid.kernal.processors.rest.*; -import org.gridgain.grid.kernal.processors.rest.request.*; -import org.apache.ignite.plugin.security.*; -import org.apache.ignite.internal.util.typedef.*; -import org.apache.ignite.internal.util.typedef.internal.*; -import org.jetbrains.annotations.*; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.net.*; -import java.util.*; - -import static org.gridgain.grid.kernal.processors.rest.GridRestResponse.*; -import static org.gridgain.grid.kernal.processors.rest.GridRestCommand.*; - -/** - * Jetty REST handler. The following URL format is supported: - * {@code /gridgain?cmd=cmdName¶m1=abc¶m2=123} - */ -public class GridJettyRestHandler extends AbstractHandler { - /** Logger. */ - private final IgniteLogger log; - - /** Request handlers. */ - private GridRestProtocolHandler hnd; - - /** Default page. */ - private volatile String dfltPage; - - /** Favicon. */ - private volatile byte[] favicon; - - /** Authentication checker. */ - private final IgniteClosure<String, Boolean> authChecker; - - /** - * Creates new HTTP requests handler. - * - * @param hnd Handler. - * @param authChecker Authentication checking closure. - * @param log Logger. - */ - GridJettyRestHandler(GridRestProtocolHandler hnd, IgniteClosure<String, Boolean> authChecker, IgniteLogger log) { - assert hnd != null; - assert log != null; - - this.hnd = hnd; - this.log = log; - this.authChecker = authChecker; - - // Init default page and favicon. - try { - initDefaultPage(); - - if (log.isDebugEnabled()) - log.debug("Initialized default page."); - } - catch (IOException e) { - U.warn(log, "Failed to initialize default page: " + e.getMessage()); - } - - try { - initFavicon(); - - if (log.isDebugEnabled()) - log.debug(favicon != null ? "Initialized favicon, size: " + favicon.length : "Favicon is null."); - } - catch (IOException e) { - U.warn(log, "Failed to initialize favicon: " + e.getMessage()); - } - } - - /** - * @throws IOException If failed. - */ - private void initDefaultPage() throws IOException { - assert dfltPage == null; - - InputStream in = getClass().getResourceAsStream("rest.html"); - - if (in != null) { - LineNumberReader rdr = new LineNumberReader(new InputStreamReader(in)); - - try { - StringBuilder buf = new StringBuilder(2048); - - for (String line = rdr.readLine(); line != null; line = rdr.readLine()) { - buf.append(line); - - if (!line.endsWith(" ")) - buf.append(" "); - } - - dfltPage = buf.toString(); - } - finally { - U.closeQuiet(rdr); - } - } - } - - /** - * @throws IOException If failed. - */ - private void initFavicon() throws IOException { - assert favicon == null; - - InputStream in = getClass().getResourceAsStream("favicon.ico"); - - if (in != null) { - BufferedInputStream bis = new BufferedInputStream(in); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - try { - byte[] buf = new byte[2048]; - - while (true) { - int n = bis.read(buf); - - if (n == -1) - break; - - bos.write(buf, 0, n); - } - - favicon = bos.toByteArray(); - } - finally { - U.closeQuiet(bis); - } - } - } - - /** {@inheritDoc} */ - @Override public void handle(String target, Request req, HttpServletRequest srvReq, HttpServletResponse res) - throws IOException, ServletException { - if (log.isDebugEnabled()) - log.debug("Handling request [target=" + target + ", req=" + req + ", srvReq=" + srvReq + ']'); - - if (target.startsWith("/gridgain")) { - processRequest(target, srvReq, res); - - req.setHandled(true); - } - else if (target.startsWith("/favicon.ico")) { - if (favicon == null) { - res.setStatus(HttpServletResponse.SC_NOT_FOUND); - - req.setHandled(true); - - return; - } - - res.setStatus(HttpServletResponse.SC_OK); - - res.setContentType("image/x-icon"); - - res.getOutputStream().write(favicon); - res.getOutputStream().flush(); - - req.setHandled(true); - } - else { - if (dfltPage == null) { - res.setStatus(HttpServletResponse.SC_NOT_FOUND); - - req.setHandled(true); - - return; - } - - res.setStatus(HttpServletResponse.SC_OK); - - res.setContentType("text/html"); - - res.getWriter().write(dfltPage); - res.getWriter().flush(); - - req.setHandled(true); - } - } - - /** - * Process HTTP request. - * - * @param act Action. - * @param req Http request. - * @param res Http response. - */ - private void processRequest(String act, HttpServletRequest req, HttpServletResponse res) { - res.setContentType("application/json"); - res.setCharacterEncoding("UTF-8"); - - GridRestCommand cmd = command(req); - - if (cmd == null) { - res.setStatus(HttpServletResponse.SC_BAD_REQUEST); - - return; - } - - if (!authChecker.apply(req.getHeader("X-Signature"))) { - res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - - return; - } - - GridRestResponse cmdRes; - - Map<String, Object> params = parameters(req); - - try { - GridRestRequest cmdReq = createRequest(cmd, params, req); - - if (log.isDebugEnabled()) - log.debug("Initialized command request: " + cmdReq); - - cmdRes = hnd.handle(cmdReq); - - if (cmdRes == null) - throw new IllegalStateException("Received null result from handler: " + hnd); - - byte[] sesTok = cmdRes.sessionTokenBytes(); - - if (sesTok != null) - cmdRes.setSessionToken(U.byteArray2HexString(sesTok)); - - res.setStatus(HttpServletResponse.SC_OK); - } - catch (Throwable e) { - res.setStatus(HttpServletResponse.SC_OK); - - U.error(log, "Failed to process HTTP request [action=" + act + ", req=" + req + ']', e); - - cmdRes = new GridRestResponse(STATUS_FAILED, e.getMessage()); - } - - JsonConfig cfg = new GridJettyJsonConfig(); - JSON json; - - try { - json = JSONSerializer.toJSON(cmdRes, cfg); - } - catch (JSONException e) { - U.error(log, "Failed to convert response to JSON: " + cmdRes, e); - - json = JSONSerializer.toJSON(new GridRestResponse(STATUS_FAILED, e.getMessage()), cfg); - } - - try { - if (log.isDebugEnabled()) - log.debug("Parsed command response into JSON object: " + json.toString(2)); - - res.getWriter().write(json.toString()); - - if (log.isDebugEnabled()) - log.debug("Processed HTTP request [action=" + act + ", jsonRes=" + cmdRes + ", req=" + req + ']'); - } - catch (IOException e) { - U.error(log, "Failed to send HTTP response: " + json.toString(2), e); - } - } - - /** - * Creates REST request. - * - * @param cmd Command. - * @param params Parameters. - * @return REST request. - * @throws IgniteCheckedException If creation failed. - */ - @Nullable private GridRestRequest createRequest(GridRestCommand cmd, Map<String, Object> params, - ServletRequest req) throws IgniteCheckedException { - GridRestRequest restReq; - - switch (cmd) { - case CACHE_GET: - case CACHE_GET_ALL: - case CACHE_PUT: - case CACHE_PUT_ALL: - case CACHE_REMOVE: - case CACHE_REMOVE_ALL: - case CACHE_ADD: - case CACHE_CAS: - case CACHE_METRICS: - case CACHE_REPLACE: - case CACHE_DECREMENT: - case CACHE_INCREMENT: - case CACHE_APPEND: - case CACHE_PREPEND: { - GridRestCacheRequest restReq0 = new GridRestCacheRequest(); - - restReq0.cacheName((String) params.get("cacheName")); - restReq0.key(params.get("key")); - restReq0.value(params.get("val")); - restReq0.value2(params.get("val2")); - - Object val1 = params.get("val1"); - - if (val1 != null) - restReq0.value(val1); - - restReq0.cacheFlags(intValue("cacheFlags", params, 0)); - restReq0.ttl(longValue("exp", params, null)); - restReq0.initial(longValue("init", params, null)); - restReq0.delta(longValue("delta", params, null)); - - if (cmd == CACHE_GET_ALL || cmd == CACHE_PUT_ALL || cmd == CACHE_REMOVE_ALL) { - List<Object> keys = values("k", params); - List<Object> vals = values("v", params); - - if (keys.size() < vals.size()) - throw new IgniteCheckedException("Number of keys must be greater or equals to number of values."); - - Map<Object, Object> map = U.newHashMap(keys.size()); - - Iterator<Object> keyIt = keys.iterator(); - Iterator<Object> valIt = vals.iterator(); - - while (keyIt.hasNext()) - map.put(keyIt.next(), valIt.hasNext() ? valIt.next() : null); - - restReq0.values(map); - } - - restReq = restReq0; - - break; - } - - case TOPOLOGY: - case NODE: { - GridRestTopologyRequest restReq0 = new GridRestTopologyRequest(); - - restReq0.includeMetrics(Boolean.parseBoolean((String) params.get("mtr"))); - restReq0.includeAttributes(Boolean.parseBoolean((String) params.get("attr"))); - - restReq0.nodeIp((String) params.get("ip")); - - restReq0.nodeId(uuidValue("id", params)); - - restReq = restReq0; - - break; - } - - case EXE: - case RESULT: - case NOOP: { - GridRestTaskRequest restReq0 = new GridRestTaskRequest(); - - restReq0.taskId((String) params.get("id")); - restReq0.taskName((String) params.get("name")); - - restReq0.params(values("p", params)); - - restReq0.async(Boolean.parseBoolean((String) params.get("async"))); - - restReq0.timeout(longValue("timeout", params, 0L)); - - restReq = restReq0; - - break; - } - - case LOG: { - GridRestLogRequest restReq0 = new GridRestLogRequest(); - - restReq0.path((String) params.get("path")); - - restReq0.from(intValue("from", params, -1)); - restReq0.to(intValue("to", params, -1)); - - restReq = restReq0; - - break; - } - - case VERSION: { - restReq = new GridRestRequest(); - - break; - } - - default: - throw new IgniteCheckedException("Invalid command: " + cmd); - } - - restReq.address(new InetSocketAddress(req.getRemoteAddr(), req.getRemotePort())); - - restReq.command(cmd); - - if (params.containsKey("gridgain.login") || params.containsKey("gridgain.password")) { - GridSecurityCredentials cred = new GridSecurityCredentials( - (String)params.get("gridgain.login"), (String)params.get("gridgain.password")); - - restReq.credentials(cred); - } - - String clientId = (String) params.get("clientId"); - - try { - if (clientId != null) - restReq.clientId(UUID.fromString(clientId)); - } - catch (Exception ignored) { - // Ignore invalid client id. Rest handler will process this logic. - } - - String destId = (String) params.get("destId"); - - try { - if (destId != null) - restReq.destinationId(UUID.fromString(destId)); - } - catch (IllegalArgumentException ignored) { - // Don't fail - try to execute locally. - } - - String sesTokStr = (String) params.get("sessionToken"); - - try { - if (sesTokStr != null) - restReq.sessionToken(U.hexString2ByteArray(sesTokStr)); - } - catch (IllegalArgumentException ignored) { - // Ignore invalid session token. - } - - return restReq; - } - - /** - * Retrieves long value from parameters map. - * - * @param key Key. - * @param params Parameters map. - * @param dfltVal Default value. - * @return Long value from parameters map or {@code dfltVal} if null - * or not exists. - * @throws IgniteCheckedException If parsing failed. - */ - @Nullable private static Long longValue(String key, Map<String, Object> params, Long dfltVal) throws IgniteCheckedException { - assert key != null; - - String val = (String) params.get(key); - - try { - return val == null ? dfltVal : Long.valueOf(val); - } - catch (NumberFormatException ignore) { - throw new IgniteCheckedException("Failed to parse parameter of Long type [" + key + "=" + val + "]"); - } - } - - /** - * Retrieves int value from parameters map. - * - * @param key Key. - * @param params Parameters map. - * @param dfltVal Default value. - * @return Integer value from parameters map or {@code dfltVal} if null - * or not exists. - * @throws IgniteCheckedException If parsing failed. - */ - @Nullable private static Integer intValue(String key, Map<String, Object> params, Integer dfltVal) throws IgniteCheckedException { - assert key != null; - - String val = (String) params.get(key); - - try { - return val == null ? dfltVal : Integer.valueOf(val); - } - catch (NumberFormatException ignore) { - throw new IgniteCheckedException("Failed to parse parameter of Integer type [" + key + "=" + val + "]"); - } - } - - /** - * Retrieves UUID value from parameters map. - * - * @param key Key. - * @param params Parameters map. - * @return UUID value from parameters map or {@code null} if null - * or not exists. - * @throws IgniteCheckedException If parsing failed. - */ - @Nullable private static UUID uuidValue(String key, Map<String, Object> params) throws IgniteCheckedException { - assert key != null; - - String val = (String) params.get(key); - - try { - return val == null ? null : UUID.fromString(val); - } - catch (NumberFormatException ignore) { - throw new IgniteCheckedException("Failed to parse parameter of UUID type [" + key + "=" + val + "]"); - } - } - - /** - * Gets values referenced by sequential keys, e.g. {@code key1...keyN}. - * - * @param keyPrefix Key prefix, e.g. {@code key} for {@code key1...keyN}. - * @param params Parameters map. - * @return Values. - */ - @Nullable protected List<Object> values(String keyPrefix, Map<String, Object> params) { - assert keyPrefix != null; - - List<Object> vals = new LinkedList<>(); - - for (int i = 1; ; i++) { - String key = keyPrefix + i; - - if (params.containsKey(key)) - vals.add(params.get(key)); - else - break; - } - - return vals; - } - - /** - * @param req Request. - * @return Command. - */ - @Nullable GridRestCommand command(ServletRequest req) { - String cmd = req.getParameter("cmd"); - - return cmd == null ? null : GridRestCommand.fromKey(cmd.toLowerCase()); - } - - /** - * Parses HTTP parameters in an appropriate format and return back map of - * values to predefined list of names. - * - * @param req Request. - * @return Map of parsed parameters. - */ - @SuppressWarnings({"unchecked"}) - private Map<String, Object> parameters(ServletRequest req) { - Map<String, String[]> params = req.getParameterMap(); - - if (F.isEmpty(params)) - return Collections.emptyMap(); - - Map<String, Object> map = U.newHashMap(params.size()); - - for (Map.Entry<String, String[]> entry : params.entrySet()) - map.put(entry.getKey(), parameter(entry.getValue())); - - return map; - } - - /** - * @param obj Parameter object. - * @return Parameter value. - */ - @Nullable private String parameter(Object obj) { - if (obj instanceof String) - return (String)obj; - else if (obj instanceof String[] && ((String[])obj).length > 0) - return ((String[])obj)[0]; - - return null; - } -}
