Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
Source Code for Connector/J 8.0.31 - Core Impl
Where to get the Java source code for Connector/J 8.0 Core Impl module?
✍: FYIcenter.com
Java source code files for Connector/J 8.0 Core Impl module are:
⏎ com/mysql/cj/ServerPreparedQuery.java
/* * Copyright (c) 2017, 2022, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the * Free Software Foundation. * * This program is also distributed with certain software (including but not * limited to OpenSSL) that is licensed under separate terms, as designated in a * particular file or component or in included license documentation. The * authors of MySQL hereby grant you an additional permission to link the * program and your derivative works with the separately licensed software that * they have included with MySQL. * * Without limiting anything contained in the foregoing, this file, which is * part of MySQL Connector/J, is also subject to the Universal FOSS Exception, * version 1.0, a copy of which can be found at * http://oss.oracle.com/licenses/universal-foss-exception. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, * for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysql.cj; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.sql.Clob; import com.mysql.cj.conf.PropertyKey; import com.mysql.cj.conf.RuntimeProperty; import com.mysql.cj.exceptions.CJException; import com.mysql.cj.exceptions.ExceptionFactory; import com.mysql.cj.exceptions.MysqlErrorNumbers; import com.mysql.cj.exceptions.WrongArgumentException; import com.mysql.cj.log.ProfilerEvent; import com.mysql.cj.protocol.ColumnDefinition; import com.mysql.cj.protocol.Message; import com.mysql.cj.protocol.ProtocolEntityFactory; import com.mysql.cj.protocol.Resultset; import com.mysql.cj.protocol.Resultset.Type; import com.mysql.cj.protocol.a.ColumnDefinitionFactory; import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType; import com.mysql.cj.protocol.a.NativeConstants.StringLengthDataType; import com.mysql.cj.protocol.a.NativeConstants.StringSelfDataType; import com.mysql.cj.protocol.a.NativeMessageBuilder; import com.mysql.cj.protocol.a.NativePacketPayload; import com.mysql.cj.result.Field; import com.mysql.cj.util.StringUtils; // TODO should not be protocol-specific public class ServerPreparedQuery extends ClientPreparedQuery { public static final int BLOB_STREAM_READ_BUF_SIZE = 8192; public static final byte OPEN_CURSOR_FLAG = 0x01; public static final byte PARAMETER_COUNT_AVAILABLE = 0x08; /** The ID that the server uses to identify this PreparedStatement */ private long serverStatementId; /** Field-level metadata for parameters */ private Field[] parameterFields; /** Field-level metadata for result sets. From statement prepare. */ private ColumnDefinition resultFields; /** The "profileSQL" connection property value */ protected boolean profileSQL = false; /** The "gatherPerfMetrics" connection property value */ protected boolean gatherPerfMetrics; /** The "logSlowQueries" connection property value */ protected boolean logSlowQueries = false; private boolean useAutoSlowLog; protected RuntimeProperty<Integer> slowQueryThresholdMillis; protected RuntimeProperty<Boolean> explainSlowQueries; protected boolean useCursorFetch = false; protected boolean queryWasSlow = false; protected NativeMessageBuilder commandBuilder = null; public static ServerPreparedQuery getInstance(NativeSession sess) { if (sess.getPropertySet().getBooleanProperty(PropertyKey.autoGenerateTestcaseScript).getValue()) { return new ServerPreparedQueryTestcaseGenerator(sess); } return new ServerPreparedQuery(sess); } protected ServerPreparedQuery(NativeSession sess) { super(sess); this.profileSQL = sess.getPropertySet().getBooleanProperty(PropertyKey.profileSQL).getValue(); this.gatherPerfMetrics = sess.getPropertySet().getBooleanProperty(PropertyKey.gatherPerfMetrics).getValue(); this.logSlowQueries = sess.getPropertySet().getBooleanProperty(PropertyKey.logSlowQueries).getValue(); this.useAutoSlowLog = sess.getPropertySet().getBooleanProperty(PropertyKey.autoSlowLog).getValue(); this.slowQueryThresholdMillis = sess.getPropertySet().getIntegerProperty(PropertyKey.slowQueryThresholdMillis); this.explainSlowQueries = sess.getPropertySet().getBooleanProperty(PropertyKey.explainSlowQueries); this.useCursorFetch = sess.getPropertySet().getBooleanProperty(PropertyKey.useCursorFetch).getValue(); this.commandBuilder = (NativeMessageBuilder) sess.getProtocol().getMessageBuilder(); } /** * * @param sql * query string * @throws IOException * if an i/o error occurs */ public void serverPrepare(String sql) throws IOException { this.session.checkClosed(); synchronized (this.session) { long begin = this.profileSQL ? System.currentTimeMillis() : 0; NativePacketPayload prepareResultPacket = this.session.getProtocol() .sendCommand(this.commandBuilder.buildComStmtPrepare(this.session.getSharedSendPacket(), sql, this.session.getPropertySet().getStringProperty(PropertyKey.characterEncoding).getValue()), false, 0); // 4.1.1 and newer use the first byte as an 'ok' or 'error' flag, so move the buffer pointer past it to start reading the statement id. prepareResultPacket.setPosition(1); this.serverStatementId = prepareResultPacket.readInteger(IntegerDataType.INT4); int fieldCount = (int) prepareResultPacket.readInteger(IntegerDataType.INT2); setParameterCount((int) prepareResultPacket.readInteger(IntegerDataType.INT2)); this.queryBindings = new NativeQueryBindings(this.parameterCount, this.session, NativeQueryBindValue::new); if (this.gatherPerfMetrics) { this.session.getProtocol().getMetricsHolder().incrementNumberOfPrepares(); } if (this.profileSQL) { this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_PREPARE, this.session, this, null, this.session.getCurrentTimeNanosOrMillis() - begin, new Throwable(), truncateQueryToLog(sql)); } boolean checkEOF = !this.session.getServerSession().isEOFDeprecated(); if (this.parameterCount > 0) { if (checkEOF) { // Skip the following EOF packet. this.session.getProtocol().skipPacket(); } this.parameterFields = this.session.getProtocol().read(ColumnDefinition.class, new ColumnDefinitionFactory(this.parameterCount, null)) .getFields(); } // Read in the result set column information if (fieldCount > 0) { this.resultFields = this.session.getProtocol().read(ColumnDefinition.class, new ColumnDefinitionFactory(fieldCount, null)); } } } @Override public void statementBegins() { super.statementBegins(); this.queryWasSlow = false; } /** * @param <T> * extends {@link Resultset} * @param maxRowsToRetrieve * rows limit * @param createStreamingResultSet * should c/J create a streaming result? * @param metadata * use this metadata instead of the one provided on wire * @param resultSetFactory * {@link ProtocolEntityFactory} * @return T instance */ public <T extends Resultset> T serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, ColumnDefinition metadata, ProtocolEntityFactory<T, NativePacketPayload> resultSetFactory) { if (this.session.shouldIntercept()) { T interceptedResults = this.session.invokeQueryInterceptorsPre(() -> { return getOriginalSql(); }, this, true); if (interceptedResults != null) { return interceptedResults; } } String queryAsString = this.profileSQL || this.logSlowQueries || this.gatherPerfMetrics ? asSql() : ""; return readExecuteResult(sendExecutePacket(prepareExecutePacket(), queryAsString), maxRowsToRetrieve, createStreamingResultSet, metadata, resultSetFactory, queryAsString); } public NativePacketPayload prepareExecutePacket() { BindValue[] bindValues = this.queryBindings.getBindValues(); if (this.queryBindings.isLongParameterSwitchDetected()) { // Check when values were bound boolean firstFound = false; long boundTimeToCheck = 0; for (int i = 0; i < this.parameterCount - 1; i++) { if (bindValues[i].isStream()) { if (firstFound && boundTimeToCheck != bindValues[i].getBoundBeforeExecutionNum()) { throw ExceptionFactory.createException( Messages.getString("ServerPreparedStatement.11") + Messages.getString("ServerPreparedStatement.12"), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, 0, true, null, this.session.getExceptionInterceptor()); } firstFound = true; boundTimeToCheck = bindValues[i].getBoundBeforeExecutionNum(); } } // Okay, we've got all "newly"-bound streams, so reset server-side state to clear out previous bindings serverResetStatement(); } this.queryBindings.checkAllParametersSet(); // // Send all long data // for (int i = 0; i < this.parameterCount; i++) { if (bindValues[i].isStream()) { serverLongData(i, bindValues[i]); } } // // store the parameter values // boolean sendQueryAttributes = false; if (this.session.getServerSession().supportsQueryAttributes()) { // Servers between 8.0.23 8.0.25 are affected by Bug#103102, Bug#103268 and Bug#103377. Query attributes cannot be sent to these servers. sendQueryAttributes = this.session.getServerSession().getServerVersion().meetsMinimum(new ServerVersion(8, 0, 26)); } else if (this.queryAttributesBindings.getCount() > 0) { this.session.getLog().logWarn(Messages.getString("QueryAttributes.SetButNotSupported")); } byte flags = 0; if (this.resultFields != null && this.resultFields.getFields() != null && this.useCursorFetch && this.resultSetType == Type.FORWARD_ONLY && this.fetchSize > 0) { // we only create cursor-backed result sets if // a) The query is a SELECT // b) The server supports it // c) We know it is forward-only (note this doesn't preclude updatable result sets) // d) The user has set a fetch size flags |= OPEN_CURSOR_FLAG; } if (sendQueryAttributes) { flags |= PARAMETER_COUNT_AVAILABLE; } return this.commandBuilder.buildComStmtExecute(this.session.getSharedSendPacket(), this.serverStatementId, flags, sendQueryAttributes, this); } public NativePacketPayload sendExecutePacket(NativePacketPayload packet, String queryAsString) { // TODO queryAsString should be shared instead of passed final long begin = this.session.getCurrentTimeNanosOrMillis(); resetCancelledState(); CancelQueryTask timeoutTask = null; try { // Get this before executing to avoid a shared packet pollution in the case some other query is issued internally, such as when using I_S. timeoutTask = startQueryTimer(this, this.timeoutInMillis); statementBegins(); NativePacketPayload resultPacket = this.session.getProtocol().sendCommand(packet, false, 0); final long queryEndTime = this.session.getCurrentTimeNanosOrMillis(); if (timeoutTask != null) { stopQueryTimer(timeoutTask, true, true); timeoutTask = null; } final long executeTime = queryEndTime - begin; setExecuteTime(executeTime); if (this.logSlowQueries) { this.queryWasSlow = this.useAutoSlowLog ? // this.session.getProtocol().getMetricsHolder().checkAbonormallyLongQuery(executeTime) : executeTime > this.slowQueryThresholdMillis.getValue(); if (this.queryWasSlow) { this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_SLOW_QUERY, this.session, this, null, executeTime, new Throwable(), Messages.getString("ServerPreparedStatement.15", new String[] { String.valueOf(this.session.getSlowQueryThreshold()), String.valueOf(executeTime), this.originalSql, queryAsString })); } } if (this.gatherPerfMetrics) { this.session.getProtocol().getMetricsHolder().registerQueryExecutionTime(executeTime); this.session.getProtocol().getMetricsHolder().incrementNumberOfPreparedExecutes(); } if (this.profileSQL) { this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_EXECUTE, this.session, this, null, executeTime, new Throwable(), truncateQueryToLog(queryAsString)); } return resultPacket; } catch (CJException sqlEx) { if (this.session.shouldIntercept()) { this.session.invokeQueryInterceptorsPost(() -> { return getOriginalSql(); }, this, null, true); } throw sqlEx; } finally { this.statementExecuting.set(false); stopQueryTimer(timeoutTask, false, false); } } public <T extends Resultset> T readExecuteResult(NativePacketPayload resultPacket, int maxRowsToRetrieve, boolean createStreamingResultSet, ColumnDefinition metadata, ProtocolEntityFactory<T, NativePacketPayload> resultSetFactory, String queryAsString) { // TODO queryAsString should be shared instead of passed try { long fetchStartTime = this.profileSQL ? this.session.getCurrentTimeNanosOrMillis() : 0; T rs = this.session.getProtocol().readAllResults(maxRowsToRetrieve, createStreamingResultSet, resultPacket, true, metadata != null ? metadata : this.resultFields, resultSetFactory); if (this.session.shouldIntercept()) { T interceptedResults = this.session.invokeQueryInterceptorsPost(() -> { return getOriginalSql(); }, this, rs, true); if (interceptedResults != null) { rs = interceptedResults; } } if (this.profileSQL) { this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_FETCH, this.session, this, rs, this.session.getCurrentTimeNanosOrMillis() - fetchStartTime, new Throwable(), null); } if (this.queryWasSlow && this.explainSlowQueries.getValue()) { this.session.getProtocol().explainSlowQuery(queryAsString, queryAsString); } this.queryBindings.getSendTypesToServer().set(false); if (this.session.hadWarnings()) { this.session.getProtocol().scanForAndThrowDataTruncation(); } return rs; } catch (IOException ioEx) { throw ExceptionFactory.createCommunicationsException(this.session.getPropertySet(), this.session.getServerSession(), this.session.getProtocol().getPacketSentTimeHolder(), this.session.getProtocol().getPacketReceivedTimeHolder(), ioEx, this.session.getExceptionInterceptor()); } catch (CJException sqlEx) { if (this.session.shouldIntercept()) { this.session.invokeQueryInterceptorsPost(() -> { return getOriginalSql(); }, this, null, true); } throw sqlEx; } } /** * Sends stream-type data parameters to the server. * * <pre> * Long data handling: * * - Server gets the long data in pieces with command type 'COM_LONG_DATA'. * - The packet received will have the format: * [COM_LONG_DATA: 1][STMT_ID:4][parameter_number:2][type:2][data] * - Checks if the type is specified by client, and if yes reads the type, * and stores the data in that format. * - It is up to the client to check for read data ended. The server does not * care; and also server does not notify to the client that it got the * data or not; if there is any error; then during execute; the error * will be returned * </pre> * * @param parameterIndex * parameter index * @param binding * {@link BindValue containing long data} * */ private void serverLongData(int parameterIndex, BindValue binding) { synchronized (this) { NativePacketPayload packet = this.session.getSharedSendPacket(); Object value = binding.getValue(); if (value instanceof byte[]) { this.session.getProtocol() .sendCommand(this.commandBuilder.buildComStmtSendLongData(packet, this.serverStatementId, parameterIndex, (byte[]) value), true, 0); } else if (value instanceof InputStream) { storeStreamOrReader(parameterIndex, packet, (InputStream) value); } else if (value instanceof java.sql.Blob) { try { storeStreamOrReader(parameterIndex, packet, ((java.sql.Blob) value).getBinaryStream()); } catch (Throwable t) { throw ExceptionFactory.createException(t.getMessage(), this.session.getExceptionInterceptor()); } } else if (value instanceof Reader) { if (binding.isNational() && !this.charEncoding.equalsIgnoreCase("UTF-8") && !this.charEncoding.equalsIgnoreCase("utf8")) { throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.31"), this.session.getExceptionInterceptor()); } storeStreamOrReader(parameterIndex, packet, (Reader) value); } else if (value instanceof Clob) { if (binding.isNational() && !this.charEncoding.equalsIgnoreCase("UTF-8") && !this.charEncoding.equalsIgnoreCase("utf8")) { throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.31"), this.session.getExceptionInterceptor()); } try { storeStreamOrReader(parameterIndex, packet, ((Clob) value).getCharacterStream()); } catch (Throwable t) { throw ExceptionFactory.createException(t.getMessage(), t); } } else { throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ServerPreparedStatement.18") + value.getClass().getName() + "'", this.session.getExceptionInterceptor()); } } } @Override public void closeQuery() { this.queryBindings = null; this.parameterFields = null; this.resultFields = null; super.closeQuery(); } public long getServerStatementId() { return this.serverStatementId; } public void setServerStatementId(long serverStatementId) { this.serverStatementId = serverStatementId; } public Field[] getParameterFields() { return this.parameterFields; } public void setParameterFields(Field[] parameterFields) { this.parameterFields = parameterFields; } public ColumnDefinition getResultFields() { return this.resultFields; } public void setResultFields(ColumnDefinition resultFields) { this.resultFields = resultFields; } private void storeStreamOrReader(int parameterIndex, NativePacketPayload packet, Closeable streamOrReader) { // TODO consider to use more precise type than just Closable this.session.checkClosed(); boolean isStream = InputStream.class.isAssignableFrom(streamOrReader.getClass()); byte[] bBuf = null; char[] cBuf = null; String clobEncoding = null; synchronized (this.session) { if (isStream) { bBuf = new byte[BLOB_STREAM_READ_BUF_SIZE]; } else { clobEncoding = this.session.getPropertySet().getStringProperty(PropertyKey.clobCharacterEncoding).getStringValue(); if (clobEncoding == null) { clobEncoding = this.session.getPropertySet().getStringProperty(PropertyKey.characterEncoding).getValue(); } int maxBytesChar = 2; if (clobEncoding != null) { maxBytesChar = this.session.getServerSession().getCharsetSettings().getMaxBytesPerChar(clobEncoding); if (maxBytesChar == 1) { maxBytesChar = 2; // for safety } } cBuf = new char[ServerPreparedQuery.BLOB_STREAM_READ_BUF_SIZE / maxBytesChar]; } boolean readAny = false; int bytesInPacket = 0; int totalBytesRead = 0; int bytesReadAtLastSend = 0; int packetIsFullAt = this.session.getPropertySet().getMemorySizeProperty(PropertyKey.blobSendChunkSize).getValue(); int numRead = 0; try { packet.setPosition(0); this.commandBuilder.buildComStmtSendLongDataHeader(packet, this.serverStatementId, parameterIndex); while ((numRead = (isStream ? ((InputStream) streamOrReader).read(bBuf) : ((Reader) streamOrReader).read(cBuf))) != -1) { readAny = true; if (isStream) { packet.writeBytes(StringLengthDataType.STRING_FIXED, bBuf, 0, numRead); bytesInPacket += numRead; totalBytesRead += numRead; } else { byte[] valueAsBytes = StringUtils.getBytes(cBuf, 0, numRead, clobEncoding); packet.writeBytes(StringSelfDataType.STRING_EOF, valueAsBytes); bytesInPacket += valueAsBytes.length; totalBytesRead += valueAsBytes.length; } if (bytesInPacket >= packetIsFullAt) { bytesReadAtLastSend = totalBytesRead; this.session.getProtocol().sendCommand(packet, true, 0); bytesInPacket = 0; packet.setPosition(0); this.commandBuilder.buildComStmtSendLongDataHeader(packet, this.serverStatementId, parameterIndex); } } if (!readAny || totalBytesRead != bytesReadAtLastSend) { this.session.getProtocol().sendCommand(packet, true, 0); } } catch (IOException ioEx) { throw ExceptionFactory.createException( (isStream ? Messages.getString("ServerPreparedStatement.24") : Messages.getString("ServerPreparedStatement.25")) + ioEx.toString(), ioEx, this.session.getExceptionInterceptor()); } finally { if (this.autoClosePStmtStreams.getValue()) { if (streamOrReader != null) { try { streamOrReader.close(); } catch (IOException ioEx) { // ignore } } } } } } /** * @param clearServerParameters * flag indicating whether we need an additional clean up */ public void clearParameters(boolean clearServerParameters) { boolean hadLongData = false; if (this.queryBindings != null) { hadLongData = this.queryBindings.clearBindValues(); this.queryBindings.setLongParameterSwitchDetected(clearServerParameters && hadLongData ? false : true); } if (clearServerParameters && hadLongData) { serverResetStatement(); } } public void serverResetStatement() { this.session.checkClosed(); synchronized (this.session) { try { this.session.getProtocol().sendCommand(this.commandBuilder.buildComStmtReset(this.session.getSharedSendPacket(), this.serverStatementId), false, 0); } finally { this.session.clearInputStream(); } } } /** * Computes the maximum parameter set size and the size of the entire batch given * the number of arguments in the batch. */ @Override protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) { long maxSizeOfParameterSet = 0; long sizeOfEntireBatch = 1 /* com_stmt_execute */ + 4 /* statement_id */ + 1 /* flags */ + 4 /* iteration_count */ + 1 /* new_params_bind_flag */; boolean supportsQueryAttributes = this.session.getServerSession().supportsQueryAttributes(); if (supportsQueryAttributes) { sizeOfEntireBatch += 9 /* parameter_count */; sizeOfEntireBatch += (this.queryAttributesBindings.getCount() + 7) / 8 /* null_bitmap */; for (int i = 0; i < this.queryAttributesBindings.getCount(); i++) { BindValue queryAttribute = this.queryAttributesBindings.getAttributeValue(i); sizeOfEntireBatch += 2 /* parameter_type */ + queryAttribute.getName().length() /* parameter_name */ + queryAttribute.getBinaryLength(); } } for (int i = 0; i < numBatchedArgs; i++) { long sizeOfParameterSet = (this.parameterCount + 7) / 8 /* null_bitmap */ + this.parameterCount * 2 /* parameter_type */; if (supportsQueryAttributes) { sizeOfParameterSet += this.parameterCount /* parameter_name (all empty) */; } BindValue[] bindValues = ((QueryBindings) this.batchedArgs.get(i)).getBindValues(); for (int j = 0; j < bindValues.length; j++) { sizeOfParameterSet += bindValues[j].getBinaryLength(); } sizeOfEntireBatch += sizeOfParameterSet; if (sizeOfParameterSet > maxSizeOfParameterSet) { maxSizeOfParameterSet = sizeOfParameterSet; } } return new long[] { maxSizeOfParameterSet, sizeOfEntireBatch }; } private String truncateQueryToLog(String sql) { String queryStr = null; int maxQuerySizeToLog = this.session.getPropertySet().getIntegerProperty(PropertyKey.maxQuerySizeToLog).getValue(); if (sql.length() > maxQuerySizeToLog) { StringBuilder queryBuf = new StringBuilder(maxQuerySizeToLog + 12); queryBuf.append(sql.substring(0, maxQuerySizeToLog)); queryBuf.append(Messages.getString("MysqlIO.25")); queryStr = queryBuf.toString(); } else { queryStr = sql; } return queryStr; } @Override public <M extends Message> M fillSendPacket(QueryBindings bindings) { return null; // we don't use this type of packet } }
⏎ com/mysql/cj/ServerPreparedQuery.java
Or download all of them as a single archive file:
File name: mysql-connector-java-core-impl-8.0.31.zip File size: 201048 bytes Release date: 2022-09-03 Download
⇒ Source Code for Connector/J 8.0.31 - Protocol Impl
⇐ Source Code for Connector/J 8.0.31 - Core API
2023-05-31, 11822👍, 0💬
Popular Posts:
What Is ojdbc7.jar for Oracle 12c R1? ojdbc7.jar for Oracle 12c R1 is the JAR files of ojdbc.jar, JD...
commons-net-1.4.1.jar is the JAR file for Apache Commons Net 1.4.1, which implements the client side...
maven-core-3.8.6.jar is the JAR file for Apache Maven 3.8.6 Core module. Apache Maven is a software ...
Apache ZooKeeper is an open-source server which enables highly reliable distributed coordination. Ap...
How to merge two JAR files with "jar" commands? I am tired of specifying multiple JAR files in the c...