0) charsReadTotal += charsRead; } while (charsRead >= 0); charsRead = 0; car = new CharArrayReader(ca = caw.toCharArray()); int charsReadTotalCheck = 0; while (charsRead >= 0 && charsRead < charsReadTotal) { final char[] cbuf = new char[1]; charsRead = car.read(cbuf); if (charsRead > 0) charsReadTotalCheck += charsRead; if (cbuf[0] == ':') break; userNameSB.append(cbuf[0]); } if (charsRead >= 0 && charsRead < charsReadTotal) { password = new char[charsReadTotal - charsReadTotalCheck]; final int passwordSize = car.read(password); if (passwordSize + charsReadTotalCheck != charsReadTotal) throw new IllegalStateException("passwordSize and charsRead must match charsReadTotal!" + " passwordSize=" + passwordSize + ", charsRead=" + charsRead + ", charsReadTotal=" + charsReadTotal);//TODO for testing } } catch (final Exception e) { throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).type(MediaType.APPLICATION_XML).entity(new Error(e)).build()); } finally { // For extra safety: Overwrite all sensitive memory with 0. // Unfortunately, we cannot overwrite auth.password. But at least we minimize things as much as possible. Arrays.fill(basicAuthDecodedBA, (byte)0); if (ca != null) Arrays.fill(ca, (char)0); if (caw != null) { final char[] zeroArray = new char[caw.size()]; caw.reset(); try { caw.write(zeroArray); caw = null; } catch (final IOException e) { throw new RuntimeException(e); } } } final Auth auth = new Auth(); ]]> [] getInterfaces(final ObjectRef objectRef) { ClassInfo classInfo = classInfoMap.getClassInfo(objectRef.getClassId()); if (classInfo == null) { classInfo = objectRef.getClassInfo(); if (classInfo == null) throw new IllegalStateException("There is no ClassInfo in the ClassInfoMap and neither in the ObjectRef! " + objectRef); classInfoMap.putClassInfo(classInfo); objectRef.setClassInfo(null); } final ClassManager classManager = objectManager.getClassManager(); final Set interfaceNames = classInfo.getInterfaceNames(); final List> interfaces = new ArrayList<>(interfaceNames.size() + 1); for (final String interfaceName : interfaceNames) { Class iface = null; try { iface = classManager.getClassOrFail(interfaceName); } catch (RuntimeException x) { if (ExceptionUtil.getCause(x, ClassNotFoundException.class) == null) throw x; } if (iface != null) interfaces.add(iface); } interfaces.add(RemoteObjectProxy.class); return interfaces.toArray(new Class[interfaces.size()]); } ]]> * Slashes are not encoded, but retained as they are; only the path segments (the strings between the slashes) are * encoded. *

* Duplicate slashes are removed. *

* The result of this method can be used in both URL-paths and URL-query-parameters. *

* For example the input "/some//ex ample///path/" becomes "some/ex%20ample/path". * @param path the path to be encoded. Must not be null. * @return the encoded path. Never null. */ protected String encodePath(final String path) { requireNonNull(path, "path"); final StringBuilder sb = new StringBuilder(); final String[] segments = path.split("/"); for (final String segment : segments) { if (segment.isEmpty()) continue; if (sb.length() != 0) sb.append('/'); sb.append(urlEncode(segment)); } return sb.toString(); } protected void assertResponseIndicatesSuccess(final Response response) { if (400 <= response.getStatus() && response.getStatus() <= 599) { response.bufferEntity(); if (response.hasEntity()) { Error error = null; try { error = response.readEntity(Error.class); } catch (final Exception y) { logger.error("handleException: " + y, y); } if (error != null) { throwOriginalExceptionIfPossible(error); throw new RemoteException(error); } } throw new WebApplicationException(response); } } protected void throwOriginalExceptionIfPossible(final Error error) { RemoteExceptionUtil.throwOriginalExceptionIfPossible(error); } } ]]> catch Throwable logger.warn("initDriverClass: " + e, e); } } @Override public Connection createConnection() throws SQLException { if (connectionURL == null) { initProperties(); initDriverClass(); } if (isEmpty(connectionUserName) && isEmpty(connectionPassword)) return DriverManager.getConnection(connectionURL); else return DriverManager.getConnection(connectionURL, connectionUserName, connectionPassword); } private void initProperties() { PersistencePropertiesProvider persistencePropertiesProvider = new PersistencePropertiesProvider(getRepositoryIdOrFail(), getLocalRootOrFail()); persistenceProperties = persistencePropertiesProvider.getPersistenceProperties(); connectionDriverName = persistenceProperties.get(PersistencePropertiesEnum.CONNECTION_DRIVER_NAME.key); connectionURL = persistenceProperties.get(PersistencePropertiesEnum.CONNECTION_URL.key); connectionUserName = persistenceProperties.get(PersistencePropertiesEnum.CONNECTION_USER_NAME.key); connectionPassword = persistenceProperties.get(PersistencePropertiesEnum.CONNECTION_PASSWORD.key); } ]]> clazz = className == null ? null : classManager.getClassOrFail(className); final String methodName = methodInvocationRequest.getMethodName(); if (ObjectRef.VIRTUAL_METHOD_NAME_INC_REF_COUNT.equals(methodName)) { final ObjectRefWithRefId[] objectRefWithRefIds = cast(methodInvocationRequest.getArguments()[0]); for (final ObjectRefWithRefId objectRefWithRefId : objectRefWithRefIds) objectManager.incRefCount(objectRefWithRefId.object, objectRefWithRefId.refId); return MethodInvocationResponse.forInvocation(null, null); } else if (ObjectRef.VIRTUAL_METHOD_NAME_DEC_REF_COUNT.equals(methodName)) { final ObjectRefWithRefId[] objectRefWithRefIds = cast(methodInvocationRequest.getArguments()[0]); for (final ObjectRefWithRefId objectRefWithRefId : objectRefWithRefIds) objectManager.decRefCount(objectRefWithRefId.object, objectRefWithRefId.refId); return MethodInvocationResponse.forInvocation(null, null); } ]]> clientThreadLocal = new ThreadLocal(); private static class ClientRef { public final Client client; public int refCount = 1; public boolean broken; public ClientRef(final Client client) { this.client = requireNonNull(client, "client"); } } /** * Acquire a {@link Client} and bind it to the current thread. *

* Important: You must {@linkplain #releaseClient() release} the client! Use a try/finally block! * @see #releaseClient() * @see #getClientOrFail() */ private synchronized void acquireClient(){ final ClientRef clientRef = clientThreadLocal.get(); if (clientRef != null) { ++clientRef.refCount; return; } Client client = clientCache.poll(); if (client == null) { ]]> * This method does not use {@link java.net.URLEncoder URLEncoder}, because of * JERSEY-417. *

* The result of this method can be used in both URL-paths and URL-query-parameters. * @param string the {@code String} to be encoded. Must not be null. * @return the encoded {@code String}. */ protected static String urlEncode(final String string) { requireNonNull(string, "string"); // This UriComponent method is safe. It does not try to handle the '{' and '}' // specially and with type PATH_SEGMENT, it encodes spaces using '%20' instead of '+'. // It can therefore be used for *both* path segments *and* query parameters. // return org.glassfish.jersey.uri.UriComponent.encode(string, UriComponent.Type.PATH_SEGMENT); return UrlEncoder.encode(string); } /** * Create a {@link WebTarget} from the given path segments. *

* This method prefixes the path with the {@link #getBaseURL() base-URL} and appends * all path segments separated via slashes ('/'). *

* We do not use client.target(getBaseURL()).path("..."), because the * {@link WebTarget#path(String) path(...)} method does not encode curly braces * (which might be part of a file name!). * Instead it resolves them using {@linkplain WebTarget#matrixParam(String, Object...) matrix-parameters}. * The matrix-parameters need to be encoded manually, too (at least I tried it and it failed, if I didn't). * Because of these reasons and in order to make the calls more compact, we assemble the path * ourselves here. * @param pathSegments the parts of the path. May be null. The path segments are * appended to the path as they are. They are not encoded at all! If you require encoding, * use {@link #encodePath(String)} or {@link #urlEncode(String)} before! Furthermore, all path segments * are separated with a slash inbetween them, but not at the end. If a single path segment * already contains a slash, duplicate slashes might occur. * @return the target. Never null. */ protected WebTarget createWebTarget(final String ... pathSegments) { final Client client = getClientOrFail(); final StringBuilder sb = new StringBuilder(); sb.append(getBaseURL()); boolean first = true; if (pathSegments != null && pathSegments.length != 0) { for (final String pathSegment : pathSegments) { if (!first) // the base-URL already ends with a slash! sb.append('/'); first = false; sb.append(pathSegment); } } final WebTarget webTarget = client.target(URI.create(sb.toString())); return webTarget; } /** * Get the server's base-URL. *

* This base-URL is the base of the CloudStoreREST application. Hence all URLs * beneath this base-URL are processed by the CloudStoreREST application. *

* In other words: All repository-names are located directly beneath this base-URL. The special services, too, * are located directly beneath this base-URL. *

* For example, if the server's base-URL is "https://host.domain:8443/", then the test-service is * available via "https://host.domain:8443/_test" and the repository with the alias "myrepo" is * "https://host.domain:8443/myrepo". * @return the base-URL. This URL always ends with "/". */ protected String getBaseURL() { return getCloudStoreRestClientOrFail().getBaseUrl(); ]]> = 0) { className = fieldName.substring(0, lastDotIndex); simpleFieldName = fieldName.substring(lastDotIndex + 1); } final List declaredFields = getAllDeclaredFields(object.getClass()); for (final Field field : declaredFields) { if (className != null && !className.equals(field.getDeclaringClass().getName())) continue; if (!simpleFieldName.equals(field.getName())) continue; field.setAccessible(true); try { ]]> null. * @throws IllegalStateException if there is no {@link Client} bound to the current thread. * @see #acquireClient() */ public Client getClientOrFail() { final ClientRef clientRef = clientThreadLocal.get(); if (clientRef == null) throw new IllegalStateException("acquireClient() not called on the same thread (or releaseClient() already called)!"); return clientRef.client; } /** * Release a {@link Client} which was previously {@linkplain #acquireClient() acquired}. * @see #acquireClient() */ private synchronized void releaseClient() { final ClientRef clientRef = clientThreadLocal.get(); if (clientRef == null) throw new IllegalStateException("acquireClient() not called on the same thread (or releaseClient() called more often than acquireClient())!"); if (--clientRef.refCount == 0) { clientThreadLocal.remove(); if (!clientRef.broken) clientCache.add(clientRef.client); } } private void markClientBroken() { final ClientRef clientRef = clientThreadLocal.get(); if (clientRef == null) throw new IllegalStateException("acquireClient() not called on the same thread (or releaseClient() called more often than acquireClient())!"); clientRef.broken = true; } ]]> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { // We return always true, because we declared our media-type already in the @Produces above and thus don't need to check it here. // At least I hope we don't get consulted for media-types that were not declared in @Produces. return true; } @Override public void writeTo( Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream ) throws IOException, WebApplicationException { ]]>