mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
network shutdown code
This commit is contained in:
parent
b24cf4715b
commit
2d4432d87e
@ -23,6 +23,8 @@ import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.TableGenerator;
|
||||
@ -104,7 +106,8 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
private boolean crossZones = false;
|
||||
|
||||
@Column(name="hypervisor_type")
|
||||
private String hypervisorType;
|
||||
@Enumerated(value=EnumType.STRING)
|
||||
private HypervisorType hypervisorType;
|
||||
|
||||
@Column(name="extractable")
|
||||
private boolean extractable = true;
|
||||
@ -145,7 +148,7 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
this.created = created;
|
||||
this.guestOSId = guestOSId;
|
||||
this.bootable = bootable;
|
||||
this.hypervisorType = hyperType.toString();
|
||||
this.hypervisorType = hyperType;
|
||||
}
|
||||
|
||||
// Has an extra attribute - isExtractable
|
||||
@ -168,7 +171,7 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
this.created = created;
|
||||
this.guestOSId = guestOSId;
|
||||
this.bootable = bootable;
|
||||
this.hypervisorType = hyperType.toString();
|
||||
this.hypervisorType = hyperType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,7 +220,8 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
return requiresHvm;
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
@Override
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
@ -247,6 +251,7 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
this.publicTemplate = publicTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFeatured() {
|
||||
return featured;
|
||||
}
|
||||
@ -332,11 +337,11 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
|
||||
@Override
|
||||
public HypervisorType getHypervisorType() {
|
||||
return HypervisorType.getType(hypervisorType);
|
||||
return hypervisorType;
|
||||
}
|
||||
|
||||
public void setHypervisorType(HypervisorType hyperType) {
|
||||
hypervisorType = hyperType.toString();
|
||||
hypervisorType = hyperType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -355,8 +360,9 @@ public class VMTemplateVO implements VirtualMachineTemplate {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (this == that )
|
||||
return true;
|
||||
if (this == that ) {
|
||||
return true;
|
||||
}
|
||||
if (!(that instanceof VMTemplateVO)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -191,6 +191,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag
|
||||
SearchBuilder<IPAddressVO> IpAddressSearch;
|
||||
|
||||
private Map<String, String> _configs;
|
||||
|
||||
HashMap<Long, Long> _lastNetworkIdsToFree = new HashMap<Long, Long>();
|
||||
|
||||
@Override @DB
|
||||
public PublicIp fetchNewPublicIp(long dcId, VlanType vlanUse, Account owner, Long networkId, boolean sourceNat) throws InsufficientAddressCapacityException {
|
||||
@ -761,7 +763,6 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag
|
||||
NetworkOfferingVO defaultGuestDirectPodBasedNetworkOffering = new NetworkOfferingVO(NetworkOffering.DefaultDirectPodBasedNetworkOffering, "DirectPodBased", TrafficType.Public, GuestIpType.DirectPodBased, true, false, rateMbps, multicastRateMbps, null, true);
|
||||
defaultGuestNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultGuestDirectPodBasedNetworkOffering);
|
||||
|
||||
|
||||
AccountsUsingNetworkSearch = _accountDao.createSearchBuilder();
|
||||
SearchBuilder<NetworkAccountVO> networkAccountSearch = _networksDao.createSearchBuilderForAccount();
|
||||
AccountsUsingNetworkSearch.join("nc", networkAccountSearch, AccountsUsingNetworkSearch.entity().getId(), networkAccountSearch.entity().getAccountId(), JoinType.INNER);
|
||||
@ -1944,6 +1945,55 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag
|
||||
|
||||
}
|
||||
|
||||
@DB
|
||||
public void shutdownNetwork(long networkId) {
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
txn.start();
|
||||
NetworkVO network = _networksDao.lockRow(networkId, true);
|
||||
if (network == null) {
|
||||
s_logger.debug("Unable to find network with id: " + networkId);
|
||||
return;
|
||||
}
|
||||
if (network.getState() != Network.State.Implemented && network.getState() != Network.State.Destroying) {
|
||||
s_logger.debug("Network is not implemented: " + network);
|
||||
return;
|
||||
}
|
||||
network.setState(Network.State.Destroying);
|
||||
_networksDao.update(network.getId(), network);
|
||||
txn.commit();
|
||||
|
||||
boolean success = true;
|
||||
for (NetworkElement element : _networkElements) {
|
||||
try {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Sending network shutdown to " + element);
|
||||
}
|
||||
element.shutdown(network, null);
|
||||
} catch (ResourceUnavailableException e) {
|
||||
s_logger.warn("Unable to complete shutdown of the network due to element: " + element.getName(), e);
|
||||
success = false;
|
||||
} catch (ConcurrentOperationException e) {
|
||||
s_logger.warn("Unable to complete shutdown of the network due to element: " + element.getName(), e);
|
||||
success = false;
|
||||
} catch (Exception e) {
|
||||
s_logger.warn("Unable to complete shutdown of the network due to element: " + element.getName(), e);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
NetworkGuru guru = _networkGurus.get(network.getGuruName());
|
||||
guru.destroy(network, _networkOfferingDao.findById(network.getNetworkOfferingId()));
|
||||
network.setState(Network.State.Allocated);
|
||||
_networksDao.update(network.getId(), network);
|
||||
} else {
|
||||
network.setState(Network.State.Implemented);
|
||||
_networksDao.update(network.getId(), network);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyRules(Ip ip, List<? extends FirewallRule> rules, boolean continueOnError) throws ResourceUnavailableException {
|
||||
if (rules.size() == 0) {
|
||||
@ -1968,4 +2018,39 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public class NetworkGarbageCollector implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<Long> shutdownList = new ArrayList<Long>();
|
||||
long currentTime = System.currentTimeMillis() >> 10;
|
||||
HashMap<Long, Long> stillFree = new HashMap<Long, Long>();
|
||||
|
||||
List<Long> networkIds = _nicDao.listNetworksWithNoActiveNics();
|
||||
for (Long networkId : networkIds) {
|
||||
Long time = _lastNetworkIdsToFree.remove(networkId);
|
||||
if (time == null) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("We found network " + networkId + " to be free for the first time. Adding it to the list: " + currentTime);
|
||||
}
|
||||
stillFree.put(networkId, currentTime);
|
||||
} else if (time < (currentTime + 600)) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Network " + networkId + " is still free but it's not time to shutdown yet: " + time);
|
||||
}
|
||||
stillFree.put(networkId, time);
|
||||
} else {
|
||||
shutdownList.add(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
_lastNetworkIdsToFree = stillFree;
|
||||
|
||||
for (Long networkId : shutdownList) {
|
||||
shutdownNetwork(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,4 +14,6 @@ public interface NicDao extends GenericDao<NicVO, Long> {
|
||||
List<String> listIpAddressInNetworkConfiguration(long networkConfigId);
|
||||
|
||||
List<NicVO> listByNetworkId(long networkId);
|
||||
|
||||
List<Long> listNetworksWithNoActiveNics();
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import com.cloud.utils.db.GenericSearchBuilder;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.SearchCriteria.Func;
|
||||
import com.cloud.utils.db.SearchCriteria.Op;
|
||||
import com.cloud.vm.NicVO;
|
||||
|
||||
@Local(value=NicDao.class)
|
||||
@ -19,23 +20,30 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
|
||||
private final SearchBuilder<NicVO> InstanceSearch;
|
||||
private final GenericSearchBuilder<NicVO, String> IpSearch;
|
||||
private final SearchBuilder<NicVO> NetworkSearch;
|
||||
private final GenericSearchBuilder<NicVO, Long> GarbageCollectSearch;
|
||||
|
||||
protected NicDaoImpl() {
|
||||
super();
|
||||
|
||||
InstanceSearch = createSearchBuilder();
|
||||
InstanceSearch.and("instance", InstanceSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
|
||||
InstanceSearch.and("instance", InstanceSearch.entity().getInstanceId(), Op.EQ);
|
||||
InstanceSearch.done();
|
||||
|
||||
IpSearch = createSearchBuilder(String.class);
|
||||
IpSearch.select(null, Func.DISTINCT, IpSearch.entity().getIp4Address());
|
||||
IpSearch.and("nc", IpSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
|
||||
IpSearch.and("address", IpSearch.entity().getIp4Address(), SearchCriteria.Op.NNULL);
|
||||
IpSearch.and("nc", IpSearch.entity().getNetworkId(), Op.EQ);
|
||||
IpSearch.and("address", IpSearch.entity().getIp4Address(), Op.NNULL);
|
||||
IpSearch.done();
|
||||
|
||||
NetworkSearch = createSearchBuilder();
|
||||
NetworkSearch.and("networkId", NetworkSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
|
||||
NetworkSearch.and("networkId", NetworkSearch.entity().getNetworkId(), Op.EQ);
|
||||
NetworkSearch.done();
|
||||
|
||||
GarbageCollectSearch = createSearchBuilder(Long.class);
|
||||
GarbageCollectSearch.select(null, Func.DISTINCT, GarbageCollectSearch.entity().getNetworkId());
|
||||
GarbageCollectSearch.and("reservation", GarbageCollectSearch.entity().getReservationId(), Op.NULL);
|
||||
GarbageCollectSearch.groupBy(GarbageCollectSearch.entity().getNetworkId()).having(Func.COUNT, GarbageCollectSearch.entity().getId(), Op.EQ, null);
|
||||
GarbageCollectSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -58,4 +66,11 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
|
||||
sc.setParameters("networkId", networkId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listNetworksWithNoActiveNics() {
|
||||
SearchCriteria<Long> sc = GarbageCollectSearch.create();
|
||||
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,4 +221,9 @@ public class Attribute {
|
||||
|
||||
return columnName.equals(that.columnName) && table.equals(that.table);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return table + "." + columnName;
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,7 +331,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> implements Gene
|
||||
}
|
||||
}
|
||||
|
||||
addGroupBy(str, sc);
|
||||
List<Object> groupByValues = addGroupBy(str, sc);
|
||||
addFilter(str, filter);
|
||||
|
||||
final Transaction txn = Transaction.currentTxn();
|
||||
@ -356,6 +356,12 @@ public abstract class GenericDaoBase<T, ID extends Serializable> implements Gene
|
||||
if (joins != null) {
|
||||
i = addJoinAttributes(i, pstmt, joins);
|
||||
}
|
||||
|
||||
if (groupByValues != null) {
|
||||
for (Object value : groupByValues) {
|
||||
pstmt.setObject(i++, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (s_logger.isDebugEnabled() && lock != null) {
|
||||
txn.registerLock(pstmt.toString());
|
||||
@ -392,7 +398,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> implements Gene
|
||||
}
|
||||
}
|
||||
|
||||
addGroupBy(str, sc);
|
||||
List<Object> groupByValues = addGroupBy(str, sc);
|
||||
addFilter(str, filter);
|
||||
|
||||
final String sql = str.toString();
|
||||
@ -412,6 +418,12 @@ public abstract class GenericDaoBase<T, ID extends Serializable> implements Gene
|
||||
i = addJoinAttributes(i, pstmt, joins);
|
||||
}
|
||||
|
||||
if (groupByValues != null) {
|
||||
for (Object value : groupByValues) {
|
||||
pstmt.setObject(i++, value);
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
SelectType st = sc.getSelectType();
|
||||
ArrayList<M> results = new ArrayList<M>();
|
||||
@ -918,17 +930,13 @@ public abstract class GenericDaoBase<T, ID extends Serializable> implements Gene
|
||||
}
|
||||
|
||||
@DB(txn=false)
|
||||
protected void addGroupBy(final StringBuilder sql, SearchCriteria<?> sc) {
|
||||
List<Attribute> groupBys = sc.getGroupBy();
|
||||
if(groupBys != null) {
|
||||
sql.append(" GROUP BY ");
|
||||
for(int i = 0; i < groupBys.size(); i++) {
|
||||
Attribute attr = groupBys.get(i);
|
||||
sql.append(attr.table).append(".").append(attr.columnName);
|
||||
if(i < groupBys.size() - 1) {
|
||||
sql.append(", ");
|
||||
}
|
||||
}
|
||||
protected List<Object> addGroupBy(final StringBuilder sql, SearchCriteria<?> sc) {
|
||||
Pair<GroupBy<?, ?>, List<Object>> groupBys = sc.getGroupBy();
|
||||
if (groupBys != null) {
|
||||
groupBys.first().toSql(sql);
|
||||
return groupBys.second();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
@ -48,7 +49,7 @@ public class GenericSearchBuilder<T, K> implements MethodInterceptor {
|
||||
protected ArrayList<Condition> _conditions;
|
||||
protected HashMap<String, JoinBuilder<GenericSearchBuilder<?, ?>>> _joins;
|
||||
protected ArrayList<Select> _selects;
|
||||
protected ArrayList<Attribute> _groupBys;
|
||||
protected GroupBy<T, K> _groupBy = null;
|
||||
protected Class<T> _entityBeanType;
|
||||
protected Class<K> _resultType;
|
||||
protected SelectType _selectType;
|
||||
@ -210,18 +211,15 @@ public class GenericSearchBuilder<T, K> implements MethodInterceptor {
|
||||
return left(name, useless, op);
|
||||
}
|
||||
|
||||
public GenericSearchBuilder<T, K> groupBy(Object... useless) {
|
||||
if(_groupBys == null) {
|
||||
_groupBys = new ArrayList<Attribute>();
|
||||
}
|
||||
public GroupBy<T, K> groupBy(Object... useless) {
|
||||
assert _groupBy == null : "Can't do more than one group bys";
|
||||
_groupBy = new GroupBy<T, K>(this);
|
||||
|
||||
Attribute[] attrs = _specifiedAttrs.toArray(new Attribute[_specifiedAttrs.size()]);
|
||||
for(Attribute attr : attrs) {
|
||||
_groupBys.add(attr);
|
||||
}
|
||||
|
||||
_specifiedAttrs.clear();
|
||||
return this;
|
||||
return _groupBy;
|
||||
}
|
||||
|
||||
protected List<Attribute> getSpecifiedAttributes() {
|
||||
return _specifiedAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
109
utils/src/com/cloud/utils/db/GroupBy.java
Normal file
109
utils/src/com/cloud/utils/db/GroupBy.java
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the GNU General Public License v3 or later.
|
||||
*
|
||||
* It is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or any later version.
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.cloud.utils.db;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.SearchCriteria.Func;
|
||||
import com.cloud.utils.db.SearchCriteria.Op;
|
||||
|
||||
public class GroupBy<T, R> {
|
||||
GenericSearchBuilder<T, R> _builder;
|
||||
List<Pair<Func, Attribute>> _groupBys;
|
||||
Having _having;
|
||||
|
||||
public GroupBy(GenericSearchBuilder<T, R> builder) {
|
||||
_builder = builder;
|
||||
_groupBys = new ArrayList<Pair<Func, Attribute>>();
|
||||
_having = null;
|
||||
for (Attribute attr : _builder.getSpecifiedAttributes()) {
|
||||
_groupBys.add(new Pair<Func, Attribute>(null, attr));
|
||||
}
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
}
|
||||
|
||||
public GroupBy<T, R> group(Object useless) {
|
||||
_groupBys.add(new Pair<Func, Attribute>(null, _builder.getSpecifiedAttributes().get(0)));
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public GroupBy<T, R> group(Func func, Object useless) {
|
||||
_groupBys.add(new Pair<Func, Attribute>(func, _builder.getSpecifiedAttributes().get(0)));
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public GenericSearchBuilder<T, R> having(Func func, Object obj, Op op, Object value) {
|
||||
assert(_having == null) : "You can only specify one having in a group by";
|
||||
List<Attribute> attrs = _builder.getSpecifiedAttributes();
|
||||
assert attrs.size() == 1 : "You didn't specified an attribute";
|
||||
|
||||
_having = new Having(func, attrs.get(0), op, value);
|
||||
_builder.getSpecifiedAttributes().clear();
|
||||
return _builder;
|
||||
}
|
||||
|
||||
public void toSql(StringBuilder builder) {
|
||||
builder.append(" GROUP BY ");
|
||||
for (Pair<Func, Attribute> groupBy : _groupBys) {
|
||||
if (groupBy.first() != null) {
|
||||
String func = groupBy.first().toString();
|
||||
func.replaceFirst("@", groupBy.second().table + "." + groupBy.second().columnName);
|
||||
builder.append(func);
|
||||
} else {
|
||||
builder.append(groupBy.second().table + "." + groupBy.second().columnName);
|
||||
}
|
||||
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.delete(builder.length() - 2, builder.length());
|
||||
if (_having != null) {
|
||||
_having.toSql(builder);
|
||||
}
|
||||
}
|
||||
|
||||
protected class Having {
|
||||
public Func func;
|
||||
public Attribute attr;
|
||||
public Op op;
|
||||
public Object value;
|
||||
|
||||
public Having(Func func, Attribute attr, Op op, Object value) {
|
||||
this.func = func;
|
||||
this.attr = attr;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void toSql(StringBuilder builder) {
|
||||
if (func != null) {
|
||||
String f = func.toString();
|
||||
f.replaceFirst("@", attr.toString());
|
||||
builder.append(func);
|
||||
} else {
|
||||
builder.append(attr.toString());
|
||||
}
|
||||
|
||||
builder.append(op.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,7 +118,8 @@ public class SearchCriteria<K> {
|
||||
private int _counter;
|
||||
private HashMap<String, JoinBuilder<SearchCriteria<?>>> _joins;
|
||||
private final ArrayList<Select> _selects;
|
||||
private final ArrayList<Attribute> _groupBys;
|
||||
private final GroupBy<?, K> _groupBy;
|
||||
private final List<Object> _groupByValues;
|
||||
private final Class<K> _resultType;
|
||||
private final SelectType _selectType;
|
||||
|
||||
@ -136,7 +137,12 @@ public class SearchCriteria<K> {
|
||||
}
|
||||
}
|
||||
_selects = sb._selects;
|
||||
_groupBys = sb._groupBys;
|
||||
_groupBy = sb._groupBy;
|
||||
if (_groupBy != null) {
|
||||
_groupByValues = new ArrayList<Object>();
|
||||
} else {
|
||||
_groupByValues = null;
|
||||
}
|
||||
_resultType = sb._resultType;
|
||||
_selectType = sb._selectType;
|
||||
}
|
||||
@ -204,12 +210,18 @@ public class SearchCriteria<K> {
|
||||
join.getT().addOr(field, op, values);
|
||||
}
|
||||
|
||||
public SearchCriteria getJoin(String joinName) {
|
||||
public SearchCriteria<?> getJoin(String joinName) {
|
||||
return _joins.get(joinName).getT();
|
||||
}
|
||||
|
||||
public List<Attribute> getGroupBy() {
|
||||
return _groupBys;
|
||||
public Pair<GroupBy<?, ?>, List<Object>> getGroupBy() {
|
||||
return new Pair<GroupBy<?, ?>, List<Object>>(_groupBy, _groupByValues);
|
||||
}
|
||||
|
||||
public void setGroupByValues(Object... values) {
|
||||
for (Object value : values) {
|
||||
_groupByValues.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<K> getResultType() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user